简体   繁体   English

如何拖放具有附加属性的 QListWidgetItem 的子 class 实例?

[英]How can I Drag and Drop instances of a child class of QListWidgetItem, which have additional attributes?

I am using PyQt5 and quite new to it.我正在使用 PyQt5 并且对它很新。 I would like to drag and drop a QListWidgetItem from one QListWidget to the other, such that the resulting QListWidgetItem on one side will contain additional data.我想将 QListWidgetItem 从一个 QListWidget 拖放到另一个,这样在一侧生成的 QListWidgetItem 将包含其他数据。 I tried sub-classing QListWidgetItem, but the type does not carry through drag and drop as it seems to create a new instance of QListWidgetItem.我尝试对 QListWidgetItem 进行子类化,但该类型不会通过拖放进行,因为它似乎创建了 QListWidgetItem 的新实例。

Here is the code:这是代码:

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()

Here I get the error: "AttributeError: 'QListWidgetItem' object has no attribute 'extra_data'", when I try to drag and drop the custom_item.当我尝试拖放 custom_item 时,出现错误:“AttributeError: 'QListWidgetItem' object has no attribute 'extra_data'”。

I did have look at this thread [ 1 ], but it is out of date and there were no clear solutions.我确实看过这个线程 [ 1 ],但它已经过时并且没有明确的解决方案。

Subclassing QListWidgetItem won't help you, as the drag and drop data is always serialized, which means that no instance reference is ever exchanged.子类化 QListWidgetItem 对您没有帮助,因为拖放数据始终是序列化的,这意味着不会交换任何实例引用。

If your data is serializable (strings, obviously, but usually any QVariant type is fine, such as QColor or QPixmap), you can just use QListWidgetItem.setData() with a custom role specific for every data field you want to store, and then use QListWidgetItem.data() to get that data back.如果您的数据是可序列化的(显然是字符串,但通常任何 QVariant 类型都可以,例如 QColor 或 QPixmap),您可以将QListWidgetItem.setData()与您要存储的每个数据字段特定的自定义角色一起使用,然后使用QListWidgetItem.data()取回该数据。

In this case I created a custom function, but that's obviously not necessary, as you can just manually set the data for each item before or after inserting it, as long as you have the right row index.在这种情况下,我创建了一个自定义 function,但这显然不是必需的,因为您可以在插入之前或之后手动设置每个项目的数据,只要您有正确的行索引。

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')

Note that the following line will give you an error:请注意,以下行会给您一个错误:

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

Not only the list widget doesn't have an emit attribute (like any other subclass of QObject doesn't), but also custom signals have to be declared in the class definition and then emitted directly.不仅列表小部件没有emit属性(就像 QObject 的任何其他子类一样没有),而且自定义信号必须在 class 定义中声明,然后直接发出。

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 拖放子类QListWidgetItem - Drag and Drop of subclassed QListWidgetItem 如何在REPL中测试同一类的两个实例是否具有相同的属性? - How can I test in REPL whether two instances of the same class have the same attributes? 如何生成具有相互依赖属性的类的实例? - How can I generate instances of a class with inter-dependent attributes? 如何将带有标签的 GIF 添加到 QListWidgetItem? - How can I add a GIF with a label to a QListWidgetItem? 如何在子 class 中使用父 class 的属性 - How can I use attributes from a parent class in a child class 如何访问另一个类的实例中保存的类实例中的属性? - How can I access attributes in class instances held inside an instance of another class? 如何将robot-ide (RIDE) 扩展为有一个选项卡,以便我可以拖放机器人脚本命令? - How to extend robot-ide (RIDE) to have a tab so that I can drag and drop robot script commands? 如何向类添加其他属性? - How to add additional attributes to a class? 如何动态生成具有从Python中的平面文件读取的单个属性的类实例? - How can I dynamically generate class instances with single attributes read from flat file in Python? 确保类的实例具有唯一的属性值 - Ensuring instances of the class have unique attributes' values
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM