Merge branch 'feature/main_ui' of http://www.xj-robot.com:3000/xiongyi/Feeding_control_system into feature/main_ui

This commit is contained in:
2026-03-13 21:29:05 +08:00
10 changed files with 540 additions and 102 deletions

View File

@ -10,9 +10,9 @@ interval_days = 10
# 方量调节控件最小值 # 方量调节控件最小值
min = 0.0 min = 0.0
# 方量调节控件最大值 # 方量调节控件最大值
max = 99.0 max = 2.4
# 方量调节控件初始值 # 方量调节控件初始值
initial = 2.5 initial = 1.9

View File

@ -4,7 +4,7 @@ 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 from view.widgets.system_diagnostics_dialog import SystemDiagnosticsDialog
from view.widgets.message_popup_widget import MessagePopupWidget from view.widgets.message_popup_widget import MessagePopupWidget
from view.main_window import MainWindow
from service.msg_recorder import MessageRecorder from service.msg_recorder import MessageRecorder
from service.msg_query_thread import MsgQueryThread from service.msg_query_thread import MsgQueryThread
@ -16,7 +16,7 @@ from service.monitor_thread import MonitorThread
""" """
class BottomControlController: class BottomControlController:
def __init__(self, bottom_control_widget:BottomControlWidget, main_window): def __init__(self, bottom_control_widget:BottomControlWidget, main_window:MainWindow):
self.bottom_control_widget = bottom_control_widget self.bottom_control_widget = bottom_control_widget
self.main_window = main_window self.main_window = main_window
@ -25,7 +25,7 @@ class BottomControlController:
self.diagnostics_device_row = 0 # 系统诊断弹窗的设备检测行号从0开始 self.diagnostics_device_row = 0 # 系统诊断弹窗的设备检测行号从0开始
self.diagnostics_device_col = 0 # 系统诊断弹窗的设备检测列号从0开始 self.diagnostics_device_col = 0 # 系统诊断弹窗的设备检测列号从0开始
self.diagnostics_service_row = 0 # 系统诊断弹窗的服务检测行号从0开始 self.diagnostics_service_row = 0 # 系统诊断弹窗的服务检测行号从0开始
self.diagnostics_service_col = 2 # 系统诊断弹窗的服务检测从2开始 self.diagnostics_service_col = 2 # 系统诊断弹窗的服务检测从2开始
# 系统中心弹窗 # 系统中心弹窗
self.system_center_dialog = SystemCenterDialog(self.main_window) self.system_center_dialog = SystemCenterDialog(self.main_window)
@ -211,8 +211,17 @@ class BottomControlController:
self.system_diagnostics_dialog.set_ms_value(row, col, delay) self.system_diagnostics_dialog.set_ms_value(row, col, delay)
if delay <= self.device_monitor.warning_delay: if delay <= self.device_monitor.warning_delay:
self.system_diagnostics_dialog.set_circle_status(row, col, "normal") self.system_diagnostics_dialog.set_circle_status(row, col, "normal")
# 设置上料斗或者下料斗的设备状态(主界面的上料斗、下料斗控件中)
if device_name == "上料斗":
self.main_window.hopper_widget.setUpperHopperStatus(0)
elif device_name == "下料斗":
self.main_window.hopper_widget.setLowerHopperStatus(0)
else: else:
self.system_diagnostics_dialog.set_circle_status(row, col, "warning") self.system_diagnostics_dialog.set_circle_status(row, col, "warning")
if device_name == "上料斗":
self.main_window.hopper_widget.setUpperHopperStatus(1)
elif device_name == "下料斗":
self.main_window.hopper_widget.setLowerHopperStatus(1)
# 显示检测组件 # 显示检测组件
self.system_diagnostics_dialog.set_selected_show(row, col) self.system_diagnostics_dialog.set_selected_show(row, col)
@ -225,6 +234,11 @@ class BottomControlController:
self.system_diagnostics_dialog.set_circle_status(row, col, "error") self.system_diagnostics_dialog.set_circle_status(row, col, "error")
# 显示检测组件 # 显示检测组件
self.system_diagnostics_dialog.set_selected_show(row, col) self.system_diagnostics_dialog.set_selected_show(row, col)
# 设置上料斗或者下料斗的设备状态(主界面的上料斗、下料斗控件中)
if device_name == "上料斗":
self.main_window.hopper_widget.setUpperHopperStatus(2)
elif device_name == "下料斗":
self.main_window.hopper_widget.setLowerHopperStatus(2)
def _get_diagnostics_service_row_col(self): def _get_diagnostics_service_row_col(self):
"""获取系统诊断弹窗中 服务检测的 行号 和 列号""" """获取系统诊断弹窗中 服务检测的 行号 和 列号"""

View File

