首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >QStyledItemDelegate的选项未更新

QStyledItemDelegate的选项未更新
EN

Stack Overflow用户
提问于 2021-05-02 20:04:04
回答 2查看 90关注 0票数 0

我在使用PyQt5时遇到了一个问题。我有一个用QStyledItemDelegate类绘制它的项目的列表。下面是最小的可重现示例:

代码语言:javascript
复制
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坐标。因为我只有两个项目,所以我希望item1的坐标等于item0的高度。乍一看,这似乎是可行的:

代码语言:javascript
复制
sizeHint 0 23
paint 0 0
sizeHint 1 23
paint 1 23

然而,当我缩小窗口的范围时,sizeHint中的高度开始增长(因为窄窗口无法容纳所有内容),但option.rect的Y坐标保持不变:

代码语言:javascript
复制
sizeHint 0 46
paint 0 0
sizeHint 1 23
paint 1 23

即使到了第三行,option.rect的位置也没有更新:

代码语言:javascript
复制
sizeHint 0 69
paint 0 0
sizeHint 1 23
paint 1 23

因此,item1与item0重叠而不是向下移动。

有没有办法在之前某一项的大小发生变化时立即更新option.rect位置?

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2021-05-08 15:22:00

当索引的大小提示发生变化时,您需要发出MessageDelegate.sizeHintChanged,让条目的布局管理器知道它需要重新分布条目。在这种情况下,只有在调整窗口大小时,项目的高度才会改变,所以您可以在Dialog.resizeEvent中发出(自定义)信号并将其连接到MessageDelegate.sizeHintChanged。对于这个Dialog,需要根据如下所示进行修改

代码语言:javascript
复制
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中创建一个插槽以将新的宽度分配给一个实例变量。

票数 0
EN

Stack Overflow用户

发布于 2021-05-03 04:58:56

项目内容的包装已经在QListView中实现,并且可以使用self.messages.setWordWrap(True)启用。如果你只使用委托进行文本换行,你根本就不需要它。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/67356159

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档