Search code examples
pythonpyqt5qlistwidget

How do you detect QListWidet internal move signal while setAcceptDrops setting is set to True


I am trying to detect internal move signal from QListWidget while I have my drag and drop effect implement. But currently with my code, because I am self-handling dragEnterEvent, dragMoveEvent, and moveEvent, the internal move signal is being ignored. Any way I can get around that issue? Below is a simple code to duplicate the issue.

You can drag external items to the list widget but you now cannot move the items around.

import sys, os
from PyQt5.QtWidgets import QApplication, QWidget, QLabel, QLineEdit, QListWidget, \
                            QVBoxLayout, QHBoxLayout, QAbstractItemView
from PyQt5.QtCore import Qt

class ListWidget(QListWidget):
    def __init__(self, parent=None):
        super().__init__(parent=None)
        self.setAcceptDrops(True)
        self.setStyleSheet('''font-size:25px''')

    def dragEnterEvent(self, event):
        # print(event.mimeData().urls())
        # print(dir(event.mimeData()))
        if event.mimeData().hasUrls():
            event.accept()
        else:

            event.ignore()

    def dragMoveEvent(self, event):

        if event.mimeData().hasUrls():
            event.setDropAction(Qt.CopyAction)
            event.accept()
        else:
            print('y')
            event.ignore()

    def dropEvent(self, event):
        if event.mimeData().hasUrls():
            event.setDropAction(Qt.CopyAction)
            event.accept()

            pdfFiles = []

            for url in event.mimeData().urls():
                if url.isLocalFile():                   
                    pdfFiles.append(str(url.toLocalFile()))
            self.addItems(pdfFiles)
        else:
            event.ignore()

class AppDemo(QWidget):
    def __init__(self):
        super().__init__()
        self.resize(1200, 800)

        data = ['Microsoft', 'Facebook', 'Google']
        mainLayout = QHBoxLayout()


        self.lst = ListWidget()
        self.lst.addItems(data)
        self.lst.setStyleSheet('''
            font-size:30px
        ''')
        self.lst.setDragDropMode(QAbstractItemView.InternalMove)



        mainLayout.addWidget(self.lst)

        self.setLayout(mainLayout)

if __name__ == '__main__':
    app = QApplication(sys.argv)

    demo = AppDemo()
    demo.show()

    sys.exit(app.exec_())

Solution

  • Instead of invoking event.ignore() when the mimedata has no urls, call the parent's method so the drag & drop will be handled like the default QListWidget. Also, another way to determine the source of the drag operation is with event.source().

    def dragEnterEvent(self, event):
        if event.mimeData().hasUrls():
            event.accept()
        else:
            super().dragEnterEvent(event)
    
    def dragMoveEvent(self, event):
        if event.mimeData().hasUrls():
            event.setDropAction(Qt.CopyAction)
            event.accept()
        else:
            super().dragMoveEvent(event)
    
    def dropEvent(self, event):
        if event.mimeData().hasUrls():
            event.setDropAction(Qt.CopyAction)
            event.accept()
            pdfFiles = []
            for url in event.mimeData().urls():
                if url.isLocalFile():                   
                    pdfFiles.append(str(url.toLocalFile()))
            self.addItems(pdfFiles)
        else:
            super().dropEvent(event)