Compare commits

...

2 Commits

19 changed files with 845 additions and 16 deletions

View File

@ -2,6 +2,7 @@
from PySide6.QtCore import Qt, QPropertyAnimation, QRect, QParallelAnimationGroup, QEasingCurve from PySide6.QtCore import Qt, QPropertyAnimation, QRect, QParallelAnimationGroup, QEasingCurve
from view.widgets.system_center_dialog import SystemCenterDialog from view.widgets.system_center_dialog import SystemCenterDialog
from view.widgets.bottom_control_widget import BottomControlWidget from view.widgets.bottom_control_widget import BottomControlWidget
from view.widgets.system_diagnostics_dialog import SystemDiagnosticsDialog
""" """
控制主界面底部的所有按钮, 包括系统诊断、系统中心等的行为。 控制主界面底部的所有按钮, 包括系统诊断、系统中心等的行为。
@ -19,12 +20,20 @@ class BottomControlController:
self.system_center_dialog.hide() # 初始隐藏 (必须) self.system_center_dialog.hide() # 初始隐藏 (必须)
self._init_system_center_dialog_hide_animations() self._init_system_center_dialog_hide_animations()
# 系统诊断弹窗
self.system_diagnostics_dialog = SystemDiagnosticsDialog(self.main_window)
self.system_diagnostics_dialog.hide()
self._init_system_diagnostics_dialog_hide_animations()
self._bind_dialog_signals() self._bind_dialog_signals()
def _bind_buttons(self): def _bind_buttons(self):
# 底部系统中心按钮 → 触发弹窗显示/隐藏 # 底部系统中心按钮 → 触发弹窗显示/隐藏
self.bottom_control_widget.center_btn.clicked.connect(self.toggle_system_center_dialog) self.bottom_control_widget.center_btn.clicked.connect(self.toggle_system_center_dialog)
# 底部系统诊断按钮 → 触发弹窗显示/隐藏
self.bottom_control_widget.diagnosis_btn.clicked.connect(self.toggle_system_diagnostics_dialog)
def _bind_dialog_signals(self): def _bind_dialog_signals(self):
"""绑定弹窗按钮的信号""" """绑定弹窗按钮的信号"""
self.system_center_dialog.sys_setting_clicked.connect(self.handle_sys_setting) self.system_center_dialog.sys_setting_clicked.connect(self.handle_sys_setting)
@ -53,7 +62,7 @@ class BottomControlController:
self.hide_anim_group.finished.connect(self.system_center_dialog.hide) self.hide_anim_group.finished.connect(self.system_center_dialog.hide)
def toggle_system_center_dialog(self): def toggle_system_center_dialog(self):
"""切换弹窗的显示/隐藏状态""" """切换系统中心弹窗的显示/隐藏状态"""
if self.system_center_dialog.isVisible(): if self.system_center_dialog.isVisible():
# 已显示 → 隐藏 # 已显示 → 隐藏
# self.system_center_dialog.hide() # self.system_center_dialog.hide()
@ -94,7 +103,7 @@ class BottomControlController:
# 设置弹窗位置 # 设置弹窗位置
self.system_center_dialog.move(dialog_x, dialog_y) self.system_center_dialog.move(dialog_x, dialog_y)
# ------------------- 业务逻辑方法------------------- # ------------------- 系统中心弹窗业务逻辑-------------------
def handle_sys_setting(self): def handle_sys_setting(self):
"""系统设置按钮的业务逻辑""" """系统设置按钮的业务逻辑"""
# print("执行系统设置逻辑:如打开系统配置窗口、修改参数等") # print("执行系统设置逻辑:如打开系统配置窗口、修改参数等")
@ -109,3 +118,77 @@ class BottomControlController:
def handle_user_center(self): def handle_user_center(self):
"""用户中心按钮的业务逻辑""" """用户中心按钮的业务逻辑"""
# print("执行用户中心逻辑:如切换用户、修改密码等") # print("执行用户中心逻辑:如切换用户、修改密码等")
# ------------------- 系统诊断弹窗逻辑-------------------
def _init_system_diagnostics_dialog_hide_animations(self):
"""初始化系统诊断弹窗隐藏动画(与显示动画反向:滑出+淡出)"""
# 1. 淡出动画(与显示动画时长一致)
self.dia_hide_opacity_anim = QPropertyAnimation(
self.system_diagnostics_dialog, b"windowOpacity", self.system_diagnostics_dialog
)
self.dia_hide_opacity_anim.setDuration(300) # 显示动画为400ms
self.dia_hide_opacity_anim.setStartValue(1.0)
self.dia_hide_opacity_anim.setEndValue(0.0)
# 2. 位置动画从当前位置滑出到下方100px与显示动画反向
self.dia_hide_pos_anim = QPropertyAnimation(
self.system_diagnostics_dialog, b"geometry", self.system_diagnostics_dialog
)
self.dia_hide_pos_anim.setDuration(300)
self.dia_hide_pos_anim.setEasingCurve(QEasingCurve.InQuart) # 滑出曲线与显示反向
# 3. 组合动画(同时执行滑出和淡出)
self.dia_hide_anim_group = QParallelAnimationGroup(self.system_diagnostics_dialog)
self.dia_hide_anim_group.addAnimation(self.dia_hide_opacity_anim)
self.dia_hide_anim_group.addAnimation(self.dia_hide_pos_anim)
# 动画结束后强制隐藏弹窗
self.dia_hide_anim_group.finished.connect(self.system_diagnostics_dialog.hide)
def toggle_system_diagnostics_dialog(self):
"""切换系统诊断弹窗的显示/隐藏状态"""
if self.system_diagnostics_dialog.isVisible():
# 已显示 → 执行隐藏动画
self._start_diagnostics_hide_animation()
else:
# 未显示 → 计算位置并显示(触发显示动画)
self._calc_system_diagnostics_dialog_position()
self.system_diagnostics_dialog.show()
def _calc_system_diagnostics_dialog_position(self):
"""计算系统诊断弹窗位置(显示在诊断按钮上方,与中心弹窗布局一致)"""
btn = self.bottom_control_widget.diagnosis_btn # 诊断按钮
bottom_widget = self.bottom_control_widget
# 计算按钮在主窗口中的绝对位置
bottom_pos_rel_main = bottom_widget.pos() # 底部控件相对于主窗口的位置
btn_pos_rel_bottom = btn.pos() # 诊断按钮相对于底部控件的位置
btn_pos_rel_main = bottom_pos_rel_main + btn_pos_rel_bottom # 诊断按钮在主窗口中的绝对位置
# 计算弹窗坐标(显示在按钮上方,水平居中对齐)
btn_width = btn.width()
dialog_size = self.system_diagnostics_dialog.size()
# 水平方向:与按钮居中对齐
# dialog_x = btn_pos_rel_main.x() + (btn_width - dialog_size.width()) // 2
# dialog_x = btn_pos_rel_main.x() + (btn_width - dialog_size.width())
dialog_x = btn_pos_rel_main.x() # 与系统诊断按钮的左边平齐
# 垂直方向在按钮上方与按钮保持10px间距
# dialog_y = btn_pos_rel_main.y() - dialog_size.height() - 10
dialog_y = btn_pos_rel_main.y() - dialog_size.height()
# 设置弹窗位置(动画会基于此位置执行滑入效果)
self.system_diagnostics_dialog.move(dialog_x, dialog_y)
def _start_diagnostics_hide_animation(self):
"""启动系统诊断弹窗的隐藏动画(滑出+淡出)"""
current_geo = self.system_diagnostics_dialog.geometry() # 当前位置和尺寸
# 计算隐藏动画终点当前位置下方100px与显示动画起点对应
end_rect = QRect(
current_geo.x(),
current_geo.y() + 100, # 向下滑出100px
current_geo.width(),
current_geo.height()
)
# 设置动画参数并启动
self.dia_hide_pos_anim.setStartValue(current_geo)
self.dia_hide_pos_anim.setEndValue(end_rect)
self.dia_hide_anim_group.start()

