Files
Feeding_control_system/view/widgets/message_popup_widget.py

278 lines
12 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

from PySide6.QtWidgets import (
QWidget,
QVBoxLayout,
QListWidget,
QListWidgetItem,
QLabel,
QHBoxLayout,
QDialog
)
from PySide6.QtCore import Qt, QRect, QSize
from PySide6.QtGui import QFont, QColor, QPixmap, QPainter
from utils.image_paths import ImagePaths
"""
系统状态消息按钮 和 预警消息按钮点击之后出现的消息列表, 显示系统状态消息 和 预警消息
"""
# 自定义消息项组件
class MessageItemWidget(QWidget):
def __init__(self, content, is_processed, msg_time:str = "", parent=None):
super().__init__(parent)
self.content = content
self.is_processed = is_processed
self.msg_time = msg_time # 对应产生该消息的时间,如: 2025-11-9 19:09:09
self.bg_pixmap = QPixmap(ImagePaths.SYSTEM_MSG_ITEM_BG) # 消息项背景图
self.icon_pixmap = QPixmap(ImagePaths.SYSTEM_MSG_HORN_ICON) # 左侧喇叭图标
self.setStyleSheet("background: none; border: none;")
self.setFixedWidth(310) # 内部显示的消息条目的宽度,控制每条消息的宽度
self.init_ui()
def init_ui(self):
# 布局:仅显示消息文本
layout = QHBoxLayout(self)
layout.setContentsMargins(6, 6, 6, 6)
layout.setSpacing(5) # 调整图标与文本的间距
# 1. 左侧喇叭图标
self.icon_label = QLabel()
if not self.icon_pixmap.isNull():
# 设置图标大小例如20x20可根据需求调整
self.icon_label.setFixedSize(13, 21)
# 图片自适应label大小保持比例
self.icon_label.setScaledContents(True)
self.icon_label.setPixmap(self.icon_pixmap)
# 图标背景透明
self.icon_label.setStyleSheet("background: transparent;padding-top:3px;")
layout.addWidget(self.icon_label, alignment=Qt.AlignTop)
# 2. 右侧消息时间
# if self.msg_time:
# self.time_label = QLabel(self.msg_time)
# self.time_label.setFont(QFont("微软雅黑", 8)) # 消息时间的字体
# self.time_label.setStyleSheet("color: #03f5ff;")
# self.time_label.setFixedWidth(106) # 显示时间的标签的宽度,需要根据上面的消息时间字体来调整
# self.time_label.setFixedHeight(19)
# layout.addWidget(self.time_label, alignment=Qt.AlignTop)
# # 3. 右侧消息内容文本
# self.label = QLabel(self.content)
# self.label.setFont(QFont("微软雅黑", 12)) # 消息内容中的字体
# self.label.setWordWrap(True)
# # 已处理消息文字变灰
# if self.is_processed:
# self.label.setStyleSheet("color: gray;")
# else:
# self.label.setStyleSheet("color: white;")
# layout.addWidget(self.label)
# 2. 右侧:时间 + 内容的垂直布局
right_layout = QVBoxLayout()
right_layout.setContentsMargins(0, 0, 0, 0)
right_layout.setSpacing(0)
# 时间标签(可选:若有时间则显示)
if self.msg_time:
self.time_label = QLabel(self.msg_time)
self.time_label.setFont(QFont("微软雅黑", 8))
self.time_label.setStyleSheet("color: #03f5ff; margin: 0px; padding: 0px;")
right_layout.addWidget(self.time_label, alignment=Qt.AlignTop)
# 内容标签
self.label = QLabel(self.content)
# font = QFont("微软雅黑", 11)
# font.setStyleStrategy(QFont.PreferAntialias | QFont.PreferDefault)
self.label.setFont(QFont("微软雅黑", 11))
self.label.setWordWrap(True)
if self.is_processed:
self.label.setStyleSheet("color: gray; margin: 0px; padding: 0px;")
else:
self.label.setStyleSheet("color: white; margin: 0px; padding: 0px;")
right_layout.addWidget(self.label, alignment=Qt.AlignTop)
# 将右侧垂直布局加入主水平布局
layout.addLayout(right_layout)
def paintEvent(self, event):
# 绘制消息项背景图(自适应组件大小)
if not self.bg_pixmap.isNull():
painter = QPainter(self)
scaled_pixmap = self.bg_pixmap.scaled(
self.size(),
Qt.KeepAspectRatioByExpanding, # Qt.KeepAspectRatioByExpanding按比例缩放填满
Qt.SmoothTransformation, # 平滑缩放
)
painter.drawPixmap(self.rect(), scaled_pixmap)
super().paintEvent(event)
# 消息列表弹窗 (整个显示消息的区域)
class MessagePopupWidget(QWidget):
def __init__(self, message_type, parent=None):
super().__init__(parent)
self.message_type = message_type
self.bg_pixmap = QPixmap(ImagePaths.MESSAGE_LIST_POPUP_BG) # 列表背景图
# 弹窗样式:无框 + 置顶宽度固定为339与按钮一致
self.setWindowFlags(Qt.FramelessWindowHint)
self.hide() # 初始状态为隐藏 (必须)
self.setFixedWidth(339) # 宽度与下方的按钮一致
# self.setMinimumHeight(181) # 整个消息列表的高度,需要根据实际情况调整
# self.setMinimumHeight(211) # 整个消息列表的高度,需要根据实际情况调整
# self.setFixedHeight(260) # 整个消息列表的高度,需要根据实际情况调整
# self.setFixedHeight(290) # 整个消息列表的高度,需要根据实际情况调整
self.setFixedHeight(299) # 整个消息列表弹窗的高度,需要根据实际情况调整 (一页能够有六行消息[单行])
# 布局:标题 + 消息列表
layout = QVBoxLayout(self)
layout.setContentsMargins(10, 10, 10, 20)
layout.setSpacing(3) # 消息项间距
# 标题
self.title_label = QLabel(self.message_type)
self.title_label.setFont(QFont("微软雅黑", 14, QFont.Bold))
self.title_label.setStyleSheet(
"background: none; color: #03f5ff; text-align: center;"
)
self.title_label.setAlignment(Qt.AlignCenter)
layout.addWidget(self.title_label)
# 消息列表(支持选中)
self.list_widget = QListWidget()
# 禁用列表选择
self.list_widget.setSelectionMode(QListWidget.SelectionMode.NoSelection)
# 禁用水平滚动条
self.list_widget.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.list_widget.setStyleSheet(
"""
QListWidget { background-color: transparent; border: none; }
QListWidget::item { height: 30px; }
QListWidget QScrollBar:vertical {
background: transparent; /* 轨道背景色 */
width: 8px;
margin: 0;
border-radius: 4px;
}
/* 滚动条滑块 */
QListWidget QScrollBar::handle:vertical {
background: qlineargradient(x1:0, y1:0, x2:1, y2:0, stop:0 #039ec3, stop:1 #03f5ff);
border-radius: 4px;
min-height: 20px;
}
/* 滑块悬停效果 */
QListWidget QScrollBar::handle:vertical:hover {
background: qlineargradient(x1:0, y1:0, x2:1, y2:0, stop:0 #16ffff, stop:1 #00347e);
}
/* 隐藏上下箭头按钮 */
QListWidget QScrollBar::add-line:vertical, QListWidget QScrollBar::sub-line:vertical {
height: 0;
}
/* 滑块上下的空白区域(透明) */
QListWidget QScrollBar::add-page:vertical, QListWidget QScrollBar::sub-page:vertical {
background: transparent;
}
"""
)
layout.addWidget(self.list_widget)
def paintEvent(self, event):
# 这里需要根据不变的图片部分来决定
self.top = 32 # 上分割线距离顶部
self.right = 32 # 右分割线距离右侧
self.bottom = 32 # 下分割线距离底部
self.left = 32 # 左分割线距离左侧
# 按九宫格来加载图片
painter = QPainter(self)
w, h = self.width(), self.height() # 控件当前大小
img_w, img_h = self.bg_pixmap.width(), self.bg_pixmap.height() # 原图大小
# 1. 绘制四个角(不拉伸)
# 左上
painter.drawPixmap(
0, 0, self.left, self.top,
self.bg_pixmap, 0, 0, self.left, self.top
)
# 右上
painter.drawPixmap(
w - self.right, 0, self.right, self.top,
self.bg_pixmap, img_w - self.right, 0, self.right, self.top
)
# 左下
painter.drawPixmap(
0, h - self.bottom, self.left, self.bottom,
self.bg_pixmap, 0, img_h - self.bottom, self.left, self.bottom
)
# 右下
painter.drawPixmap(
w - self.right, h - self.bottom, self.right, self.bottom,
self.bg_pixmap, img_w - self.right, img_h - self.bottom, self.right, self.bottom
)
# 2. 绘制四条边(拉伸中间部分)
# 上边(水平拉伸)
painter.drawPixmap(
self.left, 0, w - self.left - self.right, self.top,
self.bg_pixmap, self.left, 0, img_w - self.left - self.right, self.top
)
# 下边(水平拉伸)
painter.drawPixmap(
self.left, h - self.bottom, w - self.left - self.right, self.bottom,
self.bg_pixmap, self.left, img_h - self.bottom, img_w - self.left - self.right, self.bottom
)
# 左边(垂直拉伸)
painter.drawPixmap(
0, self.top, self.left, h - self.top - self.bottom,
self.bg_pixmap, 0, self.top, self.left, img_h - self.top - self.bottom
)
# 右边(垂直拉伸)
painter.drawPixmap(
w - self.right, self.top, self.right, h - self.top - self.bottom,
self.bg_pixmap, img_w - self.right, self.top, self.right, img_h - self.top - self.bottom
)
# 3. 绘制中心区域(自由拉伸)
painter.drawPixmap(
self.left, self.top, w - self.left - self.right, h - self.top - self.bottom,
self.bg_pixmap, self.left, self.top, img_w - self.left - self.right, img_h - self.top - self.bottom
)
super().paintEvent(event)
# 显示设置
def showEvent(self, event):
super().showEvent(event)
# 弹窗显示时,强制滚动到底部,以便显示最新的消息
self.list_widget.scrollToBottom()
"""
对外接口: 添加消息到消息列表
is_processed状态为True表示 已经处理/已经解决, 此时显示的字体为灰色
"""
def add_message(self, content, is_processed:bool = False, msg_time:str = ""):
"""添加带背景的消息项
Args:
content: 消息的内容
is_processed: 消息的状态 (是否已经解决, 已经解决,字体变为灰色)
msg_time: 该消息对应的时间 (如: 产生消息的时间等), 会显示在消息内容之前,如: 2025-11-12 消息内容
"""
# 1、创建自定义消息项组件
item_widget = MessageItemWidget(content, is_processed, msg_time)
# 2、创建列表项并关联组件
item = QListWidgetItem()
# item.setSizeHint(item_widget.sizeHint()) # 自适应组件大小
# item.setSizeHint(QSize(item_widget.sizeHint().width(), 30)) # 固定行高设置为30px (不行必须根据item_widget的高度决定)
# 行高减去6px, 刚好可以显示出4行, 对应了消息的字体大小为11px的情况
item.setSizeHint(QSize(item_widget.sizeHint().width(), item_widget.sizeHint().height() - 6))
self.list_widget.addItem(item)
self.list_widget.setItemWidget(item, item_widget)
# 3、添加新消息后滚动到列表底部以便显示最新的消息
self.list_widget.scrollToBottom()