QGraphicsItem to react on CTRL left click



  • I have a QGraphicsScene with different items such a rectangles and circles. It must be possible to click on them in different ways, eg a Control left click, and that should trigger a function call with one or more parameters. What is the best way to achieve this?


  • Qt Champions 2016

    hi
    if you override mousePressEvent(QMouseEvent *event)
    you can check for ctrl there

    virtual void mousePressEvent(QMouseEvent *event) {
    if (event->modifiers() == Qt::ControlModifier && event->button() == Qt::LeftButton)
    call whatever
    }
    

    you can do that for
    QGraphicsItem or QGraphicsScene depending on your needs.
    see
    http://www.qtforum.org/article/20343/qgraphicsscenemouseevent-event-has-no-value.html
    for code sample.
    Note the use of scenePos() and not Pos()

    You can use QGraphicsScene::itemAt to find out which items was under mouse.



  • I tried the approach but it seems not working. I made a simple example with a class Circle that inherts from QGraphicsEllipseItem. Clicking on a circle does not trigger the Circle::mousePressEvent(). What am I missing?

    #include <QApplication>
    #include <QMessageBox>
    #include <QtWidgets/QMainWindow>
    #include <QtWidgets/QGraphicsScene>
    #include <QtWidgets/QGraphicsView>
    #include <QtWidgets/QGridLayout>
    #include <QtWidgets/QGraphicsEllipseItem>
    
    class Circle : public QGraphicsEllipseItem {
    public:
    	Circle::Circle(const qreal ax, const qreal ay, const qreal wx, const qreal wy, QGraphicsScene *scene) :
    		QGraphicsEllipseItem(ax, ay, wx, wy)
    	{
    		setPen( QPen(QBrush(Qt::black), 2, Qt::SolidLine, Qt::SquareCap, Qt::MiterJoin) );
    		setBrush( QBrush(Qt::gray) );
    		setRect(ax, ay, wx, wy);
    		setAcceptedMouseButtons(Qt::LeftButton | Qt::RightButton);
    		scene->addItem(this);
    	}
    
    	~Circle(){}
    
    	void Circle::mousePressEvent(QMouseEvent *event) {
    		QMessageBox msgBox;
    		QString QResult{ QString::fromLatin1("Circle clicked") };
    		msgBox.setText( QResult );
    		msgBox.exec();
    	}
    };
    
    
    
    class MainWindow : public QMainWindow
    {
    public:
    	QWidget *baseWidget;
    	QGridLayout *baseWidgetGrid;
    	QGraphicsScene *graphicsScene;
    	QGraphicsView *graphicsView;
    
    public:
    	MainWindow::MainWindow(QWidget *parent=0) :
    		QMainWindow(parent)
    	{
    		graphicsScene = new QGraphicsScene(0, 0, 100, 100);
    		new Circle(  5,  5, 20, 20, graphicsScene );
    		new Circle( 35, 35, 20, 20, graphicsScene );
    		new Circle( 65, 65, 20, 20, graphicsScene );
    
    		baseWidget = new QWidget(this);
    		baseWidgetGrid = new QGridLayout(baseWidget);
    			graphicsView = new QGraphicsView(baseWidget);
    			graphicsView->setScene(graphicsScene);
    		baseWidgetGrid->addWidget(graphicsView, 0, 0, 1, 1);
    
    		setCentralWidget(baseWidget);
    	}
    
    	~MainWindow(){}
    };
    
    
    int main(int argc, char *argv[])
    {
    	QApplication Application(argc, argv);
    	MainWindow MainWin;
    	MainWin.show();
    	return Application.exec();
    }
    
    

  • Qt Champions 2016

    Hmmm, you have listed it under public:
    Try move it to
    protected:
    Also normally its virtual

    so try with

    protected: virtual void Circle::mousePressEvent(QMouseEvent *event) { .. }

    note:
    you can remove the Circle:: when the function is implemented in the .H file.



  • @mrjj said:

    Also normally its virtual

    so try with

    protected: virtual void Circle::mousePressEvent(QMouseEvent *event) { .. }

    From a purely technical point of view, the virtual is not necessary when reimplementing a virtual method, the `virtualness' is inherited from the base class. I therefore don't think this should help.



  • No help, not with protected: nor protected: virtual...


  • Qt Champions 2016

    @JanLaloux
    Ok, Im on mobile so cant check the code.
    Have a look at the 40000 sample (in chip.h)
    It has a clickable item.



  • I noticed in Chip that the event passed is of type QGraphicsSceneMouseEvent * instead of QMouseEvent *. Changed it and now it works, even as public. Thanks for the support!


  • Qt Champions 2016

    @JanLaloux
    Good spotted! :)


  • Moderators

    On a side note: when overriding virtual methods use the override specifier. This way you'll get an instant error message at compile time if you make a typo or mismatch arguments like in this case.



  • OK, now I understand the meaning of the Q_DECL_OVERRIDE macro I used to see in the examples...



  • I have modified my example. The mouse click works but I experience something strange. After something was clicked whatever click anywhere on the scene triggers the same mouse click method of the first clicked object! Any ideas on this?

    #include <string>;
    #include <QtWidgets/QApplication>
    #include <QtWidgets/QMessageBox>
    #include <QtWidgets/QMainWindow>
    #include <QtWidgets/QGraphicsScene>
    #include <QtWidgets/QGraphicsView>
    #include <QtWidgets/QGridLayout>
    #include <QtWidgets/QGraphicsEllipseItem>
    
    using namespace std;
    
    
    class Circle : public QGraphicsEllipseItem {
    public:
    	Circle::Circle(int Id, const qreal ax, const qreal ay, const qreal wx, const qreal wy, QGraphicsScene *scene) :
    		QGraphicsEllipseItem(ax, ay, wx, wy),
    		Id(Id)
    	{
    		setPen( QPen(QBrush(Qt::black), 1, Qt::SolidLine, Qt::SquareCap, Qt::MiterJoin) );
    		setBrush( QBrush(Qt::gray) );
    		setRect(ax, ay, wx, wy);
    		setAcceptedMouseButtons(Qt::LeftButton | Qt::RightButton);
    		scene->addItem(this);
    	}
    
    	~Circle(){}
    
    	void Circle::mousePressEvent(QGraphicsSceneMouseEvent *event) {
    		QMessageBox msgBox;
    		string msg{ "Circle " + to_string(Id) + " clicked" };
    		QString QResult{ QString::fromLatin1(msg.c_str()) };
    		msgBox.setText( QResult );
    		msgBox.exec();
    	}
    	int Id;
    };
    
    
    
    
    class MainWindow : public QMainWindow
    {
    public:
    	QWidget *baseWidget;
    	QGridLayout *baseWidgetGrid;
    	QGraphicsScene *graphicsScene;
    	QGraphicsView *graphicsView;
    
    public:
    	MainWindow::MainWindow(QWidget *parent=0) :
    		QMainWindow(parent)
    	{
    		graphicsScene = new QGraphicsScene(0, 0, 120, 120);
    		graphicsScene->addRect( 10, 10,  100, 100 );
    		new Circle( 1, 20, 20, 20, 20, graphicsScene );
    		new Circle( 2, 75, 75, 20, 20, graphicsScene );
    
    		baseWidget = new QWidget(this);
    		baseWidgetGrid = new QGridLayout(baseWidget);
    			graphicsView = new QGraphicsView(baseWidget);
    			graphicsView->setRenderHint(QPainter::Antialiasing);
    			graphicsView->setScene(graphicsScene);
    		baseWidgetGrid->addWidget(graphicsView, 0, 0, 1, 1);
    
    		setCentralWidget(baseWidget);
    	}
    
    	~MainWindow(){}
    };
    
    
    int main(int argc, char *argv[])
    {
    	QApplication Application(argc, argv);
    	MainWindow MainWin;
    	MainWin.show();
    	return Application.exec();
    }
    

  • Qt Champions 2016

    @JanLaloux said:

    so when clicking outside, it would still say
    "Circle " bla bla from the msgBox ?

    I wonder if scene or view need
    setMouseTracking(true);



  • @mrjj
    Yes, wherever you click on the scene you get the messagebox with the ID of the circle that was first clicked. The code I included is "ready to go" if you want to try it.


  • Qt Champions 2016

    @JanLaloux
    Hi , very strange. it said nothing when I clicked. not on circle or outside.

    update:
    Ahh I see that older post used QGraphicsSceneMouseEvent and not QMouseEvent
    so just copy & paste error I guess.

    I then added

    protected:
        virtual void mousePressEvent(QGraphicsSceneMouseEvent *event) {
        qDebug() << "test";
        }
    

    to Circle and now it print test when click on circle but not outside.

    So not really sure whats going on.

    Does that work for you too?

    #include <QApplication>
    #include <QMessageBox>
    #include <QtWidgets/QMainWindow>
    #include <QtWidgets/QGraphicsScene>
    #include <QtWidgets/QGraphicsView>
    #include <QtWidgets/QGridLayout>
    #include <QtWidgets/QGraphicsEllipseItem>
    #include <qDebug>
    
    class Circle : public QGraphicsEllipseItem {
    public:
        Circle(const qreal ax, const qreal ay, const qreal wx, const qreal wy, QGraphicsScene *scene) :
            QGraphicsEllipseItem(ax, ay, wx, wy)
        {
            setPen( QPen(QBrush(Qt::black), 2, Qt::SolidLine, Qt::SquareCap, Qt::MiterJoin) );
            setBrush( QBrush(Qt::gray) );
            setRect(ax, ay, wx, wy);
            setAcceptedMouseButtons(Qt::LeftButton | Qt::RightButton);
            scene->addItem(this);
        }
    
        ~Circle(){}
    protected:
        virtual void mousePressEvent(QGraphicsSceneMouseEvent *event) {
        qDebug() << "test";
        }
    
       virtual void mousePressEvent(QMouseEvent *event) {
            QMessageBox msgBox;
            QString QResult("DEER");
            msgBox.setText( QResult );
            msgBox.exec();
        }
    };
    
    
    
    class MainWindow : public QMainWindow
    {
    public:
        QWidget *baseWidget;
        QGridLayout *baseWidgetGrid;
        QGraphicsScene *graphicsScene;
        QGraphicsView *graphicsView;
    
    public:
        MainWindow(QWidget *parent=0) :
            QMainWindow(parent)
        {
            graphicsScene = new QGraphicsScene(0, 0, 100, 100);
            new Circle(  5,  5, 20, 20, graphicsScene );
            new Circle( 35, 35, 20, 20, graphicsScene );
            new Circle( 65, 65, 20, 20, graphicsScene );
    
            baseWidget = new QWidget(this);
            baseWidgetGrid = new QGridLayout(baseWidget);
                graphicsView = new QGraphicsView(baseWidget);
                graphicsView->setScene(graphicsScene);
            baseWidgetGrid->addWidget(graphicsView, 0, 0, 1, 1);
    
            setCentralWidget(baseWidget);
        }
    
        ~MainWindow(){}
    };
    
    
    int main(int argc, char *argv[])
    {
        QApplication Application(argc, argv);
        MainWindow MainWin;
        MainWin.show();
        return Application.exec();
    }
    


  • It's the msgBox that is the culprit!
    Replace the qDebug output with the messagebox I used in my code and it goes wrong. The behaviour is as it should without it, I suppose this is a bug...
    Remark: it is not needed to make the member protected
    Anyway, thanks a lot for the help!


  • Qt Champions 2016

    aha!
    Pretty strange.

    ok, its a habit for mousepressed for widgets :)

    np. Good with code you can just run :)


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.