#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ DUT表单对话框(全局复用) - 新建/编辑公用一个对话框,根据模式禁用部分字段 - 数据以 dict(dut_form) 形式在模块间传递 - 设备分配为二级选择:机台 -> 通道 """ from PyQt5.QtWidgets import ( QDialog, QVBoxLayout, QHBoxLayout, QFormLayout, QLineEdit, QComboBox, QTextEdit, QPushButton, QLabel, QWidget ) from PyQt5.QtCore import Qt import requests # 尝试导入配置 try: from config import USE_HTTP_API, HTTP_API_BASE_URL except ImportError: USE_HTTP_API = False HTTP_API_BASE_URL = "http://127.0.0.1:5050" class DUTFormDialog(QDialog): def __init__(self, mode="create", dut_form=None, lock_fields=None, system_model=None, parent=None): super().__init__(parent) self.mode = mode # "create" | "edit" self.dut_form = dut_form or {} self.lock_fields = set(lock_fields or []) self.system_model = system_model # 用于加载机台数据 self.setWindowTitle("编辑试验样品详情" if self.mode == "edit" else "新建试验样品") self.setModal(True) self.resize(800, 480) # HTTP API 配置 self.use_http_api = USE_HTTP_API self.api_base_url = HTTP_API_BASE_URL # 机台数据缓存 self.machines = [] self.current_machine = None # 项目阶段数据缓存 self.project_phases = [] # 存储从后端获取的项目阶段列表 self._build_ui() self._load_machines() # 加载机台数据 self._load_project_phases() # 加载项目阶段数据 self._fill_form(self.dut_form) self._apply_lock() def _build_ui(self): layout = QVBoxLayout(self) layout.setContentsMargins(20, 20, 20, 20) layout.setSpacing(12) #title = QLabel("编辑试验样品详情" if self.mode == "edit" else "新建试验样品详情") #title.setObjectName("title") #title.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) #layout.addWidget(title) form = QFormLayout() form.setLabelAlignment(Qt.AlignRight) form.setFormAlignment(Qt.AlignTop) form.setHorizontalSpacing(16) form.setVerticalSpacing(12) # 左右两列容器 row = QHBoxLayout() left = QVBoxLayout() right = QVBoxLayout() # 左列控件 self.input_SN = QLineEdit() self.input_project = QLineEdit() self.input_testReq = QLineEdit() self.input_phase = QComboBox() self.input_phase.setEditable(True) # 项目阶段选项将在 _load_project_phases() 中从后端加载 # 右列控件 - 设备分配(二级选择) self.input_dtMachine = QComboBox() # 机台选择 self.input_dtMachine.setEditable(False) self.input_dtMachine.addItem("", None) # 空选项 self.input_dtMachine.currentIndexChanged.connect(self._on_machine_changed) self.input_station = QComboBox() # 通道选择 self.input_station.setEditable(False) self.input_station.addItem("", None) # 空选项 self.input_weeks = QLineEdit() self.input_workOrder = QLineEdit() self.input_inspector = QLineEdit() # 说明 self.input_description = QTextEdit() self.input_description.setPlaceholderText("请输入内容") # 左列布局 left_form = QFormLayout() left_form.setLabelAlignment(Qt.AlignRight) left_form.addRow("* 产品序列号", self.input_SN) left_form.addRow("项目代码", self.input_project) left_form.addRow("项目方案", self.input_testReq) left_form.addRow("项目阶段", self.input_phase) left.addLayout(left_form) # 右列布局 right_form = QFormLayout() right_form.setLabelAlignment(Qt.AlignRight) right_form.addRow("机台选择", self.input_dtMachine) right_form.addRow("通道选择", self.input_station) right_form.addRow("测试周次", self.input_weeks) right_form.addRow("工单名称", self.input_workOrder) right_form.addRow("测试人员", self.input_inspector) right.addLayout(right_form) row.addLayout(left, 1) row.addLayout(right, 1) layout.addLayout(row) # 说明一行单独处理 desc_form = QFormLayout() desc_form.setLabelAlignment(Qt.AlignRight) desc_form.addRow("说明", self.input_description) layout.addLayout(desc_form) # 底部按钮 btn_row = QHBoxLayout() btn_row.addStretch() self.btn_submit = QPushButton("修改" if self.mode == "edit" else "保存") self.btn_close = QPushButton("关闭") btn_row.addWidget(self.btn_submit) btn_row.addWidget(self.btn_close) layout.addLayout(btn_row) self.btn_submit.clicked.connect(self.accept) self.btn_close.clicked.connect(self.reject) def _load_machines(self): """加载机台数据""" if not self.system_model: return try: self.machines = self.system_model.load_machines() # 填充机台下拉框 self.input_dtMachine.clear() self.input_dtMachine.addItem("", None) # 空选项 for machine in self.machines: sn = machine.get("SN", "") label = machine.get("label", "") display = f"{sn} - {label}" if label else sn self.input_dtMachine.addItem(display, machine) except Exception as e: print(f"加载机台数据失败: {e}") def _load_project_phases(self): """从后端加载项目阶段数据""" try: # 尝试使用 HTTP API if self.use_http_api: try: url = f"{self.api_base_url}/dbTableAccess" response = requests.get( url, params={"table": "projectPhase"}, timeout=5 ) response.raise_for_status() result = response.json() data = result.get("data", []) if data: self.project_phases = data print(f"从 HTTP API 加载了 {len(data)} 个项目阶段") else: print("后端返回的项目阶段数据为空") self.project_phases = [] except Exception as e: print(f"HTTP API 加载项目阶段失败: {e},回退到 SystemModel") # 回退到使用 SystemModel if self.system_model: self.project_phases = self.system_model.load_project_phases() else: self.project_phases = [] else: # 直接使用 SystemModel if self.system_model: self.project_phases = self.system_model.load_project_phases() else: self.project_phases = [] # 填充下拉框:显示 name,存储 PN 作为 itemData self.input_phase.clear() for phase in self.project_phases: name = phase.get("name", "") pn = phase.get("PN", "") if name: # 只添加有名称的项 self.input_phase.addItem(name, pn) # 显示 name,数据存储 PN print(f"项目阶段下拉框已填充 {self.input_phase.count()} 个选项") except Exception as e: print(f"加载项目阶段数据失败: {e}") import traceback traceback.print_exc() def _on_machine_changed(self, index): """机台选择变化时更新通道列表""" self.input_station.clear() self.input_station.addItem("", None) # 空选项 machine = self.input_dtMachine.currentData() if not machine: return self.current_machine = machine # 添加有效的通道(station1-4) for i in range(1, 5): station_key = f"station{i}" status_key = f"status{i}" try: # 安全获取通道名称,处理None和空值 station_name = machine.get(station_key) if station_name is None: continue station_name = str(station_name).strip() station_status = machine.get(status_key, "") # 过滤掉空值和 NA/N/A 的通道 if station_name and station_name.upper() not in ['NA', 'N/A']: display = f"{i:02d} - {station_name}" if station_status: display += f" ({station_status})" self.input_station.addItem(display, f"{i:02d}") except Exception as e: print(f"加载通道数据失败: {e}") def _fill_form(self, f): # 将已有数据填充到控件 self.input_SN.setText(str(f.get("SN", ""))) self.input_project.setText(str(f.get("project", ""))) self.input_testReq.setText(str(f.get("testReq", ""))) # 项目阶段:f.get("phase") 存储的是 PN 值,需要匹配 itemData phase_pn = str(f.get("phase", "")) if phase_pn: # 查找匹配的 PN found = False for i in range(self.input_phase.count()): item_pn = self.input_phase.itemData(i) if item_pn == phase_pn: self.input_phase.setCurrentIndex(i) found = True break # 如果没有找到匹配项,使用可编辑功能填充 if not found: self.input_phase.setEditText(phase_pn) # 设备分配:使用 Model 解析后的 dtMachine 和 station 字段 dtm = f.get("dtMachine", "").strip() station = f.get("station", "").strip() # 选中机台 if dtm: for i in range(self.input_dtMachine.count()): machine = self.input_dtMachine.itemData(i) if machine and machine.get("SN") == dtm: self.input_dtMachine.setCurrentIndex(i) break # 选中通道(需要在机台选中后) if station: for i in range(self.input_station.count()): station_num = self.input_station.itemData(i) if station_num == station: self.input_station.setCurrentIndex(i) break self.input_weeks.setText(str(f.get("weeks", ""))) self.input_workOrder.setText(str(f.get("workOrder", ""))) self.input_inspector.setText(str(f.get("inspector", ""))) self.input_description.setPlainText(str(f.get("description", ""))) def _apply_lock(self): # 根据锁定字段禁用编辑(默认在编辑模式锁定 SN) defaults = {"SN"} if self.mode == "edit" else set() locked = defaults | self.lock_fields if "SN" in locked: self.input_SN.setDisabled(True) # 注意:设备分配在编辑模式下是可以更改的 def get_form_dict(self): # 以 dut_form 的结构返回数据 # 项目阶段:优先获取 itemData(PN值),如果没有则使用用户输入的文本 phase_pn = self.input_phase.currentData() # 获取当前选中项的 PN if phase_pn is None: # 如果是用户手动输入的文本 phase_pn = self.input_phase.currentText().strip() dut_form = { "SN": self.input_SN.text().strip(), "project": self.input_project.text().strip(), "testReq": self.input_testReq.text().strip(), "phase": phase_pn, # 存储 PN 值 "weeks": int(self.input_weeks.text() or 0), "workOrder": self.input_workOrder.text().strip(), "inspector": self.input_inspector.text().strip(), "description": self.input_description.toPlainText().strip(), } # 设备分配:从二级选择中获取 dtMachine 和 station # Model 会自动将这两个字段合成为 JSON 字符串存入 stationAssigned machine = self.input_dtMachine.currentData() station_num = self.input_station.currentData() if machine and station_num: dut_form["dtMachine"] = machine.get("SN", "") dut_form["station"] = station_num else: dut_form["dtMachine"] = "" dut_form["station"] = "" # 保留可能存在的 id 等字段 if "id" in self.dut_form: dut_form["id"] = self.dut_form["id"] return dut_form