sjt.is

Pipeline. Period.

PyQt and drag’n’drop

Today I was trying to get drag and drop working in PyQt and I had the worst time getting it to work. I was trying to have a QListWidget accept file drops which would then add the path to the file to the list. To enable dropping on a widget you must do several things. First of all you must call:

setAcceptDrops(True)

Then you have to implement the following methods:

dragEnterEvent(self, event)
dropEvent(self, event)

What I forgot to do (when I read the docs carefully) is that you also need to implement

dragMoveEvent(self, event)

Which of course makes sense when you think about it. First the cursor enters the widget, then moves within it, then you drop. Simple, right? And since I couldn’t find an example of this behavior when I was googling (drop file on list, list adds file path) I threw together this example. This example can also reorder the list after you have dragged files on it.

import sys
from PyQt4.QtGui import *
from PyQt4.QtCore import *

class MyListWidget(QListWidget):
  def __init__(self, parent):
    super(MyListWidget, self).__init__(parent)
    self.setAcceptDrops(True)
    self.setDragDropMode(QAbstractItemView.InternalMove)

  def dragEnterEvent(self, event):
    if event.mimeData().hasUrls():
      event.acceptProposedAction()
    else:
      super(MyListWidget, self).dragEnterEvent(event)

  def dragMoveEvent(self, event):
    super(MyListWidget, self).dragMoveEvent(event)

  def dropEvent(self, event):
    if event.mimeData().hasUrls():
      for url in event.mimeData().urls():
        self.addItem(url.path())
      event.acceptProposedAction()
    else:
      super(MyListWidget,self).dropEvent(event)

class MyWindow(QWidget):
  def __init__(self):
    super(MyWindow,self).__init__()
    self.setGeometry(100,100,300,400)
    self.setWindowTitle("Filenames")

    self.list = MyListWidget(self)
    layout = QVBoxLayout(self)
    layout.addWidget(self.list)

    self.setLayout(layout)

if __name__ == '__main__':

  app = QApplication(sys.argv)
  app.setStyle("plastique")

  window = MyWindow()
  window.show()

  sys.exit(app.exec_())