Files
Feeding_control_system/view/main_window.py

642 lines
33 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.frequency_button_group import FrequencyButtonGroup
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
from busisness.models import ArtifactInfoModel
from busisness.models import PDRecordModel
import json
class MainWindow(QWidget):
# 定义“即将关闭”的信号
about_to_close = Signal()
# 计划方量修改信号
plan_volume_modified_signal = Signal(str)
def __init__(self):
super().__init__()
self.initWindow()
self.init_business_attributes() # 初始化业务属性
self.createSubWidgets() # 创建子部件
self.initSubWidgets() # 初始化子部件
self.setupLayout() # 设置布局
self.connectSignalToSlot()
# 安装事件过滤器,处理计划方量的 QLineEdit的失去焦点事件
self.installEventFilter(self)
# 初始化派单任务控件
self.dispatch_task_widget.set_task_select_btn_selected("task2") # 派单任务控件,初始化选中第二条派单任务
# 连接槽函数
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) # 派单任务详情按钮
# 派单任务选择 (用于更新计划表单)
self.dispatch_task_widget.task_selected_signal.connect(self.handleDispatchTaskSelected) # 派单任务选择按钮
# 计划表单方量修改
self.plan_table_widget.final_modify_volume_signal.connect(self.handleDispatchTaskVolumeModified) # 计划方量修改
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 init_business_attributes(self):
# 保存管片任务信息的字典 task1: ArtifactInfoModel1.... (用于显示管片任务详情)
self.artifact_dict = {}
# 当前点击/选中的 管片任务详情对应的任务名(task1\task2\task3) (用于刷新选中的管片任务详情)
self.current_selected_segment_detail_name = None
# 保存派单任务信息的字典 task1: PDRecordModel1.... (用于显示派单任务详情)
self.dispatch_dict = {}
# 当前点击/选中的 派单任务详情对应的任务名(task1\task2\task3) (用于刷新选中的派单任务详情)
self.current_selected_dispatch_detail_name = None
# 当前点击/选中的 派单任务选择按钮对应的任务名 (用于刷新 计划表单)
self.current_dispatch_task_select_btn_name = None
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.frequency_button_group = FrequencyButtonGroup(self) # 右侧:振捣频率按钮组(220hz/230hz/240hz)
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):
# 初始化派单任务的 任务id
# 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")
# 初始化 管片任务 和 派单任务显示的数据
self._init_segment_tasks()
self._init_dispatch_tasks()
def convert_to_ampm(self, time_str: str) -> str:
"""
将时间转换为"hh:mmAM/PM"形式(如03:22PM)
Args:
time_str: 原始时间字符串"
Returns:
转换后的时间字符串(如"03:22PM")
"""
from datetime import datetime
# 可能的时间格式(优先尝试带微秒的格式)
time_formats = [
"%Y-%m-%d %H:%M:%S", # 不带微秒
"%Y-%m-%d %H:%M:%S.%f" # 带微秒(如.528453
]
for fmt in time_formats:
try:
dt = datetime.strptime(time_str, fmt)
return dt.strftime("%I:%M%p") # 转换为12小时制时分+AM/PM
except ValueError:
continue # 格式不匹配,尝试下一种格式
# 所有格式都不匹配时,返回占位符
return "--:--"
def _init_segment_tasks(self):
"""初始化管片任务, 从数据库中读取管片任务数据并更新到UI"""
try:
from busisness.blls import ArtifactBll
artifact_dal = ArtifactBll()
artifacts = artifact_dal.get_artifact_task() # 获取管片任务数据
# 更新管片任务
self.update_segment_tasks(artifacts)
except Exception as e:
print(f"更新管片任务数据失败: {e}")
def update_segment_tasks(self, artifact_list:List[ArtifactInfoModel]):
# 更新管片任务相关的信息
# 遍历数据并更新UI (包括左侧的管片任务 以及 管片任务详情)
SEGMENT_STATUS_MAP = {
1: "待生产",
2: "正生产",
3: "已生产" # 完成生产
}
SEGMENT_STATUS_IMAGE = {
1: ImagePaths.TASK_RECT5,
2: ImagePaths.TASK_RECT4,
3: ImagePaths.TASK_RECT3
}
for index, artifact in enumerate(reversed(artifact_list), 1):
# 提取管片生产状态
segment_status_text = SEGMENT_STATUS_MAP.get(artifact.Status, "未知") # 管片状态文本
segment_status_icon = SEGMENT_STATUS_IMAGE.get(artifact.Status, ImagePaths.TASK_RECT1) # 管片状态图标
if artifact.MouldCode: # 更新模具号
self.segment_task_widget.set_task_id(f"task{index}", artifact.MouldCode)
if artifact.BetonVolume is not None: # 更新浇筑方量
self.segment_task_widget.set_task_volume(f"task{index}", artifact.BetonVolume)
if artifact.BeginTime: # 更新时间 (管片任务的开始时间)
# print("artifact.BeginTime: ", artifact.BeginTime)
self.segment_task_widget.set_task_time(f"task{index}", self.convert_to_ampm(artifact.BeginTime))
if segment_status_text: # 更新管片状态
self.segment_task_widget.set_task_status(f"task{index}", segment_status_text, segment_status_icon)
# 更新模具车上的管片信息
if segment_status_text == "正生产": # 模具车上只显示 正生产的管片信息
self.arc_progress.setRingNumber(artifact.ProduceRingNumber) # 设置模具车上的环号
self.arc_progress.setSegmentModel(artifact.BuriedDepth+": "+artifact.RingTypeCode) # 管片类型(如:中埋: R12
self.arc_progress.setSegmentSize(artifact.SizeSpecification) # 管片规格(尺寸)
self.arc_progress.setSegmentNumber(artifact.MouldCode) # 管片号SHRB1-3[模具编号]
self.SetSegmentTaskDetails(f"task{index}", artifact) # 设置管片任务详情信息
def _init_dispatch_tasks(self):
"""初始化派单任务, 从数据库中读取派单任务数据并更新到UI"""
try:
from busisness.blls import PDRecordBll
pdrecord_dal = PDRecordBll()
pdrecords = pdrecord_dal.get_PD_record() # 获取派单任务数据
# 更新派单任务
self.update_dispatch_tasks(pdrecords)
except Exception as e:
print(f"更新派单任务数据失败: {e}")
def update_dispatch_tasks(self, pdrecord_list:List[PDRecordModel]):
# 更新派单任务相关的信息
# 遍历数据并更新UI
# 倒着查,过滤掉 搅拌生产状态不是 未知的,
# 1、查到的第一个 派单状态为 已下发或者已超时 作为task1
# 2、查到的第一个 派单状态为 计划中的作为 task2第二个派单状态为 计划中作为 task3
# 初始化标记控制只取第一条Status=2/3、前两条Status=1的记录
# 派单状态映射1计划中 2已下发 0未知 3已超时
DISPATCH_STATUS_MAP = {
0: "未知",
1: "计划中",
2: "派单中", # 已下发
3: "已超时",
4: "未扫码"
}
# 派单状态图标映射
DISPATCH_STATUS_IMAGE = {
0: ImagePaths.TASK_RECT1,
1: ImagePaths.TASK_RECT2,
2: ImagePaths.TASK_RECT3,
3: ImagePaths.TASK_RECT4,
4: ImagePaths.TASK_RECT5,
}
# 搅拌生产状态映射
PRODUCE_STATUS_MAP = {
0: "未知",
1: "已取消",
2: "已完成",
3: "异常"
}
task_count = 0 # 有效的派单任务数量(需要显示的, task1,task2,task3
# 倒序遍历pdrecord_list
for record in reversed(pdrecord_list):
# 提取状态文字
dispatch_status_text = DISPATCH_STATUS_MAP.get(record.Status, "未知") # 派单状态
dispatch_status_icon = DISPATCH_STATUS_IMAGE.get(record.Status, ImagePaths.TASK_RECT1) # 派单状态图标
produce_status_text = PRODUCE_STATUS_MAP.get(record.GStatus, "未知") # 搅拌生产状态
# 过滤掉GStatus不为0的记录搅拌生产状态为未知0才需要显示
if produce_status_text != "未知":
continue
task_count += 1 # 有效任务数量加一
self._set_dispatch_tasks_info(f"task{task_count}", record, dispatch_status_text, dispatch_status_icon)
def _set_dispatch_tasks_info(self, task_name, record:PDRecordModel, status_text:str, status_icon:str):
# 设置派单任务的tasks信息
if record.MouldCode:
self.dispatch_task_widget.set_task_id(task_name, record.MouldCode)
if record.FBetonVolume is not None:
self.dispatch_task_widget.set_task_volume(task_name, record.FBetonVolume)
if record.CreateTime:
self.dispatch_task_widget.set_task_time(task_name, self.convert_to_ampm(record.CreateTime))
if status_text:
self.dispatch_task_widget.set_task_status(task_name, status_text, status_icon)
self.SetPDRecordTask(task_name, record)
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() # 更新计划表单坐标
self.update_frequency_button_group_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)
if not hasattr(self, "segment_details_dialog"):
self.segment_details_dialog = SegmentDetailsDialog(self)
artifact_info:ArtifactInfoModel = self.artifact_dict.get(segment_task_name)
# 更新管片任务详情按钮弹窗的显示
self.updateSegmentTaskDetailsDialog(artifact_info)
# 显示管片任务详情对话框
self.segment_details_dialog.show()
# 更新选中的管片任务详情对应的任务名
self.current_selected_segment_detail_name = segment_task_name
def updateSegmentTaskDetailsDialog(self, artifact_info:ArtifactInfoModel):
if artifact_info and hasattr(self, "segment_details_dialog"):
# 这里设置管片详情对话框显示的内容 如 set_segment_id
self.segment_details_dialog.set_segment_id(artifact_info.ArtifactActionID) # 管片ID
# 设置管片详情界面的左边一列
self.segment_details_dialog.set_left_value(0, artifact_info.ArtifactID) # 管片编号
self.segment_details_dialog.set_left_value(1, artifact_info.ArtifactIDVice1) # 管片副标识
self.segment_details_dialog.set_left_value(2, artifact_info.ProduceRingNumber) # 生产环号
self.segment_details_dialog.set_left_value(3, artifact_info.MouldCode) # 模具编号
self.segment_details_dialog.set_left_value(4, artifact_info.SkeletonID) # 骨架编号
self.segment_details_dialog.set_left_value(5, artifact_info.RingTypeCode) # 环类型编号
self.segment_details_dialog.set_left_value(6, artifact_info.SizeSpecification) # 尺寸规格
# 设置管片详情界面的右边一列
self.segment_details_dialog.set_right_value(0, artifact_info.BlockNumber) # 分块号
self.segment_details_dialog.set_right_value(1, artifact_info.HoleRingMarking) # 出洞环标记
self.segment_details_dialog.set_right_value(2, artifact_info.GroutingPipeMarking) # 注浆管标记
self.segment_details_dialog.set_right_value(3, artifact_info.PolypropyleneFiberMarking) # 聚丙烯纤维标记
self.segment_details_dialog.set_right_value(4, artifact_info.BetonVolume) # 浇筑方量
self.segment_details_dialog.set_right_value(5, artifact_info.BetonTaskID) # 任务单号
self.segment_details_dialog.set_right_value(6, artifact_info.BuriedDepth) # 埋深
def handleDispatchTaskDetails(self, dispatch_task_name:str):
# 派单任务名 task1、task2、task3 (分别对应第一条派单任务、 第二条派单任务...)
# print("main_window: handleDispatchTaskDetails", dispatch_task_name)
# 显示派单任务详情对话框
if not hasattr(self, "dispatch_details_dialog"):
self.dispatch_details_dialog = DispatchDetailsDialog(dispatch_task_name, self)
self.dispatch_details_dialog.confirm_modify_volume.connect(self.handleModifyDispatchTaskVolume)
pd_record:PDRecordModel = self.dispatch_dict.get(dispatch_task_name)
# 这里可以设置对话框显示的内容 如 set_segment_id
# dispatch_details_dialog.set_segment_id("9999999999")
# 设置派单任务详情中的方量的值
# current_volume = self.dispatch_task_widget.get_task_volume(dispatch_task_name)
# self.dispatch_details_dialog.set_row_value(4, str(current_volume)) # 派单方量的值的行号为4第五行
# 更新选中的派单任务详情对应的任务名
self.current_selected_dispatch_detail_name = dispatch_task_name
# 更新派单任务详情按钮弹窗的显示
self.updateDispatchTaskDetailsDialog(pd_record)
# 派单任务详情页面中确定修改了派单任务的方量
# 备注:褚工说管片任务和派单任务中的方量都只有一位小数,料斗上的方量显示两位 2025/11/8
self.dispatch_details_dialog.set_dispatch_task_name(dispatch_task_name) # 更新派单任务名
self.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)
# 其他操作,可能需要修改数据库的派单任务方量
self.handleDispatchTaskVolumeModified(dispatch_task_name, modifyed_volume)
def updateDispatchTaskDetailsDialog(self, pd_record:PDRecordModel):
"""更新派单详情对话框"""
if pd_record and hasattr(self, "dispatch_details_dialog"):
self.dispatch_details_dialog.set_segment_id(pd_record.ArtifactActionID) # 管片ID
self.dispatch_details_dialog.set_row_value(0, pd_record.CreateTime.split('.')[0] if pd_record.CreateTime else None) # 创建时间 (去掉毫秒)
self.dispatch_details_dialog.set_row_value(1, pd_record.OptTime.split('.')[0] if pd_record.OptTime else None) # 派单时间 (去掉毫秒)
self.dispatch_details_dialog.set_row_value(2, pd_record.TaskID) # 任务编号
self.dispatch_details_dialog.set_row_value(3, pd_record.ProduceMixID) # 生产配合比编号(配比编号)
self.dispatch_details_dialog.set_row_value(4, pd_record.FBetonVolume) # 派单方量(最终计算出来的实际派单方量)
# 派单状态 1计划中2已下发0未知)默认1
status_mapping = {
0: None, # 未知
1: "计划中",
2: "派单中", # 已下发 # 付工说改为派单中更好
3: "已超时",
4: "未扫码"
}
status_text = status_mapping.get(pd_record.Status)
self.dispatch_details_dialog.set_row_value(5, status_text)
if status_text == "计划中": # 只有派单状态为计划中,详情界面才能够修改方量
self.dispatch_details_dialog.set_modify_volume_status(True)
else:
self.dispatch_details_dialog.set_modify_volume_status(False)
# 派单模式(1自动派单 2手动派单 0未知 Mode: int = 0
mode_mapping = {
1: "自动派单",
2: "手动派单",
0: None # 未知
}
mode_text = mode_mapping.get(pd_record.Mode)
self.dispatch_details_dialog.set_row_value(6, mode_text)
def handleDispatchTaskSelected(self, dispatch_task_name:str):
"""处理派单任务按钮选中, 更新计划表单相关的(计划单号、计划方量等)"""
pd_record:PDRecordModel = self.dispatch_dict.get(dispatch_task_name) # 根据派单任务名获取派单记录
self.current_dispatch_task_select_btn_name = dispatch_task_name # 更新派单任务选择按钮对应的任务名
self.updatePlanTableWidget(pd_record) # 更新计划表单
def handleDispatchTaskVolumeModified(self, dispatch_task_name:str, final_volume:float):
"""处理派单任务方量修改"""
pd_record:PDRecordModel = self.dispatch_dict.get(dispatch_task_name)
if pd_record:
volume_dict = {
"ID": pd_record.ID,
"ArtifactActionID": pd_record.ArtifactActionID,
"Volume": final_volume
}
volume_json_str = json.dumps(volume_dict, ensure_ascii=False)
# print("volume_json_str: ", volume_json_str)
self.plan_volume_modified_signal.emit(volume_json_str)
def updatePlanTableWidget(self, pd_record:PDRecordModel):
"""更新计划表单"""
def format_plan_number(num):
# 格式化计划单号
# 保留最后5位再补零
remainder = num % 100000
return f"{remainder:05d}"
if pd_record and hasattr(self, "plan_table_widget"):
plan_number = format_plan_number(pd_record.ID) # 计划单号
self.plan_table_widget.set_plan_no(f"PD{plan_number}") # 更新计划表单的计划单号 (PD+表单ID)
self.plan_table_widget.set_plan_volume(pd_record.FBetonVolume) # 更新计划表单的计划方量
self.plan_table_widget.set_plan_ratio(pd_record.ProduceMixID) # 更新计划表单的计划配比
# 派单模式(1自动派单 2手动派单 0未知 Mode: int = 0
status_mapping = {
0: "", # 未知
1: "计划中",
2: "派单中", # 已下发 # 付工说改为派单中更好
3: "已超时",
4: "未扫码"
}
status_text = status_mapping.get(pd_record.Status)
self.plan_table_widget.set_status(status_text)
if status_text == "计划中": # 计划表单只有派单状态为计划中,才能够修改方量
self.plan_table_widget.set_modify_volume_status(True)
else:
self.plan_table_widget.set_modify_volume_status(False)
# 更新计划表单对应的任务名
self.plan_table_widget.set_plan_table_task_name(self.current_dispatch_task_select_btn_name)
# 更新 派单任务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获取料斗控件左上角坐标相对于父控件
hopper_pos = self.hopper_widget.pos()
# print(f"料斗控件左上角坐标相对父控件x={arc_pos.x()}, y={arc_pos.y()}")
# x+462, y-249
self.plan_table_widget.move(hopper_pos.x()+362, hopper_pos.y()+40)
def update_frequency_button_group_position(self):
# 方法1获取模具车控件左上角坐标相对于父控件
arc_pos = self.arc_progress.pos()
self.frequency_button_group.move(arc_pos.x()+572, arc_pos.y() + 125)
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() # 更新计划表单坐标
self.update_frequency_button_group_position() # 更新振捣频率按钮坐标
def closeEvent(self, e):
"""窗口关闭时的回调"""
self.hide() # 隐藏界面
self.about_to_close.emit()
super().closeEvent(e)
def keyPressEvent(self, event):
if event.key() == Qt.Key_Escape: # 按下Esc键, 退出界面
self.close()
super().keyPressEvent(event)
# ======= 设置管片任务详情接口 ==========
# self.artifact_dict 管片信息的字典
def SetSegmentTaskDetails(self, task_name:str, artifact_info:ArtifactInfoModel):
self.artifact_dict[task_name] = artifact_info
if hasattr(self, "segment_details_dialog") and self.segment_details_dialog and self.segment_details_dialog.isVisible():
if task_name == self.current_selected_segment_detail_name:
self.updateSegmentTaskDetailsDialog(artifact_info) # 刷新管片任务详情按钮弹窗
# ======= 设置派单任务接口 ==========
# self.dispatch_dict 派单信息的字典
def SetPDRecordTask(self, task_name:str, pd_record:PDRecordModel):
self.dispatch_dict[task_name] = pd_record
if hasattr(self, "dispatch_details_dialog") and self.dispatch_details_dialog and self.dispatch_details_dialog.isVisible():
if task_name == self.current_selected_dispatch_detail_name:
self.updateDispatchTaskDetailsDialog(pd_record) # 刷新派单任务详情按钮弹窗
if hasattr(self, "plan_table_widget") and task_name == self.current_dispatch_task_select_btn_name:
self.updatePlanTableWidget(pd_record) # 刷新计划表单
if __name__ == "__main__":
import sys
app = QApplication([])
window = MainWindow()
window.show()
sys.exit(app.exec())