Python QStyledItemDelegate';s选项未更新
我在使用PyQt5时遇到问题。我有一个列表,上面有Python QStyledItemDelegate';s选项未更新,python,pyqt,pyqt5,qstyleditemdelegate,Python,Pyqt,Pyqt5,Qstyleditemdelegate,我在使用PyQt5时遇到问题。我有一个列表,上面有QStyledItemDelegate类绘制其项目。以下是最小可重复性示例: import sys from PyQt5.QtCore import ( QAbstractListModel, Qt, QSize, QRect, QRectF, ) from PyQt5.QtGui import ( QPainter, QFontMetrics, QFont, QTextDo
QStyledItemDelegate
类绘制其项目。以下是最小可重复性示例:
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
之前,我正在打印每个项目的高度。我还打印在paint中接收的option.rect
的Y坐标。因为我只有两个项目,所以我希望项目1的坐标等于项目0的高度。一开始它似乎在解决:
sizeHint 0 23
paint 0 0
sizeHint 1 23
paint 1 23
但是,当我缩小窗口时,sizeHint
中的高度开始增加(因为窄窗口无法容纳所有内容),但选项.rect的Y坐标保持不变:
sizeHint 0 46
paint 0 0
sizeHint 1 23
paint 1 23
即使到达第三行,option.rect的位置也不会更新:
sizeHint 0 69
paint 0 0
sizeHint 1 23
paint 1 23
因此,item1与item0重叠,而不是向下移动
有没有一种方法可以在前面的一个项目的大小发生变化时立即更新选项.rect
位置?项目内容的包装已经在QListView
中实现,并且可以通过self.messages.setWordWrap(True)
启用。如果仅将委托用于文本包装,则根本不需要它。当索引的大小提示更改时,需要发出MessageDelegate.sizeHintChanged
,让项目的布局管理器知道它需要重新分发项目。在这种情况下,项目的高度仅在调整窗口大小时更改,因此您可以在对话框.resizeEvent
中发出(自定义)信号,并将其连接到MessageDelegate.sizeHintChanged
。对于此对话框
需要根据以下内容进行修改
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()
在上面的代码中,我没有修改代码中的任何其他内容,但是您可以选择通过修改自定义信号来释放新宽度,并在MessageDelegate
中创建一个槽来将新宽度分配给实例变量,从而去掉全局window\u width
变量。此代码只是问题的演示。实际上,我画了很多画,我从问题中删去了,因为这与我的作品无关。