Files
dtm-py-all/UI/views/dut_form_dialog.py

336 lines
13 KiB
Python
Raw Permalink Normal View History

#!/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 的结构返回数据
# 项目阶段:优先获取 itemDataPN值如果没有则使用用户输入的文本
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