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. Closing a QDockWidget While the User is Actively Resizing It
QtWS25 Last Chance

Closing a QDockWidget While the User is Actively Resizing It

Scheduled Pinned Locked Moved Solved General and Desktop
8 Posts 4 Posters 495 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.
  • N Offline
    N Offline
    NickJT
    wrote on last edited by
    #1

    Hey,

    I found that when a QDockWidget is closed while the users is actively dragging a separator it will trigger a segmentation fault. This is on Qt 5.9.6, I haven't tested on other versions.

    I was able to put together a minimum reproducible example:

    TestDockWidgetResizeWindow::TestDockWidgetResizeWindow(QWidget *parent)
        : QMainWindow(parent)
    {
        i = 10;
    
        QLabel * center = new QLabel("Central Widget", this);
        setCentralWidget(center);
    
        QDockWidget * dock = new QDockWidget(this);
        QLabel * dockLabel = new QLabel("10", dock);
        dock->setWidget(dockLabel);
        addDockWidget(Qt::TopDockWidgetArea, dock);
    
        QTimer * t = new QTimer();
        connect(t, &QTimer::timeout, [this, dockLabel, t, dock](){
    
            i--;
            dockLabel->setText(QString::number(i));
    
            if(!i){
                t->stop();
                t->deleteLater();
    
                dock->hide();
                dock->deleteLater();
            }
        });
    
        t->start(1000);
    }
    

    This will show a mainwindow with a dockwidget docked to the top area. Text in the dockwidget will count down from 10 and run deleteLater() when it reaches zero. If you are holding and dragging the separator that lines the bottom on the dockwidget when this happens, it will segfault.

    The segfault doesnt occur within the destructor of the dockwidget, or from anything else that my code does. The stacktrace shows it crashing in qdockarealayout.cpp at:

    bool QDockAreaLayoutItem::skip() const
    {
        if (placeHolderItem != 0)
            return true;
    
        if (flags & GapItem)
            return false;
    
        if (widgetItem != 0)
            return widgetItem->isEmpty(); <--- CRASH HERE
    

    The stacktrace also shows that it looks like this thread of execution was triggered by an event where it runs separatorMove. It seems like there are still user driven events coming from the mousing dragging which qt atempts to service with the dockwidget, but by the time the event is serviced, the dockwidget is deleted.

    At first i thought deleteLater() would solve the problem, originally we had just used close(). I've also tried adding before the call to deleteLater():

    dock->setFixedSize(dock->size());
    qApp->processEvents();
    

    Attempting to disable the ability to resize before we delete the dockwidget. But I still get the same segfault.

    I was wondering if there were any solutions I could implement to work around this problem? Or maybe Im doing something wrong?

    The reason we are programatically closing a dockwidget is because we display loading boxes to the user in the top dock area, and when the loading finishes we run the code to delete the dockwidget. The top dock area could also have other prompts for the user so theres a chance the user would want to drag one dock widget bigger to read a longer message. Worst-case, we can set a fixed size for only the loading screen dockwidgets which would eliminate most of the possible cases where the user could trigger this crash, but we'd rather maintain the resizable nature of the dockwidgets if possible.

    Thanks,
    Nick

    jsulmJ 1 Reply Last reply
    1
    • N NickJT

      Hey,

      I found that when a QDockWidget is closed while the users is actively dragging a separator it will trigger a segmentation fault. This is on Qt 5.9.6, I haven't tested on other versions.

      I was able to put together a minimum reproducible example:

      TestDockWidgetResizeWindow::TestDockWidgetResizeWindow(QWidget *parent)
          : QMainWindow(parent)
      {
          i = 10;
      
          QLabel * center = new QLabel("Central Widget", this);
          setCentralWidget(center);
      
          QDockWidget * dock = new QDockWidget(this);
          QLabel * dockLabel = new QLabel("10", dock);
          dock->setWidget(dockLabel);
          addDockWidget(Qt::TopDockWidgetArea, dock);
      
          QTimer * t = new QTimer();
          connect(t, &QTimer::timeout, [this, dockLabel, t, dock](){
      
              i--;
              dockLabel->setText(QString::number(i));
      
              if(!i){
                  t->stop();
                  t->deleteLater();
      
                  dock->hide();
                  dock->deleteLater();
              }
          });
      
          t->start(1000);
      }
      

      This will show a mainwindow with a dockwidget docked to the top area. Text in the dockwidget will count down from 10 and run deleteLater() when it reaches zero. If you are holding and dragging the separator that lines the bottom on the dockwidget when this happens, it will segfault.

      The segfault doesnt occur within the destructor of the dockwidget, or from anything else that my code does. The stacktrace shows it crashing in qdockarealayout.cpp at:

      bool QDockAreaLayoutItem::skip() const
      {
          if (placeHolderItem != 0)
              return true;
      
          if (flags & GapItem)
              return false;
      
          if (widgetItem != 0)
              return widgetItem->isEmpty(); <--- CRASH HERE
      

      The stacktrace also shows that it looks like this thread of execution was triggered by an event where it runs separatorMove. It seems like there are still user driven events coming from the mousing dragging which qt atempts to service with the dockwidget, but by the time the event is serviced, the dockwidget is deleted.

      At first i thought deleteLater() would solve the problem, originally we had just used close(). I've also tried adding before the call to deleteLater():

      dock->setFixedSize(dock->size());
      qApp->processEvents();
      

      Attempting to disable the ability to resize before we delete the dockwidget. But I still get the same segfault.

      I was wondering if there were any solutions I could implement to work around this problem? Or maybe Im doing something wrong?

      The reason we are programatically closing a dockwidget is because we display loading boxes to the user in the top dock area, and when the loading finishes we run the code to delete the dockwidget. The top dock area could also have other prompts for the user so theres a chance the user would want to drag one dock widget bigger to read a longer message. Worst-case, we can set a fixed size for only the loading screen dockwidgets which would eliminate most of the possible cases where the user could trigger this crash, but we'd rather maintain the resizable nature of the dockwidgets if possible.

      Thanks,
      Nick

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

      @NickJT said in Closing a QDockWidget While the User is Actively Resizing It:

      I haven't tested on other versions

      Would be interesting to test with latest Qt5 version and file a bug if reproducible.

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

      1 Reply Last reply
      0
      • N Offline
        N Offline
        NickJT
        wrote on last edited by
        #3

        Upgrading our Qt version is definitely in the backlog. We've had some trouble getting a 32bit version of 5.15 (unfortunately our internal API is still 32bit) compiled and loaded in qtcreator, but thats for another thread.
        Maybe someone with a working newer build kit can try the example code posted above, just create a new project and paste the code in the constructor.

        Really what i want is ideas on how to work around this or address it within my project. I was thinking maybe I could disable receiving of events in the dockwidget before deleting it?

        1 Reply Last reply
        0
        • Christian EhrlicherC Offline
          Christian EhrlicherC Offline
          Christian Ehrlicher
          Lifetime Qt Champion
          wrote on last edited by
          #4

          It also crashes with Qt5.15.2 and I would guess also with Qt6. Still don't understand why ...

          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
          2
          • N Offline
            N Offline
            NickJT
            wrote on last edited by
            #5

            I'm really looking for a workaround right now to prevent the user from triggering a segfault. Could I disable receiving of events on the separator just before deleting the dockwidget, and how would that be done? Thanks.

            1 Reply Last reply
            0
            • N Offline
              N Offline
              NickJT
              wrote on last edited by
              #6
              QMouseEvent mEvnRelease(QEvent::MouseButtonRelease,
                                          QCursor::pos(),
                                          Qt::LeftButton,
                                          Qt::LeftButton,
                                          Qt::NoModifier);
              
              QCoreApplication::sendEvent(target, &mEvnRelease);
              

              The above code force releases the mouse and resolves the crash. In this context 'target' is the QMainWindow the QDockWidget is docked to.

              Pl45m4P 1 Reply Last reply
              2
              • N NickJT
                QMouseEvent mEvnRelease(QEvent::MouseButtonRelease,
                                            QCursor::pos(),
                                            Qt::LeftButton,
                                            Qt::LeftButton,
                                            Qt::NoModifier);
                
                QCoreApplication::sendEvent(target, &mEvnRelease);
                

                The above code force releases the mouse and resolves the crash. In this context 'target' is the QMainWindow the QDockWidget is docked to.

                Pl45m4P Offline
                Pl45m4P Offline
                Pl45m4
                wrote on last edited by Pl45m4
                #7

                @NickJT said in Closing a QDockWidget While the User is Actively Resizing It:

                The above code force releases the mouse and resolves the crash. In this context 'target' is the QMainWindow the QDockWidget is docked to.

                Could crash, because AFAIK undocking changes the parent of the dockWidget, so there is no parent anymore to send the event to. Then it tries to send the move / drag events to the invalid parent which will produce a segfault.
                But I'm not 100% sure ;-)


                If debugging is the process of removing software bugs, then programming must be the process of putting them in.

                ~E. W. Dijkstra

                N 1 Reply Last reply
                0
                • Pl45m4P Pl45m4

                  @NickJT said in Closing a QDockWidget While the User is Actively Resizing It:

                  The above code force releases the mouse and resolves the crash. In this context 'target' is the QMainWindow the QDockWidget is docked to.

                  Could crash, because AFAIK undocking changes the parent of the dockWidget, so there is no parent anymore to send the event to. Then it tries to send the move / drag events to the invalid parent which will produce a segfault.
                  But I'm not 100% sure ;-)

                  N Offline
                  N Offline
                  NickJT
                  wrote on last edited by NickJT
                  #8

                  @Pl45m4 I want to close this out for future devs looking for the right answer.
                  The above solution is run before dockwidget->hide() and deleteLater().
                  We aren't ever undocking the dockwidget here. As far as I understand, my solution sends an event to the QMainWindow telling it the left mouse button was released, and the mainwindow responds accordingly, in this case, it ends the resizing of the dockwidget. The reason we send the event to the qmainwindow is that it is in charge of the dockwidget separators; I figured this out from some inspection of the source code and searching online.
                  Also notably, the pointer to our parent qmainwindow is guaranteed safe in my implementation. The code that closes the dockwidget is running in the scope of the qmainwindow and we are just using 'this'. It might be dangerous to use this code within the scope of the qdockwidget and get the pointer to the parent with parent().

                  1 Reply Last reply
                  0

                  • Login

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