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



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

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

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

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

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

下一个是显示正确消息的示例(好例子)

为了实现自调整消息,需要注意一些事项。

正如我在相关文章的回答和评论中所解释的那样,你必须考虑布局尺寸与其要求之间复杂而微妙的关系,当文本布局没有固定的比例时,这种关系变得更加复杂。

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

考虑到以上,我们必须对我的原始代码做以下更改:
  • 滚动区域必须始终为其消息部件设置最大宽度,可能在视图调整大小时使用指定的边距(用于发送/接收区分);
  • 部件必须通过align参数添加到布局中;
  • 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())

最新更新