PyQt5 QListWidget – 设置拖放属性

  • Post category:Python

当你需要向用户展示一个列表,或让用户在列表中进行选择时,QListWidget是一个非常有用的控件。你还可以在列表项之间拖放数据或重新排序。本文将详细讲解如何在PyQt5中使用QListWidget实现拖放功能。

设置拖放属性

要启用QListWidget中的拖放功能,需要设置dragEnableddropEnabled为True。其中,dragEnabled控制列表项的拖动前的初始操作,而dropEnabled控制列表项如何放置或接受数据。

list_widget = QtWidgets.QListWidget()
list_widget.setDragEnabled(True)
list_widget.setDropEnabled(True)

拖放事件处理

当用户在QListWidget控件上拖动鼠标时,会发出一系列的QDrag事件和QDrop事件。要处理这些事件,我们需要重写QListWidgetdragEnterEventdropEvent方法。

# 重写dragEnterEvent
def dragEnterEvent(self, event):
    if event.mimeData().hasUrls():
        event.accept()
    else:
        event.ignore()

# 重写dropEvent
def dropEvent(self, event):
    if event.mimeData().hasUrls():
        event.setDropAction(QtCore.Qt.CopyAction)
        event.accept()
        urls = []
        for url in event.mimeData().urls():
            urls.append(str(url.toLocalFile()))
        self.addItems(urls)
    else:
        event.ignore()

在这个事件处理代码中,我们首先判断拖放事件中的数据是否是URL,如果是,我们接受事件数据并使用addItems()方法将数据添加到列表中。如果不是URL,则忽略事件数据。

完整示例

import sys
from PyQt5 import QtCore, QtWidgets

class ListWidget(QtWidgets.QListWidget):
    def __init__(self, parent):
        super().__init__(parent)
        self.setDragEnabled(True)
        self.setDropEnabled(True)

    # 重写dragEnterEvent
    def dragEnterEvent(self, event):
        if event.mimeData().hasUrls():
            event.accept()
        else:
            event.ignore()

    # 重写dropEvent
    def dropEvent(self, event):
        if event.mimeData().hasUrls():
            event.setDropAction(QtCore.Qt.CopyAction)
            event.accept()
            urls = []
            for url in event.mimeData().urls():
                urls.append(str(url.toLocalFile()))
            self.addItems(urls)
        else:
            event.ignore()

if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    window = QtWidgets.QMainWindow()
    list_widget = ListWidget(window)
    window.setCentralWidget(list_widget)
    window.show()
    sys.exit(app.exec_())

这个示例展示了如何使用ListWidget实现拖放文件到列表中。当用户在ListWidget上拖拽一个或多个文件时,这些文件的路径被添加到列表中。

示例二:重新排序

除了接受拖放数据,QListWidget还支持在列表项中进行拖放排序。下面的示例代码实现了在同一个ListWidget控件中,对数据进行拖动排序的功能。

import sys
from PyQt5 import QtCore, QtWidgets

class ListWidget(QtWidgets.QListWidget):
    def __init__(self, parent):
        super().__init__(parent)
        self.setDragEnabled(True)
        self.setAcceptDrops(True)

    def dragEnterEvent(self, event):
        if event.mimeData().hasFormat('application/x-qabstractitemmodeldatalist'):
            event.accept()

    def dropEvent(self, event):
        if event.mimeData().hasFormat('application/x-qabstractitemmodeldatalist'):
            data = event.mimeData()
            bstream = data.retrieveData('application/x-qabstractitemmodeldatalist', QtCore.QVariant.Bytearray)[0]
            istream = QtCore.QDataStream(bstream)
            row, col, _ = (QtCore.QModelIndex() / row / col).fromDataStream(istream)
            target = self.itemAt(event.pos())
            if not target:
                target = self.item(self.count() - 1)
            self.takeItem(row)
            self.insertItem(self.row(target), self.decode(bstream))
            event.acceptProposedAction()

    def startDrag(self, dropActions):
        mimeData = self.model().mimeData(self.selectedIndexes())
        drag = QtGui.QDrag(self)
        drag.setMimeData(mimeData)
        drag.start(QtCore.Qt.MoveAction)

    def decode(self, byte):
        stream = QtCore.QDataStream(byte)
        data = {}

        while not stream.atEnd():
            row = stream.readInt()
            dicts = {}
            keys = []

            while True:
                key = stream.readQString()
                if key == 'end':
                    break
                keys.append(key)

            for key in keys:
                value = stream.readQString()
                dicts[key] = value

            data[row] = dicts

        return list(data.values())[0]['text']

if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    window = QtWidgets.QMainWindow()
    list_widget = ListWidget(window)
    list_widget.addItems(['Item {}'.format(i) for i in range(10)])
    window.setCentralWidget(list_widget)
    window.show()
    sys.exit(app.exec_())

这个示例展示了如何在一个QListWidget控件中对列表项进行拖动重新排序。当用户在列表项上拖动鼠标时,它们会自动重新排序。