PySide2 QTextEdit не меняет свой размер при размещении текста.
Я пытаюсь создать что-то вроде окна чата, где каждое сообщение - QTextEdit в режиме OnlyRead. Все «сообщения» помещаются в QScrollArea. Основная цель состоит в том, чтобы позволить окнам сообщений (окнам сообщений, как на экране ниже) подстраивать свой размер под контент. неправильный рабочий пример
я попробовал этот код https://ru.stackoverflow.com/questions/1408239/Как-сделать-двухсторонний-чат-в-qt-pyqt/1408264#1408264
который был скопирован много раз. Но это не делает то, что я хочу. Он создает фиксированные, не изменяемые по размеру окна сообщений QTextEdit.
Например, что я на самом деле имею в виду, если у нас есть сообщение из одного слова, виджет QTextEdit должен стать рамкой с одним штрихом с шириной сообщения. Если у нас есть сообщение, состоящее из нескольких предложений, виджет QTextEdit должен стать многострочным полем (уже расширенным по высоте, без необходимости прокручивать его внутри) с максимальной постоянной длиной (которую я выберу).
Далее пример с правильным отображением сообщений (хороший пример)
@musicamante Финнали, я встретил тебя, автор примера. Пожалуйста, помогите с идеями т_т. Я на самом деле не понимаю, как QTextEdit может приспосабливаться к содержимому. В вашем примере QTextEdit просто добавляет ScrollArea и размеры к ширине всего окна, но мне нужно прекратить изменение размера сообщения, когда оно достигает 100 пикселей (например), и перенести слово на следующую строку, поэтому сообщение должно выглядеть как прямоугольник, который не расширяется на всю ширину окна и не превышает ширину 100 пикселей. Но когда у меня есть небольшое сообщение, которое будет меньше 100 пикселей, QTextEdit должен стать меньше, как и ширина сообщения.
@musicamante Где я могу найти информацию о переопределении функций MinimumSizeHint, SizeHint и ResizeEvent?
Если ваше единственное требование — ограничить ширину до 100 пикселей, измените возвращаемое значение minimumSizeHint()
на return QtCore.QSize(100, height)
. Вся необходимая информация есть в документации: minimumSizeHint()
, sizeHint()
и resizeEvent()
. Помните, что это все методы виртуальный, которые могут быть переопределены в подклассах, и каждый класс может переопределить их (что и происходит для QTextEdit и унаследованного QAbstractScrollArea).
Имейте в виду, что, как объясняется в комментариях к исходному вопросу, существуют важные проблемы, связанные с изменением размера элемента в соответствии с его содержимым, когда соотношение сторон содержимого может измениться на лету. Это обычная проблема с менеджерами компоновки (не только для Qt), поскольку размер намеки может меняться в зависимости от брат или сестра и родительских элементов (что происходит с областями прокрутки из-за того, что полосы прокрутки могут изменять размер внутренней области). видового экрана и в конечном итоге вызвать рекурсию). Не существует «окончательного» решения, вы должны найти свой собственный обходной путь в зависимости от ваших потребностей.
@musicamante большое спасибо за ваши ответы! Я попытался изменить возвращаемое значение MinimumSizeHint, но это не сработало. Все, что я могу сделать, это увеличить значение ширины, но не уменьшить. Он всегда остается на ширине окна и никогда не уменьшается.
@musicamante не знаю, но иногда мои комментарии пропадают. Кстати, я хотел сказать спасибо за вашу помощь. Я действительно ценю это. (у меня есть еще один вопрос. как я понял, нет возможности добавлять поля внутри QTextEdit из-за изменения его размера? может быть, мне следует поместить QTextEdit во фрейм и добавить поля в фрейм?)
Если вы хотите добавить поля, вы либо делаете это (и правильно) в таблице стилей, либо устанавливаете их для редактирования текста textDocument()
через setDocumentMargin()
. Обратите внимание, что добавление полей с таблицами стилей может изменить результат minimumSizeHint()
, как объясняется в моем ответе.
@musicamante еще раз большое спасибо!!! он снова работает))) (извините за пинг в комментариях. я на самом деле не могу задавать вопросы за 4 дня на stackoverflow Т-Т. кстати, я хочу задать вам вопрос в последний (надеюсь) раз. можно ли создать прозрачное окно с эффектом размытия рабочего стола? Я пытался использовать QGraphicsBlurEffect с прозрачностью, но только содержимое окна приложения будет размыто вместо рабочего стола под приложением. Это действительно возможно сделать? (еще раз большое спасибо !!) )
Не напрямую из Qt, так как это полностью зависит от ОС, но см. этот github.com/Peticali/PythonBlurBehind
@musicamante омг. Это золотой самородок! На самом деле, это делает то, что я хочу. (есть некоторое отставание, но все равно отлично!) Большое спасибо!
Пожалуйста, не используйте комментарии для вопросов не по теме. Если вы еще не можете задавать новые вопросы, пожалуйста, подождите, пока не сможете. Кстати, я не могу ответить, просто взглянув на код, так как я не знаю, в чем вопрос.
Чтобы реализовать самонастраивающееся сообщение, требуются некоторые меры предосторожности.
Как объяснялось в моем ответе и комментариях к связанный пост, вы должны учитывать сложную и тонкую связь между размерами макета и его требованиями, которая становится еще более сложной, поскольку макет текста не имеет фиксированного соотношения.
Основная проблема с установкой ширины на основе текста заключается в том, что он может изменить высоту, необходимую для его отображения, что может вызвать рекурсию, а поскольку размер основан на размере окна просмотра Текущий, результатом будет то, что minimumSizeHint()
всегда будет возвращаться наименьший возможный размер после определенного количества рекурсивных вызовов.
Учитывая вышеизложенное, мы должны внести следующие изменения в исходный код:
minimumSizeHint()
необходимо заменить на:
idealWidth()
на основе максимального размера виджета;Обратите внимание, что есть несколько отличий от измененного кода вашей ссылки: самое главное, переписывание таблицы стилей не имеет большого смысла, а установка поля создает проблему со значением, возвращаемым 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())
Что вы подразумеваете под «изменяемым размером»? Код, который вы предоставляете (который, кстати, полностью основан на мой собственный ответ), уже делает это: он изменяет размер содержимого на основе текста и доступного размера и, в конечном итоге, переносит текст, когда это необходимо.