Showing a QMessageBox or QColorDialog disrupts mouse clicks in a QGraphicsScene



  • I have a QGraphicsScene with circles for which the mousePressEvent method is defined. If in handling the mouse event for circle x a QMessageBox is shown, from then on all clicks wherever in the scene will trigger the mouse event for circle x again.
    In the code below if the messagebox is removed, the mouse clicks are performing normal.

    Is there a way to avoid this behaviour?

    #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

    Hi
    I found this very strange so I played around with it.

    The call msgBox.exec(); creates a new message loop which apparently completely confuses
    QGraphicsScene / view.

    I also tried with QDialog and got same result.

    Its related to this
    https://blog.qt.io/blog/2010/02/23/unpredictable-exec/
    (i think)

    using msgBox.open makes it close at once so found no workaround.


  • Lifetime Qt Champion

    Hi,

    To add to @mrjj, using open with a heap allocated msgBox where theQt::WA_DeleteOnClose flag is set is the way to go.


  • Qt Champions 2016

    @SGaist said:
    Doh. I cant believe I made the run out of scope error.
    Of Course it will close at once when on stack...
    Thank you.

    Anyways,

    using this

           QMessageBox *msgBox=new QMessageBox() ;
            msgBox->setText( "QResult" );
            msgBox->setAttribute(Qt::WA_DeleteOnClose);
            msgBox->open();
    

    Does not mess with the actually clicking but you can open more than one.


  • Lifetime Qt Champion

    IIRC, giving it a parent at creation time should take care of that.


  • Qt Champions 2016

    @SGaist
    It does. so 100% working.

    So change the constructor to include pointer to parent
    Circle (QWidget *Parent, .....

    and store that in variable and use that in
    QMessageBox *msgBox=new QMessageBox(Parent) ;

    And you should be happy.


  • Lifetime Qt Champion

    I was thinking of something like QMessageBox *msgBox=new QMessageBox(this);


  • Qt Champions 2016

    @SGaist
    But its not a QObject ?
    no matching function for call to 'QMessageBox::QMessageBox(Circle*)'

    so a cast will work?
    ^


  • Lifetime Qt Champion

    My bad, I've misread, in that case I'd rather use:
    QMessageBox *msgBox=new QMessageBox(scene());

    But no, casting will not help at all, since QGraphicsItem is not a QWidget


  • Qt Champions 2016

    @SGaist
    Still it is not really happy
    no matching function for call to 'QMessageBox::QMessageBox(QGraphicsScene*)'
    But it is a QObject it seems.. ?


  • Lifetime Qt Champion

    Damn, I've mixed scene and view...


  • Qt Champions 2016

    aha!
    so
    QMessageBox *msgBox = new QMessageBox ( scene()->views()[0] ) ;
    works even if ugly :)

    should be
    QGraphicsView *Par=scene()->views()[0];
    if (!Par) return;
    QMessageBox *msgBox = new QMessageBox ( Par) ;

    for minimum safety.


  • Lifetime Qt Champion

    Well for extra safety you should loop on the views to find one that currently shows the item clicked unless you know that you have exactly one view on the scene.



  • Thanks guys, this effectively works



  • I have to reopen this topic. I now want to open a QColorDialog when an item is clicked. I first used the static funtion QColorDialog::getColor() but experienced the same problem as with the QMessageBox.
    A bit wiser now I modified it to mimic the solution for QMessageBox. Below is the "ready to go" code.

    A problem is that I cannot set the Qt::WA_DeleteOnClose attribute on the dialog because I have to retrieve the selected color with selectedColor(). So I delete the dialog explictely after that. But no help, a second click where ever in the scene triggers a click on the first object. Any ideas?

    #include <string>
    #include <QtWidgets/QApplication>
    #include <QtWidgets/QColorDialog>
    #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, QWidget *parent) :
            QGraphicsEllipseItem(ax, ay, wx, wy),
            Id(Id),
    		Parent(parent)
        {
            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) {
    		QColorDialog* ColorDialog{ new QColorDialog( this->brush().color(), Parent ) };
    		//ColorDialog->setAttribute(Qt::WA_DeleteOnClose);
    		ColorDialog->exec();
    		QColor ColourPick{ ColorDialog->selectedColor() };
    		if( ColourPick.isValid() )
    			setBrush( ColourPick );
      		delete ColorDialog;
      }
        int Id;
    	QWidget *Parent;
    };
    
    
    
    class MainWindowC : public QMainWindow
    {
    public:
        QWidget *baseWidget;
        QGridLayout *baseWidgetGrid;
        QGraphicsScene *graphicsScene;
        QGraphicsView *graphicsView;
    
    public:
        MainWindowC::MainWindowC(QWidget *parent=0) :
            QMainWindow(parent)
        {
            baseWidget = new QWidget(this);
            baseWidgetGrid = new QGridLayout(baseWidget);
    			graphicsScene = new QGraphicsScene(0, 0, 120, 120);
    			graphicsScene->addRect( 10, 10,  100, 100 );
    			new Circle( 1, 20, 20, 20, 20, graphicsScene, baseWidget );
    			new Circle( 2, 75, 75, 20, 20, graphicsScene, baseWidget );
    
    			graphicsView = new QGraphicsView(baseWidget);
                graphicsView->setRenderHint(QPainter::Antialiasing);
                graphicsView->setScene(graphicsScene);
            baseWidgetGrid->addWidget(graphicsView, 0, 0, 1, 1);
    
            setCentralWidget(baseWidget);
        }
    
        ~MainWindowC(){}
    };
    
    
    int main(int argc, char *argv[])
    {
        QApplication ApplicationC(argc, argv);
        MainWindowC MainWinC;
        MainWinC.show();
        return ApplicationC.exec();
    }
    

  • Qt Champions 2016

    Hi , its the
    ColorDialog->exec(); (makes own event loop)

    We found out that using open() helped.

    Try to see if open() works for ColorDialog ?

    Also read docs on "open." you can hook it up to a slot in your code when user
    select color and click ok . That way it can work.



  • No help, same behaviour. This is the modified code:

    #include <string>
    #include <QtWidgets/QApplication>
    #include <QtWidgets/QColorDialog>
    #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, QWidget *parent) :
            QGraphicsEllipseItem(ax, ay, wx, wy),
            Id(Id),
            Parent(parent)
        {
            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);
    
        int Id;
        QWidget *Parent;
    };
    
    
    
    class MainWin : public QMainWindow
    {
    public:
        QWidget *baseWidget;
        QGridLayout *baseWidgetGrid;
        QGraphicsScene *graphicsScene;
        QGraphicsView *graphicsView;
    
        MainWin::MainWin(QWidget *parent=0) :
            QMainWindow(parent)
        {
            baseWidget = new QWidget(this);
            baseWidgetGrid = new QGridLayout(baseWidget);
                graphicsScene = new QGraphicsScene(0, 0, 120, 120);
                graphicsScene->addRect( 10, 10,  100, 100 );
                new Circle( 1, 20, 20, 20, 20, graphicsScene, baseWidget );
                new Circle( 2, 75, 75, 20, 20, graphicsScene, baseWidget );
    
                graphicsView = new QGraphicsView(baseWidget);
                graphicsView->setRenderHint(QPainter::Antialiasing);
                graphicsView->setScene(graphicsScene);
            baseWidgetGrid->addWidget(graphicsView, 0, 0, 1, 1);
    
            setCentralWidget(baseWidget);
        }
    
        ~MainWin(){}
    
    	void colorSelected(const QColor & color);
    };
    
    Circle* CircleClicked;
    QColorDialog* ColorDialog;
    MainWin* pMainWindow;
    
    void MainWin::colorSelected(const QColor & color){
    	QColor ColorPick{ color };
    
    	CircleClicked->setBrush( QBrush( ColorPick ) );
    	CircleClicked->update();
    
    	ColorDialog->deleteLater();
    }
    
    void Circle::mousePressEvent(QGraphicsSceneMouseEvent *event) {
    	CircleClicked = this;
    
    	ColorDialog = new QColorDialog( this->brush().color(), pMainWindow->baseWidget );
    	pMainWindow->connect( ColorDialog, &QColorDialog::colorSelected, pMainWindow, &MainWin::colorSelected );
    	ColorDialog->open();
    }
    
    
    int main(int argc, char *argv[])
    {
        QApplication Application(argc, argv);
        MainWin MainWindow;
    	pMainWindow = &MainWindow;
        MainWindow.show();
        return Application.exec();
    }
    

  • Qt Champions 2016

    @JanLaloux said:

    Well the void QDialog::open() seems not the same as
    QMessageBox::open
    so sadly it still creates a message loop it seems
    so it wont work.

    That what I mean by
    "Try to see if open() works for ColorDialog ?"
    Did have doc so could not check.

    So unless ->show() do not create a messageloop , I dont know how you
    can make it work.


Log in to reply
 

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