View File

@ -18,6 +18,7 @@ class MainController:
def showMainWindow(self): def showMainWindow(self):
self.main_window.showFullScreen() self.main_window.showFullScreen()
# self.main_window.show()
self.main_window.dispatch_task_widget.set_task_time("task1","15:44 PM") self.main_window.dispatch_task_widget.set_task_time("task1","15:44 PM")
self.main_window.dispatch_task_widget.set_task_time("task2","17:37 PM") self.main_window.dispatch_task_widget.set_task_time("task2","17:37 PM")
self.main_window.segment_task_widget.set_task_time("task1","15:38 PM") self.main_window.segment_task_widget.set_task_time("task1","15:38 PM")

BIN
images/关闭图标.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 229 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 369 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 172 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 213 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 735 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 257 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 251 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 265 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

BIN
images/详情标题.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@ -93,3 +93,18 @@ class ImagePaths:
# 功能:主界面相关 # 功能:主界面相关
MAIN_INTERFACE_BACKGROUND = ":/icons/images/主界面背景.png" MAIN_INTERFACE_BACKGROUND = ":/icons/images/主界面背景.png"
# 功能: 系统诊断弹窗
SYSTEM_DIAGNOSTICS_POPUP_BG = "images/系统诊断弹出背景.png"
SYSTEM_DIAGNOSTICS_BOX = "images/系统诊断小框.png"
SYSTEM_DIAGNOSTICS_STATUS_GREEN = "images/系统诊断状态绿.png"
SYSTEM_DIAGNOSTICS_STATUS_YELLOW = "images/系统诊断状态黄.png"
SYSTEM_DIAGNOSTICS_STATUS_RED = "images/系统诊断状态红.png"
SYSTEM_DIAGNOSTICS_MS_BG = "images/系统诊断毫秒背景.png"
SYSTEM_DIAGNOSTICS_DROPDOWN_ARROW = "images/系统诊断下拉箭头.png"
# 功能:管片任务详情按钮弹窗
SEGMENT_DETAILS_POPUP_BG = "images/详情弹出背景.png"
SEGMENT_DETAILS_TITLE_BG = "images/详情标题.png"
SEGMENT_DETAILS_INFO_BAR = "images/管片任务信息栏.png"
SEGMENT_DETAILS_CLOSE_ICON = "images/关闭图标.png"

View File

