@ -0,0 +1,277 @@
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 ( )