@ -11,7 +11,9 @@ from service.msg_recorder import MessageRecorder
from service.opcua_ui_client import OpcuaUiClient from service.opcua_ui_client import OpcuaUiClient
from service.artifact_query_thread import ArtifactInfoQueryThread # 管片任务查询 from service.artifact_query_thread import ArtifactInfoQueryThread # 管片任务查询
from busisness.models import ArtifactInfoModel from service.pdrecord_query_thread import PDRecordQueryThread # 派单任务查询
from busisness.models import ArtifactInfoModel
from busisness.models import PDRecordModel
from typing import List from typing import List
@ -42,8 +44,8 @@ class MainController:
self.__connectSignals() self.__connectSignals()
def showMainWindow(self): def showMainWindow(self):
# self.main_window.showFullScreen() self.main_window.showFullScreen()
self.main_window.show() # self.main_window.show()
def _initSubControllers(self): def _initSubControllers(self):
# 右侧视频显示控制模块 # 右侧视频显示控制模块
@ -78,6 +80,11 @@ class MainController:
self.opc_client.opc_signal.opc_log.connect(self.msg_recorder.normal_record, Qt.QueuedConnection) # opcua客户端日志 self.opc_client.opc_signal.opc_log.connect(self.msg_recorder.normal_record, Qt.QueuedConnection) # opcua客户端日志
# 主界面的计划表单自动派单控制
self.main_window.plan_table_widget.auto_dispatch_signal.connect(self.handlePlanTableAutoDispatch) # 计划表单的自动派单切换
# 主界面的计划表单中的计划方量修改控制
self.main_window.plan_volume_modified_signal.connect(self.handleVolumeModified) # 计划方量修改
def handleMainWindowClose(self): def handleMainWindowClose(self):
"""主界面关闭""" """主界面关闭"""
@ -90,6 +97,19 @@ class MainController:
if hasattr(self, 'opc_client'): if hasattr(self, 'opc_client'):
self.opc_client.stop_run() self.opc_client.stop_run()
def handlePlanTableAutoDispatch(self, auto_status:bool):
"""处理计划表单的 自动派单和手动派单的切换
pd_mode: 1,自动派单 2,手动派单
"""
if auto_status: # 自动派单
self.opc_client.write_value_by_name("pd_mode", 1)
else: # 手动派单
self.opc_client.write_value_by_name("pd_mode", 2)
def handleVolumeModified(self, volume_json_str:str):
"""处理 修改方量 (计划表单中 和 派单详情中)"""
self.opc_client.write_value_by_name("pd_plan_volume", volume_json_str)
def start_msg_database_clean_task(self): def start_msg_database_clean_task(self):
"""启动清理消息数据库(messages.db)中过期消息的定时任务""" """启动清理消息数据库(messages.db)中过期消息的定时任务"""
from PySide6.QtCore import QTimer, QDateTime, QDate, QTime from PySide6.QtCore import QTimer, QDateTime, QDate, QTime
@ -158,34 +178,18 @@ class MainController:
import traceback import traceback
traceback.print_exc() traceback.print_exc()
def convert_to_ampm(self, time_str: str) -> str:
"""时间格式转换: 转换为AM/PM形式"""
from datetime import datetime
time_formats = [
"%Y-%m-%d %H:%M:%S",
"%Y-%m-%d %H:%M:%S.%f"
]
for fmt in time_formats:
try:
dt = datetime.strptime(time_str, fmt)
return dt.strftime("%I:%M%p")
except ValueError:
continue
return "--:--"
def onUpdateUiByArtifactInfo(self, artifact_list:List[ArtifactInfoModel]): def onUpdateUiByArtifactInfo(self, artifact_list:List[ArtifactInfoModel]):
for index, artifact in enumerate(artifact_list, 1): # 更新管片任务
if artifact.MouldCode: self.main_window.update_segment_tasks(artifact_list)
self.main_window.segment_task_widget.set_task_id(f"task{index}", artifact.MouldCode) # 模具号
if artifact.BetonVolume:
self.main_window.segment_task_widget.set_task_volume(f"task{index}", artifact.BetonVolume) # 浇筑方量
if artifact.BeginTime:
time_str = self.convert_to_ampm(artifact.BeginTime)
self.main_window.segment_task_widget.set_task_time(f"task{index}", time_str) # 开始时间
self.main_window.SetSegmentTaskDetails(f"task{index}", artifact) # 更新管片任务详情
# 将opc服务中的 segment_tasks的值复原为 0以便下次触发管片更新 # 将opc服务中的 segment_tasks的值复原为 0以便下次触发管片更新
self.opc_client.write_value_by_name("segment_tasks", 0) self.opc_client.write_value_by_name("segment_tasks", 0)
def onUpdateUiByPDRecord(self, pdrecord_list:List[PDRecordModel]):
# 更新派单任务
self.main_window.update_dispatch_tasks(pdrecord_list)
# 将opc服务中的 dispatch_tasks的值复原为 0以便下次触发派单更新
self.opc_client.write_value_by_name("dispatch_tasks", 0)
# ======================== OPCUA值更新界面方法 ====================== # ======================== OPCUA值更新界面方法 ======================
def _update_upper_weight(self, val): def _update_upper_weight(self, val):
# 更新上料斗重量 # 更新上料斗重量
@ -224,20 +228,64 @@ class MainController:
def _update_segment_tasks(self, val): def _update_segment_tasks(self, val):
if val: # 需要更新管片任务 if val: # 需要更新管片任务
"""更新左侧的管片任务""" """更新左侧的管片任务"""
if hasattr(self, "query_thread") and self.query_thread.isRunning(): if hasattr(self, "segment_query_thread") and self.segment_query_thread.isRunning():
return return
# 1. 管片信息查询线程 # 1. 管片信息查询线程
self.query_thread = ArtifactInfoQueryThread() self.segment_query_thread = ArtifactInfoQueryThread()
# 2. 主线程更新管片任务UI # 2. 主线程更新管片任务UI
self.query_thread.query_finished.connect(self.onUpdateUiByArtifactInfo) self.segment_query_thread.query_finished.connect(self.onUpdateUiByArtifactInfo)
# 3. 查询管片信息错误 # 3. 查询管片信息错误
self.query_thread.query_error.connect(self.onQueryArtifactInfoError) self.segment_query_thread.query_error.connect(self.onQueryInfoError)
self.query_thread.start() self.segment_query_thread.start()
def onQueryArtifactInfoError(self, error_msg:str): def _update_dispatch_tasks(self, val):
# 查询管片信息失败预警 if val: # 需要更新派单任务
"""更新右侧的派单任务"""
if hasattr(self, "dispatch_query_thread") and self.dispatch_query_thread.isRunning():
return
# 1. 派单信息查询线程
self.dispatch_query_thread = PDRecordQueryThread()
# 2. 主线程更新派单任务UI
self.dispatch_query_thread.query_finished.connect(self.onUpdateUiByPDRecord)
# 3. 查询派单信息错误
self.dispatch_query_thread.query_error.connect(self.onQueryInfoError)
self.dispatch_query_thread.start()
def onQueryInfoError(self, error_msg:str):
# 查询信息失败预警
self.msg_recorder.warning_record(error_msg) self.msg_recorder.warning_record(error_msg)
def _update_vibration_frequency(self, val): def _update_vibration_frequency(self, val):
# 更新振捣频率 # 更新振捣频率
self.main_window.frequency_button_group.set_selected_frequency(val) self.main_window.frequency_button_group.set_selected_frequency(val) # 频率选择按钮上显示的选中的频率
self.main_window.arc_progress.setFrequency(val) # 模具车上显示的振捣频率
def _update_mould_vibrate_status(self, vibrate_status:bool):
# 更新模具车上显示的振捣状态
# vibrate_status: False:未振捣、True:振捣中
if vibrate_status:
self.main_window.arc_progress.setState("振捣中")
else:
self.main_window.arc_progress.setState("未振捣")
self.opc_client.write_value_by_name("vibration_frequency", 0) # 将振捣频率设置为0
def _update_mould_finish_weight(self, finish_weight:int):
# 更新模具车中的下料重量
# finish_weight已下料重量
self.main_window.arc_progress.setWeight(finish_weight)
def _update_pd_mode(self, mode:int):
# 更新计划表单中的 派单模式(主界面下发状态的下面的切换开关),自动派单/手动派单
mode_mapping = {
1: "自动派单",
2: "手动派单",
0: None # 未知
}
mode_text = mode_mapping.get(mode)
if mode_text == "自动派单":
# 设置主界面下发状态的下面的切换开关
self.main_window.plan_table_widget.set_auto_dispatch(True) # 开启,自动派单
# 修改系统配置文件中的 派单状态为自动派单...
else:
self.main_window.plan_table_widget.set_auto_dispatch(False) # 关闭,手动派单
# 修改系统配置文件中的 派单状态为手动派单...

