Как сделать двухсторонний чат в QT (PyQt)

Пытался сделать окно чата в Qt. Столкнулся с проблемой отображения сообщений. Само окно с сообщениями сделал с помощью QTextBrowser. Но весь текст отображается только с одной стороны окна чата. Есть какие-либо варианты (без хардкода в QML) чтобы реализовать подобное примеру ниже окно чата ? (на скриншоте сделал с помощью label, но каждое сообщение добавлять в box в виде отдельного объекта - разориться на ресурсах, поэтому ищу оптимальные варианты)

Пример

пример


Ответы (1 шт):

Автор решения: S. Nick

Возможный вариант, обратите внимание:

  • QTextEdit readOnly должно быть True;
  • полосы прокрутки отключены;
  • политика вертикального размера должна быть Preferred;
  • как MinimumSizeHint(), так и sizeHint() должны использовать внутренний QTextDocument для возврата правильной высоты с минимальной шириной по умолчанию;
  • любое изменение размера или содержимого должно вызвать updateGeometry(), чтобы родительский макет знал, что подсказка изменилась, и геометрию можно было вычислить снова;

import sys
from random import randrange
from PyQt5 import QtWidgets, QtGui, QtCore, QtMultimedia
from PyQt5.Qt import *


class WrapLabel(QtWidgets.QTextEdit):
    def __init__(self, text=''):
        super().__init__(text)
        self.setStyleSheet('''
            WrapLabel {
                border: 1px outset palette(dark);
                border-radius: 8px;
                background: palette(light);
            }
        ''')
        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):
        doc = self.document().clone()
        doc.setTextWidth(self.viewport().width())
        height = doc.size().height()
        height += self.frameWidth() * 2
        return QtCore.QSize(150, height-100)

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

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


class ChatTest(QtWidgets.QScrollArea):
    def __init__(self):
        super().__init__()

        container = QtWidgets.QWidget()
        self.setMinimumSize(250, 250)
        self.setWidget(container)
        self.setWidgetResizable(True)

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

        for i in range(1, 11):
            QtCore.QTimer.singleShot(1000 * i, lambda i=i:
                self.addMessage('1' * randrange(70, 350), i)
            )  

    def addMessage(self, text, i):
        wrapLabel = WrapLabel(text)
        if i % 2:
            wrapLabel.setStyleSheet('''
                WrapLabel {
                    border: 1px outset palette(dark);
                    border-radius: 8px;
                    background: palette(light);
                    margin-left: 50px;
                    background: #FFFEB7;
                }
            ''')        
        else:
            wrapLabel.setStyleSheet('''
                WrapLabel {
                    border: 1px outset palette(dark);
                    border-radius: 8px;
                    background: palette(light);
                    margin-right: 50px;
                    background: #C8E6F5;
                    color: #6D3939;
                }
            ''')        
        
        self.widget().layout().addWidget(wrapLabel)
        QtCore.QTimer.singleShot(0, self.scrollToBottom)

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

if __name__ == "__main__":
    app = QApplication(sys.argv)
    w = ChatTest()
    w.show()
    sys.exit(app.exec_())

введите сюда описание изображения

→ Ссылка