QListView not accepting drag and drop

Python + Qt (in the form of PyQt5 or PySide2) is a weird mash-up of the famously slow interpreted dynamic language plus a heavyweight C++ GUI library. It certainly has its advantages over writing in C++, but I’m really wondering if there aren’t better ways to write cross-platform desktop apps.

Anyways, in Qt, you’re supposed to be able to accept drag-and-drop in a widget by doing something like:

# install some event handlers
myWidget.dragMoveEvent = ...
myWidget.dropEvent = ...

If you use a QListView to present an explorer-style view of some items, this mysteriously doesn’t work. Why? Qt is object-oriented from top to bottom, and you think you’re using a QListView but it’s actually a QAbstractItemView which is actually a QAbstractScrollArea. But a QAbstractScrollArea is very different conceptually from a QListView.

It turns out that when you drop something onto the QListView, it’s not the QListView that gets the drop events. Instead, its internal viewport (the scroll area) gets the event.

And if you’ve setAcceptDrops(True) on myWidget, that’s not what you actually want. The viewport is its own QWidget with its own acceptDrops flag that isn’t True.

class MyWidget(QListView):
    def __init__(self):
        # NOT self.setAcceptDrops(True)

    def eventFilter(self, obj, event):
        # grab any drag and drop events that the viewport receives...

Is this a bug? It certainly seems like poor API design, because if doing a setAcceptDrops(True) on a QListView doesn’t also setAcceptDrops(True) on its internal viewport, then what does it accomplish?

Technically, this behavior seems to be mentioned in this old Qt 4.6 page: https://doc.qt.io/archives/4.6/model-view-dnd.html

But this kind of requirement that I cobble together bits and pieces of Qt 4, C++, PyQt*, and StackOverflow documentation to figure out what to do in PySide2 is apparently the state of using Qt with Python. Excuse me while I go back to complaining about JavaScript…

Leave a Reply