如何解决QStyledItemDelegate 的选项未更新
我在使用 PyQt5 时遇到了问题。我有一个带有 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,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,**kwargs):
super(MessageModel,**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 的高度。起初它似乎奏效了:
sizeHint 0 23
paint 0 0
sizeHint 1 23
paint 1 23
然而,当我缩小窗口时,sizeHint
中的高度开始增长(因为狭窄的窗口无法容纳所有内容)但 option.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 重叠而不是向下移动。
有没有办法在先前项目之一的大小发生变化时立即更新 option.rect
位置?
解决方法
项目内容的包装已经在 QListView
中实现,可以通过 self.messages.setWordWrap(True)
启用。如果您仅将委托用于文本换行,则根本不需要它。
当索引的大小提示发生变化时,您需要发出 MessageDelegate.sizeHintChanged
以让项目的布局管理器知道它需要重新分配项目。在这种情况下,项目的高度仅在调整窗口大小时发生变化,因此您可以做的是在 Dialog.resizeEvent
中发出(自定义)信号并将其连接到 MessageDelegate.sizeHintChanged
。为此,Dialog
需要根据类似这样的内容进行修改
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
变量{1}} 将新宽度分配给实例变量。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。