View File

@ -0,0 +1,27 @@
from PySide6.QtWidgets import QWidget
from PySide6.QtCore import QThread, Signal
from busisness.blls import PDRecordBll
from busisness.models import PDRecordModel
class PDRecordQueryThread(QThread):
# 定义信号:子线程查询完成
query_finished = Signal(list)
# 定义信号:发送错误信息
query_error = Signal(str)
def __init__(self):
super().__init__()
def run(self):
try:
pdrecord_dal = PDRecordBll()
pdrecords = pdrecord_dal.get_PD_record() # 获取派单任务数据
if pdrecords:
# 查询完成,发射信号
self.query_finished.emit(pdrecords)
else:
raise ValueError("未查询到有效数据")
except Exception as e:
print(f"更新派单任务数据失败: {str(e)}")
self.query_error.emit(f"更新派单任务数据失败: {str(e)}")

View File

@ -26,29 +26,32 @@ from .widgets.segment_details_dialog import SegmentDetailsDialog
from .widgets.dispatch_details_dialog import DispatchDetailsDialog from .widgets.dispatch_details_dialog import DispatchDetailsDialog
from busisness.models import ArtifactInfoModel from busisness.models import ArtifactInfoModel
from busisness.models import PDRecordModel
import json
class MainWindow(QWidget): class MainWindow(QWidget):
# 定义“即将关闭”的信号 # 定义“即将关闭”的信号
about_to_close = Signal() about_to_close = Signal()
# 计划方量修改信号
plan_volume_modified_signal = Signal(str)
def __init__(self): def __init__(self):
super().__init__() super().__init__()
self.initWindow() self.initWindow()
# 保存管片任务信息的字典 task1: ArtifactInfoModel1.... (用于显示管片任务详情) self.init_business_attributes() # 初始化业务属性
self.artifact_dict = {}
# 当前点击/选中的 管片任务详情对应的任务名(task1\task2\task3) (用于刷新选中的管片任务详情)
self.current_selected_segment_detail_name = None
self.createSubWidgets() # 创建子部件 self.createSubWidgets() # 创建子部件
self.initSubWidgets() # 初始化子部件 self.initSubWidgets() # 初始化子部件
self.setupLayout() # 设置布局 self.setupLayout() # 设置布局
self.connectSignalToSlot() self.connectSignalToSlot()
# 安装事件过滤器,处理计划方量的 QLineEdit的失去焦点事件 # 安装事件过滤器,处理计划方量的 QLineEdit的失去焦点事件
self.installEventFilter(self) self.installEventFilter(self)
# 初始化派单任务控件
self.dispatch_task_widget.set_task_select_btn_selected("task2") # 派单任务控件,初始化选中第二条派单任务
# 连接槽函数 # 连接槽函数
def connectSignalToSlot(self): def connectSignalToSlot(self):
# 可添加信号槽连接 # 可添加信号槽连接
@ -65,6 +68,11 @@ class MainWindow(QWidget):
# 派单任务详情 # 派单任务详情
self.dispatch_task_widget.task_details_signal.connect(self.handleDispatchTaskDetails) # 派单任务详情按钮 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): def handleSystemStart(self):
# 测试系统开启,进度条动画 # 测试系统开启,进度条动画
@ -103,6 +111,18 @@ class MainWindow(QWidget):
self.update_background() 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): def createSubWidgets(self):
"""创建所有子部件实例""" """创建所有子部件实例"""
@ -157,44 +177,122 @@ class MainWindow(QWidget):
return "--:--" return "--:--"
def _init_segment_tasks(self): def _init_segment_tasks(self):
"""从数据库中读取管片任务数据并更新到UI""" """初始化管片任务, 从数据库中读取管片任务数据并更新到UI"""
try: try:
from busisness.blls import ArtifactBll from busisness.blls import ArtifactBll
artifact_dal = ArtifactBll() artifact_dal = ArtifactBll()
artifacts = artifact_dal.get_artifact_task() # 获取管片任务数据 artifacts = artifact_dal.get_artifact_task() # 获取管片任务数据
# 遍历数据并更新UI # 更新管片任务
for i, artifact in enumerate(artifacts, 1): self.update_segment_tasks(artifacts)
if artifact.MouldCode: # 更新模具号
self.segment_task_widget.set_task_id(f"task{i}", artifact.MouldCode)
if artifact.BetonVolume: # 更新方量
self.segment_task_widget.set_task_volume(f"task{i}", artifact.BetonVolume)
if artifact.BeginTime: # 更新时间 (管片任务的开始时间)
# print("artifact.BeginTime: ", artifact.BeginTime)
self.segment_task_widget.set_task_time(f"task{i}", self.convert_to_ampm(artifact.BeginTime))
self.SetSegmentTaskDetails(f"task{i}", artifact) # 设置管片任务详情信息
except Exception as e: except Exception as e:
print(f"更新管片任务数据失败: {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: # 更新浇筑方量
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): def _init_dispatch_tasks(self):
"""从数据库中读取派单任务数据并更新到UI""" """初始化派单任务, 从数据库中读取派单任务数据并更新到UI"""
try: try:
from busisness.blls import PDRecordBll from busisness.blls import PDRecordBll
pdrecord_dal = PDRecordBll() pdrecord_dal = PDRecordBll()
pdrecords = pdrecord_dal.get_PD_record() # 获取派单任务数据 pdrecords = pdrecord_dal.get_PD_record() # 获取派单任务数据
# 遍历数据并更新UI # 更新派单任务
for i, record in enumerate(pdrecords, 1): self.update_dispatch_tasks(pdrecords)
if record.MouldCode: # 更新模具号
self.dispatch_task_widget.set_task_id(f"task{i}", record.MouldCode)
if record.BetonVolume: # 更新方量
self.dispatch_task_widget.set_task_volume(f"task{i}", record.BetonVolume)
if record.CreateTime: # 更新时间 (派单任务的创建时间)
self.dispatch_task_widget.set_task_time(f"task{i}", self.convert_to_ampm(record.CreateTime))
except Exception as e: except Exception as e:
print(f"更新派单任务数据失败: {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:
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): def setupLayout(self):
"""设置垂直布局,从上到下排列部件""" """设置垂直布局,从上到下排列部件"""
main_layout = QVBoxLayout(self) # 主布局:垂直布局 main_layout = QVBoxLayout(self) # 主布局:垂直布局
@ -323,29 +421,124 @@ class MainWindow(QWidget):
def handleDispatchTaskDetails(self, dispatch_task_name:str): def handleDispatchTaskDetails(self, dispatch_task_name:str):
# 派单任务名 task1、task2、task3 (分别对应第一条派单任务、 第二条派单任务...) # 派单任务名 task1、task2、task3 (分别对应第一条派单任务、 第二条派单任务...)
print("main_window: handleDispatchTaskDetails", dispatch_task_name) # print("main_window: handleDispatchTaskDetails", dispatch_task_name)
# 显示派单任务详情对话框 # 显示派单任务详情对话框
dispatch_details_dialog = DispatchDetailsDialog(dispatch_task_name, self) 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 # 这里可以设置对话框显示的内容 如 set_segment_id
# dispatch_details_dialog.set_segment_id("9999999999") # dispatch_details_dialog.set_segment_id("9999999999")
# 设置派单任务详情中的方量的值 # 设置派单任务详情中的方量的值
current_volume = self.dispatch_task_widget.get_task_volume(dispatch_task_name) # current_volume = self.dispatch_task_widget.get_task_volume(dispatch_task_name)
dispatch_details_dialog.set_row_value(4, str(current_volume)) # 派单方量的值的行号为4第五行 # 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 # 备注:褚工说管片任务和派单任务中的方量都只有一位小数,料斗上的方量显示两位 2025/11/8
dispatch_details_dialog.confirm_modify_volume.connect(self.handleModifyDispatchTaskVolume) self.dispatch_details_dialog.set_dispatch_task_name(dispatch_task_name) # 更新派单任务名
dispatch_details_dialog.show() self.dispatch_details_dialog.show()
def handleModifyDispatchTaskVolume(self, dispatch_task_name:str, modifyed_volume:float): def handleModifyDispatchTaskVolume(self, dispatch_task_name:str, modifyed_volume:float):
"""派单任务详情页面中, 修改派单任务的方量""" """派单任务详情页面中, 修改派单任务的方量"""
# 修改相应的派单任务条目显示的 派单任务方量 # 修改相应的派单任务条目显示的 派单任务方量
self.dispatch_task_widget.set_task_volume(dispatch_task_name, modifyed_volume) 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的坐标 # 更新 派单任务widget的坐标
def update_dispatch_task_position(self): def update_dispatch_task_position(self):
@ -426,8 +619,19 @@ class MainWindow(QWidget):
# self.artifact_dict 管片信息的字典 # self.artifact_dict 管片信息的字典
def SetSegmentTaskDetails(self, task_name:str, artifact_info:ArtifactInfoModel): def SetSegmentTaskDetails(self, task_name:str, artifact_info:ArtifactInfoModel):
self.artifact_dict[task_name] = artifact_info self.artifact_dict[task_name] = artifact_info
if task_name == self.current_selected_segment_detail_name: if hasattr(self, "segment_details_dialog") and self.segment_details_dialog and self.segment_details_dialog.isVisible():
self.updateSegmentTaskDetailsDialog(artifact_info) # 刷新管片任务详情按钮弹窗 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__": if __name__ == "__main__":
import sys import sys

