pythonc++qtdrag-and-drop

QListWidget drag and drop configuration: When to use the mode instead of the drag/drop properties?


I'm learning how to setup drag and drop in the view-model framework description at Qt site. When applied to convenience views (QListWidget, QTableWidget, QTreeWidget), the documentation uses either (in original C++ version):

listWidget->setDragEnabled(true);
listWidget->viewport()->setAcceptDrops(true);

or:

listWidget->setDragDropMode(QAbstractItemView::InternalMove);

I'm unable to figure out whether setDragDropMode is a shortcut to set individual properties of the widget and which ones in addition of the two above, or it actually does more. I see using QAbstractItemView::InternalMove and QAbstractItemView::DragDrop both set DragEnabled and setAcceptDrops to true, but lead to a different behavior for the item (move vs. copy), so I know there is more behind the mode method. I would like to clarify this point in order to know when to use the mode method.

My questions:

I use Qt for Python if that matters.


As an example, if I create a list widget with each approach, it seems the result is perfectly equivalent (in this specific case):

from qtpy.QtWidgets import (QApplication, QWidget, QListWidget, QHBoxLayout)

class Window(QWidget):

    texts = ['Sycamore', 'Chestnut', 'Walnut', 'Mahogany']

    def __init__(self):
        super().__init__()

        lw_1 = QListWidget()
        lw_1.addItems(self.texts)
        self.print_props('When created', lw_1)

        lw_2 = QListWidget()
        lw_2.addItems(self.texts)

        # Comparing
        lw_1.setDragEnabled(True)
        lw_1.viewport().setAcceptDrops(True)
        lw_1.setDropIndicatorShown(True)
        self.print_props('Using properties', lw_1)

        # With
        mode = lw_2.DragDrop
        lw_2.setDragDropMode(mode)
        self.print_props(f'Using mode ({mode})', lw_2)

        layout = QHBoxLayout(self)
        layout.addWidget(lw_1)
        layout.addWidget(lw_2)

    def print_props(self, text, widget):
        print()
        print(text)
        print('drag:', widget.dragEnabled())
        print('drop:', widget.viewport().acceptDrops())
        print('mode:', widget.dragDropMode())


def main():
    app = QApplication([])
    window = Window()
    window.show()
    app.exec()

main()

Solution

  • We can look at the source of the getter and setter of dragDropMode property:

    void QAbstractItemView::setDragDropMode(DragDropMode behavior)
    {
        Q_D(QAbstractItemView);
        d->dragDropMode = behavior;
        setDragEnabled(behavior == DragOnly || behavior == DragDrop || behavior == InternalMove);
        setAcceptDrops(behavior == DropOnly || behavior == DragDrop || behavior == InternalMove);
    }
    
    QAbstractItemView::DragDropMode QAbstractItemView::dragDropMode() const
    {
        Q_D(const QAbstractItemView);
        DragDropMode setBehavior = d->dragDropMode;
        if (!dragEnabled() && !acceptDrops())
            return NoDragDrop;
    
        if (dragEnabled() && !acceptDrops())
            return DragOnly;
    
        if (!dragEnabled() && acceptDrops())
            return DropOnly;
    
        if (dragEnabled() && acceptDrops()) {
            if (setBehavior == InternalMove)
                return setBehavior;
            else
                return DragDrop;
        }
    
        return NoDragDrop;
    }
    

    As you can see, the setter indeed itself uses setDragEnabled and setAcceptDrops to set corresponding properties, which corresponds to behaviour you noticed. The only additional thing it does is setting underlying d->dragDropMode (d appears via Q_D macro and is used to implement private data, see this question), but the getter doesn't even consider its value except when dragEnabled() && acceptDrops(), for which case, as you observed, the saved member differentiates between move and copy behaviour. By default it is set to QAbstractItemView::NoDragDrop (see here), so the getter returns DragDrop (whenever it is called by you or Qt itself) if you didn't set the property yourself and used only setDragEnabled and setAcceptDrops (setting them to true). Also note that the getter and setter aren't virtual (see here).

    So, if you don't need InternalMove behaviour, you can ignore dragDropMode propery and just use setDragEnabled and setAcceptDrops, behaviour will be identical.