Planned maintenance has been done but it did not solve the problem. So work will continue on this and a new time for trying updates will be announced asap.

PyQt5 - incorrect behavior of treeview when dragging and dropping



  • I have a a treeview with three items

    • item1
      • item2
    • item3

    If I pick up item3 and try to drag it onto item1 or item2 then nothing happens.
    Until I drag item3 OUTSIDE the window and then back in. Then all of a sudden everything works as it should.

    No idea if I'm doing something wrong or that this is a Qt bug.

    The following python code should reproduce the issue. Running Windows10 with Python 3.7.1 and Qt 5.7.9

    from PyQt5.QtCore import QMimeData
    from PyQt5.QtGui import QStandardItemModel, QStandardItem
    from PyQt5 import QtCore, QtWidgets
    
    
    
    class SceneTreeModel(QStandardItemModel):
    
        def mimeData(self, indexes):
    
            name = indexes[0].data()
            print('called mimeData on ' + name)
            mimedata = QMimeData()
            mimedata.setText(name)
            return mimedata
    
        def supportedDropActions(self):
            return QtCore.Qt.MoveAction
    
        def canDropMimeData(self, data, action, row, column, parent):
            print('can drop called on')
            print(parent.data())
            return True
    
        def dropMimeData(self, data, action, row, column, parent):
            parent_name = parent.data()
            node_name = data.text()
            print("Dropped {} onto {}".format(node_name, parent_name))
            return True
    
    def give_model():
        model = SceneTreeModel()
        # create a tree item
        item1 = QStandardItem('item1')
        item2 = QStandardItem('item2')
        item3 = QStandardItem('item3')
    
        model.invisibleRootItem().appendRow(item1)
        item1.appendRow(item2)
        model.invisibleRootItem().appendRow(item3)
    
        return model
    
    class Ui_MainWindow(object):
        def setupUi(self, MainWindow):
            MainWindow.setObjectName("MainWindow")
            MainWindow.resize(200, 400)
            MainWindow.setLocale(QtCore.QLocale(QtCore.QLocale.English, QtCore.QLocale.UnitedStates))
    
            self.treeView = QtWidgets.QTreeView(MainWindow)
            self.treeView.setRootIsDecorated(False)
            self.treeView.setObjectName("treeView")
            self.treeView.header().setVisible(False)
    
            MainWindow.setCentralWidget(self.treeView)
    
            self.treeView.setModel(give_model())
            self.treeView.setDragDropMode(QtWidgets.QAbstractItemView.InternalMove)
            self.treeView.expandAll()
    
    
    if __name__ == "__main__":
        import sys
        app = QtWidgets.QApplication(sys.argv)
        MainWindow = QtWidgets.QMainWindow()
        ui = Ui_MainWindow()
        ui.setupUi(MainWindow)
        MainWindow.show()
        sys.exit(app.exec_())
    


  • First while this might not have any effect on what you are doing you should do one or the other and the more standardish version would be the following if importing everything:

    import sys
    
    from PyQt5.QtCore import * 
    from PyQt5.QtGui import * 
    from PyQt5.QtWidgets import * 
    

    Also keep all your imports gathered together at the top makes it a lot easier to know what you are including that way.

    Or if importing specifically (always the better route unless you are importing a lot of things. Note you could just leave it exit and argv but I find this helps with knowing where it came from without importing that entire library. On the flip-side you really do not need sysArgv since you do not use it as such I would just replace that call with >> app = QApplication([]) Aka K.I.S.S. it (Keep It Simple but Smart) never add anything to your code you do not need and definitely if you are not going to use it.

    from sys import sysExit
    from sys import sysArgv
    
    from PyQt5.QtCore import QMimeData
    from PyQt5.QtGui import QStandardItemModel, QStandardItem 
    from PyQt5.QtWidgets import * 
    

    Doing both together does not break anything I do not think but it is a poor coding practice such as:

    from PyQt5.QtCore import QMimeData
    from PyQt5.QtCore import *
    

    As you can see there is no need to do the former if you are doing the latter. Just a few up front pointers I will see I can figure out the issue you are having with drag/drop.



  • Okay first I copied your code as is and ran it on my Python 3.7 / PyQt5 / Windows 10 system and it works just fine other the Window Title not getting set. Then I cleaned up the code by applying the K.I.S.S principle and it still works just looks nicer --- now if you are running an earlier version of python (my first guess) or a different operating system (maybe but doubt it) perhaps there in lies your issue otherwise I have included the cleaned up code below. Oh and the Title works in this one. Note I did not clean up everything just the main call -- the Main Window class -- and the import statements

    import sys
    
    from PyQt5.QtCore    import Qt, QMimeData, QLocale
    from PyQt5.QtGui     import QStandardItemModel, QStandardItem
    from PyQt5.QtWidgets import QApplication, QMainWindow, QAbstractItemView, QTreeView
    
    class SceneTreeModel(QStandardItemModel):
        def mimeData(self, indexes):
            name = indexes[0].data()
            print('called mimeData on ' + name)
            mimedata = QMimeData()
            mimedata.setText(name)
            return mimedata
    
        def supportedDropActions(self):
            return Qt.MoveAction
    
        def canDropMimeData(self, data, action, row, column, parent):
            print('can drop called on')
            print(parent.data())
            return True
    
        def dropMimeData(self, data, action, row, column, parent):
            parent_name = parent.data()
            node_name = data.text()
            print("Dropped {} onto {}".format(node_name, parent_name))
            return True
    
    def give_model():
        model = SceneTreeModel()
        # create a tree item
        item1 = QStandardItem('item1')
        item2 = QStandardItem('item2')
        item3 = QStandardItem('item3')
    
        model.invisibleRootItem().appendRow(item1)
        item1.appendRow(item2)
        model.invisibleRootItem().appendRow(item3)
    
        return model
    
    class UI_MainWindow(QMainWindow):
        def __init__(self, parent=None):
            super(UI_MainWindow, self).__init__(parent)
            Left = 250
            Top = 100
            Width = 250
            Height = 300
            self.setGeometry(Left, Top, Width, Height)
            self.setWindowTitle("MainWindow")
            self.setLocale(QLocale(QLocale.English, QLocale.UnitedStates))
    
            self.treeView = QTreeView(self)
            self.treeView.setRootIsDecorated(False)
            self.treeView.setObjectName("treeView")
            self.treeView.header().setVisible(False)
    
            self.setCentralWidget(self.treeView)
    
            self.treeView.setModel(give_model())
            self.treeView.setDragDropMode(QAbstractItemView.InternalMove)
            self.treeView.expandAll()
        
    if __name__ == '__main__':
        MainAppThread = QApplication([])
    
        MainWindow = UI_MainWindow()
        MainWindow.show()
    
        sys.exit(MainAppThread.exec_())
    


  • Hi Denni, thanks looking into this and for cleaning up.

    Unfortunately I still have the same issue, although not every time.

    Even with your clean version of the source code, if you pick up item3 and move it directly onto item2 or item1 then the canDrop event is not called.

    Movie:
    https://www.jottacloud.com/s/169522c904c6cd344dfb4cb72c9d9e99481

    I have been able to re-produce this issue on another windows 10 PC with a different python version (3.6.8) and the same Qt version (5.9.7)



  • Okay so have you tried doing this in python 3.7 with pyqt5 ? I ask that because pyqt5 does not always seem to work well with previous versions of python. I know that they have made ports so that earlier versions of python can run pyqt5 but when I see that and the fact that folks on earlier versions of python experience issues that do not exist when run on python 3.7 then it says to me that while the port exists it is not fully stable so one has to expect oddities (perhaps like this) to spring up from time to time. Also are you using the MacOS as someone I think posted a similar issue that was restricted to just that OS

    Still to me the obvious solution is use Python 3.7 because frankly things are only going to get worse when they upgrade 3.7 to a newer version.

    If your insistent on staying on an earlier version of Python than 3.7 then I strongly suggest creating a Python 3.7 environment so that you can test things like that within that environment to see if the issue exists there for you and if it does not then you will know it is because you are using a previous version of Python and some very in-depth work around is going to be need on your part to fix your issue

    My last statement would be (and its directed at everyone using an earlier version of python) : If you do not like change then do not be a programmer or do stuff within the IT industry as it is a constant series of changes. For instance hardware can become obsolete or at least nearly so within 5 years and there are programs that if not updated cannot run on newer hardware and that is not even taking into account OS changes making some software incompatible. So it is best to embrace change, and adapt to to it quickly. Yes sometimes it is best to delay a bit but when that is done an upgrade plan with a timetable ought to put into place so that the necessary change will take place in a timely manner where the sooner is often the better.



  • I always try to use the most recent stable versions.
    I just wanted to make sure that the problem was not due to my PC configuration, so I re-checked on an other PC which as it happened was a few months behind on its updates and thus was running an older python version. This is good because now we know that the issue is not related to the specific python version or the hardware.
    So it is either

    • a mistake from my side
    • an error in Qt present at least in version 5.7.9 in combination with Windows 10.

    In a few days I will see if I can reproduce the issue on Linux as well. if so then I will file it as a bug report.



  • Okay? But as I stated it works perfectly fine for me but then I have a clean install of the latest python 3.7 and and latest pyqt5 so there maybe an issue with that or its a bug they already fixed within the latest version. My guess if you submit this as a bug that because it can be proven to work just fine using the latest code that it will get ignored.



  • This post is deleted!


  • I was able to reproduce the behavior on both Windows and Linux using different configuration and in python as well as directly in C++ (eliminating pyhton/pyqt).

    Filed as bug report.

    https://bugreports.qt.io/browse/QTBUG-76418

    Will close the issue it here as it is not a python / pyqt -only issue.


Log in to reply