Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. paintEvent called incorrectly when monitor changed
Forum Updated to NodeBB v4.3 + New Features

paintEvent called incorrectly when monitor changed

Scheduled Pinned Locked Moved Solved General and Desktop
12 Posts 4 Posters 1.1k Views
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • A Offline
    A Offline
    Artem Shapovalov
    wrote on last edited by
    #1

    Hi there.
    My program suddenly crash. I implemented a custom dialog widget and it has overriden paintEvent member. This widget has no parent and has next flags:

    • Qt::FramelessWindowHint
    • Qt::WindowStaysOnTopHint
    • Qt::Tool

    These flags allows me to draw this widget in a new window without frame, it doesn't appears in a task panel and in system task monitor.

    My program crashed after next actions:

    • my_widget.show(), all okay, widget shows normally
    • my_widget.hide(), all okay, program alive
    • drag program's window to another monitor, all okay, program still alive
    • my_widget.show()

    After that program put these strings in output:

    QPainter::begin: A paint device can only be painted by one painter at a time.
    QPainter::setCompositionMode: Painter not active
    QWidget::repaint: Recursive repaint detected
    QBackingStore::endPaint() called with active painter; did you forget to destroy it or call QPainter::end() on it?
    

    I tried to find out what happens and place many qDebug() << "0"; in paint event handler's code and find out that before program crash this handler called twice. Further more, when paintEvent called second time, the first call isn't complete.

    Guys, what do you think about it? Looks like I forgot something necessary to handle second monitor.

    Christian EhrlicherC 1 Reply Last reply
    0
    • A Artem Shapovalov

      Hi there.
      My program suddenly crash. I implemented a custom dialog widget and it has overriden paintEvent member. This widget has no parent and has next flags:

      • Qt::FramelessWindowHint
      • Qt::WindowStaysOnTopHint
      • Qt::Tool

      These flags allows me to draw this widget in a new window without frame, it doesn't appears in a task panel and in system task monitor.

      My program crashed after next actions:

      • my_widget.show(), all okay, widget shows normally
      • my_widget.hide(), all okay, program alive
      • drag program's window to another monitor, all okay, program still alive
      • my_widget.show()

      After that program put these strings in output:

      QPainter::begin: A paint device can only be painted by one painter at a time.
      QPainter::setCompositionMode: Painter not active
      QWidget::repaint: Recursive repaint detected
      QBackingStore::endPaint() called with active painter; did you forget to destroy it or call QPainter::end() on it?
      

      I tried to find out what happens and place many qDebug() << "0"; in paint event handler's code and find out that before program crash this handler called twice. Further more, when paintEvent called second time, the first call isn't complete.

      Guys, what do you think about it? Looks like I forgot something necessary to handle second monitor.

      Christian EhrlicherC Offline
      Christian EhrlicherC Offline
      Christian Ehrlicher
      Lifetime Qt Champion
      wrote on last edited by
      #2

      @Artem-Shapovalov said in paintEvent called incorrectly when monitor changed:

      Guys, what do you think about it?

      Without code and the Qt version you're using?

      Minimize your program until it no longer crashes and post it here then.

      Qt Online Installer direct download: https://download.qt.io/official_releases/online_installers/
      Visit the Qt Academy at https://academy.qt.io/catalog

      A 1 Reply Last reply
      1
      • Christian EhrlicherC Christian Ehrlicher

        @Artem-Shapovalov said in paintEvent called incorrectly when monitor changed:

        Guys, what do you think about it?

        Without code and the Qt version you're using?

        Minimize your program until it no longer crashes and post it here then.

        A Offline
        A Offline
        Artem Shapovalov
        wrote on last edited by
        #3

        @Christian-Ehrlicher
        I use Qt 6.5 on Windows 11. I found what caused problem. Minimum code to reproduce it:

        CrashWidget.h:

        #ifndef CRASHWIDGET_H
        #define CRASHWIDGET_H
        
        #include <QWidget>
        #include <QPainter>
        
        /** \brief example widget that covers selected area with new empty window */
        class CrashWidget : public QWidget
        {
            Q_OBJECT
        public slots:
            /** \brief makes window visible
             *  \param global coordinates of area that should be covered */
            void invoke(QRect globalButtonCoordinates)
            {
                // setGeometry(globalButtonCoordinates); // <- no fault, no crash
                global = globalButtonCoordinates;
                show();
            }
        
        public:
            /** \brief constructor
             *  \details set up the widget window properties and hides it
             *  \param parent pointer to the parent widget, may be null */
            explicit CrashWidget(QWidget *parent = nullptr) : QWidget(parent)
            {
                setWindowFlags(Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint
                               | Qt::Tool);
                hide();
            }
        
            /** \brief paint event handler
             *  \details called by Qt on it's own, set up geometry and fill itself by
             *           green color
             *  \param ev pointer to the event data */
            virtual void paintEvent(QPaintEvent *ev) override
            {
                // setGeometry(global); // <- fault, but no crash
                QPainter paint(this);
                setGeometry(global); // <- fault and crash
        
                paint.setPen(Qt::NoPen);
                paint.setBrush(Qt::green);
                paint.drawRect(rect());
            }
        
            /** \brief handler of the mouse leaving widget's area event
             *  \details called by Qt on it's own
             *  \param ev pointer to the event data */
            virtual void leaveEvent(QEvent *ev) override { hide(); }
        
        private:
            /** \brief global coordinates of the widget
             *  \details updates by invoke() and used in paintEvent() to change
             *           area of widget */
            QRect global;
        };
        
        #endif // CRASHWIDGET_H
        

        Somewhere in mainwindow.cpp:

        MainWindow::MainWindow(QWidget *parent)
            : QMainWindow(parent), ui(new Ui::MainWindow)
        {
        ...
            CrashWidget *crash = new CrashWidget(ui->pushButton);
            QObject::connect(
                    ui->pushButton, &QPushButton::clicked, this, [this, crash]() {
                        QPoint topLeft(ui->pushButton->x(), ui->pushButton->y());
                        QPoint bottomRight(
                                ui->pushButton->x() + ui->pushButton->width(),
                                ui->pushButton->y() + ui->pushButton->height());
                        QRect global = QRect(QWidget::mapToGlobal(topLeft),
                                             QWidget::mapToGlobal(bottomRight));
                        crash->invoke(global);
                    });
        ...
        }
        

        Explaination:
        CrashWidget invokes a new window without frame and fill it with green color. Seems to be that setGeometry() should not call in event handlers, at least at the paintEvent(), because it produce new events. Online documentation warns against call it inside resizeEvent() and moveEvent() only, but as you see this list should be extended.

        Thanks for pay attention to this problem. I hope it helps to somebody, I googled it for a day and a half with absolutely no result before start to write minimum example and try to crash it.

        jsulmJ JonBJ Christian EhrlicherC 3 Replies Last reply
        0
        • A Artem Shapovalov

          @Christian-Ehrlicher
          I use Qt 6.5 on Windows 11. I found what caused problem. Minimum code to reproduce it:

          CrashWidget.h:

          #ifndef CRASHWIDGET_H
          #define CRASHWIDGET_H
          
          #include <QWidget>
          #include <QPainter>
          
          /** \brief example widget that covers selected area with new empty window */
          class CrashWidget : public QWidget
          {
              Q_OBJECT
          public slots:
              /** \brief makes window visible
               *  \param global coordinates of area that should be covered */
              void invoke(QRect globalButtonCoordinates)
              {
                  // setGeometry(globalButtonCoordinates); // <- no fault, no crash
                  global = globalButtonCoordinates;
                  show();
              }
          
          public:
              /** \brief constructor
               *  \details set up the widget window properties and hides it
               *  \param parent pointer to the parent widget, may be null */
              explicit CrashWidget(QWidget *parent = nullptr) : QWidget(parent)
              {
                  setWindowFlags(Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint
                                 | Qt::Tool);
                  hide();
              }
          
              /** \brief paint event handler
               *  \details called by Qt on it's own, set up geometry and fill itself by
               *           green color
               *  \param ev pointer to the event data */
              virtual void paintEvent(QPaintEvent *ev) override
              {
                  // setGeometry(global); // <- fault, but no crash
                  QPainter paint(this);
                  setGeometry(global); // <- fault and crash
          
                  paint.setPen(Qt::NoPen);
                  paint.setBrush(Qt::green);
                  paint.drawRect(rect());
              }
          
              /** \brief handler of the mouse leaving widget's area event
               *  \details called by Qt on it's own
               *  \param ev pointer to the event data */
              virtual void leaveEvent(QEvent *ev) override { hide(); }
          
          private:
              /** \brief global coordinates of the widget
               *  \details updates by invoke() and used in paintEvent() to change
               *           area of widget */
              QRect global;
          };
          
          #endif // CRASHWIDGET_H
          

          Somewhere in mainwindow.cpp:

          MainWindow::MainWindow(QWidget *parent)
              : QMainWindow(parent), ui(new Ui::MainWindow)
          {
          ...
              CrashWidget *crash = new CrashWidget(ui->pushButton);
              QObject::connect(
                      ui->pushButton, &QPushButton::clicked, this, [this, crash]() {
                          QPoint topLeft(ui->pushButton->x(), ui->pushButton->y());
                          QPoint bottomRight(
                                  ui->pushButton->x() + ui->pushButton->width(),
                                  ui->pushButton->y() + ui->pushButton->height());
                          QRect global = QRect(QWidget::mapToGlobal(topLeft),
                                               QWidget::mapToGlobal(bottomRight));
                          crash->invoke(global);
                      });
          ...
          }
          

          Explaination:
          CrashWidget invokes a new window without frame and fill it with green color. Seems to be that setGeometry() should not call in event handlers, at least at the paintEvent(), because it produce new events. Online documentation warns against call it inside resizeEvent() and moveEvent() only, but as you see this list should be extended.

          Thanks for pay attention to this problem. I hope it helps to somebody, I googled it for a day and a half with absolutely no result before start to write minimum example and try to crash it.

          jsulmJ Offline
          jsulmJ Offline
          jsulm
          Lifetime Qt Champion
          wrote on last edited by
          #4

          @Artem-Shapovalov said in paintEvent called incorrectly when monitor changed:

          new CrashWidget(ui->pushButton);

          Why are you passing ui->pushButton as parent? If you want to show CrashWidget as a window it should not have any parent.

          https://forum.qt.io/topic/113070/qt-code-of-conduct

          A 1 Reply Last reply
          0
          • A Artem Shapovalov

            @Christian-Ehrlicher
            I use Qt 6.5 on Windows 11. I found what caused problem. Minimum code to reproduce it:

            CrashWidget.h:

            #ifndef CRASHWIDGET_H
            #define CRASHWIDGET_H
            
            #include <QWidget>
            #include <QPainter>
            
            /** \brief example widget that covers selected area with new empty window */
            class CrashWidget : public QWidget
            {
                Q_OBJECT
            public slots:
                /** \brief makes window visible
                 *  \param global coordinates of area that should be covered */
                void invoke(QRect globalButtonCoordinates)
                {
                    // setGeometry(globalButtonCoordinates); // <- no fault, no crash
                    global = globalButtonCoordinates;
                    show();
                }
            
            public:
                /** \brief constructor
                 *  \details set up the widget window properties and hides it
                 *  \param parent pointer to the parent widget, may be null */
                explicit CrashWidget(QWidget *parent = nullptr) : QWidget(parent)
                {
                    setWindowFlags(Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint
                                   | Qt::Tool);
                    hide();
                }
            
                /** \brief paint event handler
                 *  \details called by Qt on it's own, set up geometry and fill itself by
                 *           green color
                 *  \param ev pointer to the event data */
                virtual void paintEvent(QPaintEvent *ev) override
                {
                    // setGeometry(global); // <- fault, but no crash
                    QPainter paint(this);
                    setGeometry(global); // <- fault and crash
            
                    paint.setPen(Qt::NoPen);
                    paint.setBrush(Qt::green);
                    paint.drawRect(rect());
                }
            
                /** \brief handler of the mouse leaving widget's area event
                 *  \details called by Qt on it's own
                 *  \param ev pointer to the event data */
                virtual void leaveEvent(QEvent *ev) override { hide(); }
            
            private:
                /** \brief global coordinates of the widget
                 *  \details updates by invoke() and used in paintEvent() to change
                 *           area of widget */
                QRect global;
            };
            
            #endif // CRASHWIDGET_H
            

            Somewhere in mainwindow.cpp:

            MainWindow::MainWindow(QWidget *parent)
                : QMainWindow(parent), ui(new Ui::MainWindow)
            {
            ...
                CrashWidget *crash = new CrashWidget(ui->pushButton);
                QObject::connect(
                        ui->pushButton, &QPushButton::clicked, this, [this, crash]() {
                            QPoint topLeft(ui->pushButton->x(), ui->pushButton->y());
                            QPoint bottomRight(
                                    ui->pushButton->x() + ui->pushButton->width(),
                                    ui->pushButton->y() + ui->pushButton->height());
                            QRect global = QRect(QWidget::mapToGlobal(topLeft),
                                                 QWidget::mapToGlobal(bottomRight));
                            crash->invoke(global);
                        });
            ...
            }
            

            Explaination:
            CrashWidget invokes a new window without frame and fill it with green color. Seems to be that setGeometry() should not call in event handlers, at least at the paintEvent(), because it produce new events. Online documentation warns against call it inside resizeEvent() and moveEvent() only, but as you see this list should be extended.

            Thanks for pay attention to this problem. I hope it helps to somebody, I googled it for a day and a half with absolutely no result before start to write minimum example and try to crash it.

            JonBJ Offline
            JonBJ Offline
            JonB
            wrote on last edited by
            #5

            @Artem-Shapovalov said in paintEvent called incorrectly when monitor changed:

            Online documentation warns against call it inside resizeEvent() and moveEvent() only, but as you see this list should be extended.

            I guess there are lots of functions which should not be called inside paintEvent(), basically anything which causes a re-paint....

            1 Reply Last reply
            0
            • jsulmJ jsulm

              @Artem-Shapovalov said in paintEvent called incorrectly when monitor changed:

              new CrashWidget(ui->pushButton);

              Why are you passing ui->pushButton as parent? If you want to show CrashWidget as a window it should not have any parent.

              A Offline
              A Offline
              Artem Shapovalov
              wrote on last edited by
              #6

              @jsulm but why? There are many cases when window should have parent. For example, popup-window or hint should hide when the main window hides, it should change it's position when the main window resizes. I thought that is a common practice. What's the correct way to notify popup-window about actions of parent?

              jsulmJ 1 Reply Last reply
              0
              • A Artem Shapovalov

                @jsulm but why? There are many cases when window should have parent. For example, popup-window or hint should hide when the main window hides, it should change it's position when the main window resizes. I thought that is a common practice. What's the correct way to notify popup-window about actions of parent?

                jsulmJ Offline
                jsulmJ Offline
                jsulm
                Lifetime Qt Champion
                wrote on last edited by
                #7

                @Artem-Shapovalov You wrote "CrashWidget invokes a new window without frame" that's why I asked why you're passing a parent.
                But there is another strange thing: why do you use a button as parent?! A widget with a prent widget will be put inside its parent - do you really want CrashWidget to be inside a button?

                https://forum.qt.io/topic/113070/qt-code-of-conduct

                A 1 Reply Last reply
                0
                • A Offline
                  A Offline
                  Artem Shapovalov
                  wrote on last edited by
                  #8

                  @JonB online docs says:

                  Note: Generally, you should refrain from calling update() or repaint() inside a paintEvent(). For example, calling update() or repaint() on children inside a paintEvent() results in undefined behavior; the child may or may not get a paint event.

                  I didn't knew that setGeometry() call repaint() or update(), I thought it change rectangle only and produce resize or move events. But you're right, produce any event inside event handler is bad idea.

                  1 Reply Last reply
                  0
                  • jsulmJ jsulm

                    @Artem-Shapovalov You wrote "CrashWidget invokes a new window without frame" that's why I asked why you're passing a parent.
                    But there is another strange thing: why do you use a button as parent?! A widget with a prent widget will be put inside its parent - do you really want CrashWidget to be inside a button?

                    A Offline
                    A Offline
                    Artem Shapovalov
                    wrote on last edited by
                    #9

                    @jsulm yes, I need a new popup-window. I checked it. Invoke method:

                        /** \brief makes window visible
                         *  \param global coordinates of area that should be covered */
                        void invoke(QRect global)
                        {
                            setGeometry(global.x(), global.y(), global.width(),
                                        global.height() + 100);
                            show();
                        }
                    

                    The green rectangle is really taller than button. Did I found some undocumented and undefined behavior? :)

                    jsulmJ 1 Reply Last reply
                    0
                    • A Artem Shapovalov

                      @jsulm yes, I need a new popup-window. I checked it. Invoke method:

                          /** \brief makes window visible
                           *  \param global coordinates of area that should be covered */
                          void invoke(QRect global)
                          {
                              setGeometry(global.x(), global.y(), global.width(),
                                          global.height() + 100);
                              show();
                          }
                      

                      The green rectangle is really taller than button. Did I found some undocumented and undefined behavior? :)

                      jsulmJ Offline
                      jsulmJ Offline
                      jsulm
                      Lifetime Qt Champion
                      wrote on last edited by
                      #10

                      @Artem-Shapovalov said in paintEvent called incorrectly when monitor changed:

                      I need a new popup-window

                      A window can't be inside another widget. Are you sure you're using correct wording?

                      https://forum.qt.io/topic/113070/qt-code-of-conduct

                      A 1 Reply Last reply
                      0
                      • jsulmJ jsulm

                        @Artem-Shapovalov said in paintEvent called incorrectly when monitor changed:

                        I need a new popup-window

                        A window can't be inside another widget. Are you sure you're using correct wording?

                        A Offline
                        A Offline
                        Artem Shapovalov
                        wrote on last edited by
                        #11

                        @jsulm yeah, maybe I used wrong word. I'm sorry, I'm not a native English speaker and also I'm a newbie in Qt. Actually, in GUI applications too.

                        I tried to implement a custom popup dialog and use the frameless window for it. In this example green rectangle is a frameless window. And it works as I expected, although parent is passed to constructor and applied.

                            /** \brief constructor
                             *  \details set up the widget window properties and hides it
                             *  \param parent pointer to the parent widget, may be null */
                            explicit CrashWidget(QWidget *parent = nullptr) : QWidget(parent)
                            {
                                setWindowFlags(Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint
                                               | Qt::Tool);
                                hide();
                            }
                        

                        I think it works as needed because setWindowFlags(Qt::Tool). This flag can be represented as bitwise OR: Qt::Dialog | Qt::Popup. Both of these flags contains in their mask Qt::Window flag that Qt handles and draws a window. Maybe I wrong, but drawing widget in a new window if parent is not specified is a side effect, and seems to be that Qt just produces new window and place widget inside it in this case.

                        1 Reply Last reply
                        0
                        • A Artem Shapovalov

                          @Christian-Ehrlicher
                          I use Qt 6.5 on Windows 11. I found what caused problem. Minimum code to reproduce it:

                          CrashWidget.h:

                          #ifndef CRASHWIDGET_H
                          #define CRASHWIDGET_H
                          
                          #include <QWidget>
                          #include <QPainter>
                          
                          /** \brief example widget that covers selected area with new empty window */
                          class CrashWidget : public QWidget
                          {
                              Q_OBJECT
                          public slots:
                              /** \brief makes window visible
                               *  \param global coordinates of area that should be covered */
                              void invoke(QRect globalButtonCoordinates)
                              {
                                  // setGeometry(globalButtonCoordinates); // <- no fault, no crash
                                  global = globalButtonCoordinates;
                                  show();
                              }
                          
                          public:
                              /** \brief constructor
                               *  \details set up the widget window properties and hides it
                               *  \param parent pointer to the parent widget, may be null */
                              explicit CrashWidget(QWidget *parent = nullptr) : QWidget(parent)
                              {
                                  setWindowFlags(Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint
                                                 | Qt::Tool);
                                  hide();
                              }
                          
                              /** \brief paint event handler
                               *  \details called by Qt on it's own, set up geometry and fill itself by
                               *           green color
                               *  \param ev pointer to the event data */
                              virtual void paintEvent(QPaintEvent *ev) override
                              {
                                  // setGeometry(global); // <- fault, but no crash
                                  QPainter paint(this);
                                  setGeometry(global); // <- fault and crash
                          
                                  paint.setPen(Qt::NoPen);
                                  paint.setBrush(Qt::green);
                                  paint.drawRect(rect());
                              }
                          
                              /** \brief handler of the mouse leaving widget's area event
                               *  \details called by Qt on it's own
                               *  \param ev pointer to the event data */
                              virtual void leaveEvent(QEvent *ev) override { hide(); }
                          
                          private:
                              /** \brief global coordinates of the widget
                               *  \details updates by invoke() and used in paintEvent() to change
                               *           area of widget */
                              QRect global;
                          };
                          
                          #endif // CRASHWIDGET_H
                          

                          Somewhere in mainwindow.cpp:

                          MainWindow::MainWindow(QWidget *parent)
                              : QMainWindow(parent), ui(new Ui::MainWindow)
                          {
                          ...
                              CrashWidget *crash = new CrashWidget(ui->pushButton);
                              QObject::connect(
                                      ui->pushButton, &QPushButton::clicked, this, [this, crash]() {
                                          QPoint topLeft(ui->pushButton->x(), ui->pushButton->y());
                                          QPoint bottomRight(
                                                  ui->pushButton->x() + ui->pushButton->width(),
                                                  ui->pushButton->y() + ui->pushButton->height());
                                          QRect global = QRect(QWidget::mapToGlobal(topLeft),
                                                               QWidget::mapToGlobal(bottomRight));
                                          crash->invoke(global);
                                      });
                          ...
                          }
                          

                          Explaination:
                          CrashWidget invokes a new window without frame and fill it with green color. Seems to be that setGeometry() should not call in event handlers, at least at the paintEvent(), because it produce new events. Online documentation warns against call it inside resizeEvent() and moveEvent() only, but as you see this list should be extended.

                          Thanks for pay attention to this problem. I hope it helps to somebody, I googled it for a day and a half with absolutely no result before start to write minimum example and try to crash it.

                          Christian EhrlicherC Offline
                          Christian EhrlicherC Offline
                          Christian Ehrlicher
                          Lifetime Qt Champion
                          wrote on last edited by
                          #12

                          @Artem-Shapovalov said in paintEvent called incorrectly when monitor changed:

                          setGeometry(global); // <- fault and crash

                          You must not call setGeometry inside a paint event as this may trigger a repaint and therefore an infinite loop.

                          Qt Online Installer direct download: https://download.qt.io/official_releases/online_installers/
                          Visit the Qt Academy at https://academy.qt.io/catalog

                          1 Reply Last reply
                          3
                          • A Artem Shapovalov has marked this topic as solved on

                          • Login

                          • Login or register to search.
                          • First post
                            Last post
                          0
                          • Categories
                          • Recent
                          • Tags
                          • Popular
                          • Users
                          • Groups
                          • Search
                          • Get Qt Extensions
                          • Unsolved