@ -21,6 +21,8 @@ from .widgets.bottom_control_widget import BottomControlWidget
import resources.resources_rc import resources.resources_rc
from utils.image_paths import ImagePaths from utils.image_paths import ImagePaths
from .widgets.segment_details_dialog import SegmentDetailsDialog
class MainWindow(QWidget): class MainWindow(QWidget):
def __init__(self): def __init__(self):
@ -34,21 +36,27 @@ class MainWindow(QWidget):
# 安装事件过滤器,处理计划方量的 QLineEdit的失去焦点事件 # 安装事件过滤器,处理计划方量的 QLineEdit的失去焦点事件
self.installEventFilter(self) self.installEventFilter(self)
# 连接槽函数
def connectSignalToSlot(self): def connectSignalToSlot(self):
# 可添加信号槽连接 # 可添加信号槽连接
# self.system_button_widget.buttons["系统启动"].clicked.connect(self.handleSystemStart) # self.system_button_widget.buttons["系统启动"].clicked.connect(self.handleSystemStart)
# self.system_button_widget.buttons["系统停止"].clicked.connect(self.handleSystemStop) # self.system_button_widget.buttons["系统停止"].clicked.connect(self.handleSystemStop)
pass
self.conveyor_system_widget.left_btn.clicked.connect(self.handleHopperMoveLeft) # 传送带部分的按钮
self.conveyor_system_widget.right_btn.clicked.connect(self.handleHopperMoveRight) self.conveyor_system_widget.left_btn.clicked.connect(self.handleHopperMoveLeft) # 传送带下的左移按钮
self.conveyor_system_widget.right_btn.clicked.connect(self.handleHopperMoveRight) # 传送带下的右移按钮
# 管片任务详情
self.segment_task_widget.task_details_signal.connect(self.handleSegmentTaskDetails) # 管片任务详情按钮
def handleSystemStart(self): def handleSystemStart(self):
# 测试 # 测试系统开启,进度条动画
self.production_progress.testProgress(60) self.production_progress.testProgress(60)
self.arc_progress.testProgress(60) self.arc_progress.testProgress(60)
def handleSystemStop(self): def handleSystemStop(self):
# 测试 # 测试系统停止,进度条动画
self.production_progress.animation.stop() self.production_progress.animation.stop()
self.arc_progress.animation.stop() self.arc_progress.animation.stop()
@ -63,7 +71,8 @@ class MainWindow(QWidget):
# self.setStyleSheet("background-color: #ffffff;") # #001558 # self.setStyleSheet("background-color: #ffffff;") # #001558
# Qt.FramelessWindowHint # Qt.FramelessWindowHint
# self.setWindowFlags(Qt.FramelessWindowHint) # 没有顶部的白色边框
self.setWindowFlags(Qt.FramelessWindowHint) # 无边框
# 设置主界面背景图片 # 设置主界面背景图片
try: try:
@ -100,6 +109,7 @@ class MainWindow(QWidget):
self.dispatch_task_widget.set_task_id("task2", "PD0002") self.dispatch_task_widget.set_task_id("task2", "PD0002")
self.dispatch_task_widget.set_task_id("task3", "PD0003") self.dispatch_task_widget.set_task_id("task3", "PD0003")
# 读取数据库,初始化 管片任务的数据
from busisness.blls import ArtifactBll, PDRecordBll from busisness.blls import ArtifactBll, PDRecordBll
artifact_dal = ArtifactBll() artifact_dal = ArtifactBll()
artifacts = artifact_dal.get_artifact_task() artifacts = artifact_dal.get_artifact_task()
@ -220,6 +230,17 @@ class MainWindow(QWidget):
# 料斗右移完成,恢复料斗左移按钮 # 料斗右移完成,恢复料斗左移按钮
QTimer.singleShot(2100, lambda: self.conveyor_system_widget.left_btn.setEnabled(True)) QTimer.singleShot(2100, lambda: self.conveyor_system_widget.left_btn.setEnabled(True))
def handleSegmentTaskDetails(self, segment_task_name:str):
# 管片任务名 task1、task2、task3 (分别对应第一条管片任务、 第二条管片任务...)
print("main_window: handleSegmentTaskDetails", segment_task_name)
# 显示管片任务详情对话框
segment_details_dialog = SegmentDetailsDialog(self)
# 这里可以设置对话框显示的内容 如 set_segment_id
# segment_details_dialog.set_segment_id("9999999999")
segment_details_dialog.show()
# 更新 派单任务widget的坐标 # 更新 派单任务widget的坐标
def update_dispatch_task_position(self): def update_dispatch_task_position(self):
# 方法1获取模具车控件左上角坐标相对于父控件 # 方法1获取模具车控件左上角坐标相对于父控件

View File

