I have a QGraphicsView in a QDockWidget, in which I'm displaying a PNG image. If I create a QPixMapItem in the dockwidget's constructor, and call a dockwidget's member function (a public slot) from within the constructor to display the image, it works fine.
However, if I invoke the slot from my MainWindow (a class called 'IView') via a signal, then the image does not show. What am I doing wrong?
Dockwidget construcor:
IvConfDockWidget::IvConfDockWidget(IView *parent) :
QDockWidget(parent),
ui(new Ui::IvConfDockWidget)
{
ui->setupUi(this);
MyGraphicsView *zoomGraphicsView = new MyGraphicsView();
MyGraphicsScene *zoomScene = new MyGraphicsScene();
ui->zoomLayout->addWidget(zoomGraphicsView);
QPixmap zoomPixmap = QPixmap("/home/mischa/logo.png");
QGraphicsPixmapItem *zoomPixmapItem = new QGraphicsPixmapItem(zoomPixmap);
updateZoomWindowReceived(zoomPixmapItem); // does not work if called from MainWindow
}
Here is the public slot:
void IvConfDockWidget::updateZoomWindowReceived(QGraphicsPixmapItem *zoomPixmapItem)
{
zoomGraphicsView->resetMatrix();
zoomScene->clear();
zoomScene->addItem(zoomPixmapItem);
zoomGraphicsView->setScene(zoomScene);
zoomGraphicsView->show();
}
If I comment out updateZoomWindowReceived() in the constructor, and call it via a signal from my MainWindow, then the graphics does not show. Here is the code in my MainWindow:
IvConfDockWidget *icdw = new IvConfDockWidget;
connect(this, &IView::updateZoomWindow, icdw, &IvConfDockWidget::updateZoomWindowReceived);
QPixmap zoomPixmap = QPixmap("/home/mischa/logo.png");
icdw->zoomGraphicsView->resetMatrix();
QGraphicsPixmapItem *newItem = new QGraphicsPixmapItem(zoomPixmap);
emit updateZoomWindow(newItem);
And for completeness, my derived versions of QGraphicsView and QGraphicsScene:
#define MYGRAPHICSVIEW_H
#include <QGraphicsView>
#include <QDebug>
#include <QMouseEvent>
#include <QScrollBar>
class MyGraphicsView : public QGraphicsView
{
Q_OBJECT
private:
QPointF rightDragStartPos;
QPointF rightDragCurrentPos;
QPointF rightDragEndPos;
QPointF leftDragStartPos;
QPointF leftDragCurrentPos;
QPointF leftDragEndPos; // unused
QPointF middleDragStartPos;
QPointF middleDragCurrentPos;
QPointF middleDragEndPos;
bool rightButtonPressed = false;
bool leftButtonPressed = false;
bool middleButtonPressed = false;
QPoint previousMousePoint;
bool _pan = false;
int _panStartX = 0;
int _panStartY = 0;
public:
explicit MyGraphicsView();
void mouseMoveEvent(QMouseEvent *event);
void mousePressEvent(QMouseEvent *event);
void mouseReleaseEvent(QMouseEvent *event);
QScrollBar *sxbar = nullptr;
QScrollBar *sybar = nullptr;
QString middleMouseMode = "DragMode";
int x = 0;
int y = 0;
signals:
void currentMousePos(QPointF);
void rightDragTravelled(QPointF);
void middleDragTravelled(QPointF pointStart, QPointF pointEnd);
void middleWCSTravelled(QPointF pointStart, QPointF pointEnd);
void middleWCSreleased();
void leftDragTravelled(QPointF pointStart, QPointF pointEnd);
void rightPress();
void leftPress(QPointF pointStart);
void middlePress(QPointF point);
void leftButtonReleased();
void rightButtonReleased();
void middleButtonReleased();
void middlePressResetCRPIX();
public slots:
void updateMiddleMouseMode(QString mode);
};
#endif // MYGRAPHICSVIEW_H
and
#ifndef MYGRAPHICSSCENE_H
#define MYGRAPHICSSCENE_H
#include <QObject>
#include <QGraphicsScene>
#include <QKeyEvent>
class MyGraphicsScene : public QGraphicsScene
{
Q_OBJECT
public:
MyGraphicsScene();
signals:
void itemDeleted();
protected:
void keyReleaseEvent(QKeyEvent * keyEvent);
};
#endif // MYGRAPHICSSCENE_H
I figured it out. My MainWindow uses a hybrid drive/RAM data model which can be in different states, and it happened that in one of the states the signal was never emitted. Once I fixed that, the behavior is as desired.
Sorry for the noise.
Related
I have a child dialog and a parent dialog. I want the child dialog to follow the parent dialog to move;
my current plan is to override moveEvent() in the parent dialog, but the effect is not good: the child dialog moves delay. Is there a better solution?
Update:
platform:macos
atomwindow.h
class atomwindow : public QDialog
{
Q_OBJECT
public:
explicit atomwindow(QDialog *parent = nullptr);
~atomwindow();
protected:
virtual void moveEvent(QMoveEvent *) override;
private:
Ui::atomwindow *ui;
PeriodicTable *table;
bool tableshown = false;
};
atomwindow.cpp
void atomwindow::ShowTable(){
table = new PeriodicTable(this);
table->SetShowPos(windowpos, button_2pos, button_2height, button_2width);
tableshown = true;
table->exec();
}
void atomwindow::moveEvent(QMoveEvent *event){
if(tableshown){
table->MovePos(windowpos, button_2pos, button_2height, button_2width);
}
}
periodictable.h
class PeriodicTable : public QDialog
{
Q_OBJECT
public:
explicit PeriodicTable(QWidget *parent = nullptr);
~PeriodicTable();
void SetShowPos(QPoint, QPoint, int, int);
void MovePos(QPoint, QPoint, int, int);
private:
Ui::PeriodicTable *ui;
};
periodictable.cpp
void PeriodicTable::SetShowPos(QPoint parentpos, QPoint point, int buttonheight, int buttonwidth){
this->setGeometry(curpoint.x(), curpoint.y(), width, height);
}
void PeriodicTable::MovePos(QPoint parentpos, QPoint point, int buttonheight, int buttonwidth){
this->move(curpoint);
}
Dialog.h
#ifndef DIALOG_H
#define DIALOG_H
#include <QDialog>
QT_BEGIN_NAMESPACE
namespace Ui { class Dialog; }
QT_END_NAMESPACE
class SubDialog;
class Dialog : public QDialog
{
Q_OBJECT
public:
Dialog(QWidget *parent = nullptr);
~Dialog();
protected:
void moveEvent(QMoveEvent *event);
signals:
void signalMove( const QPoint &, const QPoint & );
private:
SubDialog *sub;
Ui::Dialog *ui;
};
#endif // DIALOG_H
Dialog.cpp
#include "Dialog.h"
#include "SubDialog.h"
#include <QMoveEvent>
#include "ui_Dialog.h"
Dialog::Dialog(QWidget *parent)
: QDialog(parent)
, ui(new Ui::Dialog)
{
ui->setupUi(this);
sub = new SubDialog;
connect( this, &Dialog::signalMove, sub, &SubDialog::slotMove );
sub->show();
}
Dialog::~Dialog()
{
delete sub;
delete ui;
}
void Dialog::moveEvent(QMoveEvent *event)
{
emit signalMove( event->pos(), event->oldPos() );
}
SubDialog.h
#ifndef SUBDIALOG_H
#define SUBDIALOG_H
#include <QDialog>
namespace Ui {
class SubDialog;
}
class SubDialog : public QDialog
{
Q_OBJECT
public:
explicit SubDialog(QWidget *parent = nullptr);
~SubDialog();
public slots:
void slotMove( const QPoint &, const QPoint & );
private:
Ui::SubDialog *ui;
};
#endif // SUBDIALOG_H
SubDialog.cpp
#include "SubDialog.h"
#include "ui_SubDialog.h"
SubDialog::SubDialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::SubDialog)
{
ui->setupUi(this);
}
SubDialog::~SubDialog()
{
delete ui;
}
void SubDialog::slotMove( const QPoint &newPos, const QPoint &oldPos )
{
auto x = newPos.x() - oldPos.x();
auto y = newPos.y() - oldPos.y();
QPoint pos( this->pos().x() + x, this->pos().y() + y );
move( pos );
}
main.cpp
#include "Dialog.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Dialog w;
w.show();
return a.exec();
}
When i try to put QLabel in QWidget class its not work properly (no hover event or click event only the label pixmap is show) only the last instance work properly, when not use set parent, it create in new window for each label but its work correctly
this gif show the problem:
https://media.giphy.com/media/3o7TKKmZSISGXN4Opq/giphy.gif
this is QLabel subclass header:
#include <QObject>
#include <QLabel>
class myLabel : public QLabel
{
Q_OBJECT
public:
myLabel();
protected:
void mousePressEvent(QMouseEvent *);
void enterEvent(QEvent *);
void leaveEvent(QEvent *);
signals :
void labelClicked();
void enterSignal();
void leaveEventSignal();
private:
};
this class to make a labelButton:
#include <QObject>
#include <QWidget>
#include "mylabel.h"
class labelButton : public QWidget
{
Q_OBJECT
public:
labelButton();
//some functions
private slots:
//slots
private:
//private member
};
and this the class that i want to use the labelButtons in:
#include <QWidget>
#include "labelbutton.h"
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = 0);
~Widget();
private:
Ui::Widget *ui;
labelButton *b_1, *b_2, *b_3;
};
here is widget.cpp:
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
b_1 = new labelButton;
b_1->setParent(this);
b_1->moveButton(70, 100);
//some functions to initialize the labelButton
b_1->show();
//-----------------------
b_2 = new labelButton;
b_2->setParent(this);
b_2->moveButton(70, 200);
//some functions to initialize the labelButton
b_2->show();
//-----------------------
b_3 = new labelButton;
b_3->setParent(this);
b_3->moveButton(70, 300);
//some functions to initialize the labelButton
b_3->show();
}
here its work, the problem was in passing the parent
i made a function that take a widget and set buttons parent from the function value
b_1 = new labelButton;
//b_1->setParent(this);
b_1->setParentFunc(this);
b_1->moveButton(70, 100);
//some functions to initialize the labelButton
// b_1->show();
in labelButton:
void labelButton::setParentFunc(QWidget *p)
{
myParent = p;
}
mLabel_1->setParent(myParent); // myParent instead of this
I want to make a program that shows the info of a QGraphicsItem (width and height) by clicking the option "info" inside a context menu I created with the QGraphicsSceneContextMenuEvent. Right now I´m just trying to call a function with a qDebug in it called info.
Here's my code
dialog.h:
#ifndef DIALOG_H
#define DIALOG_H
#include <QDialog>
#include "QtCore"
#include "QtGui"
#include "mysquare.h"
namespace Ui {
class Dialog;
}
class Dialog : public QDialog
{
Q_OBJECT
public:
explicit Dialog(QWidget *parent = 0);
~Dialog();
private:
Ui::Dialog *ui;
QGraphicsScene *scene;
MySquare *square;
};
#endif // DIALOG_H
dialog.cpp
#include "dialog.h"
#include "ui_dialog.h"
#include <QWidget>
Dialog::Dialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::Dialog)
{
ui->setupUi(this);
scene = new QGraphicsScene(this);
ui->graphicsView->setScene(scene);
square=new MySquare();
scene->addItem(square);
}
Dialog::~Dialog()
{
delete ui;
}
mysquare.h
#ifndef MYSQUARE_H
#define MYSQUARE_H
#include <QPainter>
#include <QGraphicsItem>
#include <QDebug>
#include <QMenu>
class MySquare: public QGraphicsItem
{
public:
MySquare();
QRectF boundingRect() const;
void paint (QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);
QMenu *menu;
QAction *heightAct;
QAction *widthAct;
QAction *color;
private slots:
void info();
protected:
void mousePressEvent(QGraphicsSceneMouseEvent *event);
void mouseReleaseEvent(QGraphicsSceneMouseEvent *event);
void contextMenuEvent(QGraphicsSceneContextMenuEvent *event);
};
#endif // MYSQUARE_H
mysquare.cpp
#include "mysquare.h"
#include<QMenu>
#include <string>
using namespace std;
MySquare::MySquare()
{
}
QRectF MySquare::boundingRect() const
{
return QRectF(100,100,50,50);
}
void MySquare::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
QRectF rec = boundingRect();
QBrush brushy(Qt::green);
painter->fillRect(rec,brushy);
painter->drawRect(rec);
}
void MySquare::info()
{
qDebug()<<"HERE'S MY INFO!!!";
}
void MySquare::contextMenuEvent(QGraphicsSceneContextMenuEvent *event)
{
QString height = "Height: "+QString::number(boundingRect().height());
QString width = "Width: "+QString::number(boundingRect().width());
menu = new QMenu();
heightAct = menu->addAction(height);
widthAct = menu->addAction(width);
color = menu->addAction("Change color");
menu->exec(QCursor::pos());
}
To make a "link" between a click on an action in your menu and your slot info(), you need to use the signal/slot mechanism.
You only have to add a QObject::connect(...) in your QMenu initialization.
void MySquare::contextMenuEvent(QGraphicsSceneContextMenuEvent *event)
{
QString height = "Height: "+QString::number(boundingRect().height());
QString width = "Width: "+QString::number(boundingRect().width());
menu = new QMenu();
heightAct = menu->addAction(height);
widthAct = menu->addAction(width);
color = menu->addAction("Change color");
QObject::connect(heightAct, SIGNAL(triggered()), this, SLOT(info()));
menu->exec(QCursor::pos());
}
However, the above code will not compile because signal/slot mechanism work only with subclasses of QObject and QGraphicsItem isn't. So you have to change it to a QGraphicsObject. (Don't forget the Q_OBJECT macro though)
Also, a little out of subject:
Use
#include <QtCore>
instead of
#include "QtCore"
but I really advise you to just include what you need, not the whole QtCore library.
Also, put your includes in your source files instead of your header files except when it's impossible.
Don't mix standard C++ library such as
#include <string>
except when it's absolutely needed. In this case you have to include
#include <QString>
And just simply remove
using namespace std;
Situation:
I have a Dialog class in Qt on which I draw a raster of squares. The squares are implemented in the MySquare class (MySquare: QGraphicsItem).
Question:
I want to signal the Dialog slot setTarget() that a square was clicked (and obviously I want to give it some information about that square like for example it's x and y coordinates with it later). However I get an 'undefined reference to 'MySquare::targetChanged()' error. I have looked for a solution but have not found any; anybody an idea?
edit: I have added a Q_OBJECT macro inside the MySquare however the error does not dissapear and I get an additional "'undefined reference to 'vtable for MySquare()' error
dialog.h
class Dialog : public QDialog
{
Q_OBJECT
public:
explicit Dialog(QWidget *parent = 0);
~Dialog();
public slots:
void setTarget();
private:
Ui::Dialog *ui;
QGraphicsScene *scene;
};
dialog.cpp
Dialog::Dialog(QWidget *parent) : QDialog(parent), ui(new Ui::Dialog)
{
ui->setupUi(this);
scene = new QGraphicsScene(this);
ui->graphicsView->setScene(scene);
MySquare *item;
item = new MySquare(30,30,30,30);
QObject::connect(item,SIGNAL(targetChanged()),this,SLOT(setTarget()));
}
void Dialog::setTarget(){
qDebug() << "signal recieved" << endl;
}
mysquare.h
#include <QGraphicsItem>
#include <QPainter>
#include <QDebug>
#include <QKeyEvent>
#include <QObject>
class MySquare : public QGraphicsItem, public QObject
{
Q_OBJECT
public:
MySquare(int x,int y,int h, int w);
QRectF boundingRect() const;
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);
int x,y,h,w;
signals:
void targetChanged();
protected:
void mousePressEvent(QGraphicsSceneMouseEvent *event);
void mouseReleaseEvent(QGraphicsSceneMouseEvent *event);
void mouseMoveEvent(QGraphicsSceneMouseEvent *event);
};
mysquare.cpp
#include "mysquare.h"
#include <QGraphicsSceneDragDropEvent>
#include <QWidget>
MySquare::MySquare(int _x,int _y, int _w, int _h)
{
setAcceptDrops(true);
color=Qt::red;
color_pressed = Qt::green;
x = _x;
y = _y;
w = _w;
h = _h;
}
QRectF MySquare::boundingRect() const
{
return QRectF(x,y,w,h);
}
void MySquare::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
QRectF rec = boundingRect();
QBrush brush(color);
if (Pressed){
brush.setColor(color);
} else {
brush.setColor(color_pressed);
}
painter->fillRect(rec,brush);
painter->drawRect(rec);
}
void MySquare::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
Pressed=true;
update();
QGraphicsItem::mousePressEvent(event);
emit targetChanged();
}
void MySquare::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
Pressed=false;
update();
QGraphicsItem::mouseReleaseEvent(event);
qDebug() << "mouse Released";
}
void MySquare::mouseMoveEvent(QGraphicsSceneMouseEvent *event){
qDebug() << "mouse Moved";
QDrag *drag = new QDrag(event->widget());
QMimeData *mime = new QMimeData;
drag->setMimeData(mime);
drag->exec();
}
void MySquare::keyPressEvent(QKeyEvent *event){
//out of bounds check?
int x = pos().x();
int y = pos().y();
QGraphicsItem::keyPressEvent(event);
}
Edit: If you have an undefined reference to a vtable, then it's probably because you did not implement certain virtual functions. Have you implemented the mouse event handlers of the MySquare class somewhere? Have you also implemented the boundingRect() and paint() functions of the MySquare class?
Old answer:
You need to write the Q_OBJECT macro after the opening '{' in the MySquare class. Also Qt will complain about multiple inheritance at some point. Therefore, instead of inheriting from QGraphicsItem and QObject, inherit from QGraphicsObject instead.
The actual reason, the linker complains about a missing function definition is the following: When you put a Q_OBJECT macro into the right place, then the Qt Meta Object Compiler (MOC) will generate a new cpp file for the project which contains the definition of the signal functions of the respective class. So Qt implements a function for every signal for you. If you don't insert the Q_OBJECT macro, then MOC won't do anything for you and the function definition for the signal will be missing to the linker.
Try to do the following steps:
Build-clean
Build-run qmake
Build-build.
Maybe the moc Option is not Set in the Makefile after you added the Q_Object Makro. Therefore you have to refresh your Makefile.
Hope this helps.
I want my QGraphicsPixmapItem become selectable (i.e. clickable in more general way) on QGraphicScene but it doesn't. I'm actually modifying Qt's Diagram Scene sample, where QGraphicsItem's subclass is used and is selectable. I appreciate your help.
cpp code (partial):
#include <iostream>
#include <QtGui>
#include "overlayPixmapItem.h"
OverlayPixmapItem::OverlayPixmapItem(DiagramType diagramType, QMenu *contextMenu,
QPixmap img, QGraphicsItem *parent,
QGraphicsScene *scene)
: QGraphicsPixmapItem(img, parent), QObject()
{
myDiagramType = diagramType;
myContextMenu = contextMenu;
this->setAcceptsHoverEvents(true);
this->setAcceptHoverEvents(true); //
this->setAcceptedMouseButtons(Qt::LeftButton);
this->setAcceptedMouseButtons(Qt::RightButton);
this->acceptDrops();
setFlag(QGraphicsItem::ItemIsMovable, true);
setFlag(QGraphicsItem::ItemIsSelectable, true);
this->setFlag(QGraphicsItem::ItemSendsGeometryChanges, true);
}
:
header (partial):
#ifndef OVERLAYPIXMAPITEM_H
#define OVERLAYPIXMAPITEM_H
#include <QGraphicsPixmapItem>
#include <QList>
#include <QObject>
class QPixmap;
class QGraphicsItem;
class QGraphicsScene;
class QTextEdit;
class QGraphicsSceneMouseEvent;
class QMenu;
class QGraphicsSceneContextMenuEvent;
class QPainter;
class QStyleOptionGraphicsItem;
class QWidget;
class QPolygonF;
class OverlayPixmapItem : public QObject, public QGraphicsPixmapItem
{
Q_OBJECT
public:
enum { Type = UserType + 15 };
enum DiagramType { Crosshair };
OverlayPixmapItem(DiagramType diagramType, QMenu *contextMenu,
QPixmap img, QGraphicsItem *parent = 0, QGraphicsScene *scene = 0);
DiagramType diagramType() const { return myDiagramType; }
QPolygonF polygon() const { return myPolygon; }
int type() const { return Type;}
protected:
void contextMenuEvent(QGraphicsSceneContextMenuEvent *event);
QVariant itemChange(GraphicsItemChange change, const QVariant &value);
void hoverEnterEvent ( QGraphicsSceneHoverEvent * event );
void hoverLeaveEvent ( QGraphicsSceneHoverEvent * event );
public: signals:
void mouseHoveredOnElem(OverlayPixmapItem *i);
void mouseHoveredOutOfElem(OverlayPixmapItem *i);
private:
DiagramType myDiagramType;
QPolygonF myPolygon;
QMenu *myContextMenu;
};
#endif // OVERLAYPIXMAPITEM_H
As pointed out in my first comment, you call setAcceptedMouseButtons() method twice. While the first call actually set the correct button to be accepted, the second call make this setting lost.
To accept both buttons on this item, you have to combine Qt MouseButtons flags, this way :
item->setAcceptedMouseButtons(Qt::LeftButton|Qt::RightButton) ;