QStyledItemDelegate 的选项没有更新 [英] QStyledItemDelegate's option is not updating
问题描述
我在使用 PyQt5 时遇到了问题.我有一个带有 QStyledItemDelegate
类绘制其项目的列表.这是最小的可重现示例:
I ran into a problem while using PyQt5. I have a list with QStyledItemDelegate
class painting its items. Here is the minimal reproducible example:
import sys
from PyQt5.QtCore import (
QAbstractListModel,
Qt,
QSize,
QRect,
QRectF,
)
from PyQt5.QtGui import (
QPainter,
QFontMetrics,
QFont,
QTextDocument,
QTextOption,
QPen,
)
from PyQt5.QtWidgets import (
QApplication,
QListView,
QMainWindow,
QStyledItemDelegate,
)
window_width = 0
class MessageDelegate(QStyledItemDelegate):
WINDOW_PADDING = 30
font = QFont("Times", 14)
def __init__(self, *args, **kwargs):
super(MessageDelegate, self).__init__(*args, **kwargs)
def paint(self, painter, option, index):
msg = index.model().data(index, Qt.DisplayRole)
print("paint " + str(index.row()) + " " + str(option.rect.top()))
field = QRect(option.rect)
doc = QTextDocument(msg)
doc.setDocumentMargin(0)
opt = QTextOption()
opt.setWrapMode(opt.WrapAtWordBoundaryOrAnywhere)
doc.setDefaultTextOption(opt)
doc.setDefaultFont(self.font)
doc.setTextWidth(field.size().width())
field.setHeight(int(doc.size().height()))
field.setWidth(int(doc.idealWidth()))
painter.setPen(Qt.gray)
painter.setFont(self.font)
painter.translate(field.x(), field.y())
textrectf = QRectF(field)
textrectf.moveTo(0, 0)
doc.drawContents(painter, textrectf)
painter.translate(-field.x(), -field.y())
def sizeHint(self, option, index):
global window_width
msg = index.model().data(index, Qt.DisplayRole)
doc = QTextDocument(msg)
doc.setDocumentMargin(0)
opt = QTextOption()
opt.setWrapMode(opt.WrapAtWordBoundaryOrAnywhere)
doc.setDefaultTextOption(opt)
doc.setDefaultFont(self.font)
doc.setTextWidth(window_width - self.WINDOW_PADDING)
print("sizeHint " + str(index.row()) + " " + str(int(doc.size().height())))
return QSize(0, int(doc.size().height()))
class MessageModel(QAbstractListModel):
def __init__(self, *args, **kwargs):
super(MessageModel, self).__init__(*args, **kwargs)
self.messages = []
def data(self, index, role):
if role == Qt.DisplayRole:
return self.messages[index.row()]
def rowCount(self, index):
return len(self.messages)
def add_message(self, text):
if text:
self.messages.append(text)
self.layoutChanged.emit()
class Dialog(QMainWindow):
def __init__(self):
global window_width
super(Dialog, self).__init__()
self.setMinimumSize(int(QApplication.primaryScreen().size().width() * 0.1), int(QApplication.primaryScreen().size().height() * 0.2))
self.resize(int(QApplication.primaryScreen().size().width() * 0.3), int(QApplication.primaryScreen().size().height() * 0.5))
window_width = int(QApplication.primaryScreen().size().width() * 0.3)
self.messages = QListView()
self.messages.setItemDelegate(MessageDelegate())
self.model = MessageModel()
self.messages.setModel(self.model)
self.model.add_message("qwerty qwerty qwerty qwerty qwerty qwerty qwerty qwerty qwerty qwerty qwerty qwerty qwerty qwerty")
self.model.add_message("abcdef")
self.setCentralWidget(self.messages)
def resizeEvent(self, event):
global window_width
super(Dialog, self).resizeEvent(event)
window_width = self.size().width()
app = QApplication(sys.argv)
window = Dialog()
window.show()
app.exec_()
如您所见,我在 sizeHint
中返回每个项目之前打印了它的高度.我还打印了在油漆中收到的 option.rect
的 Y 坐标.由于我只有两个项目,我希望 item1 的坐标等于 item0 的高度.起初它似乎奏效了:
As you can see I am printing the height of each item before returning it in sizeHint
. I also print the Y coordinate of option.rect
received in paint. As I have only two items, I expect the coordinate of the item1 to be equal to the height of item0. And at first it seems to be working out:
sizeHint 0 23
paint 0 0
sizeHint 1 23
paint 1 23
然而,当我缩小窗口时,sizeHint
中的高度开始增长(因为窄窗口无法容纳所有内容)但 option.rect<的 Y 坐标/code> 保持不变:
However, as I narrow down the window the height in sizeHint
starts to grow (because the narrow window can't fit all the contents) but the Y coordinate of option.rect
stays the same:
sizeHint 0 46
paint 0 0
sizeHint 1 23
paint 1 23
即使到了第三行,option.rect
的位置也没有更新:
Even when I get to the third line the position of option.rect
is not updating:
sizeHint 0 69
paint 0 0
sizeHint 1 23
paint 1 23
由于 item1 与 item0 重叠而不是向下移动.
As a result of that item1 overlaps item0 instead of moving down.
有没有办法在先前项目之一的大小发生变化时立即更新 option.rect
位置?
Is there a way to update option.rect
position as soon as the size of one of previous items changes?
推荐答案
当索引的大小提示发生变化时,您需要发出 MessageDelegate.sizeHintChanged
让项目的布局管理器知道它需要重新分配项目.在这种情况下,项目的高度仅在调整窗口大小时发生变化,因此您可以做的是在 Dialog.resizeEvent
中发出(自定义)信号并将其连接到 MessageDelegate.sizeHintChanged代码>.对于这个
Dialog
需要根据这样的东西进行修改
When the size hint of an index changes, you need to emit MessageDelegate.sizeHintChanged
to let the layout manager of the items know that it needs to redistribute the items. In this case the height of the items only changes when the window is resized, so what you could do is to emit a (custom) signal in Dialog.resizeEvent
and connect it to MessageDelegate.sizeHintChanged
. For this Dialog
would need to be modified according to something like this
from PyQt5.QtCore import pyqtSignal, QModelIndex
class Dialog(QMainWindow):
# custom signal for signalling when window is resized
width_changed = pyqtSignal()
def __init__(self):
... as before ...
self.messages = QListView()
delegate = MessageDelegate()
self.messages.setItemDelegate(delegate)
# Connect custom signal to delegate.sizeHintChanged.
# This signal expects a ModelIndex for which we take the root, i.e. QModelIndex()
self.width_changed.connect(lambda : delegate.sizeHintChanged.emit(QModelIndex()))
.... rest as before ....
def resizeEvent(self, event):
global window_width
super(Dialog, self).resizeEvent(event)
window_width = self.size().width()
# emit the custom signal
self.width_changed.emit()
在上面的代码中,我没有修改代码中的任何其他内容,但是您可以选择通过修改自定义信号以发出新的宽度并创建一个来摆脱全局 window_width
变量插入 MessageDelegate
以将新宽度分配给实例变量.
In the code above I didn't modify anything else in the code, but you could opt to get rid of the global window_width
variable by modifying the custom signal to emit the new width and create a slot in MessageDelegate
to assign the new width to an instance variable.
这篇关于QStyledItemDelegate 的选项没有更新的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!