View File

@ -278,6 +278,14 @@ class ArcProgressWidget(QWidget):
# 管片编号设置 # 管片编号设置
def setSegmentNumber(self, segmentNumberStr:str): def setSegmentNumber(self, segmentNumberStr:str):
self.segment_number_label.setText(segmentNumberStr) self.segment_number_label.setText(segmentNumberStr)
# 环号设置
def setRingNumber(self, ringNumber:int):
self.ring_number_label.setText(f"环号: {ringNumber}")
# 管片尺寸(规格)设置 比如6900*1500
def setSegmentSize(self, SizeSpecification:str):
self.segment_size_label.setText(SizeSpecification)
if __name__ == "__main__": if __name__ == "__main__":

View File

@ -171,11 +171,18 @@ class DispatchDetailsDialog(QDialog):
} }
""" """
) )
self.close_btn.clicked.connect(self.close) self.close_btn.clicked.connect(self._on_close_button_clicked)
parent_layout.addStretch() parent_layout.addStretch()
parent_layout.addWidget(self.close_btn) parent_layout.addWidget(self.close_btn)
def _on_close_button_clicked(self):
"""关闭按钮点击后的处理逻辑"""
# 1. 执行取消修改方量的逻辑
self.onCancelModifyVolume()
# 2. 执行关闭窗口的逻辑
self.close()
def _add_segment_id_area(self, parent_layout): def _add_segment_id_area(self, parent_layout):
id_layout = QHBoxLayout() id_layout = QHBoxLayout()
@ -330,10 +337,10 @@ class DispatchDetailsDialog(QDialog):
self.modify_btn.show() self.modify_btn.show()
# 2、修改 派单方量标签的值 # 2、修改 派单方量标签的值
volume_label = self.rows[4].value # volume_label = self.rows[4].value
# modifyed_value 为float类型, 一位小数 # modifyed_value 为float类型, 一位小数
modifyed_value = self.volume_value_adjuster.get_value() modifyed_value = self.volume_value_adjuster.get_value()
volume_label.setText(str(modifyed_value)) # volume_label.setText(str(modifyed_value))
# 3、发送派单方量确定修改的信号 (发送派单任务名 + 确认修改之后的派单方量) # 3、发送派单方量确定修改的信号 (发送派单任务名 + 确认修改之后的派单方量)
self.confirm_modify_volume.emit(self.dispatch_task_name, modifyed_value) self.confirm_modify_volume.emit(self.dispatch_task_name, modifyed_value)
@ -349,7 +356,8 @@ class DispatchDetailsDialog(QDialog):
self.modify_btn.show() self.modify_btn.show()
# 2、关闭派单方量调整控件 # 2、关闭派单方量调整控件
self.volume_value_adjuster.close() if hasattr(self, "volume_value_adjuster") and self.volume_value_adjuster:
self.volume_value_adjuster.close()
def paintEvent(self, event): def paintEvent(self, event):
if not self.bg_pixmap.isNull(): if not self.bg_pixmap.isNull():
@ -368,12 +376,22 @@ class DispatchDetailsDialog(QDialog):
def set_row_label(self, row, new_label_text: str): def set_row_label(self, row, new_label_text: str):
"""修改左侧的显示的标签的文本,如: 创建时间、派单时间等""" """修改左侧的显示的标签的文本,如: 创建时间、派单时间等"""
if 0 <= row < len(self.rows): if 0 <= row < len(self.rows):
self.rows[row].label.setText(new_label_text) self.rows[row].label.setText(str(new_label_text) if new_label_text is not None else "")
def set_row_value(self, row, new_value_text: str): def set_row_value(self, row, new_value_text: str):
"""修改右侧的显示的值, 如: 2025年9月9日 9:9:9""" """修改右侧的显示的值, 如: 2025年9月9日 9:9:9"""
if 0 <= row < len(self.rows): if 0 <= row < len(self.rows):
self.rows[row].value.setText(new_value_text) self.rows[row].value.setText(str(new_value_text) if new_value_text is not None else "")
def set_dispatch_task_name(self, task_name:str):
"""设置/刷新 派单任务名"""
self.dispatch_task_name = task_name
def set_modify_volume_status(self, status:bool):
"""设置是否能够在派单详情界面修改方量,
status为False即不能修改方量, 为True表示能够修改方量
"""
self.modify_btn.setVisible(status)
# 测试代码 # 测试代码

