[英]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.