@ -0,0 +1,315 @@
from PySide6.QtWidgets import (
QApplication,
QDialog,
QVBoxLayout,
QHBoxLayout,
QGridLayout,
QLabel,
QWidget,
QPushButton
)
from PySide6.QtGui import QPixmap, QFont, QPainter, QIcon
from PySide6.QtCore import Qt
import sys
from utils.image_paths import ImagePaths
"""
管片任务详情的弹出窗口: 点击管片任务的详情按钮之后弹出
"""
class SegmentDetailsDialog(QDialog):
def __init__(self, parent=None):
super().__init__(parent)
self.setAttribute(Qt.WA_TranslucentBackground)
# 初始化存储需要修改的控件
self.id_value_label = None # 管片ID值标签
self.left_cells = [] # 左列单元格列表每个元素是包含label和value的widget
self.right_cells = [] # 右列单元格列表
self._init_ui()
def _init_ui(self):
# 基础设置:无边框+窗口尺寸由背景图决定
self.setWindowFlags(Qt.FramelessWindowHint)
self._load_background()
# 主布局:
main_layout = QVBoxLayout(self)
main_layout.setContentsMargins(32, 20, 32, 50)
main_layout.setSpacing(0)
# 1. 顶部区域(标题 + 关闭按钮)
self._add_top_area(main_layout)
# 2. 管片ID区域保存ID值标签引用
self._add_segment_id_area(main_layout)
# 3. 网格信息区域(保存左右列单元格引用)
self._add_grid_info_area(main_layout)
def _load_background(self):
self.bg_pixmap = QPixmap(ImagePaths.SEGMENT_DETAILS_POPUP_BG)
if self.bg_pixmap.isNull():
print("错误:详情弹出背景.png 加载失败,请检查路径!")
self.setFixedSize(800, 600)
else:
self.setFixedSize(self.bg_pixmap.size())
def _add_top_area(self, parent_layout):
"""创建包含标题和关闭按钮的顶部水平布局"""
top_layout = QHBoxLayout()
top_layout.setContentsMargins(0, 0, 0, 36) # 保持原标题下方36px间距
top_layout.setSpacing(0)
# 左侧弹簧(让标题居中)
top_layout.addStretch()
# 标题标签(复用原标题逻辑)
title_label = QLabel("管片任务")
font = QFont()
font.setPixelSize(24)
font.setLetterSpacing(QFont.AbsoluteSpacing, 2)
font.setBold(True)
title_label.setFont(font)
title_label.setStyleSheet("color: #13fffc; font-weight: Bold;")
title_label.setAlignment(Qt.AlignCenter)
top_layout.addWidget(title_label)
# 右侧:关闭按钮
self._create_close_button(top_layout)
parent_layout.addLayout(top_layout)
# 新增:创建关闭按钮
def _create_close_button(self, parent_layout):
"""创建36x36关闭按钮"""
self.close_btn = QPushButton()
self.close_btn.setFixedSize(36, 36) # 固定尺寸18x18
# 加载关闭图标
close_icon = QPixmap(ImagePaths.SEGMENT_DETAILS_CLOSE_ICON)
if not close_icon.isNull():
# 设置图标并自适应按钮大小
self.close_btn.setIcon(QIcon(close_icon))
# 样式设置:默认透明背景,悬停红色背景
self.close_btn.setStyleSheet("""
QPushButton {
background-color: transparent;
border: none;
padding: 0px;
}
QPushButton:hover {
background-color: red;
border-radius: 2px;
}
""")
# 点击事件:关闭窗口
self.close_btn.clicked.connect(self.close)
# 添加到布局(与标题保持间距)
parent_layout.addStretch() # 右侧弹簧,确保按钮靠右
parent_layout.addWidget(self.close_btn)
def _add_segment_id_area(self, parent_layout):
id_layout = QHBoxLayout()
# 左侧管片ID标签
id_label = QLabel("管片ID")
id_label.setFixedSize(318, 32)
id_font = QFont()
id_font.setPixelSize(18)
id_font.setLetterSpacing(QFont.AbsoluteSpacing, 2)
id_font.setBold(True)
id_label.setFont(id_font)
id_label.setStyleSheet(f"""
background-image: url({ImagePaths.SEGMENT_DETAILS_TITLE_BG});
background-repeat: no-repeat;
background-position: center;
color: #13ffff;
""")
id_label.setContentsMargins(16, 0, 0, 0)
id_label.setAlignment(Qt.AlignLeft | Qt.AlignVCenter)
# 右侧管片ID值保存引用到实例变量
self.id_value_label = QLabel("346482967298119")
value_font = QFont()
value_font.setPixelSize(18)
value_font.setBold(True)
value_font.setLetterSpacing(QFont.AbsoluteSpacing, 2)
self.id_value_label.setFont(value_font)
self.id_value_label.setStyleSheet("color: #13ffff;")
self.id_value_label.setAlignment(Qt.AlignRight | Qt.AlignVCenter)
id_layout.addWidget(id_label)
id_layout.addStretch()
id_layout.addWidget(self.id_value_label)
id_layout.setContentsMargins(0, 0, 0, 16)
parent_layout.addLayout(id_layout)
def _add_grid_info_area(self, parent_layout):
grid_layout = QGridLayout()
grid_layout.setSpacing(12)
# 初始化显示的数据
# 左侧信息条目
left_info_items = [
("管片编号", "QR1B32000153AD"),
("管片副标识", "QR1B32000153AD"),
("生产环号", "QR1B32000153AD"),
("模具编号", "QR1B32000153AD"),
("骨架编号", "QR1B32000153AD"),
("环类型编号", "QR1B32000153AD"),
("尺寸规格", "QR1B32000153AD"),
]
# 右侧信息条目
right_info_items = [
("分块号", "QR3143243423543254"),
("出洞环标记", "QR3143243423543254"),
("注浆管标记", "QR3143243423543254"),
("聚丙烯纤维标记", "QR3143243423543254"),
("浇筑方量", "QR3143243423543254"),
("任务单号", "QR3143243423543254"),
("埋深", "QR3143243423543254"),
]
# 填充左列并保存单元格引用
self.left_cells.clear() # 清空列表
for row, (label_text, value_text) in enumerate(left_info_items):
cell_widget = self._create_info_cell(label_text, value_text)
self.left_cells.append(cell_widget) # 保存到列表
grid_layout.addWidget(cell_widget, row, 0)
# 填充右列并保存单元格引用
self.right_cells.clear() # 清空列表
for row, (label_text, value_text) in enumerate(right_info_items):
cell_widget = self._create_info_cell(label_text, value_text)
self.right_cells.append(cell_widget) # 保存到列表
grid_layout.addWidget(cell_widget, row, 1)
parent_layout.addLayout(grid_layout)
def _create_info_cell(self, label_text, value_text):
cell_widget = QWidget()
cell_bg = QPixmap(ImagePaths.SEGMENT_DETAILS_INFO_BAR)
if not cell_bg.isNull():
cell_widget.setFixedSize(cell_bg.size())
cell_widget.setStyleSheet(f"""
background-image: url({ImagePaths.SEGMENT_DETAILS_INFO_BAR});
background-repeat: no-repeat;
background-position: Center;
""")
else:
print("警告:管片任务信息栏.png 加载失败,使用默认背景!")
cell_widget.setStyleSheet("background-color: #0a2463;")
cell_layout = QHBoxLayout(cell_widget)
cell_layout.setContentsMargins(2, 0, 0, 0)
# 左侧标签保存到cell_widget的属性中
label = QLabel(label_text)
label.setFixedSize(136, 60)
label_font = QFont()
label_font.setPixelSize(16)
label_font.setLetterSpacing(QFont.AbsoluteSpacing, 2)
label.setFont(label_font)
label.setStyleSheet("background: none; background-color: #1369b4; color: #fffffd; font-weight:Bold;")
label.setAlignment(Qt.AlignCenter)
cell_widget.label = label
# 右侧值保存到cell_widget的属性中
value = QLabel(value_text)
value_font = QFont()
value_font.setPixelSize(18)
value.setFont(value_font)
value.setStyleSheet("background: none; color: #9fbfd4;")
value.setAlignment(Qt.AlignVCenter | Qt.AlignLeft)
cell_widget.value = value
cell_layout.addWidget(label)
cell_layout.addSpacing(60)
cell_layout.addWidget(value)
return cell_widget
def paintEvent(self, event):
if not self.bg_pixmap.isNull():
painter = QPainter(self)
painter.drawPixmap(self.rect(), self.bg_pixmap)
super().paintEvent(event)
# ------------------- 对外修改接口 -------------------
# --------------修改管片任务详情中显示的值 ------------
def set_segment_id(self, new_id):
"""修改管片ID的值"""
if self.id_value_label:
self.id_value_label.setText(str(new_id))
def set_left_label(self, row, new_label_text:str):
"""
修改左列网格的标签文本 ("生产环号")
Args:
row: 左列网格行号(0-6,共7行)
new_label_text: 新的标签文字(如“管片编号”)
"""
if 0 <= row < len(self.left_cells):
cell = self.left_cells[row]
cell.label.setText(new_label_text)
def set_left_value(self, row, new_value_text:str):
"""
修改左列网格的值
Args:
row: 左列网格行号(0-6,共7行)
new_value_text: 新的值(如“FB789”)
"""
if 0 <= row < len(self.left_cells):
cell = self.left_cells[row]
cell.value.setText(new_value_text)
def set_right_label(self, row, new_label_text:str):
"""
修改右列网格的标签文本 ("任务单号")
Args:
row: 右列网格行号(0-6,共7行)
new_label_text: 新的标签文字(如“分块号”)
"""
if 0 <= row < len(self.right_cells):
cell = self.right_cells[row]
cell.label.setText(new_label_text)
def set_right_value(self, row, new_value_text:str):
"""
修改右列网格的值
Args:
row: 右列网格行号(0-6,共7行)
new_value_text: 新的值(如“FB789”)
"""
if 0 <= row < len(self.left_cells):
cell = self.right_cells[row]
cell.value.setText(new_value_text)
# 测试代码
if __name__ == "__main__":
app = QApplication(sys.argv)
dialog = SegmentDetailsDialog()
dialog.show()
# 测试修改接口
dialog.set_segment_id("999999999999999") # 修改管片ID值
# 左列修改
dialog.set_left_label(0, "新管片编号") # 修改左列第0行的标签文本
dialog.set_left_value(0, "QR6666666666666") # 修改左列第0行的值
# 右列修改
dialog.set_right_label(0, "新分块号") # 修改右列第0行的标签文本
dialog.set_right_value(0, "QR99999999999999999") # 修改右列第0行的值
sys.exit(app.exec())