View File

@ -1,6 +1,6 @@
from PySide6.QtWidgets import (QWidget, QVBoxLayout, QHBoxLayout, QLabel, from PySide6.QtWidgets import (QWidget, QVBoxLayout, QHBoxLayout, QLabel,
QApplication) QApplication)
from PySide6.QtCore import Qt from PySide6.QtCore import Qt, Signal, QTimer
from PySide6.QtGui import QPainter, QPixmap from PySide6.QtGui import QPainter, QPixmap
from .value_adjuster import ValueAdjuster from .value_adjuster import ValueAdjuster
from .switch_button import SwitchButton from .switch_button import SwitchButton
@ -10,6 +10,14 @@ import resources.resources_rc
from utils.image_paths import ImagePaths from utils.image_paths import ImagePaths
class PlanWidget(QWidget): class PlanWidget(QWidget):
# 自动派单切换信号
# True: 自动派单 False: 手动派单
auto_dispatch_signal = Signal(bool)
# 计划表单最终的确认的修改的方量
# 对应的任务名, 最终的方量
final_modify_volume_signal = Signal(str, float)
def __init__(self, parent=None): def __init__(self, parent=None):
super().__init__(parent) super().__init__(parent)
# 加载背景图 # 加载背景图
@ -25,6 +33,16 @@ class PlanWidget(QWidget):
"auto_dispatch": None # 自动派单SwitchButton "auto_dispatch": None # 自动派单SwitchButton
} }
# ==================================================
# 用于计划表单的修改方量的最终确认
# 1、确认最终修改的方量的计时器超时代表着确认了最终的方量
self.volume_timer = QTimer(self)
self.volume_timer.setInterval(2500) # 2.5秒延迟(根据需求调整)
self.volume_timer.setSingleShot(True) # 单次触发:避免重复执行
self.volume_timer.timeout.connect(self._sync_final_volume)
# 2、该计划表单对应的任务名
self.plan_table_task_name = None
# 主垂直布局 # 主垂直布局
main_layout = QVBoxLayout(self) main_layout = QVBoxLayout(self)
main_layout.setContentsMargins(13, 5, 6, 15) main_layout.setContentsMargins(13, 5, 6, 15)
@ -63,6 +81,8 @@ class PlanWidget(QWidget):
row2_layout.addWidget(label2) row2_layout.addWidget(label2)
self.fangliang_adjuster = ValueAdjuster() # 导入的方量调整控件 self.fangliang_adjuster = ValueAdjuster() # 导入的方量调整控件
self.fangliang_adjuster.minus_btn.clicked.connect(self._reset_volume_timer)
self.fangliang_adjuster.plus_btn.clicked.connect(self._reset_volume_timer)
row2_layout.addWidget(self.fangliang_adjuster) row2_layout.addWidget(self.fangliang_adjuster)
main_layout.addLayout(row2_layout) main_layout.addLayout(row2_layout)
self.controls["volume"] = self.fangliang_adjuster self.controls["volume"] = self.fangliang_adjuster
@ -75,7 +95,7 @@ class PlanWidget(QWidget):
row3_layout.addWidget(status_icon3) row3_layout.addWidget(status_icon3)
row3_layout.addSpacing(4) row3_layout.addSpacing(4)
label3 = QLabel("计划配比") label3 = QLabel("")
label3.setStyleSheet("font-size: 18px; color: #03f5ff;") label3.setStyleSheet("font-size: 18px; color: #03f5ff;")
row3_layout.addWidget(label3) row3_layout.addWidget(label3)
@ -117,11 +137,24 @@ class PlanWidget(QWidget):
row5_layout.addWidget(label5) row5_layout.addWidget(label5)
self.switch = SwitchButton() self.switch = SwitchButton()
self.switch.switched.connect(self.auto_dispatch_signal)
self.switch.setChecked(True) self.switch.setChecked(True)
row5_layout.addWidget(self.switch, alignment=Qt.AlignLeft) row5_layout.addWidget(self.switch, alignment=Qt.AlignLeft)
main_layout.addLayout(row5_layout) main_layout.addLayout(row5_layout)
self.controls["auto_dispatch"] = self.switch self.controls["auto_dispatch"] = self.switch
def _reset_volume_timer(self):
"""每次调整方量时,重置定时器(倒计时重新开始)"""
if self.volume_timer.isActive(): # 如果定时器正在运行,先停止
self.volume_timer.stop()
self.volume_timer.start() # 重启定时器
def _sync_final_volume(self):
"""同步 确认的最终的修改方量"""
final_volume = self.fangliang_adjuster.get_value()
# print("最终确认的方量为:", final_volume)
self.final_modify_volume_signal.emit(self.plan_table_task_name, final_volume)
def paintEvent(self, event): def paintEvent(self, event):
"""绘制背景图片""" """绘制背景图片"""
painter = QPainter(self) painter = QPainter(self)
@ -150,10 +183,21 @@ class PlanWidget(QWidget):
self.controls["status"].setText(new_status) self.controls["status"].setText(new_status)
def set_auto_dispatch(self, is_checked:bool): def set_auto_dispatch(self, is_checked:bool):
"""修改自动派单开关状态, true: 开启自动派单, False: 关闭自动派单""" """修改自动派单开关状态, true: 自动派单, False: 动派单"""
if self.controls["auto_dispatch"]: if self.controls["auto_dispatch"]:
self.controls["auto_dispatch"].setChecked(is_checked) self.controls["auto_dispatch"].setChecked(is_checked)
def set_modify_volume_status(self, status:bool):
"""设置 计划表单中修改方量控件的状态, 是否能够修改方量
status: True, 能够修改方量
False, 不能修改方量
"""
self.fangliang_adjuster.setEnabled(status)
def set_plan_table_task_name(self, task_name:str):
"""设置 计划表单对应的选中任务名"""
self.plan_table_task_name = task_name
if __name__ == "__main__": if __name__ == "__main__":
app = QApplication(sys.argv) app = QApplication(sys.argv)
widget = PlanWidget() widget = PlanWidget()

