【问题标题】:How can I Drag and Drop instances of a child class of QListWidgetItem, which have additional attributes?如何拖放具有附加属性的 QListWidgetItem 子类的实例?
【发布时间】:2020-04-07 00:09:41
【问题描述】:

我正在使用 PyQt5 并且对它很陌生。我想将 QListWidgetItem 从一个 QListWidget 拖放到另一个,这样在一侧生成的 QListWidgetItem 将包含其他数据。我尝试对 QListWidgetItem 进行子类化,但该类型不会通过拖放进行,因为它似乎创建了 QListWidgetItem 的新实例。

代码如下:

from PyQt5.QtWidgets import QApplication, QMainWindow, QWidget, QHBoxLayout, QListWidget, QListWidgetItem, QAction, QVBoxLayout
from PyQt5 import QtCore, QtGui
import sys


class GUI(QMainWindow):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.central_widget = MyCentralWidget(self)
        self.setCentralWidget(self.central_widget)


class MyCustomItem(QListWidgetItem):
    def __init__(self, data, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.extra_data = data


class MyCentralWidget(QWidget):
    def __init__(self, parent):
        super(MyCentralWidget, self).__init__(parent)
        self.h_layout = QHBoxLayout(self)
        self.setLayout(self.h_layout)

        self.list_left = DragAndDropList()
        self.list_left.setDragEnabled(True)
        self.list_left.setAcceptDrops(False)

        self.list_right = DragAndDropList()

        self.h_layout.addWidget(self.list_left)
        self.h_layout.addWidget(self.list_right)

        item = MyCustomItem(69, 'custom_item')
        self.list_left.insertItem(1, item)


class DragAndDropList(QListWidget):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.setIconSize(QtCore.QSize(124, 124))
        self.setDragDropMode(self.DragDrop)
        self.setSelectionMode(self.ExtendedSelection)
        self.setAcceptDrops(True)

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

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

    def dropEvent(self, event):
        if event.mimeData().hasUrls():
            event.setDropAction(QtCore.Qt.CopyAction)
            event.accept()
            links = []
            for url in event.mimeData().urls():
                links.append(str(url.toLocalFile()))
            self.emit(QtCore.SIGNAL("dropped"), links)
        else:
            event.setDropAction(QtCore.Qt.LinkAction)
            super().dropEvent(event)

            list_items = [self.item(i) for i in range(self.count())]
            for l in list_items:
                print(l.extra_data)


def main():
    app = QApplication([])
    win = GUI()
    win.show()
    sys.exit(app.exec_())


if __name__ == '__main__':
    main()

当我尝试拖放 custom_item 时,出现错误:“AttributeError: 'QListWidgetItem' object has no attribute 'extra_data'”。

我确实看过这个帖子 [1],但它已经过时并且没有明确的解决方案。

【问题讨论】:

    标签: python python-3.x user-interface pyqt pyqt5


    【解决方案1】:

    子类化 QListWidgetItem 对您没有帮助,因为拖放数据始终是序列化的,这意味着不会交换任何实例引用。

    如果您的数据是可序列化的(显然是字符串,但通常任何 QVariant 类型都可以,例如 QColor 或 QPixmap),您可以只使用 QListWidgetItem.setData() 和特定于您要存储的每个数据字段的自定义角色,并且然后使用QListWidgetItem.data() 取回该数据。

    在这种情况下,我创建了一个自定义函数,但这显然不是必需的,因为您可以在插入之前或之后手动设置每个项目的数据,只要您有正确的行索引。

    MySpecialRole = QtCore.Qt.UserRole + 1
    
    class DragAndDropList(QtWidgets.QListWidget):
        def addCustomItem(self, name, data):
            item = QtWidgets.QListWidgetItem(name)
            item.setData(MySpecialRole, data)
            self.addItem(item)
    
        def dropEvent(self, event):
            super().dropEvent(event)
            # let's see the data for each item, including our custom role
            for row in range(self.count()):
                item = self.item(row)
                print('Data for item {}: {}'.format(
                    row + 1, item.data(MySpecialRole)))
    
    
    class MyCentralWidget(QtWidgets.QWidget):
        def __init__(self, parent=None):
            super(MyCentralWidget, self).__init__(parent)
            self.h_layout = QtWidgets.QHBoxLayout(self)
            self.setLayout(self.h_layout)
    
            self.list_left = DragAndDropList()
            self.list_left.setDragEnabled(True)
    
            self.list_right = DragAndDropList()
            self.list_right.setAcceptDrops(True)
    
            self.h_layout.addWidget(self.list_left)
            self.h_layout.addWidget(self.list_right)
    
            self.list_left.addCustomItem('item (using addCustomItem)', 'some data')
            self.list_left.addItem('item without custom data')
            self.list_left.addItem('this item will have data')
            self.list_left.item(2).setData(MySpecialRole, 'some custom data')
    

    请注意,以下行会给您一个错误:

        self.emit(QtCore.SIGNAL("dropped"), links)
    

    不仅列表小部件没有emit 属性(就像QObject 的任何其他子类一样没有),而且必须在类定义中声明自定义信号,然后直接发出。

    class DragAndDropList(QtWidgets.QListWidget):
        dropped = QtCore.pyqtSignal(object)
        def dropEvent(self, event):
            # ...
            self.dropped.emit(links)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-12-05
      • 1970-01-01
      • 2011-12-15
      • 2021-11-07
      • 1970-01-01
      • 2020-06-19
      • 1970-01-01
      相关资源
      最近更新 更多