View File

@ -60,10 +60,10 @@ class SystemCenterDialog(QDialog):
def __init__(self, parent=None): def __init__(self, parent=None):
super().__init__(parent) super().__init__(parent)
self.init_ui() self._init_ui()
self.init_animations() # 初始化动画 self.init_animations() # 初始化动画
def init_ui(self): def _init_ui(self):
# 弹窗基础设置 # 弹窗基础设置
self.setWindowTitle("系统中心") self.setWindowTitle("系统中心")
self.setWindowFlags(Qt.FramelessWindowHint) # 隐藏默认边框 self.setWindowFlags(Qt.FramelessWindowHint) # 隐藏默认边框

View File

@ -0,0 +1,385 @@
from PySide6.QtWidgets import (
QApplication,
QMainWindow,
QWidget,
QVBoxLayout,
QGridLayout,
QLabel,
QHBoxLayout,
QListWidget,
QListWidgetItem,
QSpacerItem,
QSizePolicy,
QLineEdit,
QDialog,
)
from PySide6.QtGui import QPixmap, QFont, QColor, QTransform, QPainter
from PySide6.QtCore import (
Qt,
QPoint,
QEvent,
QPropertyAnimation,
QEasingCurve,
QRect,
QParallelAnimationGroup,
)
import sys
from utils.image_paths import ImagePaths
"""
系统诊断按钮的弹窗: 可以显示设备的状态
"""
class CustomDropdown(QWidget):
"""自定义下拉框组件"""
def __init__(self, options, arrow_img_path, parent=None):
super().__init__(parent)
self.options = options
self.arrow_img_path = arrow_img_path
self.is_expanded = False
# 主布局(标签 + 箭头)
self.main_layout = QHBoxLayout(self)
self.main_layout.setContentsMargins(0, 0, 0, 0)
self.main_layout.setSpacing(0)
self.main_layout.setAlignment(Qt.AlignLeft)
self.setFixedSize(63, 19) # 需要根据下拉框需要显示的文字来修改
# self.setFixedHeight(19)
# 1. 结果显示标签QLabel无clicked信号
self.result_label = QLabel(options[0])
self.result_label.setStyleSheet(
"""
background-image: url("");
color: #16ffff;
background-color: transparent;
border: none;
padding: 0px;
font-size: 18px;
"""
)
# self.result_label.setCursor(Qt.PointingHandCursor) # 手型光标提示可点击
self.main_layout.addWidget(
self.result_label, alignment=Qt.AlignVCenter | Qt.AlignLeft
)
# 2. 可点击的箭头标签QLabel
self.arrow_label = QLabel()
self.arrow_pixmap = QPixmap(arrow_img_path)
self.arrow_label.setStyleSheet("background-image: url(" ");")
self.arrow_label.setPixmap(
self.arrow_pixmap.scaled(12, 9, Qt.KeepAspectRatio, Qt.SmoothTransformation)
)
self.arrow_label.setCursor(Qt.PointingHandCursor)
self.main_layout.addWidget(self.arrow_label, alignment=Qt.AlignTop)
# 3. 下拉选项列表(默认选中第一个)
self.list_widget = QListWidget()
self.list_widget.setWindowFlags(Qt.Popup)
# 设置选项字体
font = QFont()
font.setPixelSize(16)
# 添加所有的下拉选项
for option in options:
item = QListWidgetItem(option)
item.setTextAlignment(Qt.AlignLeft)
item.setFont(font)
self.list_widget.addItem(item)
self.list_widget.setCurrentRow(0) # 默认选中第一项
self.list_widget.itemClicked.connect(self.select_option)
# 双保险监听:全局焦点变化 + 事件过滤
self.app = QApplication.instance()
self.app.focusChanged.connect(self.on_focus_changed)
self.list_widget.installEventFilter(self)
def mousePressEvent(self, event):
"""重写鼠标点击事件实现QLabel点击功能"""
# 判断点击是否在result_label或arrow_label区域内
# if self.result_label.underMouse() or self.arrow_label.underMouse():
# self.toggle_expand()
if self.arrow_label.underMouse():
self.toggle_expand()
super().mousePressEvent(event) # 传递事件,不影响其他组件
def toggle_expand(self):
"""切换下拉框展开/收起 + 箭头旋转"""
if self.is_expanded:
self.list_widget.hide()
# 箭头恢复向下
self.arrow_label.setPixmap(self.arrow_pixmap)
else:
# 计算下拉框位置(在标签下方对齐)
label_pos = self.result_label.mapToGlobal(
QPoint(0, self.result_label.height())
)
self.list_widget.setGeometry(
label_pos.x(), label_pos.y(), self.result_label.width() + 10, 80
)
self.list_widget.show()
self.list_widget.setFocus()
# 箭头旋转180度向上
transform = QTransform().rotate(180)
rotated_pixmap = self.arrow_pixmap.transformed(
transform, Qt.SmoothTransformation
)
self.arrow_label.setPixmap(rotated_pixmap)
self.is_expanded = not self.is_expanded
def select_option(self, item):
"""选择选项后更新标签 + 收起下拉框"""
self.result_label.setText(item.text())
self.list_widget.hide()
self.arrow_label.setPixmap(self.arrow_pixmap)
self.is_expanded = False
def on_focus_changed(self, old_widget, new_widget):
"""焦点变化时关闭下拉框"""
if self.is_expanded:
is_focus_on_self = (
new_widget == self
or new_widget == self.result_label
or new_widget == self.arrow_label
or (self.list_widget.isAncestorOf(new_widget) if new_widget else False)
)
if not is_focus_on_self:
self.list_widget.hide()
self.arrow_label.setPixmap(self.arrow_pixmap)
self.is_expanded = False
def eventFilter(self, obj, event):
"""点击外部关闭下拉框"""
if obj == self.list_widget and event.type() == QEvent.MouseButtonPress:
self.list_widget.hide()
self.arrow_label.setPixmap(
self.arrow_pixmap.scaled(
12, 9, Qt.KeepAspectRatio, Qt.SmoothTransformation
)
)
self.is_expanded = False
return True
return super().eventFilter(obj, event)
def setFont(self, font):
"""设置字体"""
self.result_label.setFont(font)
for i in range(self.list_widget.count()):
self.list_widget.item(i).setFont(font)
# 获取当前选中的设备名
def get_selected_device(self):
return self.result_label.text()
class SystemDiagnosticsDialog(QDialog):
def __init__(self, parent=None):
super().__init__(parent)
self.setAttribute(Qt.WA_TranslucentBackground)
self.setWindowOpacity(0.0)
self._init_ui()
self.init_animations()
def _init_ui(self):
# 无边框模式
self.setWindowFlags(Qt.FramelessWindowHint)
# 加载系统诊断弹窗的背景图片
self.bg_pixmap = QPixmap(ImagePaths.SYSTEM_DIAGNOSTICS_POPUP_BG)
if self.bg_pixmap.isNull():
print("错误: 系统诊断弹窗背景图加载失败!请检查路径是否正确")
else:
# 窗口尺寸与图片尺寸完全一致
self.setFixedSize(self.bg_pixmap.size())
# 网格布局8行4列小框
grid_layout = QGridLayout(self)
grid_layout.setContentsMargins(24, 28, 20, 24)
# 图片路径(替换为实际路径)
box_image_path = ImagePaths.SYSTEM_DIAGNOSTICS_BOX
circle_normal_path = ImagePaths.SYSTEM_DIAGNOSTICS_STATUS_GREEN # 正常状态
circle_warning_path = ImagePaths.SYSTEM_DIAGNOSTICS_STATUS_YELLOW # 警告状态
circle_error_path = ImagePaths.SYSTEM_DIAGNOSTICS_STATUS_RED # 异常状态
ms_box_path = ImagePaths.SYSTEM_DIAGNOSTICS_MS_BG
dropdown_arrow_path = ImagePaths.SYSTEM_DIAGNOSTICS_DROPDOWN_ARROW
# 字体设置
ms_font = QFont()
ms_font.setPixelSize(14)
ms_color = QColor("#14abea")
# 生成小框
for row in range(8):
for col in range(4):
box_container = QWidget()
box_container.setObjectName(f"box_{row}_{col}")
box_container.setStyleSheet(
f"""
background-image: url("{box_image_path}");
background-repeat: no-repeat;
"""
)
box_layout = QHBoxLayout(box_container)
box_layout.setSpacing(0)
# ========== 状态圆圈(支持状态切换) ==========
circle_label = QLabel()
circle_label.status = "normal"
circle_label.pixmaps = {
"normal": QPixmap(circle_normal_path),
"warning": QPixmap(circle_warning_path),
"error": QPixmap(circle_error_path),
}
circle_label.setPixmap(circle_label.pixmaps["normal"])
circle_label.setStyleSheet("background: none;")
# ========== 自定义下拉框(支持获取设备名) ==========
led_dropdown = CustomDropdown(
options=["LED1", "LED2", "LED3"], arrow_img_path=dropdown_arrow_path
)
# ========== 秒数输入框(获取毫秒值) ==========
ms_container = QWidget()
ms_layout = QHBoxLayout(ms_container)
ms_layout.setContentsMargins(6, 0, 0, 0)
ms_edit = QLineEdit("5ms")
ms_edit.setFont(ms_font)
ms_edit.setStyleSheet(
f"""
background: none;
color: {ms_color.name()};
border: none;
outline: none;
background-color: transparent;
"""
)
ms_container.setStyleSheet(
f"""
background-image: url("{ms_box_path}");
background-repeat: no-repeat;
"""
)
ms_layout.addWidget(ms_edit)
# 保存组件引用 (动态增加)
box_container.circle = circle_label
box_container.dropdown = led_dropdown
box_container.ms_edit = ms_edit
# 间距调整
spacer1 = QSpacerItem(5, 1, QSizePolicy.Fixed, QSizePolicy.Minimum)
spacer2 = QSpacerItem(5, 1, QSizePolicy.Fixed, QSizePolicy.Minimum)
spacer3 = QSpacerItem(8, 1, QSizePolicy.Fixed, QSizePolicy.Minimum)
# box_layout.addItem(spacer1)
box_layout.addWidget(circle_label)
box_layout.addItem(spacer2)
box_layout.addWidget(led_dropdown)
# box_layout.addItem(spacer3)
box_layout.addWidget(ms_container)
grid_layout.addWidget(box_container, row, col)
def init_animations(self):
"""初始化显示动画:从下方滑入 + 淡入"""
# 1. 透明度动画从0→1与系统中心一致但时长不同
self.opacity_anim = QPropertyAnimation(self, b"windowOpacity")
self.opacity_anim.setDuration(400)
self.opacity_anim.setStartValue(0.0)
self.opacity_anim.setEndValue(1.0)
self.opacity_anim.setEasingCurve(QEasingCurve.OutCubic) # 缓动曲线不同
# 2. 位置动画从下方100px滑入目标位置核心差异点
self.pos_anim = QPropertyAnimation(self, b"geometry")
self.pos_anim.setDuration(400)
self.pos_anim.setEasingCurve(QEasingCurve.OutQuart) # 滑入效果更自然
# 3. 组合动画(同时执行滑入和淡入)
self.anim_group = QParallelAnimationGroup(self)
self.anim_group.addAnimation(self.opacity_anim)
self.anim_group.addAnimation(self.pos_anim)
def showEvent(self, event):
super().showEvent(event) # 先调用父类方法
# 动态计算动画起点在当前位置下方100px保持宽度和高度不变
current_geometry = self.geometry() # 当前位置和尺寸需提前用move设置
# 起点y坐标增加100px从下方滑入x和尺寸不变
start_rect = QRect(
current_geometry.x(),
current_geometry.y() + 100, # 下方100px
current_geometry.width(),
current_geometry.height()
)
# 设置动画起点和终点
self.pos_anim.setStartValue(start_rect)
self.pos_anim.setEndValue(current_geometry) # 终点:目标位置
# 启动动画
self.anim_group.start()
def paintEvent(self, event):
"""重写绘制事件,手动在透明背景上绘制图片"""
if not self.bg_pixmap.isNull():
painter = QPainter(self)
# 绘制背景图(完全覆盖窗口,无间隙)
painter.drawPixmap(self.rect(), self.bg_pixmap)
# 必须调用父类方法,确保子控件正常绘制
super().paintEvent(event)
"""
注意: row表示行号、col表示列号。都是从 0开始, 比如: 0行0列
"""
# ========== 对外接口:设置设备状态 ==========
def set_circle_status(self, row, col, status):
"""设置指定行列的状态(绿-黄-红) (normal/warning/error)"""
box = self.findChild(QWidget, f"box_{row}_{col}")
if box and hasattr(box, "circle"):
box.circle.setPixmap(box.circle.pixmaps[status])
box.circle.status = status
# ========== 对外接口:获取选中的设备名 ==========
def get_selected_device(self, row, col):
"""获取指定行列的选中设备名"""
box = self.findChild(QWidget, f"box_{row}_{col}")
if box and hasattr(box, "dropdown"):
return box.dropdown.get_selected_device()
return None
# ========== 对外接口:获取毫秒值 ==========
def get_ms_value(self, row, col):
"""获取指定行列的毫秒值如“5ms”"""
box = self.findChild(QWidget, f"box_{row}_{col}")
if box and hasattr(box, "ms_edit"):
# return box.ms_edit.text()
text = box.ms_edit.text().strip()
# 用正则提取数字(支持整数/小数,如"5"、"3.8"、"10.2ms"
import re
number_match = re.search(r"(\d+(?:\.\d+)?)", text)
if number_match:
return number_match.group(1)
return None
if __name__ == "__main__":
app = QApplication(sys.argv)
dialog = SystemDiagnosticsDialog()
dialog.show()
# 1. 设置0行0列的状态为“警告”状态
dialog.set_circle_status(0, 0, "warning")
# 2. 获取1行2列的选中设备名
device = dialog.get_selected_device(1, 2)
print(f"选中设备:{device}")
# 3. 获取3行1列的毫秒值
ms = dialog.get_ms_value(3, 1)
print(f"毫秒值:{ms}")
sys.exit(app.exec())