View File

@ -257,9 +257,10 @@ class SegmentDetailsDialog(QDialog):
row: 左列网格行号(0-6,共7行) row: 左列网格行号(0-6,共7行)
new_label_text: 新的标签文字(如“管片编号”) new_label_text: 新的标签文字(如“管片编号”)
""" """
if new_label_text and 0 <= row < len(self.left_cells):
if 0 <= row < len(self.left_cells):
cell = self.left_cells[row] cell = self.left_cells[row]
cell.label.setText(str(new_label_text)) cell.label.setText(str(new_label_text) if new_label_text else "")
def set_left_value(self, row, new_value_text:str): def set_left_value(self, row, new_value_text:str):
""" """
@ -268,9 +269,9 @@ class SegmentDetailsDialog(QDialog):
row: 左列网格行号(0-6,共7行) row: 左列网格行号(0-6,共7行)
new_value_text: 新的值(如“FB789”) new_value_text: 新的值(如“FB789”)
""" """
if new_value_text and 0 <= row < len(self.left_cells): if 0 <= row < len(self.left_cells):
cell = self.left_cells[row] cell = self.left_cells[row]
cell.value.setText(str(new_value_text)) cell.value.setText(str(new_value_text) if new_value_text else "")
def set_right_label(self, row, new_label_text:str): def set_right_label(self, row, new_label_text:str):
""" """
@ -279,9 +280,9 @@ class SegmentDetailsDialog(QDialog):
row: 右列网格行号(0-6,共7行) row: 右列网格行号(0-6,共7行)
new_label_text: 新的标签文字(如“分块号”) new_label_text: 新的标签文字(如“分块号”)
""" """
if new_label_text and 0 <= row < len(self.right_cells): if 0 <= row < len(self.right_cells):
cell = self.right_cells[row] cell = self.right_cells[row]
cell.label.setText(str(new_label_text)) cell.label.setText(str(new_label_text) if new_label_text else "")
def set_right_value(self, row, new_value_text:str): def set_right_value(self, row, new_value_text:str):
""" """
@ -290,10 +291,9 @@ class SegmentDetailsDialog(QDialog):
row: 右列网格行号(0-6,共7行) row: 右列网格行号(0-6,共7行)
new_value_text: 新的值(如“FB789”) new_value_text: 新的值(如“FB789”)
""" """
if new_value_text and 0 <= row < len(self.left_cells): if 0 <= row < len(self.left_cells):
cell = self.right_cells[row] cell = self.right_cells[row]
cell.value.setText(str(new_value_text)) cell.value.setText(str(new_value_text) if new_value_text else "")
# 测试代码 # 测试代码
if __name__ == "__main__": if __name__ == "__main__":

