Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

Changing dialog's parent disables drag and drop



  • I have a main window and a dialog that is opened from this window on a button click. For performance reasons, there is a dialog cache, that keeps an instance of the dialog and only shows it when the dialog should be opened instead of creating new instance. In the dialog, there's a QListWidget with some items which order can be changed by drag and drop. This works when I first open the dialog, but when I close it and open it again, I'm unable to drop the items, I get a Qt::ForbiddenCursor.

    The issue seems to be caused by calling setParent(nullptr) when closing the dialog (or likely by just changing the parent). If I remove this line, drag and drop works. However I need this to prevent the dialog from being deleted by the parent and also the dialog can have different parents in different contexts (this isn't obvious from my simplified example). Any idea what is wrong with this approach? My Qt version is 5.9.3. Can this be a Qt bug?

    MainWindow.h:

    #include "ui_mainwindow.h"
    #include "dialog.h"
    
    #include <QPushButton>
    #include <QMainWindow>
    #include <memory>
    
    class MainWindow : public QMainWindow
    {
        Q_OBJECT
    
    public:
        explicit MainWindow(QWidget* parent = nullptr) : QMainWindow(parent), ui(new Ui::MainWindow)
        {
            ui->setupUi(this);
    
            dialog.reset(new Dialog(this));
            dialog->setAttribute(Qt::WA_DeleteOnClose, false);
    
            connect(ui->button, &QPushButton::pressed, [&]
            {
                dialog->setParent(this, dialog->windowFlags());
                dialog->open();
            });
        }
    
        ~MainWindow()
        {
            delete ui;
        }
    
    private:
        Ui::MainWindow* ui;
        std::unique_ptr<Dialog> dialog;
    };
    

    Dialog.h:

    #include "ui_dialog.h"
    #include <QDialog>
    
    class Dialog : public QDialog
    {
        Q_OBJECT
    
    public:
        explicit Dialog(QWidget* parent) : QDialog(parent), ui(new Ui::Dialog)
        {
            ui->setupUi(this);
    
            ui->listWidget->addItem("first");
            ui->listWidget->addItem("second");
            ui->listWidget->addItem("third");
        }
    
        ~Dialog()
        {
            delete ui;
        }
    
    public slots:
        virtual void reject() override
        {
            setParent(nullptr);
            QDialog::reject();
        }
    
    private:
        Ui::Dialog* ui;
    };
    

    Dialog.ui - simple dialog with QListWidget and reject button

    <?xml version="1.0" encoding="UTF-8"?>
    <ui version="4.0">
     <class>Dialog</class>
     <widget class="QDialog" name="Dialog">
      <property name="geometry">
       <rect>
        <x>0</x>
        <y>0</y>
        <width>548</width>
        <height>397</height>
       </rect>
      </property>
      <property name="windowTitle">
       <string>Dialog</string>
      </property>
      <layout class="QVBoxLayout" name="verticalLayout">
       <item>
        <widget class="QListWidget" name="listWidget">
         <property name="dragDropMode">
          <enum>QAbstractItemView::DragDrop</enum>
         </property>
         <property name="defaultDropAction">
          <enum>Qt::MoveAction</enum>
         </property>
        </widget>
       </item>
       <item>
        <widget class="QDialogButtonBox" name="buttonBox">
         <property name="orientation">
          <enum>Qt::Horizontal</enum>
         </property>
         <property name="standardButtons">
          <set>QDialogButtonBox::Close</set>
         </property>
        </widget>
       </item>
      </layout>
     </widget>
     <resources/>
     <connections>
      <connection>
       <sender>buttonBox</sender>
       <signal>accepted()</signal>
       <receiver>Dialog</receiver>
       <slot>accept()</slot>
       <hints>
        <hint type="sourcelabel">
         <x>248</x>
         <y>254</y>
        </hint>
        <hint type="destinationlabel">
         <x>157</x>
         <y>274</y>
        </hint>
       </hints>
      </connection>
      <connection>
       <sender>buttonBox</sender>
       <signal>rejected()</signal>
       <receiver>Dialog</receiver>
       <slot>reject()</slot>
       <hints>
        <hint type="sourcelabel">
         <x>316</x>
         <y>260</y>
        </hint>
        <hint type="destinationlabel">
         <x>286</x>
         <y>274</y>
        </hint>
       </hints>
      </connection>
     </connections>
    </ui>
    

    MainWindow.ui - default main window with one button

    <?xml version="1.0" encoding="UTF-8"?>
    <ui version="4.0">
     <class>MainWindow</class>
     <widget class="QMainWindow" name="MainWindow">
      <property name="geometry">
       <rect>
        <x>0</x>
        <y>0</y>
        <width>432</width>
        <height>316</height>
       </rect>
      </property>
      <property name="windowTitle">
       <string>MainWindow</string>
      </property>
      <widget class="QWidget" name="centralWidget">
       <widget class="QPushButton" name="button">
        <property name="geometry">
         <rect>
          <x>40</x>
          <y>30</y>
          <width>80</width>
          <height>21</height>
         </rect>
        </property>
        <property name="text">
         <string>PushButton</string>
        </property>
       </widget>
      </widget>
      <widget class="QMenuBar" name="menuBar">
       <property name="geometry">
        <rect>
         <x>0</x>
         <y>0</y>
         <width>432</width>
         <height>20</height>
        </rect>
       </property>
      </widget>
      <widget class="QToolBar" name="mainToolBar">
       <attribute name="toolBarArea">
        <enum>TopToolBarArea</enum>
       </attribute>
       <attribute name="toolBarBreak">
        <bool>false</bool>
       </attribute>
      </widget>
      <widget class="QStatusBar" name="statusBar"/>
     </widget>
     <layoutdefault spacing="6" margin="11"/>
     <resources/>
     <connections/>
    </ui>
    

    main.cpp

    #include "mainwindow.h"
    #include <QApplication>
    
    int main(int argc, char *argv[])
    {
        QApplication a(argc, argv);
        MainWindow w;
        w.show();
        return a.exec();
    }

  • Lifetime Qt Champion

    Hi,

    Why do you need that widget not to be delete with MainWindow's own destruction ?



  • Hi,
    in my real code, parent is not necessarily the main window, it can be some other window. I just tried to create a minimal example that reproduces the bug...


  • Lifetime Qt Champion

    How are you passing that dialog around ?



  • @SGaist There are more cases... It can be another dialog from the cache as well as just some dialog that is created by new and later deleted. Sometimes parent is the main window as well... This really depends on the context where the dialog is opened.


  • Lifetime Qt Champion

    Do you have the same effect if you don't set the window flags when setting the parent ?

    Note that this is still a bit strange to have a dialog moving from objects to objects like that.



  • @SGaist Yea, I know it's not the most standard solution... But apart from this bug it seems to work well.
    In this sample code, if you remove the flags, the dialog will not show up. For my real code, I'm gonna have to test that tomorrow as I'm on a device where I don't have it right now.



  • OK, so I've reported it as Qt bug with a much simpler example. Also, there is a link to a workaround in the bug.


  • Lifetime Qt Champion

    Thanks !


Log in to reply