Search code examples
pythoneventspyqtdrag-and-droppyside

How to get dropped file names in PyQt/PySide


I am trying to set up an application that will accept havin files dropped into it. So, I am looking for a way to extract the path when they are dropped in.

Right now, I have drag and drop enabled for the right part of the application, and it will accept text dropped in, but I do not know how to handle having a file dropped in.

I am using:

def PTE_dragEnterEvent(self, e):
    if e.mimeData().hasFormat('text/plain'):
        e.accept()
    else:
        e.ignore() 

def PTE_dropEvent(self, e):
    newText = self.ui.fileListPTE.toPlainText() + '\n\n' + e.mimeData().text()
    self.ui.fileListPTE.setPlainText(newText)

Which is slightly modifying the code provided in the Zetcode Drag and Drop tutorial.


I couldn't quite get @ekhumoro answer to work for me, but it gave me more places to look, and I found Drag and drop files into QListWidget which helped.

In addition to the suggestions made by ekhumoro I needed to implement the drag move event. What I finally used looked like:

def dragEnterEvent(self, event):
    if event.mimeData().hasUrls:
        event.accept()
    else:
        event.ignore()
        
def dragMoveEvent(self, event):
    if event.mimeData().hasUrls:
        event.setDropAction(QtCore.Qt.CopyAction)
        event.accept()
    else:
        event.ignore()

def dropEvent(self, event):
    if event.mimeData().hasUrls:
        event.setDropAction(QtCore.Qt.CopyAction)
        event.accept()
        
        newText = self.ui.fileListPTE.toPlainText()
        for url in event.mimeData().urls():
            newText += '\n' + str(url.toLocalFile())
        self.ui.fileListPTE.setPlainText(newText)
        self.emit(QtCore.SIGNAL("dropped"))
    else:
        event.ignore()

Solution

  • The QMimeData class has methods for dealing with dropped urls. Below is a minimal working example:

    # from PyQt4.QtGui import QApplication, QLabel
    # from PySide2.QtWidgets import QApplication, QLabel
    from PyQt5.QtWidgets import QApplication, QLabel
    
    class Window(QLabel):
        def __init__(self):
            super().__init__()
            self.setAcceptDrops(True)
    
        def dragEnterEvent(self, event):
            print('drag-enter')
            if event.mimeData().hasUrls():
                print('has urls')
                event.accept()
            else:
                event.ignore()
    
        def dropEvent(self, event):
            lines = []
            for url in event.mimeData().urls():
                lines.append('dropped: %r' % url.toLocalFile())
            self.setText('\n'.join(lines))
        
    app = QApplication(['Drag & Drop'])
    window = Window()
    window.setGeometry(50, 100, 400, 300)
    window.show()
    app.exec_()
    

    UPDATE:

    Regarding the additions to the question:

    Some widgets (unlike the QLabel used above) have a default implementation of dragMoveEvent that explicitly ignores most events. For example, classes based on QAbstractItemView may only handle certain kinds of internal move and ignore everything else. In which case, a reimplementation of dragMoveEvent should be added that explicitly accepts the events that need to be handled differently:

    class MyView(QTableView):
        ...
        def dragMoveEvent(self, event):
            if event.mimeData().hasUrls():
                event.accept()
            else:
                super().dragMoveEvent(event)