View File

@ -11,8 +11,10 @@ from utils.image_paths import ImagePaths
任务控件,如:管片任务、派单任务 任务控件,如:管片任务、派单任务
""" """
class TaskWidget(QWidget): class TaskWidget(QWidget):
# 任务详情信号: task1表示第一条任务 # 任务详情信号: task1表示第一条任务 (用于详情对话框)
task_details_signal = Signal(str) task_details_signal = Signal(str)
# 任务选择按钮选中的信号 (用于上方的计划表单)
task_selected_signal = Signal(str)
def __init__(self, taskTitle:str, parent=None): def __init__(self, taskTitle:str, parent=None):
super().__init__(parent) super().__init__(parent)
@ -40,6 +42,7 @@ class TaskWidget(QWidget):
# 用字典存储每个任务的可修改控件(键:任务名,值:控件字典) # 用字典存储每个任务的可修改控件(键:任务名,值:控件字典)
self.task_controls = {} # 结构:{"task1": {"volume_label": xxx, "time_label": xxx, ...}, ...} self.task_controls = {} # 结构:{"task1": {"volume_label": xxx, "time_label": xxx, ...}, ...}
self.current_selected_task = None # 选择按钮,当前选中的任务的任务名
# 三条任务条目 # 三条任务条目
self._add_task("task1", "SHRB1-3", ImagePaths.TASK_RECT1) self._add_task("task1", "SHRB1-3", ImagePaths.TASK_RECT1)
@ -87,12 +90,17 @@ class TaskWidget(QWidget):
}} }}
""") """)
select_btn.setCheckable(True) select_btn.setCheckable(True)
select_btn.clicked.connect(lambda checked, tn=task_name: self._on_select_btn_clicked(tn, checked))
controls["select_btn"] = select_btn controls["select_btn"] = select_btn
row1_layout.addWidget(select_btn) row1_layout.addWidget(select_btn)
# 任务编号 # 任务编号
task_id_label = QLabel(task_id) task_id_label = QLabel(task_id)
task_id_label.setStyleSheet("font-size: 18px; color: #16ffff;padding-left: 6px;") task_id_label.setStyleSheet("font-size: 18px; color: #16ffff;padding-left: 6px;")
# 1. 设置鼠标样式为手型,提示可点击
task_id_label.setCursor(Qt.PointingHandCursor)
# 2. 绑定点击事件点击标签时选中对应的select_btn
task_id_label.mousePressEvent = lambda event, tn=task_name: self._on_task_id_clicked(tn)
controls["task_id_label"] = task_id_label controls["task_id_label"] = task_id_label
row1_layout.addWidget(task_id_label) row1_layout.addWidget(task_id_label)
@ -124,7 +132,7 @@ class TaskWidget(QWidget):
row2_layout = QHBoxLayout() row2_layout = QHBoxLayout()
# 方量标签 # 方量标签
volume_label = QLabel("方量 200") volume_label = QLabel("方量 200")
volume_label.setStyleSheet("color: #a1c1d7; font-size: 14px;padding-left: 19px;") volume_label.setStyleSheet("color: #a1c1d7; font-size: 14px;padding-left: 6px;")
controls["volume_label"] = volume_label controls["volume_label"] = volume_label
row2_layout.addWidget(volume_label) row2_layout.addWidget(volume_label)
@ -138,13 +146,19 @@ class TaskWidget(QWidget):
time_label.setStyleSheet("color: #a1c1d7; font-size: 14px;") time_label.setStyleSheet("color: #a1c1d7; font-size: 14px;")
controls["time_label"] = time_label controls["time_label"] = time_label
row2_layout.addWidget(time_label) row2_layout.addWidget(time_label)
# 状态文本标签
status_text_label = QLabel()
status_text_label.setStyleSheet("color: #16ffff; font-size: 14px;")
controls["status_text_label"] = status_text_label
row2_layout.addWidget(status_text_label)
# 状态标签 # 状态图标标签
status_icon_label = QLabel() status_icon_label = QLabel()
status_icon_label.setPixmap(QPixmap(status_icon)) status_icon_label.setPixmap(QPixmap(status_icon))
controls["status_icon_label"] = status_icon_label controls["status_icon_label"] = status_icon_label
row2_layout.addWidget(status_icon_label, alignment=Qt.AlignRight) row2_layout.addWidget(status_icon_label, alignment=Qt.AlignRight)
# 添加水平布局2到item_layout
item_layout.addLayout(row2_layout) item_layout.addLayout(row2_layout)
# 分隔线 # 分隔线
@ -165,6 +179,42 @@ class TaskWidget(QWidget):
# 发送任务详情信号 # 发送任务详情信号
self.task_details_signal.emit(task_name) self.task_details_signal.emit(task_name)
def _on_select_btn_clicked(self, task_name: str, checked: bool):
"""
处理选择按钮点击事件,实现互斥选中
:param task_name: 点击的任务名task1/task2/task3
:param checked: 按钮是否被选中True/False
"""
# 1. 如果是选中操作checked=True
if checked:
# 取消之前选中的任务按钮
if self.current_selected_task and self.current_selected_task != task_name:
prev_select_btn = self.task_controls[self.current_selected_task]["select_btn"]
prev_select_btn.setChecked(False) # 取消选中
# 更新当前选中的任务
self.current_selected_task = task_name
# 通知任务选中按钮更新了选中
self.task_selected_signal.emit(task_name)
else:
# 2. 如果是取消选中操作checked=False
# 重新设为选中状态,阻止取消 (确保该按钮继续为选中状态,保证至少有一个按钮被选中)
current_select_btn = self.task_controls[task_name]["select_btn"]
current_select_btn.setChecked(True)
# 确保选中标记不丢失
self.current_selected_task = task_name
def _on_task_id_clicked(self, task_name: str):
"""
点击任务编号标签时,选中对应的选择按钮(复用互斥逻辑)
:param task_name: 点击的任务名task1/task2/task3
"""
if task_name in self.task_controls:
# 1. 获取对应select_btn
select_btn = self.task_controls[task_name]["select_btn"]
# 2. 模拟点击相应的选择按钮
if not select_btn.isChecked():
select_btn.click()
# -------------------------- # --------------------------
# 对外接口:修改任务属性 # 对外接口:修改任务属性
# 三个任务条目对应的任务名task_name分别为 task1、task2、task3 # 三个任务条目对应的任务名task_name分别为 task1、task2、task3
@ -185,11 +235,36 @@ class TaskWidget(QWidget):
time_label = self.task_controls[task_name]["time_label"] time_label = self.task_controls[task_name]["time_label"]
time_label.setText(time_str) time_label.setText(time_str)
def set_task_status(self, task_name:str, status_str: str, icon_path:str):
"""修改指定任务的状态, 比如: 已下发、计划中等"""
ICON_COLOR_MAP = {
ImagePaths.TASK_RECT1: "#16ffff",
ImagePaths.TASK_RECT2: "#ffab11",
ImagePaths.TASK_RECT3: "#54f529",
ImagePaths.TASK_RECT4: "#ff6a5c",
ImagePaths.TASK_RECT5: "#f6f44b"
}
DEFAULT_COLOR = "#000000"
if task_name in self.task_controls:
status_label = self.task_controls[task_name]["status_text_label"] # 状态文本
status_label.setText(status_str)
icon_label = self.task_controls[task_name]["status_icon_label"] # 状态图标
icon_label.setPixmap(QPixmap(icon_path))
# 修改状态文本的颜色
status_text_color = ICON_COLOR_MAP.get(icon_path, DEFAULT_COLOR)
status_label.setStyleSheet(f"QLabel {{ color: {status_text_color}; font-size: 14px;}}")
def set_task_id(self, task_name:str, new_id: str): def set_task_id(self, task_name:str, new_id: str):
"""修改指定任务的编号, 如: SHRB2-3""" """修改指定任务的编号, 如: SHRB2-3"""
if task_name in self.task_controls: if task_name in self.task_controls:
task_id_label = self.task_controls[task_name]["task_id_label"] task_id_label = self.task_controls[task_name]["task_id_label"]
task_id_label.setText(new_id) task_id_label.setText(new_id)
def set_task_select_btn_selected(self, task_name:str):
"""设置 任务选择按钮选中 (主要用于刷新计划表单)"""
if task_name in self.task_controls:
task_select_btn = self.task_controls[task_name]["select_btn"]
task_select_btn.click()
def get_task_volume(self, task_name:str): def get_task_volume(self, task_name:str):
""" """