我有一个QListWidget
,它通过.setItemWidget()
和拖放模式InternalMove
由QLabel
填充,当我在列表中移动一个项目时,它的标签就会消失。
我该如何解决这个问题?
一个最小的例子来重现
from PyQt5.QtWidgets import (
QApplication, QLabel, QStyle,
QListWidget, QListWidgetItem
)
from PyQt5.QtCore import QSize
import sys
if __name__ == '__main__':
app = QApplication(sys.argv)
list = QListWidget()
list.setFixedHeight(400)
list.setDragDropMode(QListWidget.DragDropMode.InternalMove)
for _ in range(8):
item = QListWidgetItem()
item.setSizeHint(QSize(40, 40))
list.addItem(item)
label = QLabel()
label.setPixmap(list.style().standardIcon(
QStyle.StandardPixmap.SP_ArrowUp).pixmap(QSize(40,40)))
list.setItemWidget(item, label)
list.show()
sys.exit(app.exec())
编辑
在阅读.setItemWidget()
的文档后,其中指出:
此函数仅用于在列表小部件项的位置显示静态内容。如果您希望显示自定义动态内容或实现自定义编辑器小部件,请使用QListView和子类QStyledItemDelegate。
我想知道这是否与这个问题有关,在这个上下文中“静态内容”是什么意思,QLabel
被认为是“动态内容”吗?
编辑#2
问题是在dropEvent()
内部调用了dropMimeData()
,这反过来又创建了一个完整的新项?(调用了rowsInserted
),我猜这不应该发生在self项上,因为拖动项中的小部件集没有序列化并存储在mimedata
中,所以小部件是解耦的,当您从不同的列表中拖放项目时,通常会调用dropMimeData()
。
所以我想解决这个问题的一个丑陋的方法是通过QMimeData.setData()
将QListWidget.mimeData()
中手动序列化的小部件存储为自定义mimetype
,并在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)
# e.g. serialize pixmap
custommime = []
for item in items:
label:QLabel = self.itemWidget(item)
buff = QBuffer()
buff.open(QIODevice.OpenModeFlag.WriteOnly)
label.pixmap().save(buff, 'PNG')
buff.close()
custommime.append(buff.data())
mimedata.setData('application/custommime', pickle.dumps(custommime))
#
return mimedata
def dropMimeData(self, index:int, mimedata:QMimeData, action) -> bool:
result = QListWidget.dropMimeData(self, index, mimedata, action)
# e.g. recreate 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 = ListWidget()
list.setFixedHeight(400)
list.setDragDropMode(QListWidget.DragDropMode.InternalMove)
list.setSelectionMode(QListWidget.SelectionMode.ExtendedSelection)
for i in range(8):
item = QListWidgetItem()
item.setSizeHint(QSize(40, 40))
list.addItem(item)
label = QLabel()
label.setPixmap(list.style().standardIcon(
QStyle.StandardPixmap.SP_DialogOkButton + i).pixmap(QSize(40,40)))
list.setItemWidget(item, label)
list.show()
sys.exit(app.exec())
2条答案
按热度按时间u4dcyp6a1#
这是由一个Qt bug引起的,它只影响最近的版本。我可以在使用Qt-5.15.6和Qt-6.4.0时一直重现它-但不是例如Qt-5.12.1。这个问题似乎与QTBUG-100128密切相关。
PyQt 5/6(基于solution by PaddleStroke)的解决方案如下:
旧答案:
不幸的是,经过今天的进一步实验,似乎下面给出的建议的解决方案不是一个有效的解决方案。我发现也可以通过拖放到非空区域来使项目小部件消失。
在测试了Qt 5的其他版本后,我可以确认该错误在5.12.x,5.13.x,5.14.x,5.15.0和5.15.1中完全不存在。这与上面现有的Qt错误报告一致,该报告将Qt-5.15.2确定为引入错误的版本。
与问题中的建议相反,没有任何理由不将标签用作项目小部件。术语“静态内容”只是意味着“不通过用户定义的自定义绘图更新”。
这个bug似乎是QTBUG-87057的回归,QTBUG-87057在拖放过程中对列表视图行的移动方式进行了大量的内部更改。这些更改的复杂性可能意味着不可能有一个简单的解决方案来消除其负面影响。这些更改影响所有Qt 5版本5.15.1以上和Qt 6版本6.0以上。
AFAICS,这只影响将视图中的当前最后一项拖放到空白区域。其他项和多项选择不受影响。这建议使用以下解决方法:
或者使用事件过滤器:
t9eec4r02#
这里有一个方法可以防止bug的发生: