Files
Feeding_control_system/view/main_window.py

346 lines
16 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# coding: utf-8
from typing import List
from PySide6.QtCore import Qt, Signal, QEasingCurve, QUrl, QSize, QTimer, QEvent
from PySide6.QtGui import QIcon, QDesktopServices, QColor, QPalette, QBrush, QImage
from PySide6.QtWidgets import (QApplication, QHBoxLayout, QVBoxLayout,
QFrame, QWidget, QSpacerItem, QSizePolicy, QMainWindow, QLineEdit)
# 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
from .widgets.hopper_widget import HopperWidget
from .widgets.arc_progress_widget import ArcProgressWidget
from .widgets.production_progress_widget import ProductionProgressWidget
# from .widgets.system_button_widget import SystemButtonWidget
from .widgets.vibration_video_widget import VibrationVideoWidget
from .widgets.bottom_control_widget import BottomControlWidget
import resources.resources_rc
from utils.image_paths import ImagePaths
from .widgets.segment_details_dialog import SegmentDetailsDialog
from .widgets.dispatch_details_dialog import DispatchDetailsDialog
class MainWindow(QWidget):
def __init__(self):
super().__init__()
self.initWindow()
self.createSubWidgets() # 创建子部件
self.initSubWidgets() # 初始化子部件
self.setupLayout() # 设置布局
self.connectSignalToSlot()
# 安装事件过滤器,处理计划方量的 QLineEdit的失去焦点事件
self.installEventFilter(self)
# 连接槽函数
def connectSignalToSlot(self):
# 可添加信号槽连接
# 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) # 管片任务详情按钮
# 派单任务详情
self.dispatch_task_widget.task_details_signal.connect(self.handleDispatchTaskDetails) # 派单任务详情按钮
def handleSystemStart(self):
# 测试系统开启,进度条动画
self.production_progress.testProgress(60)
self.arc_progress.testProgress(60)
def handleSystemStop(self):
# 测试系统停止,进度条动画
self.production_progress.animation.stop()
self.arc_progress.animation.stop()
def initWindow(self):
"""初始化窗口基本属性"""
self.setWindowTitle("中交三航主界面") # 设置窗口标题
# 设置最小尺寸(可根据需求调整)
# 工控机尺寸为 1280*1024
self.setMinimumSize(1280, 1024)
self.setObjectName("MainWindow")
# 设置主界面窗口背景色
# self.setStyleSheet("background-color: #ffffff;") # #001558
# Qt.FramelessWindowHint
# 没有顶部的白色边框
self.setWindowFlags(Qt.FramelessWindowHint) # 无边框
# 设置主界面背景图片
try:
self.background_image = QImage(ImagePaths.MAIN_INTERFACE_BACKGROUND)
if self.background_image.isNull():
raise Exception("图片为空,可能路径错误或格式不支持")
# print("图片加载成功")
except Exception as e:
print(f"主界面背景图片加载失败: {e}")
self.background_image = None
return # 加载背景失败
self.update_background()
def createSubWidgets(self):
"""创建所有子部件实例"""
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() # 状态监控部件
self.hopper_widget = HopperWidget() # 中间1料斗部件
self.arc_progress = ArcProgressWidget() # 中间2弧形进度部件
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")
# 读取数据库,初始化 管片任务的数据
from busisness.blls import ArtifactBll, PDRecordBll
artifact_dal = ArtifactBll()
artifacts = artifact_dal.get_artifact_task()
# print("\n打印artifacts数据:")
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)
# print("\n打印pdrecords数据:")
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
def setupLayout(self):
"""设置垂直布局,从上到下排列部件"""
main_layout = QVBoxLayout(self) # 主布局:垂直布局
# 设置布局间距(部件之间的距离)和边距(布局与窗口边缘的距离)
main_layout.setSpacing(0) # 部件间距0px
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() # 更新计划表单坐标
# 中间的垂直子布局
sub_v_layout = QVBoxLayout()
sub_v_layout.setSpacing(0)
# 依次添加部件到布局(从上到下)
# sub_v_layout.addWidget(self.status_monitor, alignment=Qt.AlignHCenter)
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 # 垂直方向伸缩,占据剩余空间
))
# sub_v_layout.addWidget(self.system_button_widget, alignment=Qt.AlignHCenter)
# 中间的水平布局
middle_h_layout = QHBoxLayout()
# middle_h_layout.setSpacing(20)
middle_h_layout.setSpacing(0)
# 1、加入垂直子布局设置拉伸因子1让其占满水平剩余空间
middle_h_layout.addLayout(sub_v_layout, stretch=1)
# 2、加入振捣视频控件
middle_h_layout.addWidget(self.vibration_video, alignment=Qt.AlignCenter)
# === 中间子布局 添加到主布局 ===
main_layout.addLayout(middle_h_layout)
# 添加最下方的 控制按钮控件
main_layout.addWidget(self.bottom_control_widget, alignment=Qt.AlignBottom)
# 将布局应用到主窗口
self.setLayout(main_layout)
# 上料斗左移
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)
# 料斗左移完成,恢复料斗右移按钮
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()
# 以下为模拟:
# 假设两秒后,传送带中 料斗向右移动完成 (这里需要根据实际情况修改)
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()
def handleDispatchTaskDetails(self, dispatch_task_name:str):
# 派单任务名 task1、task2、task3 (分别对应第一条派单任务、 第二条派单任务...)
print("main_window: handleDispatchTaskDetails", dispatch_task_name)
# 显示派单任务详情对话框
dispatch_details_dialog = DispatchDetailsDialog(dispatch_task_name, self)
# 这里可以设置对话框显示的内容 如 set_segment_id
# dispatch_details_dialog.set_segment_id("9999999999")
# 设置派单任务详情中的方量的值
current_volume = self.dispatch_task_widget.get_task_volume(dispatch_task_name)
dispatch_details_dialog.set_row_value(4, str(current_volume)) # 派单方量的值的行号为4第五行
# 派单任务详情页面中确定修改了派单任务的方量
# 备注:褚工说管片任务和派单任务中的方量都只有一位小数,料斗上的方量显示两位 2025/11/8
dispatch_details_dialog.confirm_modify_volume.connect(self.handleModifyDispatchTaskVolume)
dispatch_details_dialog.show()
def handleModifyDispatchTaskVolume(self, dispatch_task_name:str, modifyed_volume:float):
"""派单任务详情页面中, 修改了派单任务的方量"""
# 修改相应的派单任务条目显示的 派单任务方量
self.dispatch_task_widget.set_task_volume(dispatch_task_name, modifyed_volume)
# 其他操作,可能需要修改数据库的派单任务方量
# 更新 派单任务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)
def resizeEvent(self, e):
"""窗口大小变化时的回调"""
super().resizeEvent(e)
self.update_background() # 重新加载背景图片
self.update_dispatch_task_position() # 更新 派单任务的坐标
self.update_plan_table_position() # 更新计划表单坐标
def closeEvent(self, e):
"""窗口关闭时的回调"""
super().closeEvent(e)
def keyPressEvent(self, event):
if event.key() == Qt.Key_Escape: # 按下Esc键, 退出界面
self.close()
super().keyPressEvent(event)
if __name__ == "__main__":
import sys
app = QApplication([])
window = MainWindow()
window.show()
sys.exit(app.exec())