Files
Feeding_control_system/view/main_window.py

317 lines
15 KiB
Python
Raw Normal View History

2025-10-18 18:29:40 +08:00
# coding: utf-8
from typing import List
2025-10-31 18:52:31 +08:00
from PySide6.QtCore import Qt, Signal, QEasingCurve, QUrl, QSize, QTimer, QEvent
from PySide6.QtGui import QIcon, QDesktopServices, QColor, QPalette, QBrush, QImage
2025-10-18 18:29:40 +08:00
from PySide6.QtWidgets import (QApplication, QHBoxLayout, QVBoxLayout,
2025-10-31 18:52:31 +08:00
QFrame, QWidget, QSpacerItem, QSizePolicy, QMainWindow, QLineEdit)
2025-10-18 18:29:40 +08:00
2025-10-31 18:52:31 +08:00
# from .widgets.status_monitor_widget import StatusMonitorWidget
from .widgets.system_nav_bar import SystemNavBar
from .widgets.mixer_widget import MixerWidget
from .widgets.conveyor_system_widget import ConveyorSystemWidget
from .widgets.task_widget import TaskWidget
from .widgets.plan_widget import PlanWidget
2025-10-18 18:29:40 +08:00
from .widgets.hopper_widget import HopperWidget
from .widgets.arc_progress_widget import ArcProgressWidget
from .widgets.production_progress_widget import ProductionProgressWidget
2025-10-31 18:52:31 +08:00
# from .widgets.system_button_widget import SystemButtonWidget
2025-10-20 18:10:07 +08:00
from .widgets.vibration_video_widget import VibrationVideoWidget
2025-10-31 18:52:31 +08:00
from .widgets.bottom_control_widget import BottomControlWidget
2025-11-01 16:13:14 +08:00
import resources.resources_rc
from utils.image_paths import ImagePaths
from .widgets.segment_details_dialog import SegmentDetailsDialog
2025-10-18 18:29:40 +08:00
class MainWindow(QWidget):
def __init__(self):
super().__init__()
self.initWindow()
self.createSubWidgets() # 创建子部件
2025-10-31 18:52:31 +08:00
self.initSubWidgets() # 初始化子部件
2025-10-18 18:29:40 +08:00
self.setupLayout() # 设置布局
self.connectSignalToSlot()
2025-10-31 18:52:31 +08:00
# 安装事件过滤器,处理计划方量的 QLineEdit的失去焦点事件
self.installEventFilter(self)
# 连接槽函数
2025-10-18 18:29:40 +08:00
def connectSignalToSlot(self):
# 可添加信号槽连接
2025-10-31 18:52:31 +08:00
# self.system_button_widget.buttons["系统启动"].clicked.connect(self.handleSystemStart)
# self.system_button_widget.buttons["系统停止"].clicked.connect(self.handleSystemStop)
# 传送带部分的按钮
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) # 管片任务详情按钮
2025-10-18 18:29:40 +08:00
def handleSystemStart(self):
# 测试系统开启,进度条动画
2025-10-18 18:29:40 +08:00
self.production_progress.testProgress(60)
self.arc_progress.testProgress(60)
def handleSystemStop(self):
# 测试系统停止,进度条动画
2025-10-18 18:29:40 +08:00
self.production_progress.animation.stop()
self.arc_progress.animation.stop()
def initWindow(self):
"""初始化窗口基本属性"""
self.setWindowTitle("中交三航主界面") # 设置窗口标题
2025-10-31 18:52:31 +08:00
# 设置最小尺寸(可根据需求调整)
# 工控机尺寸为 1280*1024
self.setMinimumSize(1280, 1024)
self.setObjectName("MainWindow")
2025-10-18 18:29:40 +08:00
# 设置主界面窗口背景色
2025-10-31 18:52:31 +08:00
# self.setStyleSheet("background-color: #ffffff;") # #001558
# Qt.FramelessWindowHint
2025-11-07 15:45:54 +08:00
# 没有顶部的白色边框
self.setWindowFlags(Qt.FramelessWindowHint) # 无边框
2025-10-31 18:52:31 +08:00
# 设置主界面背景图片
try:
2025-11-01 16:13:14 +08:00
self.background_image = QImage(ImagePaths.MAIN_INTERFACE_BACKGROUND)
2025-10-31 18:52:31 +08:00
if self.background_image.isNull():
raise Exception("图片为空,可能路径错误或格式不支持")
# print("图片加载成功")
except Exception as e:
print(f"主界面背景图片加载失败: {e}")
self.background_image = None
return # 加载背景失败
self.update_background()
2025-10-18 18:29:40 +08:00
def createSubWidgets(self):
"""创建所有子部件实例"""
2025-10-31 18:52:31 +08:00
self.system_nav_bar = SystemNavBar() # 最上方:系统导航栏
self.mixer_widget = MixerWidget(self) # 左侧: 搅拌机
self.conveyor_system_widget = ConveyorSystemWidget(self) # 左侧: 传送带系统
self.segment_task_widget = TaskWidget("管片任务", self) # 左侧:管片任务
self.dispatch_task_widget = TaskWidget("派单任务", self) # 右侧:派单任务
self.plan_table_widget = PlanWidget(self) # 右侧: 计划表单
# self.status_monitor = StatusMonitorWidget() # 状态监控部件
2025-10-18 18:29:40 +08:00
self.hopper_widget = HopperWidget() # 中间1料斗部件
self.arc_progress = ArcProgressWidget() # 中间2弧形进度部件
2025-10-31 18:52:31 +08:00
self.production_progress = ProductionProgressWidget() # 中间3: 生产进度部件
# self.system_button_widget = SystemButtonWidget() # 系统控制按钮
self.vibration_video = VibrationVideoWidget() # 振捣视频控件 (右侧)
self.bottom_control_widget = BottomControlWidget() # 最下方: 控制的按钮 (系统诊断、系统中心等)
def initSubWidgets(self):
self.dispatch_task_widget.set_task_id("task1", "PD0001")
self.dispatch_task_widget.set_task_id("task2", "PD0002")
self.dispatch_task_widget.set_task_id("task3", "PD0003")
# 读取数据库,初始化 管片任务的数据
2025-10-31 18:52:31 +08:00
from busisness.blls import ArtifactBll, PDRecordBll
artifact_dal = ArtifactBll()
artifacts = artifact_dal.get_artifact_task()
2025-11-01 16:13:14 +08:00
# print("\n打印artifacts数据:")
2025-10-31 18:52:31 +08:00
for i, artifact in enumerate(artifacts):
# 如果是数据类对象,转换为字典输出
# print(artifact.MouldCode)
# if hasattr(artifact, "__dataclass_fields__"):
# print(f"第{i+1}条: {artifact.__dict__}")
# else:
# print(f"第{i+1}条: {artifact}")
self.segment_task_widget.set_task_id(f"task{i + 1}", artifact.MouldCode)
self.segment_task_widget.set_task_volume(f"task{i + 1}", artifact.BetonVolume)
pass
pdrecord_dal = PDRecordBll()
pdrecords = pdrecord_dal.get_PD_record()
# print(pdrecords[0].MouldCode)
2025-11-01 16:13:14 +08:00
# print("\n打印pdrecords数据:")
2025-10-31 18:52:31 +08:00
for i, record in enumerate(pdrecords):
# 如果是数据类对象,转换为字典输出
# print(record.__dict__["MouldCode"])
# if hasattr(record, "__dataclass_fields__"):
# print(f"第{i+1}条: {record.__dict__}")
# else:
# print(f"第{i+1}条: {record}")
self.dispatch_task_widget.set_task_volume(f"task{i + 1}", record.BetonVolume)
pass
2025-10-18 18:29:40 +08:00
def setupLayout(self):
"""设置垂直布局,从上到下排列部件"""
main_layout = QVBoxLayout(self) # 主布局:垂直布局
# 设置布局间距(部件之间的距离)和边距(布局与窗口边缘的距离)
main_layout.setSpacing(0) # 部件间距0px
2025-10-31 18:52:31 +08:00
main_layout.setContentsMargins(0, 0, 0, 0) # 左上右下边距0px
# 添加最上方的 系统导航栏包括系统标题、中交三航的logo等
main_layout.addWidget(self.system_nav_bar, alignment=Qt.AlignTop)
# 相应控件的坐标设置
self.mixer_widget.move(0, 70) # 搅拌楼坐标
# self.conveyor_system_widget.move(0, 191)
self.conveyor_system_widget.move(0, 171) # 传送带系统坐标
# self.segment_task_widget.move(54, 406)
self.segment_task_widget.move(54, 382) # 管片任务坐标
# self.dispatch_task_widget.move(629, 384)
self.update_dispatch_task_position() # 更新派单任务坐标
self.update_plan_table_position() # 更新计划表单坐标
2025-10-18 18:29:40 +08:00
2025-10-31 18:52:31 +08:00
# 中间的垂直子布局
2025-10-20 18:10:07 +08:00
sub_v_layout = QVBoxLayout()
sub_v_layout.setSpacing(0)
2025-10-18 18:29:40 +08:00
# 依次添加部件到布局(从上到下)
2025-10-20 18:10:07 +08:00
# sub_v_layout.addWidget(self.status_monitor, alignment=Qt.AlignHCenter)
2025-10-31 18:52:31 +08:00
sub_v_layout.addSpacerItem(QSpacerItem(
20, 40, # 最小宽高
QSizePolicy.Minimum,
QSizePolicy.Expanding # 垂直方向伸缩,占据剩余空间
))
sub_v_layout.addWidget(self.hopper_widget, alignment=Qt.AlignHCenter) # 料斗
sub_v_layout.addWidget(self.arc_progress, alignment=Qt.AlignHCenter) # 拱形进度条(模具车)
sub_v_layout.addWidget(self.production_progress, alignment=Qt.AlignHCenter) # 生产进度条
sub_v_layout.addSpacerItem(QSpacerItem(
20, 40, # 最小宽高
QSizePolicy.Minimum,
QSizePolicy.Expanding # 垂直方向伸缩,占据剩余空间
))
2025-10-20 18:10:07 +08:00
# sub_v_layout.addWidget(self.system_button_widget, alignment=Qt.AlignHCenter)
2025-10-31 18:52:31 +08:00
# 中间的水平布局
2025-10-20 18:10:07 +08:00
middle_h_layout = QHBoxLayout()
2025-10-31 18:52:31 +08:00
# middle_h_layout.setSpacing(20)
middle_h_layout.setSpacing(0)
# 1、加入垂直子布局设置拉伸因子1让其占满水平剩余空间
2025-10-20 18:10:07 +08:00
middle_h_layout.addLayout(sub_v_layout, stretch=1)
2025-10-31 18:52:31 +08:00
# 2、加入振捣视频控件
middle_h_layout.addWidget(self.vibration_video, alignment=Qt.AlignCenter)
2025-10-20 18:10:07 +08:00
2025-10-31 18:52:31 +08:00
# === 中间子布局 添加到主布局 ===
2025-10-20 18:10:07 +08:00
main_layout.addLayout(middle_h_layout)
2025-10-18 18:29:40 +08:00
2025-10-31 18:52:31 +08:00
# 添加最下方的 控制按钮控件
main_layout.addWidget(self.bottom_control_widget, alignment=Qt.AlignBottom)
2025-10-18 18:29:40 +08:00
# 将布局应用到主窗口
self.setLayout(main_layout)
2025-10-31 18:52:31 +08:00
# 上料斗左移
def handleHopperMoveLeft(self):
# 演示效果
self.hopper_widget.hideUpperHopper()
self.conveyor_system_widget.showHopper() # 开始向左移动,显示料斗 (此时,料斗一定在传送带上的过渡位置)
# 按钮状态:点击料斗左移按钮后,禁用料斗右移按钮
self.conveyor_system_widget.right_btn.setEnabled(False)
# 以下为模拟:
# 假设两秒种之后,移动到了搅拌机下 (这里需要根据实际情况修改)
QTimer.singleShot(2000, self.conveyor_system_widget.moveHopperBelowMixer)
# 移动到搅拌楼下,搅拌桨就开始旋转
QTimer.singleShot(2100, self.mixer_widget.startBladeMix)
2025-10-31 18:52:31 +08:00
# 料斗左移完成,恢复料斗右移按钮
QTimer.singleShot(2100, lambda: self.conveyor_system_widget.right_btn.setEnabled(True))
# 上料斗右移
def handleHopperMoveRight(self):
# 演示效果
self.conveyor_system_widget.moveHopperToTransition() # 移动到过渡的位置
self.hopper_widget.upper_clamp_widget.set_angle(0) # 上料斗向右移动到目的地时夹爪的角度一定是0
# 按钮状态:点击料斗右移按钮后,禁用料斗左移按钮
self.conveyor_system_widget.left_btn.setEnabled(False)
# 开始右移,搅拌桨就停止转动
self.mixer_widget.stopBladeMix()
2025-10-31 18:52:31 +08:00
# 以下为模拟:
# 假设两秒后,传送带中 料斗向右移动完成 (这里需要根据实际情况修改)
QTimer.singleShot(1900, self.conveyor_system_widget.hideHopper) # 料斗向右移动完成,隐藏料斗
QTimer.singleShot(2000, self.hopper_widget.showUpperHopper)
# 料斗右移完成,恢复料斗左移按钮
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()
2025-10-31 18:52:31 +08:00
# 更新 派单任务widget的坐标
def update_dispatch_task_position(self):
# 方法1获取模具车控件左上角坐标相对于父控件
arc_pos = self.arc_progress.pos()
# print(f"料斗控件左上角坐标相对父控件x={arc_pos.x()}, y={arc_pos.y()}")
# x+462, y-249
self.dispatch_task_widget.move(arc_pos.x()+462, arc_pos.y()-249)
# print("update_dispatch_task_position", arc_pos.x()+462, arc_pos.y()-241)
# 更新 计划表单widget的坐标
def update_plan_table_position(self):
# 方法1获取料斗控件左上角坐标相对于父控件
arc_pos = self.hopper_widget.pos()
# print(f"料斗控件左上角坐标相对父控件x={arc_pos.x()}, y={arc_pos.y()}")
# x+462, y-249
self.plan_table_widget.move(arc_pos.x()+362, arc_pos.y()+40)
def update_background(self):
"""更新主界面背景图片"""
if self.background_image and not self.background_image.isNull():
palette = self.palette()
# 按当前窗口尺寸缩放图片
scaled_image = self.background_image.scaled(
self.size(),
Qt.IgnoreAspectRatio,
Qt.SmoothTransformation
)
palette.setBrush(QPalette.Window, QBrush(scaled_image))
self.setPalette(palette)
self.setAutoFillBackground(True)
def eventFilter(self, watched, event):
"""过滤鼠标点击事件, 让计划方量的QLineEdit在鼠标点击外部时失去焦点"""
# 只处理鼠标左键点击事件
if event.type() == QEvent.MouseButtonPress and event.button() == Qt.LeftButton:
# 获取当前拥有焦点的控件
focus_widget = QApplication.focusWidget()
# 判断焦点是否在QLineEdit上
if isinstance(focus_widget, QLineEdit):
# 将全局鼠标位置转换为焦点控件QLineEdit的局部坐标
local_pos = focus_widget.mapFromGlobal(event.globalPos())
# 若点击位置不在QLineEdit内部则清除焦点
if not focus_widget.rect().contains(local_pos):
focus_widget.clearFocus()
# 将焦点转移到主窗口,避免无焦点状态
self.setFocus()
return super().eventFilter(watched, event)
2025-10-18 18:29:40 +08:00
def resizeEvent(self, e):
"""窗口大小变化时的回调"""
super().resizeEvent(e)
2025-10-31 18:52:31 +08:00
self.update_background() # 重新加载背景图片
self.update_dispatch_task_position() # 更新 派单任务的坐标
self.update_plan_table_position() # 更新计划表单坐标
2025-10-18 18:29:40 +08:00
def closeEvent(self, e):
"""窗口关闭时的回调"""
super().closeEvent(e)
2025-10-31 18:52:31 +08:00
def keyPressEvent(self, event):
if event.key() == Qt.Key_Escape: # 按下Esc键, 退出界面
self.close()
super().keyPressEvent(event)
2025-10-18 18:29:40 +08:00
if __name__ == "__main__":
import sys
app = QApplication([])
window = MainWindow()
window.show()
sys.exit(app.exec())