2023-11-29 22:57:58 +08:00
|
|
|
|
import os.path
|
2024-01-07 20:24:12 +08:00
|
|
|
|
import subprocess
|
|
|
|
|
import platform
|
2023-11-29 22:57:58 +08:00
|
|
|
|
|
2023-11-17 15:31:21 +08:00
|
|
|
|
from PyQt5 import QtGui
|
2023-11-20 19:08:11 +08:00
|
|
|
|
from PyQt5.QtCore import QSize, pyqtSignal, Qt, QThread
|
|
|
|
|
from PyQt5.QtGui import QPainter, QFont, QColor, QPixmap, QPolygon, QFontMetrics
|
|
|
|
|
from PyQt5.QtWidgets import QWidget, QLabel, QHBoxLayout, QSizePolicy, QVBoxLayout, QSpacerItem, \
|
2024-01-07 20:24:12 +08:00
|
|
|
|
QScrollArea
|
2023-11-16 19:53:23 +08:00
|
|
|
|
|
2023-12-14 22:24:35 +08:00
|
|
|
|
from app.components.scroll_bar import ScrollBar
|
|
|
|
|
|
2023-11-16 19:53:23 +08:00
|
|
|
|
|
2023-11-20 19:08:11 +08:00
|
|
|
|
class MessageType:
|
|
|
|
|
Text = 1
|
2023-11-20 23:08:10 +08:00
|
|
|
|
Image = 3
|
2023-11-20 19:08:11 +08:00
|
|
|
|
|
|
|
|
|
|
2023-11-17 15:31:21 +08:00
|
|
|
|
class TextMessage(QLabel):
|
|
|
|
|
heightSingal = pyqtSignal(int)
|
2023-11-16 19:53:23 +08:00
|
|
|
|
|
2023-11-17 15:31:21 +08:00
|
|
|
|
def __init__(self, text, is_send=False, parent=None):
|
2023-12-06 23:54:12 +08:00
|
|
|
|
if isinstance(text, bytes):
|
|
|
|
|
text = text.decode('utf-8')
|
2023-11-17 15:31:21 +08:00
|
|
|
|
super(TextMessage, self).__init__(text, parent)
|
2023-11-20 19:08:11 +08:00
|
|
|
|
font = QFont('微软雅黑', 12)
|
|
|
|
|
self.setFont(font)
|
2023-11-17 15:31:21 +08:00
|
|
|
|
self.setWordWrap(True)
|
|
|
|
|
self.setMaximumWidth(800)
|
2023-11-24 20:16:01 +08:00
|
|
|
|
# self.setMinimumWidth(100)
|
2023-11-17 21:34:22 +08:00
|
|
|
|
self.setMinimumHeight(45)
|
2023-11-17 15:31:21 +08:00
|
|
|
|
self.setTextInteractionFlags(Qt.TextSelectableByMouse)
|
2023-11-16 19:53:23 +08:00
|
|
|
|
self.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Ignored)
|
2023-11-17 15:31:21 +08:00
|
|
|
|
if is_send:
|
|
|
|
|
self.setAlignment(Qt.AlignCenter | Qt.AlignRight)
|
|
|
|
|
self.setStyleSheet(
|
|
|
|
|
'''
|
2023-11-20 19:08:11 +08:00
|
|
|
|
background-color:#b2e281;
|
2023-11-17 15:31:21 +08:00
|
|
|
|
border-radius:10px;
|
2023-11-20 19:08:11 +08:00
|
|
|
|
padding:10px;
|
2023-11-17 15:31:21 +08:00
|
|
|
|
'''
|
|
|
|
|
)
|
|
|
|
|
else:
|
|
|
|
|
self.setStyleSheet(
|
|
|
|
|
'''
|
2023-11-20 19:08:11 +08:00
|
|
|
|
background-color:white;
|
2023-11-17 15:31:21 +08:00
|
|
|
|
border-radius:10px;
|
2023-11-20 19:08:11 +08:00
|
|
|
|
padding:10px;
|
2023-11-17 15:31:21 +08:00
|
|
|
|
'''
|
|
|
|
|
)
|
2023-11-20 19:08:11 +08:00
|
|
|
|
font_metrics = QFontMetrics(font)
|
|
|
|
|
rect = font_metrics.boundingRect(text)
|
2023-11-24 20:16:01 +08:00
|
|
|
|
# rect = font_metrics
|
|
|
|
|
self.setMaximumWidth(rect.width() + 40)
|
2023-11-16 19:53:23 +08:00
|
|
|
|
|
2023-11-17 15:31:21 +08:00
|
|
|
|
def paintEvent(self, a0: QtGui.QPaintEvent) -> None:
|
|
|
|
|
super(TextMessage, self).paintEvent(a0)
|
2023-11-16 19:53:23 +08:00
|
|
|
|
|
2023-11-17 15:31:21 +08:00
|
|
|
|
|
|
|
|
|
class Triangle(QLabel):
|
2024-01-07 21:03:05 +08:00
|
|
|
|
def __init__(self, Type, is_send=False, position=(0, 0), parent=None):
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
@param Type:
|
|
|
|
|
@param is_send:
|
|
|
|
|
@param position:(x,y)
|
|
|
|
|
@param parent:
|
|
|
|
|
"""
|
2023-11-17 15:31:21 +08:00
|
|
|
|
super().__init__(parent)
|
|
|
|
|
self.Type = Type
|
|
|
|
|
self.is_send = is_send
|
2024-01-07 21:03:05 +08:00
|
|
|
|
self.position = position
|
2023-11-17 15:31:21 +08:00
|
|
|
|
|
|
|
|
|
def paintEvent(self, a0: QtGui.QPaintEvent) -> None:
|
2024-01-07 21:03:05 +08:00
|
|
|
|
|
2023-11-17 15:31:21 +08:00
|
|
|
|
super(Triangle, self).paintEvent(a0)
|
2023-11-20 19:08:11 +08:00
|
|
|
|
if self.Type == MessageType.Text:
|
2024-01-07 21:03:05 +08:00
|
|
|
|
self.setFixedSize(6, 45)
|
2023-11-17 15:31:21 +08:00
|
|
|
|
painter = QPainter(self)
|
|
|
|
|
triangle = QPolygon()
|
2024-01-07 21:03:05 +08:00
|
|
|
|
x, y = self.position
|
2023-11-17 15:31:21 +08:00
|
|
|
|
if self.is_send:
|
|
|
|
|
painter.setPen(QColor('#b2e281'))
|
|
|
|
|
painter.setBrush(QColor('#b2e281'))
|
2024-01-07 21:03:05 +08:00
|
|
|
|
triangle.setPoints(0, 20+y, 0, 34+y, 6, 27+y)
|
2023-11-20 19:08:11 +08:00
|
|
|
|
else:
|
|
|
|
|
painter.setPen(QColor('white'))
|
|
|
|
|
painter.setBrush(QColor('white'))
|
2024-01-07 21:03:05 +08:00
|
|
|
|
triangle.setPoints(0, 27+y, 6, 20+y, 6, 34+y)
|
2023-11-17 15:31:21 +08:00
|
|
|
|
painter.drawPolygon(triangle)
|
|
|
|
|
|
|
|
|
|
|
2023-11-17 17:20:13 +08:00
|
|
|
|
class Notice(QLabel):
|
|
|
|
|
def __init__(self, text, type_=3, parent=None):
|
|
|
|
|
super().__init__(text, parent)
|
|
|
|
|
self.type_ = type_
|
2024-01-07 20:24:12 +08:00
|
|
|
|
self.setFont(QFont('微软雅黑', 10))
|
2023-11-17 17:20:13 +08:00
|
|
|
|
self.setWordWrap(True)
|
|
|
|
|
self.setTextInteractionFlags(Qt.TextSelectableByMouse)
|
|
|
|
|
self.setAlignment(Qt.AlignCenter)
|
|
|
|
|
|
|
|
|
|
|
2023-11-17 15:31:21 +08:00
|
|
|
|
class Avatar(QLabel):
|
|
|
|
|
def __init__(self, avatar, parent=None):
|
|
|
|
|
super().__init__(parent)
|
|
|
|
|
if isinstance(avatar, str):
|
|
|
|
|
self.setPixmap(QPixmap(avatar).scaled(45, 45))
|
|
|
|
|
self.image_path = avatar
|
|
|
|
|
elif isinstance(avatar, QPixmap):
|
2023-11-17 21:34:22 +08:00
|
|
|
|
self.setPixmap(avatar.scaled(45, 45))
|
|
|
|
|
self.setFixedSize(QSize(45, 45))
|
2023-11-17 15:31:21 +08:00
|
|
|
|
|
|
|
|
|
|
2024-01-07 20:24:12 +08:00
|
|
|
|
def open_image_viewer(file_path):
|
|
|
|
|
system_platform = platform.system()
|
|
|
|
|
|
|
|
|
|
if system_platform == "Darwin": # macOS
|
|
|
|
|
subprocess.run(["open", file_path])
|
|
|
|
|
elif system_platform == "Windows":
|
|
|
|
|
subprocess.run(["start", " ", file_path], shell=True)
|
|
|
|
|
elif system_platform == "Linux":
|
|
|
|
|
subprocess.run(["xdg-open", file_path])
|
|
|
|
|
else:
|
|
|
|
|
print("Unsupported platform")
|
|
|
|
|
|
|
|
|
|
|
2023-11-17 17:20:13 +08:00
|
|
|
|
class OpenImageThread(QThread):
|
|
|
|
|
def __init__(self, image_path):
|
|
|
|
|
super().__init__()
|
|
|
|
|
self.image_path = image_path
|
|
|
|
|
|
|
|
|
|
def run(self) -> None:
|
2023-11-29 22:57:58 +08:00
|
|
|
|
if os.path.exists(self.image_path):
|
2024-01-07 20:24:12 +08:00
|
|
|
|
open_image_viewer(self.image_path)
|
2023-11-17 17:20:13 +08:00
|
|
|
|
|
|
|
|
|
|
2023-11-17 15:31:21 +08:00
|
|
|
|
class ImageMessage(QLabel):
|
2023-11-24 23:49:37 +08:00
|
|
|
|
def __init__(self, image, is_send, image_link='', max_width=480, max_height=240, parent=None):
|
2023-11-20 23:08:10 +08:00
|
|
|
|
"""
|
|
|
|
|
param:image 图像路径或者QPixmap对象
|
|
|
|
|
param:image_link='' 点击图像打开的文件路径
|
|
|
|
|
"""
|
2023-11-16 19:53:23 +08:00
|
|
|
|
super().__init__(parent)
|
2023-11-17 15:31:21 +08:00
|
|
|
|
self.image = QLabel(self)
|
2023-11-21 21:48:54 +08:00
|
|
|
|
self.max_width = max_width
|
|
|
|
|
self.max_height = max_height
|
2024-01-07 20:24:12 +08:00
|
|
|
|
# self.setFixedSize(self.max_width,self.max_height)
|
|
|
|
|
self.setMaximumWidth(self.max_width)
|
|
|
|
|
self.setMaximumHeight(self.max_height)
|
2023-11-20 23:08:10 +08:00
|
|
|
|
if isinstance(image, str):
|
2023-11-21 21:48:54 +08:00
|
|
|
|
pixmap = QPixmap(image)
|
2023-11-20 23:08:10 +08:00
|
|
|
|
self.image_path = image
|
|
|
|
|
elif isinstance(image, QPixmap):
|
2023-11-21 21:48:54 +08:00
|
|
|
|
pixmap = image
|
|
|
|
|
self.set_image(pixmap)
|
2023-11-20 23:08:10 +08:00
|
|
|
|
if image_link:
|
|
|
|
|
self.image_path = image_link
|
2024-01-07 21:03:05 +08:00
|
|
|
|
self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
|
2024-01-07 20:24:12 +08:00
|
|
|
|
|
2023-11-24 23:49:37 +08:00
|
|
|
|
if is_send:
|
|
|
|
|
self.setAlignment(Qt.AlignCenter | Qt.AlignRight)
|
2023-11-20 23:08:10 +08:00
|
|
|
|
# self.setScaledContents(True)
|
2023-11-17 15:31:21 +08:00
|
|
|
|
|
2023-11-21 21:48:54 +08:00
|
|
|
|
def set_image(self, pixmap):
|
|
|
|
|
# 计算调整后的大小
|
|
|
|
|
adjusted_width = min(pixmap.width(), self.max_width)
|
|
|
|
|
adjusted_height = min(pixmap.height(), self.max_height)
|
|
|
|
|
self.setPixmap(pixmap.scaled(adjusted_width, adjusted_height, Qt.KeepAspectRatio))
|
|
|
|
|
# 调整QLabel的大小以适应图片的宽高,但不超过最大宽高
|
2024-01-07 21:03:05 +08:00
|
|
|
|
# self.setFixedSize(adjusted_width, adjusted_height)
|
2023-11-21 21:48:54 +08:00
|
|
|
|
|
2023-11-17 15:31:21 +08:00
|
|
|
|
def mousePressEvent(self, event):
|
|
|
|
|
if event.buttons() == Qt.LeftButton: # 左键按下
|
2023-11-20 23:08:10 +08:00
|
|
|
|
print('打开图像', self.image_path)
|
2023-11-17 17:20:13 +08:00
|
|
|
|
self.open_image_thread = OpenImageThread(self.image_path)
|
|
|
|
|
self.open_image_thread.start()
|
2023-11-17 15:31:21 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class BubbleMessage(QWidget):
|
2024-01-07 21:03:05 +08:00
|
|
|
|
def __init__(self, str_content, avatar, Type, is_send=False, display_name=None, parent=None):
|
2023-11-17 15:31:21 +08:00
|
|
|
|
super().__init__(parent)
|
|
|
|
|
self.isSend = is_send
|
2023-11-17 17:20:13 +08:00
|
|
|
|
# self.set
|
|
|
|
|
self.setStyleSheet(
|
|
|
|
|
'''
|
|
|
|
|
border:none;
|
|
|
|
|
'''
|
|
|
|
|
)
|
2023-11-16 19:53:23 +08:00
|
|
|
|
layout = QHBoxLayout()
|
2023-11-17 15:31:21 +08:00
|
|
|
|
layout.setSpacing(0)
|
2023-11-17 17:20:13 +08:00
|
|
|
|
layout.setContentsMargins(0, 5, 5, 5)
|
2023-11-18 13:25:56 +08:00
|
|
|
|
# self.resize(QSize(200, 50))
|
2023-11-17 15:31:21 +08:00
|
|
|
|
self.avatar = Avatar(avatar)
|
2024-01-07 21:03:05 +08:00
|
|
|
|
triangle = Triangle(Type, is_send, (0, 0))
|
2023-11-20 19:08:11 +08:00
|
|
|
|
if Type == MessageType.Text:
|
2023-11-17 15:31:21 +08:00
|
|
|
|
self.message = TextMessage(str_content, is_send)
|
2023-11-17 21:34:22 +08:00
|
|
|
|
# self.message.setMaximumWidth(int(self.width() * 0.6))
|
2023-11-20 19:08:11 +08:00
|
|
|
|
elif Type == MessageType.Image:
|
2023-11-24 23:49:37 +08:00
|
|
|
|
self.message = ImageMessage(str_content, is_send)
|
2023-11-20 19:08:11 +08:00
|
|
|
|
else:
|
|
|
|
|
raise ValueError("未知的消息类型")
|
2024-01-07 20:24:12 +08:00
|
|
|
|
if display_name:
|
2024-01-07 21:03:05 +08:00
|
|
|
|
triangle = Triangle(Type, is_send, (0, 10))
|
|
|
|
|
label_name = QLabel(display_name, self)
|
|
|
|
|
label_name.setFont(QFont('微软雅黑', 10))
|
2024-01-07 20:24:12 +08:00
|
|
|
|
if is_send:
|
|
|
|
|
label_name.setAlignment(Qt.AlignRight)
|
|
|
|
|
vlayout = QVBoxLayout()
|
|
|
|
|
vlayout.setSpacing(0)
|
2024-01-07 21:03:05 +08:00
|
|
|
|
if is_send:
|
|
|
|
|
vlayout.addWidget(label_name, 0, Qt.AlignTop | Qt.AlignRight)
|
|
|
|
|
vlayout.addWidget(self.message, 0, Qt.AlignTop | Qt.AlignRight)
|
|
|
|
|
else:
|
|
|
|
|
vlayout.addWidget(label_name)
|
|
|
|
|
vlayout.addWidget(self.message)
|
2023-11-17 17:20:13 +08:00
|
|
|
|
self.spacerItem = QSpacerItem(45 + 6, 45, QSizePolicy.Expanding, QSizePolicy.Minimum)
|
2023-11-17 15:31:21 +08:00
|
|
|
|
if is_send:
|
|
|
|
|
layout.addItem(self.spacerItem)
|
2024-01-07 20:24:12 +08:00
|
|
|
|
if display_name:
|
2024-01-07 21:03:05 +08:00
|
|
|
|
layout.addLayout(vlayout, 1)
|
2024-01-07 20:24:12 +08:00
|
|
|
|
else:
|
|
|
|
|
layout.addWidget(self.message, 1)
|
2023-11-17 17:20:13 +08:00
|
|
|
|
layout.addWidget(triangle, 0, Qt.AlignTop | Qt.AlignLeft)
|
2023-11-17 15:31:21 +08:00
|
|
|
|
layout.addWidget(self.avatar, 0, Qt.AlignTop | Qt.AlignLeft)
|
2023-11-16 19:53:23 +08:00
|
|
|
|
else:
|
2023-11-17 15:31:21 +08:00
|
|
|
|
layout.addWidget(self.avatar, 0, Qt.AlignTop | Qt.AlignRight)
|
|
|
|
|
layout.addWidget(triangle, 0, Qt.AlignTop | Qt.AlignRight)
|
2024-01-07 20:24:12 +08:00
|
|
|
|
if display_name:
|
2024-01-07 21:03:05 +08:00
|
|
|
|
layout.addLayout(vlayout, 1)
|
2024-01-07 20:24:12 +08:00
|
|
|
|
else:
|
|
|
|
|
layout.addWidget(self.message, 1)
|
2023-11-17 15:31:21 +08:00
|
|
|
|
layout.addItem(self.spacerItem)
|
2023-11-16 19:53:23 +08:00
|
|
|
|
self.setLayout(layout)
|
|
|
|
|
|
|
|
|
|
|
2023-11-17 17:20:13 +08:00
|
|
|
|
class ScrollAreaContent(QWidget):
|
|
|
|
|
def __init__(self, parent=None):
|
|
|
|
|
super().__init__(parent)
|
2023-11-18 13:25:56 +08:00
|
|
|
|
self.adjustSize()
|
2023-11-17 17:20:13 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ScrollArea(QScrollArea):
|
|
|
|
|
def __init__(self, parent=None):
|
|
|
|
|
super().__init__(parent)
|
|
|
|
|
self.setWidgetResizable(True)
|
2023-11-18 13:25:56 +08:00
|
|
|
|
self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
|
2023-11-17 17:20:13 +08:00
|
|
|
|
self.setStyleSheet(
|
|
|
|
|
'''
|
|
|
|
|
border:none;
|
|
|
|
|
'''
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
2023-11-18 11:51:58 +08:00
|
|
|
|
class ChatWidget(QWidget):
|
2023-11-16 19:53:23 +08:00
|
|
|
|
def __init__(self):
|
|
|
|
|
super().__init__()
|
2023-11-17 17:20:13 +08:00
|
|
|
|
self.resize(500, 200)
|
2023-11-18 13:25:56 +08:00
|
|
|
|
|
2023-11-16 19:53:23 +08:00
|
|
|
|
layout = QVBoxLayout()
|
2023-11-17 17:20:13 +08:00
|
|
|
|
layout.setSpacing(0)
|
2023-11-18 13:25:56 +08:00
|
|
|
|
self.adjustSize()
|
2023-11-17 17:20:13 +08:00
|
|
|
|
# 生成滚动区域
|
2023-11-18 13:25:56 +08:00
|
|
|
|
self.scrollArea = ScrollArea(self)
|
2023-11-17 17:20:13 +08:00
|
|
|
|
scrollBar = ScrollBar()
|
|
|
|
|
self.scrollArea.setVerticalScrollBar(scrollBar)
|
|
|
|
|
# self.scrollArea.setGeometry(QRect(9, 9, 261, 211))
|
|
|
|
|
# 生成滚动区域的内容部署层部件
|
2023-11-18 13:25:56 +08:00
|
|
|
|
self.scrollAreaWidgetContents = ScrollAreaContent(self.scrollArea)
|
2023-11-17 17:20:13 +08:00
|
|
|
|
self.scrollAreaWidgetContents.setMinimumSize(50, 100)
|
|
|
|
|
# 设置滚动区域的内容部署部件为前面生成的内容部署层部件
|
|
|
|
|
self.scrollArea.setWidget(self.scrollAreaWidgetContents)
|
|
|
|
|
layout.addWidget(self.scrollArea)
|
2023-11-18 11:51:58 +08:00
|
|
|
|
self.layout0 = QVBoxLayout()
|
|
|
|
|
self.layout0.setSpacing(0)
|
|
|
|
|
self.scrollAreaWidgetContents.setLayout(self.layout0)
|
2023-11-17 21:34:22 +08:00
|
|
|
|
self.setLayout(layout)
|
|
|
|
|
|
2023-11-18 13:25:56 +08:00
|
|
|
|
def add_message_item(self, bubble_message, index=1):
|
|
|
|
|
if index:
|
|
|
|
|
self.layout0.addWidget(bubble_message)
|
|
|
|
|
else:
|
|
|
|
|
self.layout0.insertWidget(0, bubble_message)
|
2023-11-18 14:41:40 +08:00
|
|
|
|
# self.set_scroll_bar_last()
|
2023-11-18 11:51:58 +08:00
|
|
|
|
|
|
|
|
|
def set_scroll_bar_last(self):
|
2023-11-18 14:41:40 +08:00
|
|
|
|
self.scrollArea.verticalScrollBar().setValue(
|
|
|
|
|
self.scrollArea.verticalScrollBar().maximum()
|
|
|
|
|
)
|
2023-11-18 11:51:58 +08:00
|
|
|
|
|
2023-11-18 13:25:56 +08:00
|
|
|
|
def set_scroll_bar_value(self, val):
|
|
|
|
|
self.verticalScrollBar().setValue(val)
|
|
|
|
|
|
|
|
|
|
def verticalScrollBar(self):
|
|
|
|
|
return self.scrollArea.verticalScrollBar()
|
|
|
|
|
|
2023-11-18 14:41:40 +08:00
|
|
|
|
def update(self) -> None:
|
|
|
|
|
super().update()
|
|
|
|
|
self.scrollAreaWidgetContents.adjustSize()
|
|
|
|
|
self.scrollArea.update()
|
|
|
|
|
# self.scrollArea.repaint()
|
|
|
|
|
# self.verticalScrollBar().setMaximum(self.scrollAreaWidgetContents.height())
|