gpt4 book ai didi

python-3.x - QListWidgetItem 内的小部件在内部移动后消失

转载 作者:行者123 更新时间:2023-12-03 08:00:50 27 4
gpt4 key购买 nike

我有一个 QListWidget,它由 QLabel 通过 .setItemWidget() 和拖放模式 InternalMove 填充code>,当我在列表中移动项目时,其标签就会消失。

如何解决这个问题?

enter image description here

重现的最小示例

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() 的文档后,其中指出:

This function should only be used to display static content in the place of a list widget item. If you want to display custom dynamic content or implement a custom editor widget, use QListView and subclass QStyledItemDelegate instead.

我想知道这是否与该问题有关,以及在这种情况下“静态内容”意味着什么,QLabel 是否被视为“动态内容”?

编辑#2

问题出在 dropEvent() 内部,调用 dropMimeData() 进而创建一个完整的新项目? (rowsInserted 被调用),我猜这不应该发生在 self 项目上,因为拖动项目中设置的小部件没有序列化并存储在 mimedata 中,所以该小部件是解耦的,当您从不同的列表中拖放项目时,通常会调用 dropMimeData()

所以我想解决这个问题的一个丑陋的方法是通过QMimeData将手动序列化的小部件存储在QListWidget.mimeData()中作为自定义mimetype。 setData() 并在放入 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())

最佳答案

更新

该错误现已在最新版本的 Qt5 和 Qt6 中修复。


这是由 Qt 错误引起的,该错误仅影响最近的版本。当使用 Qt-5.15.6 和 Qt-6.4.0 时,我可以一致地重现它 - 但不能,例如Qt-5.12.1。该问题似乎与 QTBUG-100128 密切相关.

PyQt5/6 的解决方法(基于 solution by PaddleStroke )如下:

class ListWidget(QListWidget):
def dragMoveEvent(self, event):
if ((target := self.row(self.itemAt(event.pos()))) ==
(current := self.currentRow()) + 1 or
(current == self.count() - 1 and target == -1)):
event.ignore()
else:
super().dragMoveEvent(event)

旧答案:

不幸的是,经过今天的一些进一步实验,下面给出的建议解决方法似乎并不是一个有效的解决方案。我发现还可以通过拖放到非空区域来使项目小部件消失。

在测试了 Qt5 的其他版本后,我可以确认该错误在 5.12.x、5.13.x、5.14.x、5.15.0 和 5.15.1 中完全不存在。这与上面现有的 Qt 错误报告一致,该报告将 Qt-5.15.2 确定为引入该错误的版本。

与问题中建议的相反,没有任何理由不应该将标签用作项目小部件。术语“静态内容”,仅表示“不由用户定义的自定义绘图更新”。

这个错误似乎是 QTBUG-87057 的回归,这对拖放过程中 ListView 行的移动方式进行了大量内部更改。这些变化的复杂性可能意味着消除其负面影响的简单解决方法是不可能的。这些更改影响所有高于 5.15.1 的 Qt5 版本和高于 6.0 的 Qt6 版本。


<删除>AFAICS,这仅影响将 View 中当前最后一项拖放到空白区域。其他项目和多项选择不受影响。这建议采用以下解决方法:
class ListWidget(QListWidget):
def dropEvent(self, event):
if (self.currentRow() < self.count() - 1 or
self.itemAt(event.pos()) is not None):
super().dropEvent(event)

list = ListWidget()
...

或使用事件过滤器:

class Monitor(QObject):
def eventFilter(self, source, event):
if event.type() == QEvent.Drop:
view = source.parent()
if (view.currentRow() == view.count() - 1 and
view.itemAt(event.pos()) is None):
return True
return super().eventFilter(source, event)

monitor = Monitor()
list = QListWidget()
list.viewport().installEventFilter(monitor)
...

关于python-3.x - QListWidgetItem 内的小部件在内部移动后消失,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/74263946/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com