Search code examples
pythonpyqtdrag-and-dropsignals-slotsqtreewidget

pyqt QTreeWidget signal on item drop


I need to enable a button in my app whenever something is dropped to my custom QTreeWidget.

I sub-classed QTreeWidget to implement drag and drop of custom data. But I'm not able to find a way to get notified when something is dropped into my custom QTreeWidget. I couldn't find a QTreeWidget signal to do this. Of course, the QTreeWidget's dropEvent() will be called each time something is dropped but that doesn't help much to achieve what I'm trying to do.

Here's where I instantiate the custom QTreeWidget that accepts drops from another widget,

from PyQt4 import QtCore, QtGui
import MyTreeWidget

class TestWindow(QtGui.QDialog):
  def __init__(self, parent=None):
    super(TestWindow, self).__init__(parent)
    self.myTreeWidget = MyTreeWidget.MyTreeWidget()
    ...
    #self.myTreeWidget.onItemDropped.connect(self.doSomethingOnItemDropped) <== I am looking for something like this

  def doSomethingOnItemDropped(self):
    # Enable certain button 

  ...

And then, here is how I sub-classed QTreeWidget,

import sys
from PyQt4 import QtGui, QtCore

class MyTreeWidget(QtGui.QTreeWidget):
  def __init__(self, parent=None):
    super(MyTreeWidget, self).__init__(parent)
    self.setAcceptDrops(True)

  def dropEvent(self, event): 
    if (event.mimeData().hasFormat('application/x-icon-and-text')):
      event.acceptProposedAction()
      data = event.mimeData().data("application/x-icon-and-text")
      stream= QtCore.QDataStream(data, QtCore.QIODevice.ReadOnly)
      text = QtCore.QString()
      icon = QtGui.QIcon()
      stream >> text >> icon
      item = QtGui.QTreeWidgetItem(self)
      item.setText(0, text)
      item.setIcon(0, icon)
      self.addTopLevelItem(item)      
    else:
      event.ignore() 

  def dragEnterEvent(self, event):
    if (event.mimeData().hasFormat('application/x-icon-and-text')):
      event.accept()
    else:
      event.ignore() 

  def dragMoveEvent(self, event):
    if event.mimeData().hasFormat("application/x-icon-and-text"):
      event.setDropAction(QtCore.Qt.CopyAction)
      event.accept()
    else:
      event.ignore()

Any ideas? Thanks!

UPDATE : This is what worked for me

Based on comment by @ekhumoro I defined a custom signal itemDropped for my custom QTreeWidget which emits in dropEvent() event handler.

    import sys
    from PyQt4 import QtGui, QtCore

    class MyTreeWidget(QtGui.QTreeWidget):

      itemDropped = QtCore.pyqtSignal()

      def __init__(self, parent=None):
        super(MyTreeWidget, self).__init__(parent)
        self.setAcceptDrops(True)

      def dropEvent(self, event): 
        if (event.mimeData().hasFormat('application/x-icon-and-text')):
          event.acceptProposedAction()
          data = event.mimeData().data("application/x-icon-and-text")
          stream= QtCore.QDataStream(data, QtCore.QIODevice.ReadOnly)
          text = QtCore.QString()
          icon = QtGui.QIcon()
          stream >> text >> icon
          item = QtGui.QTreeWidgetItem(self)
          item.setText(0, text)
          item.setIcon(0, icon)
          self.addTopLevelItem(item)
          self.itemDropped.emit()
        else:
          event.ignore() 

And in my app,

    from PyQt4 import QtCore, QtGui
    import MyTreeWidget

    class TestWindow(QtGui.QDialog):
      def __init__(self, parent=None):
        super(TestWindow, self).__init__(parent)
        self.myTreeWidget = MyTreeWidget.MyTreeWidget()
        self.myTreeWidget.itemDropped.connect(self.doSomethingOnItemDropped) 
        ...

      def doSomethingOnItemDropped(self):
        # Enable certain button 

      ...

Solution

  • You could define a custom signal and emit it from dropEvent:

    class MyTreeWidget(QtGui.QTreeWidget):
        itemDropped = QtCore.pyqtSignal()
    
        def dropEvent(self, event): 
            if (event.mimeData().hasFormat('application/x-icon-and-text')):
                ...
                self.itemDropped.emit()