QListWidgetItem内Widget在InternalMove拖拽后消失的解决方法
QListWidget内部拖拽时ItemWidget消失的解决方法
我通过.setItemWidget()方法向QListWidget中添加QLabel,并设置了InternalMove拖拽模式,但在列表内移动项时,对应的标签会消失。如何解决该问题?

复现问题的最小示例
from PyQt5.QtWidgets import ( QApplication, QLabel, QStyle, QListWidget, QListWidgetItem ) from PyQt5.QtCore import QSize import sys if __name__ == '__main__': app = QApplication(sys.argv) list_widget = QListWidget() list_widget.setFixedHeight(400) list_widget.setDragDropMode(QListWidget.DragDropMode.InternalMove) for _ in range(8): item = QListWidgetItem() item.setSizeHint(QSize(40, 40)) list_widget.addItem(item) label = QLabel() label.setPixmap(list_widget.style().standardIcon( QStyle.StandardPixmap.SP_ArrowUp).pixmap(QSize(40,40))) list_widget.setItemWidget(item, label) list_widget.show() sys.exit(app.exec())
编辑
查阅.setItemWidget()的文档后发现说明:
该函数仅应用于在列表部件项的位置显示静态内容。如果要显示自定义动态内容或实现自定义编辑器部件,请使用QListView并子类化QStyledItemDelegate。
我想知道这是否与当前问题相关,此处的“静态内容”指什么,QLabel是否属于“动态内容”?
编辑#2
问题根源在于dropEvent()会调用dropMimeData(),而该方法会创建全新的项(触发rowsInserted)——这对内部项移动来说是不合理的,因为拖拽项中设置的部件不会被序列化存储到mimedata中,导致部件与项解耦。dropMimeData()原本是为跨列表拖拽场景设计的。
可行的解决方法是:在QListWidget.mimeData()中通过QMimeData.setData()将部件的关键数据手动序列化,以自定义MIME类型存储;然后在QListWidget.dropMimeData()中完成拖拽后,重新创建部件并绑定到对应项上。示例代码如下:
from PyQt5.QtWidgets import ( QApplication, QLabel, QStyle, QListWidget, QListWidgetItem ) from PyQt5.QtCore import QSize, QMimeData, QBuffer, QIODevice from PyQt5.QtGui import QPixmap import pickle import sys class ListWidget(QListWidget): def mimeData(self, items:list[QListWidgetItem]) -> QMimeData: mimedata = QListWidget.mimeData(self, items) # 序列化标签中的Pixmap custom_data = [] for item in items: label:QLabel = self.itemWidget(item) buff = QBuffer() buff.open(QIODevice.OpenModeFlag.WriteOnly) label.pixmap().save(buff, 'PNG') buff.close() custom_data.append(buff.data()) mimedata.setData('application/custommime', pickle.dumps(custom_data)) return mimedata def dropMimeData(self, index:int, mimedata:QMimeData, action) -> bool: result = QListWidget.dropMimeData(self, index, mimedata, action) # 重新创建标签并绑定Pixmap if mimedata.hasFormat('application/custommime'): for i, data in enumerate( pickle.loads(mimedata.data('application/custommime')), start=index): pixmap = QPixmap() pixmap.loadFromData(data, 'PNG') label = QLabel() label.setPixmap(pixmap) self.setItemWidget(self.item(i), label) return result if __name__ == '__main__': app = QApplication(sys.argv) list_widget = ListWidget() list_widget.setFixedHeight(400) list_widget.setDragDropMode(QListWidget.DragDropMode.InternalMove) list_widget.setSelectionMode(QListWidget.SelectionMode.ExtendedSelection) for i in range(8): item = QListWidgetItem() item.setSizeHint(QSize(40, 40)) list_widget.addItem(item) label = QLabel() label.setPixmap(list_widget.style().standardIcon( QStyle.StandardPixmap.SP_DialogOkButton + i).pixmap(QSize(40,40))) list_widget.setItemWidget(item, label) list_widget.show() sys.exit(app.exec())
内容的提问来源于stack exchange,提问作者user12653758
相关产品推荐
相关产品推荐

