繁体   English   中英

PySide2 QTextEdit 在使用包装时不会调整到它自己的内容。 (制作聊天窗口)

[英]PySide2 QTextEdit doesn't adjust to it's own content when using wrapping. (Making chat window)

PySide2 QTextEdit 在放置文本时不会改变它自己的大小。

我正在尝试创建类似聊天窗口的东西,其中每条消息 - OnlyRead 模式下的 QTextEdit。 放置在 QScrollArea 中的所有“消息”。 主要目标是让消息框(如下屏幕上的消息框)根据内容调整其大小。 错误的工作示例

我试过这个代码https://ru.stackoverflow.com/questions/1408239/Как-сделать-двухсторонний-чат-в-qt-pyqt/1408264#1408264

这已经被复制粘贴了很多次。 但它没有做我想要的。 它创建一个固定的、不可调整大小的 QTextEdit 消息框。

例如我的实际意思是,如果我们有一个单字消息,QTextEdit 小部件必须成为一个单笔画框,具有消息的宽度。 如果我们有一个多句消息,QTextEdit 小部件必须成为一个多笔画框(已经扩展了高度,不需要在里面滚动),具有最大恒定长度(我将选择)。

接下来是显示正确消息的示例(很好的示例)

为了实现自调整消息,需要一些预防措施。

正如我对相关帖子的回答和评论中所解释的,您必须考虑布局尺寸与其要求之间的复杂而微妙的关系,由于文本布局没有固定的比例,这变得更加复杂。

根据文本设置宽度的主要问题是它可以改变显示它所需的高度,这可能会导致递归,并且由于大小是基于当前视口大小的,结果是minimumSizeHint()将在一定数量的递归调用后总是返回最小的可能大小。

考虑到上述情况,我们必须对我的原始代码进行以下更改:

  • 滚动区域必须始终为其消息小部件设置最大宽度,每当调整视图大小时,可能具有指定的边距(用于区分发送/接收);
  • 必须使用对齐参数将小部件添加到布局中;
  • minimumSizeHint()必须更改为:
    1. 根据小部件的最大尺寸计算首选文本宽度( idealWidth()
    2. 获取该文本宽度的参考高度;
    3. 将文本宽度设置为当前宽度;
    4. 比较新的文档高度和之前的高度,如果它们相同,则表示我们可以使用新的宽度作为提示的最大宽度(文本可以更短),否则我们使用基于最大尺寸的初始文本宽度;

请注意,与您的链接的修改代码存在一些差异:最重要的是,重写样式表没有多大意义,并且设置边距会导致frameWidth()返回的值出现问题(这就是为什么他们从文档高度中减去 100); 这当然不是一个好的选择,因为边距应该在布局中设置。

class WrapLabel(QtWidgets.QTextEdit):
    def __init__(self, text=''):
        super().__init__(text)
        self.setReadOnly(True)
        self.setSizePolicy(QtWidgets.QSizePolicy.Preferred, 
            QtWidgets.QSizePolicy.Maximum)
        self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
        self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
        self.textChanged.connect(self.updateGeometry)

    def minimumSizeHint(self):
        margin = self.frameWidth() * 2
        doc = self.document().clone()
        doc.setTextWidth(self.maximumWidth())
        idealWidth = doc.idealWidth()
        idealHeight = doc.size().height()
        doc.setTextWidth(self.viewport().width())
        if doc.size().height() == idealHeight:
            idealWidth = doc.idealWidth()
        return QtCore.QSize(
            max(50, idealWidth + margin), 
            doc.size().height() + margin)

    def sizeHint(self):
        return self.minimumSizeHint()

    def resizeEvent(self, event):
        super().resizeEvent(event)
        self.updateGeometry()


class ChatTest(QtWidgets.QScrollArea):
    def __init__(self):
        super().__init__()
        self.margin = 100
        self.marginRatio = .8
        self.messages = []

        container = QtWidgets.QWidget()
        self.setWidget(container)
        self.setWidgetResizable(True)

        layout = QtWidgets.QVBoxLayout(container)
        layout.addStretch()
        self.resize(480, 360)

        letters = 'abcdefghijklmnopqrstuvwxyz       '
        for i in range(1, 11):
            msg = ''.join(choice(letters) for i in range(randrange(10, 250)))
            QtCore.QTimer.singleShot(500 * i, lambda msg=msg, i=i:
                self.addMessage(msg, i & 1))

    def addMessage(self, text, sent=False):
        message = WrapLabel(text)
        message.setStyleSheet('''
            WrapLabel {{
                border: 1px outset palette(dark);
                border-radius: 8px;
                background: {};
            }}
        '''.format(
            '#fff8c7' if sent else '#ceffbd')
        )
        self.messages.append(message)
        self.widget().layout().addWidget(message, 
            alignment=QtCore.Qt.AlignRight if sent else QtCore.Qt.AlignLeft)
        QtCore.QTimer.singleShot(0, self.scrollToBottom)

    def scrollToBottom(self):
        QtWidgets.QApplication.processEvents()
        self.verticalScrollBar().setValue(
            self.verticalScrollBar().maximum())

    def resizeEvent(self, event):
        sb = self.verticalScrollBar()
        atMaximum = sb.value() == sb.maximum()
        maxWidth = max(self.width() * self.marginRatio, 
            self.width() - self.margin) - sb.sizeHint().width()
        for message in self.messages:
            message.setMaximumWidth(maxWidth)
        super().resizeEvent(event)
        if atMaximum:
            sb.setValue(sb.maximum())

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM