Создании дизайна чата в PyQt как в стиле телеграмм

Я пытаюсь сделать чат в стиле Telegram используя PyQt5.
Я попробовал пару вариантов создания чего-то похожего на чат и мне кажется, что графика для этого лучше всего подходит.
У меня есть код, но это вряд-ли похоже на чат из телеграмма. Помогите пожалуйста в создании.

main.py:

import sys
from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, \ 
    QHBoxLayout, QLabel, QTextEdit, QPushButton, QMainWindow, \ 
    QLineEdit, QGraphicsScene, QGraphicsItem, QGraphicsView, \ 
    QGraphicsTextItem, QGraphicsRectItem
from PyQt5.QtCore import Qt
        

class Window(QMainWindow):
    def __init__(self):
        super().__init__()

        self.initUI()       # Инициализирую пользовательский интерфейс
        self.current_y = 0  # Переменная для отслеживания текущей высоты
        
    def initUI(self): 
        self.setWindowTitle("Чат")
        self.setGeometry(300, 300, 1200, 700)
        
        central_widget = QWidget(self)
        self.setCentralWidget(central_widget)
        layout_input_and_send = QHBoxLayout()
        layout = QVBoxLayout(central_widget) # создаём вертикальный виджет
        layout.addLayout(layout_input_and_send)
        
        # Создаем область графики для чата 
        self.graphics_view = QGraphicsView(self)
        self.graphics_scene = QGraphicsScene(self)
        self.graphics_view.setScene(self.graphics_scene)
        layout.addWidget(self.graphics_view)  # Добавляем QGraphicsView в макет
        self.graphics_view.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)  # Отключаем горизонтальную прокрутку
        
        # Поле для ввода
        self.input_user_command = QLineEdit(self)
        self.input_user_command.setPlaceholderText("Введите текст") # Устанавливаем текст подсказку
        layout_input_and_send.addWidget(self.input_user_command) # Расположение объекта
        
        # Кнопка отправки сообщения
        self.send_button = QPushButton("Отправить", self)  
        self.send_button.clicked.connect(self.send_message)  
        layout_input_and_send.addWidget(self.send_button)  
    
    def send_message(self):
        message = self.input_user_command.text()
        if message: # Проверяем, что сообщение не пустое
            print("Сообщение: " + message)
            text_item = QGraphicsTextItem(message)  # Создаем графический элемент текста
            
            # Устанавливаем размеры фона сообщения
            rect_width = 350  # Ширина фона
            rect_height = text_item.boundingRect().height() + 10  # Высота фона с отступом
            
            # Создаем прямоугольник для фона сообщения
            rect_item = QGraphicsRectItem(0, self.current_y, rect_width, rect_height)
            rect_item.setBrush(Qt.lightGray)  # Устанавливаем цвет фона (светло-серый)
        
            # Устанавливаем позицию текста так, чтобы он был выровнен по правому краю
            text_item.setPos(rect_width - text_item.boundingRect().width() - 5, self.current_y + 5)  # Отступ 5 пикселей от правого края

            # Добавляем прямоугольник и текст в сцену
            self.graphics_scene.addItem(rect_item)  # Добавляем фон в сцену
            self.graphics_scene.addItem(text_item)  # Добавляем текст в сцену

            # Обновляем текущую высоту для следующего сообщения
            self.current_y += rect_height + 10  # Увеличиваем высоту с учетом отступа
            
            self.input_user_command.clear() # Очищаю поле для ввода
            self.graphics_view.verticalScrollBar().setValue(self.graphics_view.verticalScrollBar().maximum()) # Прокручиваем вниз, чтобы показать новое сообщение
        
    def keyPressEvent(self, event):
        # Проверяем, была ли нажата клавиша Enter
        if self.input_user_command.hasFocus() and event.key() == 16777220:  # Код клавиши Enter
            self.send_message()  # Отправляем сообщение
        
        
def main():
    app = QApplication(sys.argv)
    window = Window()
    window.show()   
    sys.exit(app.exec_())     

if __name__ == "__main__":
    main()

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

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

Как вариант:

import sys
from random import randrange
from PyQt5 import QtWidgets, QtGui, QtCore
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(2, 9, 2):
            QtCore.QTimer.singleShot(1500 * i, lambda i=i:
                self.addMessage(
                    f'<b>Client:</b> {str(i) * 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())


class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.central_widget = QWidget(self)
        self.setCentralWidget(self.central_widget)
        
        self.chatTest = ChatTest()

        self.textEdit = QTextEdit()
        
        self.splitter = QSplitter(self)
        self.splitter.setOrientation(Qt.Vertical)
        self.splitter.addWidget(self.chatTest)
        self.splitter.addWidget(self.textEdit)
        self.splitter.setSizes([500, 100])

        self.send_button = QPushButton("Отправить") 
        self.send_button.clicked.connect(self.send_message)
        
        self.h_layout = QHBoxLayout()
        self.h_layout.addStretch(1)
        self.h_layout.addWidget(self.send_button)

        self.v_layout = QVBoxLayout(self.central_widget)
        self.v_layout.addWidget(self.splitter)
        self.v_layout.addLayout(self.h_layout)

    def send_message(self):
        message = self.textEdit.toPlainText() 
        if message:
            message = message.replace("\n", "<br>")
            self.chatTest.addMessage(message, 1)
            self.textEdit.clear()  
        

if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)
    w = MainWindow()                     
    w.setWindowTitle("Чат")
    w.resize(500, 600)
    w.show()
    sys.exit(app.exec())

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

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

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

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

→ Ссылка