from PySide6.QtWidgets import (QWidget, QVBoxLayout, QHBoxLayout, QLabel, QPushButton, QMessageBox, QApplication) from PySide6.QtCore import Qt, Signal from PySide6.QtGui import QPainter, QPixmap, QFont import sys import resources.resources_rc from utils.image_paths import ImagePaths """ 任务控件,如:管片任务、派单任务 """ class TaskWidget(QWidget): # 任务详情信号: task1表示第一条任务 (用于详情对话框) task_details_signal = Signal(str) # 任务选择按钮选中的信号 (用于上方的计划表单) task_selected_signal = Signal(str) def __init__(self, taskTitle:str, parent=None): super().__init__(parent) # 设置Widget大小与背景图一致 self.bg_pixmap = QPixmap(ImagePaths.TASK_INFO_BACKGROUND1) self.setFixedSize(self.bg_pixmap.size()) # 主布局(垂直) self.main_layout = QVBoxLayout(self) self.main_layout.setContentsMargins(0, 0, 0, 6) self.main_layout.setSpacing(0) # 任务标题 title_label = QLabel(taskTitle, self) title_label.setStyleSheet("font-size: 24px; color: #16ffff;padding-top:4px;") title_label.setAlignment(Qt.AlignCenter) self.main_layout.addWidget(title_label, alignment=Qt.AlignTop) # 标题字体设置 # title_font = title_label.font() title_font = QFont("Microsoft YaHei") title_font.setLetterSpacing(QFont.AbsoluteSpacing, 3) # 字间距3px title_font.setWeight(QFont.DemiBold) title_label.setFont(title_font) # 用字典存储每个任务的可修改控件(键:任务名,值:控件字典) 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("task2", "SHRB2-3", ImagePaths.TASK_RECT2) self._add_task("task3", "SHRB1-3", ImagePaths.TASK_RECT3) def paintEvent(self, event): """绘制背景图片""" painter = QPainter(self) painter.drawPixmap(self.rect(), self.bg_pixmap) super().paintEvent(event) def _add_task(self, task_name, task_id, status_icon): "添加相应的任务条目到布局,同时将其相应的控件存入字典" # 1、创建任务条目,以及相应的控件字典 item_widget, controls = self._create_task_item(task_name, task_id, status_icon) # 2、将控件字典存入 相应的 任务条目字典 self.task_controls[task_name] = controls # 3、将任务条目添加到主布局 self.main_layout.addWidget(item_widget, alignment=Qt.AlignTop) def _create_task_item(self, task_name, task_id, status_icon): """创建单条任务条目, 返回任务条目和控件字典""" item_widget = QWidget() item_layout = QVBoxLayout(item_widget) item_layout.setContentsMargins(9, 6, 9, 8) item_layout.setSpacing(0) # 相应的 任务条目的控件字典 controls = {} # {"volume_label": ..., "time_label": ..., ...} # 水平布局1:选择按钮 + 任务名 + 详情按钮 row1_layout = QHBoxLayout() # 任务选择按钮 select_btn = QPushButton() select_btn.setFixedSize(14, 14) select_btn.setCursor(Qt.PointingHandCursor) select_btn.setStyleSheet(f""" QPushButton {{ background-image: url({ImagePaths.TASK_INFO_SELECT_BTN1}); border: none; }} QPushButton:checked {{ background-image: url({ImagePaths.TASK_INFO_SELECT_BTN2}); }} """) 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 row1_layout.addWidget(select_btn) # 任务编号 task_id_label = QLabel(task_id) 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 row1_layout.addWidget(task_id_label) # 详情按钮 detail_btn = QPushButton() detail_btn.setText("详情") detail_btn.setFixedSize(46, 26) detail_btn.setCursor(Qt.PointingHandCursor) detail_btn.setStyleSheet(f""" QPushButton {{ background-image: url({ImagePaths.TASK_INFO_DETAIL_BTN1}); border: none; color: #3bfff8; font-size: 16px; }} QPushButton:hover {{ background-image: url({ImagePaths.TASK_INFO_DETAIL_BTN2}); color: #001c83; font-size: 16px; }} """) detail_btn.clicked.connect(lambda: self._show_detail_dialog(task_name)) # 详情按钮槽函数 controls["detail_btn"] = detail_btn row1_layout.addWidget(detail_btn) item_layout.addLayout(row1_layout) # 水平布局2:方量 + / + 时间 + 状态图标 row2_layout = QHBoxLayout() # 方量标签 volume_label = QLabel("方量 99") volume_label.setStyleSheet("color: #a1c1d7; font-size: 14px;padding-left: 1px;") controls["volume_label"] = volume_label row2_layout.addWidget(volume_label) # 分隔标签 sep_label = QLabel("/") sep_label.setStyleSheet("color: #a1c1d7;") row2_layout.addWidget(sep_label, alignment=Qt.AlignCenter) # 时间标签 time_label = QLabel("03:22PM") time_label.setStyleSheet("color: #a1c1d7; font-size: 14px;") controls["time_label"] = 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.setPixmap(QPixmap(status_icon)) controls["status_icon_label"] = status_icon_label row2_layout.addWidget(status_icon_label, alignment=Qt.AlignRight) # 添加水平布局2到item_layout item_layout.addLayout(row2_layout) # 分隔线 item_layout.setSpacing(5) separator = QLabel() separator.setPixmap(QPixmap(ImagePaths.TASK_INFO_SEPARATOR)) separator.setFixedSize(196, 1) item_layout.addWidget(separator) return item_widget, controls # 返回任务条目 以及 相应的控件 def _show_detail_dialog(self, task_name): """显示任务详情弹窗""" # QMessageBox.information(self, "任务详情", f"任务 {task_name} 的详细信息...") """ task1 表示第一条任务, 依次类推 """ # 发送任务详情信号 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 # -------------------------- def set_task_volume(self, task_name:str, volume: float): """修改指定任务的方量, 传入具体的方量值,如: 200.0""" # 褚工说 管片任务 和 派单任务的方量只有一位小数。 2025-11-8 # 所以这里限制为 保留一位小数的浮点数 current_volume = round(float(volume), 1) if task_name in self.task_controls: volume_label = self.task_controls[task_name]["volume_label"] volume_label.setText(f"方量 {current_volume}") def set_task_time(self, task_name:str, time_str: str): """修改指定任务的时间, 传入对应格式的时间,如: 03:22PM""" if task_name in self.task_controls: time_label = self.task_controls[task_name]["time_label"] 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): """修改指定任务的编号, 如: SHRB2-3""" if task_name in self.task_controls: task_id_label = self.task_controls[task_name]["task_id_label"] 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): """ 获取指定任务的方量, 传入任务名,如 task1、task2、task3 return: 返回 float类型一位小数的方量值 """ if task_name in self.task_controls: volume_label = self.task_controls[task_name]["volume_label"] # 提取 volume_label中显示的 "方量 200" 中的数字部分 # 1. 去除前后空格,按空格分割字符串 volume_text = volume_label.text().strip() parts = volume_text.split() # 2. 取分割后的数字部分 if len(parts) >= 2: number_str = parts[1] # 得到 "200" else: # 格式异常(没有数字部分),返回None return None # 褚工说任务中显示的方量只有一位小数 try: volume_value = round(float(number_str), 1) return volume_value except ValueError: return None if __name__ == "__main__": app = QApplication(sys.argv) widget = TaskWidget("管片任务") # 示例:修改task2的方量为300(测试用) widget.set_task_volume("task2", 300) # 示例:修改task1的时间为04:50PM(测试用) widget.set_task_time("task1", "04:50PM") widget.show() sys.exit(app.exec())