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

336 lines
13 KiB
Python
Raw Permalink 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.

#!/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