系统诊断增加设备检测

This commit is contained in:
2026-01-11 18:00:32 +08:00
parent f860c5a216
commit b40ea0112a
13 changed files with 537 additions and 247 deletions

View File

@ -332,6 +332,7 @@ class MainWindow(QWidget):
def closeEvent(self, e):
"""窗口关闭时的回调"""
self.hide() # 隐藏界面
self.about_to_close.emit()
super().closeEvent(e)

View File

@ -140,7 +140,7 @@ class HopperWidget(QWidget):
self.upper_weight_label.move(outer_width//2 - 60, outer_height//2 - 46)
# 额外文字(上位)
self.upper_extra_label = QLabel("2.0方(预估)", self.upper_bg_widget)
self.upper_extra_label = QLabel("2.00", self.upper_bg_widget)
self.upper_extra_label.setAlignment(Qt.AlignCenter)
# #262c38 #16ffff #131427 #003669
self.upper_extra_label.setStyleSheet("background: none; background-color: #003669; color: #16ffff; font-size: 18px;")
@ -359,7 +359,8 @@ class HopperWidget(QWidget):
"""Args:
volume : 传入多少方
"""
self.upper_extra_label.setText(f"{volume}方(预估)")
volume_rounded = round(volume, 2) # 四舍五入,保留两位小数
self.upper_extra_label.setText(f"{volume_rounded:.2f}")
# 上料斗夹爪开合角度设置
def setUpperHopperClampAngle(self, angle: float):
@ -413,8 +414,9 @@ class HopperWidget(QWidget):
"""Args:
angle : 传入多少度 (单位°)
"""
self.lower_extra_label.setText(f"开: {angle}°") # 设置下料斗角度标签
self.lower_clamp_widget.set_angle(angle) # 设置下料斗夹爪开合角度
angle_rounded = round(angle, 2) # 四舍五入,保留两位小数
self.lower_extra_label.setText(f"开: {angle_rounded:.2f}°") # 设置下料斗角度标签
self.lower_clamp_widget.set_angle(angle_rounded) # 设置下料斗夹爪开合角度
# ------------------------------
# 设置上料斗状态0=绿1=黄2=红)

View File

@ -80,6 +80,9 @@ class MixerWidget(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
# 状态变量:标记搅拌桨是否正在旋转
self.is_mixing = False # 初始状态为未旋转
# 两个搅拌桨的转动的动画引用
self.animations = [] # 保存动画引用
@ -157,12 +160,20 @@ class MixerWidget(QWidget):
# 搅拌桨开始搅拌
def startBladeMix(self, duration=700):
if self.is_mixing: # 搅拌桨已经在旋转
return
self.animations.clear()
# 备注duration控制搅拌桨旋转的速度值越小旋转得越快
self._start_animation(self.blade1, duration)
self._start_animation(self.blade2, duration)
self.is_mixing = True # 更新搅拌桨状态为旋转中
def stopBladeMix(self):
if not self.is_mixing: # 搅拌桨已经停止
return
for animation in self.animations:
animation.stop()
@ -170,3 +181,5 @@ class MixerWidget(QWidget):
self.blade1.reset_to_original()
if self.blade2:
self.blade2.reset_to_original()
self.is_mixing = False # 更新搅拌桨状态为停止

View File

@ -61,7 +61,6 @@ class SystemCenterDialog(QDialog):
def __init__(self, parent=None):
super().__init__(parent)
self._init_ui()
self.init_animations() # 初始化动画
def _init_ui(self):
# 弹窗基础设置
@ -103,47 +102,6 @@ class SystemCenterDialog(QDialog):
painter.drawPixmap(self.rect(), self.background)
super().paintEvent(event)
def init_animations(self):
"""初始化显示动画(可根据喜好选择或组合)"""
# 1. 淡入动画透明度从0→1
self.opacity_anim = QPropertyAnimation(self, b"windowOpacity")
self.opacity_anim.setDuration(300) # 动画时长300ms
self.opacity_anim.setStartValue(0.0)
self.opacity_anim.setEndValue(1.0)
self.opacity_anim.setEasingCurve(QEasingCurve.InOutCubic) # 缓动曲线(平滑加速减速)
# 2. 缩放动画从80%→100%大小)
self.scale_anim = QPropertyAnimation(self, b"geometry")
self.scale_anim.setDuration(300)
# 起点和终点在显示时动态设置(依赖当前弹窗位置)
self.scale_anim.setEasingCurve(QEasingCurve.OutBack) # 带弹性的缓动曲线(弹出感)
# 3. 组合动画(同时执行淡入+缩放)
from PySide6.QtCore import QParallelAnimationGroup
self.anim_group = QParallelAnimationGroup(self)
self.anim_group.addAnimation(self.opacity_anim)
self.anim_group.addAnimation(self.scale_anim)
def showEvent(self, event):
"""重写显示事件,每次显示时启动动画"""
# 必须先调用父类showEvent否则弹窗无法正常显示
super().showEvent(event)
# 动态设置缩放动画的起点(基于当前弹窗位置和大小)
current_geometry = self.geometry() # 弹窗当前位置和大小已通过move设置
# 起点缩小到80%,并保持中心位置不变
start_rect = QRect(
current_geometry.center().x() - current_geometry.width() * 0.4,
current_geometry.center().y() - current_geometry.height() * 0.4,
int(current_geometry.width() * 0.8),
int(current_geometry.height() * 0.8)
)
self.scale_anim.setStartValue(start_rect)
self.scale_anim.setEndValue(current_geometry) # 终点:原始大小
# 启动组合动画
self.anim_group.start()
if __name__ == "__main__":
app = QApplication(sys.argv)
dialog = SystemCenterDialog()

View File

@ -66,44 +66,47 @@ class CustomDropdown(QWidget):
)
# 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)
# 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)
# 取消了下拉框 2026/1/11
# 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)
# 取消了下拉框 2026/1/11
# 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)
# 取消了下拉框 2026/1/11
# 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()
# if self.arrow_label.underMouse(): # 取消了下拉箭头 2026/1/11
# self.toggle_expand()
super().mousePressEvent(event) # 传递事件,不影响其他组件
def toggle_expand(self):
@ -173,15 +176,19 @@ class CustomDropdown(QWidget):
# 获取当前选中的设备名
def get_selected_device(self):
return self.result_label.text()
# 设置选中的设备名
def set_selected_device(self, device_name:str):
self.result_label.setText(device_name)
class SystemDiagnosticsDialog(QDialog):
def __init__(self, parent=None):
super().__init__(parent)
self.setAttribute(Qt.WA_TranslucentBackground)
self.setWindowOpacity(0.0)
self.max_row = 8 # 最大行数为8行
self.max_col = 4 # 最大列数为4列
self._init_ui()
self.init_animations()
def _init_ui(self):
# 无边框模式
@ -206,7 +213,7 @@ class SystemDiagnosticsDialog(QDialog):
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
dropdown_arrow_path = ImagePaths.SYSTEM_DIAGNOSTICS_DROPDOWN_ARROW # 箭头图标
# 字体设置
ms_font = QFont()
@ -214,8 +221,8 @@ class SystemDiagnosticsDialog(QDialog):
ms_color = QColor("#14abea")
# 生成小框
for row in range(8):
for col in range(4):
for row in range(self.max_row):
for col in range(self.max_col):
box_container = QWidget()
box_container.setObjectName(f"box_{row}_{col}")
box_container.setStyleSheet(
@ -249,6 +256,7 @@ class SystemDiagnosticsDialog(QDialog):
ms_layout.setContentsMargins(6, 0, 0, 0)
ms_edit = QLineEdit("5ms")
ms_edit.setFont(ms_font)
ms_edit.setReadOnly(True) # 禁用外部点击输入
ms_edit.setStyleSheet(
f"""
background: none;
@ -284,44 +292,6 @@ class SystemDiagnosticsDialog(QDialog):
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():
@ -345,15 +315,22 @@ class SystemDiagnosticsDialog(QDialog):
# ========== 对外接口:获取选中的设备名 ==========
def get_selected_device(self, row, col):
"""获取指定行列的选中设备名"""
"""获取指定行列的选中设备名, 行号和列号都从0开始"""
box = self.findChild(QWidget, f"box_{row}_{col}")
if box and hasattr(box, "dropdown"):
return box.dropdown.get_selected_device()
return None
# ========== 对外接口:设置选中的设备名 ==========
def set_selected_device(self, row, col, device_name:str):
"""设置指定行列的选中设备名, 行号和列号都从0开始"""
box = self.findChild(QWidget, f"box_{row}_{col}")
if box and hasattr(box, "dropdown"):
return box.dropdown.set_selected_device(device_name)
# ========== 对外接口:获取毫秒值 ==========
def get_ms_value(self, row, col):
"""获取指定行列的毫秒值如“5ms”"""
"""获取指定行列的毫秒值如“5ms”, 行号和列号都从0开始"""
box = self.findChild(QWidget, f"box_{row}_{col}")
if box and hasattr(box, "ms_edit"):
# return box.ms_edit.text()
@ -366,6 +343,13 @@ class SystemDiagnosticsDialog(QDialog):
return number_match.group(1)
return None
# ========== 对外接口:设置毫秒值 ==========
def set_ms_value(self, row, col, ms:int):
"""设置指定行列的毫秒值, 行号和列号都从0开始"""
box = self.findChild(QWidget, f"box_{row}_{col}")
if box and hasattr(box, "ms_edit"):
box.ms_edit.setText(f"{ms}ms")
if __name__ == "__main__":

View File

@ -4,6 +4,8 @@ from PySide6.QtCore import Qt
from PySide6.QtGui import QDoubleValidator
import sys
from common.constant_config_manager import ConfigManager
"""
调整计划方量, 左侧减按钮, 右侧加按钮
这里的 最小值、最大值、初始值 需要读取配置文件来决定
@ -27,13 +29,25 @@ class CustomLineEdit(QLineEdit):
self.setText(f"{value:.1f}")
self.setCursorPosition(0) # 光标移到最前面 (保证数值显示完整)
# 更新输入框的默认文本
def updateDefaultText(self, text:str):
self.default_text = text
class ValueAdjuster(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.min_value = 0.0 # 最小值
self.max_value = 99.0 # 最大值
self.value = 2.5 # 初始值 (需要显示一位数字)
self.config_manager = ConfigManager()
self.config_manager.adjuster_params_changed.connect(self.on_adjuster_params_changed) # 绑定参数变化槽函数
# self.min_value = 0.0 # 最小值
# self.max_value = 99.0 # 最大值
# self.value = 2.5 # 初始值 (需要显示一位数字)
self.min_value = self.config_manager.get_adjuster_min() # 最小值
self.max_value = self.config_manager.get_adjuster_max() # 最大值
self.value = self.config_manager.get_adjuster_initial() # 初始值 (需要显示一位小数)
self.setFixedSize(102, 32)
@ -120,6 +134,26 @@ class ValueAdjuster(QWidget):
layout.addWidget(self.line_edit)
layout.addWidget(self.plus_btn)
def on_adjuster_params_changed(self, new_min, new_max, new_initial):
"""配置文件中的min、max、initial变化时, 更新控件范围"""
# print("on_adjuster_params_changed:", new_min, new_max, new_initial)
# 1、更新最小值和最大值
if self.min_value != new_min or self.max_value != new_max:
self.min_value = new_min
self.max_value = new_max
self.line_edit.setValidator(QDoubleValidator(self.min_value, self.max_value, 1, self))
# 2、确保当前值在新范围内不在当前范围内就更新当前值
if self.value < new_min:
self.value = new_min
self.line_edit.setText(f"{self.value:.1f}")
elif self.value > new_max:
self.value = new_max
self.line_edit.setText(f"{self.value:.1f}")
# 3、更新输入编辑框默认值
self.line_edit.updateDefaultText(f"{new_initial:.1f}")
def on_minus_clicked(self):
"""减0.1"""
new_value = self.value - 0.1

View File

@ -172,13 +172,20 @@ class CameraModule(QWidget):
video_display_signal = Signal(str, bool)
def __init__(
self, camera_name="摄像头", rtsp_url="", need_rotate_180=True, parent=None
self, camera_name="摄像头", rtsp_url="", need_rotate_180=True, show_ai = False, parent=None
):
super().__init__(parent)
self.setObjectName("cameraModule")
self.camera_name = camera_name
self.rtsp_url = rtsp_url
self.need_rotate_180 = need_rotate_180 # 画面是否需要旋转180度后显示
self.show_ai = show_ai # 是否需要展示ai为True会多出AI显示按钮
# 初始化AI显示相关变量为None
self.ai_display_group = None
self.ai_display_label = None
self.ai_display_switch = None
self.setup_ui()
def setup_ui(self):
@ -195,20 +202,23 @@ class CameraModule(QWidget):
self.title_label = QLabel()
self.title_label.setAlignment(Qt.AlignLeft)
self.title_label.setText(f"{self.camera_name}视频")
# self.title_label.setFixedWidth(212)
self.title_label.setFixedSize(212, 21)
self.title_label.setObjectName("cameraTitleLabel")
# background-image: url({ImagePaths.VIDEO_TITLE_BACKGROUND});
# min-width: 126px;
self.title_label.setStyleSheet(
f"""
#cameraTitleLabel {{
font-size: 18px;
color: #16ffff;
background-image: url({ImagePaths.VIDEO_TITLE_BACKGROUND});
min-width: 180px;
padding-left: 12px;
}}
"""
)
# 创建【显示标签+开关】组合容器
# 1、创建【显示标签+开关】组合容器
self.display_group = QWidget() # 容器:包裹标签和开关
display_group_layout = QHBoxLayout(self.display_group)
display_group_layout.setContentsMargins(0, 0, 0, 0) # 容器内边距为0
@ -239,9 +249,43 @@ class CameraModule(QWidget):
display_group_layout.addWidget(self.display_label)
display_group_layout.addWidget(self.display_switch, alignment=Qt.AlignLeft)
if self.show_ai: # 当需要显示AI时才生成
# 2、创建【AI算法标签+开关】组合容器
self.ai_display_group = QWidget() # 容器包裹AI显示标签和开关
ai_display_group_layout = QHBoxLayout(self.ai_display_group)
ai_display_group_layout.setContentsMargins(0, 0, 0, 0) # 容器内边距为0
ai_display_group_layout.setSpacing(0)
# AI算法显示标签
self.ai_display_label = QLabel("AI算法")
self.ai_display_label.setObjectName("aiDisplayLabel")
# font-weight: bold;
self.ai_display_label.setStyleSheet("""
#aiDisplayLabel {
font-size: 18px;
color: #16ffff;
margin: 0px;
padding-left: 2px;
}
""")
# self.ai_display_label.setFixedWidth(40)
self.ai_display_label.setAlignment(Qt.AlignVCenter | Qt.AlignLeft)
# AI开关
self.ai_display_switch = SwitchButton()
self.ai_display_switch.setChecked(False)
# self.ai_display_switch.switched.connect(self.onDisplayButtonSwitched) # 在camera_controller中处理
# 将AI显示标签和开关添加到组合容器布局
ai_display_group_layout.addWidget(self.ai_display_label)
ai_display_group_layout.addWidget(self.ai_display_switch, alignment=Qt.AlignLeft)
# 添加到标题布局
title_layout.addWidget(self.title_label, alignment=Qt.AlignLeft)
title_layout.addWidget(self.display_group, alignment=Qt.AlignLeft)
title_layout.addWidget(self.display_group, alignment=Qt.AlignRight)
if self.show_ai: # 需要添加 AI显示控件
title_layout.addWidget(self.ai_display_group, alignment=Qt.AlignLeft)
self.title_label.setFixedSize(139, 21) # 调整 xxx视频标签的宽度
# 视频显示容器:使用堆叠布局,让重连按钮和视频标签分层显示
self.video_container = QWidget()
@ -313,7 +357,7 @@ class CameraModule(QWidget):
# 显示开关切换槽函数
def onDisplayButtonSwitched(self, state:bool):
# 显示开关打开state 为 True显示开关关闭state 为 False
# 显示开关打开state 为 True; 显示开关关闭state 为 False
if state:
self.stacked_widget.setCurrentWidget(self.raw_label) # 视频显示标签
self.stacked_widget.setHidden(False)
@ -403,8 +447,8 @@ class VibrationVideoWidget(QWidget):
# 需要修改为相应的地址!!!
# 注在camera_controller中设置地址url
self.cam1 = CameraModule("上位料斗")
self.cam2 = CameraModule("下位料斗")
self.cam3 = CameraModule("模具车")
self.cam2 = CameraModule("下位料斗", show_ai=False)
self.cam3 = CameraModule("模具车", need_rotate_180=False)
self.setup_ui()
self.connect_signals()