View File

@ -1,14 +1,19 @@
from PySide6.QtWidgets import (QWidget, QVBoxLayout, QHBoxLayout, QLabel, from PySide6.QtWidgets import (QWidget, QVBoxLayout, QHBoxLayout, QLabel,
QPushButton, QMessageBox, QApplication) QPushButton, QMessageBox, QApplication)
from PySide6.QtCore import Qt from PySide6.QtCore import Qt, Signal
from PySide6.QtGui import QPainter, QPixmap, QFont from PySide6.QtGui import QPainter, QPixmap, QFont
import sys import sys
import resources.resources_rc import resources.resources_rc
from utils.image_paths import ImagePaths from utils.image_paths import ImagePaths
# 任务控件,如:管片任务、派单任务 """
任务控件,如:管片任务、派单任务
"""
class TaskWidget(QWidget): class TaskWidget(QWidget):
# 任务详情信号: task1表示第一条任务
task_details_signal = Signal(str)
def __init__(self, taskTitle:str, parent=None): def __init__(self, taskTitle:str, parent=None):
super().__init__(parent) super().__init__(parent)
# 设置Widget大小与背景图一致 # 设置Widget大小与背景图一致
@ -153,8 +158,12 @@ class TaskWidget(QWidget):
def _show_detail_dialog(self, task_name): def _show_detail_dialog(self, task_name):
"""显示任务详情弹窗""" """显示任务详情弹窗"""
QMessageBox.information(self, "任务详情", f"任务 {task_name} 的详细信息...") # QMessageBox.information(self, "任务详情", f"任务 {task_name} 的详细信息...")
"""
task1 表示第一条任务, 依次类推
"""
# 发送任务详情信号
self.task_details_signal.emit(task_name)
# -------------------------- # --------------------------
# 对外接口:修改任务属性 # 对外接口:修改任务属性