Files
Feeding_control_system/view/widgets/message_popup_widget.py

278 lines
12 KiB
Python
Raw Normal View History

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()