内部移动后QlistWidgetItem内部的小部件消失
我有一个qlistwidget
,由qlabel
通过.setItemWidget()
和一个拖放模式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 = 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())
要复制 edit
在阅读.setItemWidget()
的文档后,
。此功能仅应用于显示列表小部件项目的静态内容。如果要显示自定义动态内容或实现自定义编辑器窗口小部件,请改用QListView和子类QStyleDitemdelegate。
我想知道这是否与问题有关?在这种情况下,“静态内容”的意思是Qlabel
被考虑到“动态内容”?
编辑#2
问题在dropevent()
a dropmimedata()
中被调用,而这又创建了一个完整的新项目? (rowsinserted
被调用),我猜是不应该发生的自我项目,因为拖动项目中设置的窗口小部件未序列化并存储在mimedata
中小部件是分离的,当您从其他列表中拖动项目时,通常调用dropmimedata()
。
因此,我想解决此问题的一种丑陋方法是将手动序列化的小部件存储在qlistwidget.mimedata()
作为自定义mimeType
通过qmimimedata.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())
I have a QListWidget
which is populated by QLabel
via .setItemWidget()
and a drag and drop mode InternalMove
, when I move an item inside the list its label disappears.
How can I solve this issue?
A minimal example to reproduce
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())
edit
After reading the documentation for the .setItemWidget()
which states:
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.
I wonder if this is related to the issue and what does "static content" mean in this context, is QLabel
considered "dynamic content"?
edit #2
The problem is inside a dropEvent()
a dropMimeData()
is called which in turn creates a complete new item? (rowsInserted
is called), which isn't supposed to happen for self items I guess, because a widget set in the dragged item isn't serialized and stored inside mimedata
so the widget is decoupled, The dropMimeData()
is usually called when you drag and drop items from a different list.
So I guess an ugly way to solve this is to store a manually serialized widget inside a QListWidget.mimeData()
as a custom mimetype
via QMimeData.setData()
and recreate the widget after a drop inside QListWidget.dropMimeData()
.
for example:
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())
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
更新
该错误现已在QT5和QT6的最新版本中修复。
这是由QT错误引起的,该错误仅影响相当最近的版本。我可以在使用QT-5.15.6和QT-6.4.0-但不能始终重现它 - 但不能QT-5.12.1。这个问题似乎与 qtbug-100128 。
PYQT5/6的工作 - 基于 paddlestroke 的解决方案)如下:
/strong>:
不幸的是,经过今天进一步的实验,下面建议的工作似乎不是一个有效的解决方案。我发现,也有可能通过拖放到非空地区而消失。
在测试了QT5的其他一些版本后,我可以确认在5.12.x,5.13.x,5.14.x,5.15.0和5.15.1中完全不存在该错误。这与上面的现有QT错误报告一致,该报告将QT-5.15.2标识为引入错误的版本。
与问题所建议的相反,没有理由为什么不应将标签用作项目网络。术语“静态内容”,只是指“未通过用户定义的自定义图形更新”。
此错误似乎是从 qtbug-87057 的回归。内部更改列表视图行在拖放过程中移动的方式。这些变化的复杂性可能意味着一个简单的工作,无法消除其负面副作用。这些变化会影响大于5.15.1和QT6版本大于6.0的所有QT5版本。
AFAICS, this only affects dragging and dropping the current last item in the view onto a blank area. Other items and multiple selections aren't affected. This suggests the following work-around:
或使用事件过滤器:
UPDATE
The bug has now been fixed in the latest versions of Qt5 and Qt6.
This is caused by a Qt bug which only affects fairly recent versions. I can consistently reproduce it when using Qt-5.15.6 and Qt-6.4.0 - but not e.g. Qt-5.12.1. The issue seems to be closely related to QTBUG-100128.
A work-around for PyQt5/6 (based on the solution by PaddleStroke) is as follows:
OLD ANSWER:
Unfortunately, after some further experimentation today, it seems the suggested work-around given below isn't an effective solution. I have found it's also possible to make item-widgets disappear by drag and drop onto non-empty areas.
After testing some other versions of Qt5, I can confirm that the bug is completely absent in 5.12.x, 5.13.x, 5.14.x, 5.15.0 and 5.15.1. This agrees with the existing Qt bug report above which identified Qt-5.15.2 as the version where the bug was introduced.
Contrary to what is suggested in the question, there's no reason whatsoever why a label should not be used as an item-widget. The term "static content", just means "not updated by user-defined custom drawing".
This bug seems to be a regression from QTBUG-87057, which made quite a large number of internal changes to how list-view rows are moved during drag and drop. The complexity of those changes may mean a simple work-around that undoes its negative side-effects isn't possible. The changes affect all Qt5 versions greater than 5.15.1 and Qt6 versions greater than 6.0.
AFAICS, this only affects dragging and dropping the current last item in the view onto a blank area. Other items and multiple selections aren't affected. This suggests the following work-around:
or using an event-filter:
在这里,防止错误发生的方法:
Here' s a way to prevent the bug from happening :