790 lines
35 KiB
Python
790 lines
35 KiB
Python
#!/usr/bin/env python3
|
||
# -*- coding: utf-8 -*-
|
||
|
||
"""
|
||
系统设置视图
|
||
"""
|
||
|
||
from PyQt5.QtWidgets import (QWidget, QVBoxLayout, QHBoxLayout, QLabel,
|
||
QGroupBox, QPushButton, QLineEdit, QComboBox,
|
||
QCheckBox, QMessageBox, QTabWidget, QTableWidget, QTableWidgetItem, QHeaderView, QGridLayout, QScrollArea)
|
||
from PyQt5.QtCore import Qt
|
||
from viewmodels.system_settings_viewmodel import SystemSettingsViewModel
|
||
from ui_utils.user_session import UserSession
|
||
import json
|
||
|
||
|
||
class SystemSettingsView(QWidget):
|
||
def __init__(self):
|
||
super().__init__()
|
||
# 使用 ViewModel 管理数据(响应式)
|
||
self.viewModel = SystemSettingsViewModel(self)
|
||
|
||
# 获取用户角色
|
||
self.user_role = UserSession.get_role()
|
||
|
||
# 检查权限
|
||
if not self._check_permission():
|
||
self._show_no_permission_ui()
|
||
return
|
||
|
||
self.init_ui()
|
||
self._bind_signals() # 绑定响应式信号
|
||
self.viewModel.loadAll() # 加载所有数据
|
||
|
||
def _check_permission(self):
|
||
"""检查用户是否有权限访问系统设置"""
|
||
return self.user_role in ['admin', 'super']
|
||
|
||
def _show_no_permission_ui(self):
|
||
"""显示无权限提示界面"""
|
||
layout = QVBoxLayout(self)
|
||
layout.setSpacing(15)
|
||
|
||
title_label = QLabel("系统设置")
|
||
title_label.setObjectName("title")
|
||
layout.addWidget(title_label)
|
||
|
||
# 无权限提示
|
||
tip_widget = QWidget()
|
||
tip_layout = QVBoxLayout(tip_widget)
|
||
tip_layout.setAlignment(Qt.AlignCenter)
|
||
|
||
icon_label = QLabel("🔒")
|
||
icon_label.setStyleSheet("font-size: 72px;")
|
||
icon_label.setAlignment(Qt.AlignCenter)
|
||
tip_layout.addWidget(icon_label)
|
||
|
||
message_label = QLabel("您没有权限访问系统设置模块")
|
||
message_label.setStyleSheet("font-size: 18px; color: #666; margin-top: 20px;")
|
||
message_label.setAlignment(Qt.AlignCenter)
|
||
tip_layout.addWidget(message_label)
|
||
|
||
detail_label = QLabel("请联系管理员获取相应权限")
|
||
detail_label.setStyleSheet("font-size: 14px; color: #999; margin-top: 10px;")
|
||
detail_label.setAlignment(Qt.AlignCenter)
|
||
tip_layout.addWidget(detail_label)
|
||
|
||
layout.addWidget(tip_widget)
|
||
|
||
def init_ui(self):
|
||
"""初始化界面(Tab 结构)"""
|
||
layout = QVBoxLayout(self)
|
||
layout.setSpacing(15)
|
||
|
||
title_label = QLabel("系统设置")
|
||
title_label.setObjectName("title")
|
||
layout.addWidget(title_label)
|
||
|
||
self.tab = QTabWidget()
|
||
|
||
# 根据角色显示不同的Tab
|
||
if self.user_role == 'super':
|
||
# super 拥有所有4个模块
|
||
self.tab.addTab(self._init_machine_tab(), "机台设置")
|
||
self.tab.addTab(self._init_phase_tab(), "项目阶段设置")
|
||
self.tab.addTab(self._init_user_tab(), "用户及权限设置")
|
||
self.tab.addTab(self._init_plc_tab(), "PLC设置")
|
||
elif self.user_role == 'admin':
|
||
# admin 只有机台设置和项目阶段设置
|
||
self.tab.addTab(self._init_machine_tab(), "机台设置")
|
||
self.tab.addTab(self._init_phase_tab(), "项目阶段设置")
|
||
|
||
layout.addWidget(self.tab)
|
||
|
||
def _bind_signals(self):
|
||
"""绑定响应式信号 - 数据变化时自动刷新界面"""
|
||
# 机台数据变化 -> 刷新机台表格
|
||
self.viewModel.machinesChanged.connect(self._refresh_machine_table)
|
||
|
||
# 样品阶段数据变化 -> 刷新阶段表格
|
||
self.viewModel.phasesChanged.connect(self._refresh_phase_table)
|
||
|
||
# 仅 super 角色才绑定 PLC 相关信号
|
||
if self.user_role == 'super':
|
||
# 机台数据变化 -> 刷新 PLC 机台下拉框
|
||
self.viewModel.machinesChanged.connect(self._refresh_plc_machine_combo)
|
||
# PLC 数据变化 -> 刷新 PLC 表单
|
||
self.viewModel.plcDataChanged.connect(self._refresh_plc_form)
|
||
# 用户数据变化 -> 刷新用户表格
|
||
self.viewModel.usersChanged.connect(self._refresh_user_table)
|
||
|
||
def _init_machine_tab(self):
|
||
w = QWidget()
|
||
v = QVBoxLayout(w)
|
||
|
||
self.machine_table = QTableWidget()
|
||
self.machine_table.setColumnCount(12)
|
||
self.machine_table.setHorizontalHeaderLabels([
|
||
"序号","设备名称","设备编号","设备状态","PLC类型",
|
||
"通道1","通道2","通道3","通道4","说明","串口","产品类型"
|
||
])
|
||
self.machine_table.setSelectionBehavior(QTableWidget.SelectRows)
|
||
self.machine_table.setSelectionMode(QTableWidget.SingleSelection)
|
||
self.machine_table.horizontalHeader().setStretchLastSection(True)
|
||
self.machine_table.horizontalHeader().setSectionResizeMode(QHeaderView.Interactive)
|
||
self.machine_table.verticalHeader().setVisible(False)
|
||
self.machine_table.setAlternatingRowColors(True)
|
||
self.machine_table.setEditTriggers(QTableWidget.AllEditTriggers)
|
||
v.addWidget(self.machine_table)
|
||
|
||
hl = QHBoxLayout()
|
||
add_btn = QPushButton("+ 增加")
|
||
add_btn.clicked.connect(self._on_add_machine_row)
|
||
save_btn = QPushButton("✓ 保存")
|
||
save_btn.clicked.connect(self._on_save_machines)
|
||
delete_btn = QPushButton("× 删除选中行")
|
||
delete_btn.setStyleSheet("QPushButton { margin-left: 40px; background-color: #f44336; color: white; }")
|
||
delete_btn.clicked.connect(self._on_delete_machine_row)
|
||
hl.addWidget(add_btn)
|
||
hl.addWidget(save_btn)
|
||
hl.addWidget(delete_btn)
|
||
hl.addStretch()
|
||
v.addLayout(hl)
|
||
return w
|
||
|
||
def _init_phase_tab(self):
|
||
w = QWidget()
|
||
v = QVBoxLayout(w)
|
||
|
||
self.phase_table = QTableWidget()
|
||
self.phase_table.setColumnCount(4)
|
||
self.phase_table.setHorizontalHeaderLabels(["序号","阶段名称","阶段标识","说明"])
|
||
self.phase_table.setSelectionBehavior(QTableWidget.SelectRows)
|
||
self.phase_table.setSelectionMode(QTableWidget.SingleSelection)
|
||
self.phase_table.horizontalHeader().setStretchLastSection(True)
|
||
self.phase_table.horizontalHeader().setSectionResizeMode(QHeaderView.Interactive)
|
||
self.phase_table.verticalHeader().setVisible(False)
|
||
self.phase_table.setAlternatingRowColors(True)
|
||
self.phase_table.setEditTriggers(QTableWidget.AllEditTriggers)
|
||
v.addWidget(self.phase_table)
|
||
|
||
hl = QHBoxLayout()
|
||
add_btn = QPushButton("+ 增加")
|
||
add_btn.clicked.connect(self._on_add_phase_row)
|
||
save_btn = QPushButton("✓ 保存")
|
||
save_btn.clicked.connect(self._on_save_phases)
|
||
delete_btn = QPushButton("× 删除选中行")
|
||
delete_btn.setStyleSheet("QPushButton { margin-left: 40px; background-color: #f44336; color: white; }")
|
||
delete_btn.clicked.connect(self._on_delete_phase_row)
|
||
hl.addWidget(add_btn)
|
||
hl.addWidget(save_btn)
|
||
hl.addWidget(delete_btn)
|
||
hl.addStretch()
|
||
v.addLayout(hl)
|
||
return w
|
||
|
||
def _init_user_tab(self):
|
||
w = QWidget()
|
||
v = QVBoxLayout(w)
|
||
|
||
self.user_table = QTableWidget()
|
||
self.user_table.setColumnCount(7)
|
||
self.user_table.setHorizontalHeaderLabels(["序号","用户名","用户角色","密码","userid","授权","说明"])
|
||
self.user_table.setSelectionBehavior(QTableWidget.SelectRows)
|
||
self.user_table.setSelectionMode(QTableWidget.SingleSelection)
|
||
self.user_table.horizontalHeader().setStretchLastSection(True)
|
||
self.user_table.horizontalHeader().setSectionResizeMode(QHeaderView.Interactive)
|
||
self.user_table.verticalHeader().setVisible(False)
|
||
self.user_table.setAlternatingRowColors(True)
|
||
self.user_table.setEditTriggers(QTableWidget.AllEditTriggers)
|
||
v.addWidget(self.user_table)
|
||
|
||
hl = QHBoxLayout()
|
||
add_btn = QPushButton("+ 增加")
|
||
add_btn.clicked.connect(self._on_add_user_row)
|
||
save_btn = QPushButton("✓ 保存")
|
||
save_btn.clicked.connect(self._on_save_users)
|
||
delete_btn = QPushButton("× 删除选中行")
|
||
delete_btn.setStyleSheet("QPushButton { margin-left: 40px; background-color: #f44336; color: white; }")
|
||
delete_btn.clicked.connect(self._on_delete_user_row)
|
||
hl.addWidget(add_btn)
|
||
hl.addWidget(save_btn)
|
||
hl.addWidget(delete_btn)
|
||
hl.addStretch()
|
||
v.addLayout(hl)
|
||
return w
|
||
|
||
def _init_plc_tab(self):
|
||
w = QWidget()
|
||
v = QVBoxLayout(w)
|
||
|
||
# 选择机台
|
||
top = QHBoxLayout()
|
||
top.addWidget(QLabel("选择机台:"))
|
||
self.plc_machine_combo = QComboBox()
|
||
# 响应式绑定:下拉框变化 -> 更新 ViewModel 的当前机台 ID
|
||
self.plc_machine_combo.currentIndexChanged.connect(self._on_plc_machine_changed)
|
||
top.addWidget(self.plc_machine_combo)
|
||
save_btn = QPushButton("✓ 保存到机台")
|
||
save_btn.clicked.connect(self._on_save_plc_for_index)
|
||
top.addWidget(save_btn)
|
||
top.addStretch()
|
||
v.addLayout(top)
|
||
|
||
# 顶层 PLC 通用参数(一行两个字段)
|
||
general = QGroupBox("PLC 通用参数")
|
||
g = QGridLayout(general)
|
||
g.addWidget(QLabel("站点地址(plcAddress):"), 0, 0)
|
||
self.plc_address_input = QLineEdit()
|
||
g.addWidget(self.plc_address_input, 0, 1)
|
||
g.addWidget(QLabel("串口(com):"), 0, 2)
|
||
self.plc_com_input = QLineEdit()
|
||
g.addWidget(self.plc_com_input, 0, 3)
|
||
|
||
g.addWidget(QLabel("串行参数(baudrate JSON):"), 1, 0)
|
||
self.plc_baud_input = QLineEdit()
|
||
g.addWidget(self.plc_baud_input, 1, 1)
|
||
g.addWidget(QLabel("测试类型(testType):"), 1, 2)
|
||
self.plc_testtype_input = QLineEdit()
|
||
g.addWidget(self.plc_testtype_input, 1, 3)
|
||
v.addWidget(general)
|
||
|
||
# 通道编辑器(每通道使用网格布局,一行两个参数)
|
||
self.ch_enable = {}
|
||
self.ch_edits = {1:{},2:{},3:{},4:{}}
|
||
|
||
# 将所有4个通道放在滚动区域
|
||
scroll = QScrollArea()
|
||
scroll.setWidgetResizable(True)
|
||
scroll_content = QWidget()
|
||
scroll_layout = QVBoxLayout(scroll_content)
|
||
|
||
def build_channel(ch_no):
|
||
box = QGroupBox(f"通道{ch_no}")
|
||
vl = QVBoxLayout(box)
|
||
# 启用状态标签(只读显示)
|
||
status_label = QLabel()
|
||
status_label.setObjectName(f"ch{ch_no}_status")
|
||
vl.addWidget(status_label)
|
||
self.ch_enable[ch_no] = status_label # 存储标签而非复选框
|
||
|
||
# 使用网格布局,每行2个参数
|
||
grid = QGridLayout()
|
||
# 每个参数项:标签 + 输入框
|
||
# 按照示例 JSON 字段,布局为 6 行 x 4 列(每行2对 label-input)
|
||
fields = [
|
||
("高度(height)", "height"),
|
||
("循环次数(cycles)", "cycles"),
|
||
("已完成(cyclesFinished)", "cyclesFinished"),
|
||
("开始(start)", "start"),
|
||
("停止(stop)", "stop"),
|
||
("暂停(pause)", "pause"),
|
||
("运行状态(runStatus)", "runStatus"),
|
||
("方向(direction)", "direction"),
|
||
("DUT基础(dutBasic)", "dutBasic"),
|
||
("DUT参数(dutParam)", "dutParam"),
|
||
("结果开关(resultSwitch)", "resultSwitch"),
|
||
("站点计数(stationResultCounter)", "stationResultCounter"),
|
||
]
|
||
row = 0
|
||
col = 0
|
||
for label_text, key in fields:
|
||
lb = QLabel(label_text)
|
||
le = QLineEdit()
|
||
grid.addWidget(lb, row, col)
|
||
grid.addWidget(le, row, col+1)
|
||
self.ch_edits[ch_no][key] = le
|
||
# 每行2个参数,布局为 4 列:(label1, input1, label2, input2)
|
||
col += 2
|
||
if col >= 4:
|
||
col = 0
|
||
row += 1
|
||
vl.addLayout(grid)
|
||
return box
|
||
|
||
for ch in range(1,5):
|
||
b = build_channel(ch)
|
||
scroll_layout.addWidget(b)
|
||
|
||
scroll.setWidget(scroll_content)
|
||
v.addWidget(scroll)
|
||
|
||
return w
|
||
|
||
# ============ 响应式刷新方法 ============
|
||
|
||
def _refresh_machine_table(self):
|
||
"""响应式刷新机台表格(由 machinesChanged 信号触发)"""
|
||
data = self.viewModel.machines
|
||
self.machine_table.setRowCount(len(data))
|
||
for i, m in enumerate(data):
|
||
self.machine_table.setItem(i, 0, QTableWidgetItem(str(i + 1)))
|
||
self.machine_table.setItem(i, 1, QTableWidgetItem(m.get("label", "")))
|
||
self.machine_table.setItem(i, 2, QTableWidgetItem(m.get("SN", "")))
|
||
self.machine_table.setItem(i, 3, QTableWidgetItem(m.get("status", "")))
|
||
self.machine_table.setItem(i, 4, QTableWidgetItem(m.get("type", "")))
|
||
# 通道状态: 使用绿色复选框表示启用
|
||
cb1 = QCheckBox()
|
||
cb1.setChecked(str(m.get("station1", "")).lower() in ("1","01","enabled"))
|
||
cb1.setStyleSheet("QCheckBox::indicator:checked { background-color: #4CAF50; border: 2px solid #4CAF50; }")
|
||
self.machine_table.setCellWidget(i, 5, cb1)
|
||
cb2 = QCheckBox()
|
||
cb2.setChecked(str(m.get("station2", "")).lower() in ("2","02","enabled"))
|
||
cb2.setStyleSheet("QCheckBox::indicator:checked { background-color: #4CAF50; border: 2px solid #4CAF50; }")
|
||
self.machine_table.setCellWidget(i, 6, cb2)
|
||
cb3 = QCheckBox()
|
||
cb3.setChecked(str(m.get("station3", "")).lower() in ("3","03","enabled"))
|
||
cb3.setStyleSheet("QCheckBox::indicator:checked { background-color: #4CAF50; border: 2px solid #4CAF50; }")
|
||
self.machine_table.setCellWidget(i, 7, cb3)
|
||
cb4 = QCheckBox()
|
||
cb4.setChecked(str(m.get("station4", "")).lower() in ("4","04","enabled"))
|
||
cb4.setStyleSheet("QCheckBox::indicator:checked { background-color: #4CAF50; border: 2px solid #4CAF50; }")
|
||
self.machine_table.setCellWidget(i, 8, cb4)
|
||
self.machine_table.setItem(i, 9, QTableWidgetItem(m.get("description", "")))
|
||
self.machine_table.setItem(i, 10, QTableWidgetItem(m.get("com", "")))
|
||
self.machine_table.setItem(i, 11, QTableWidgetItem(m.get("testType", "")))
|
||
# 将 id 缓存到行的0列的 data 中
|
||
self.machine_table.item(i,0).setData(Qt.UserRole, m.get("id"))
|
||
self.machine_table.resizeColumnsToContents()
|
||
|
||
def _refresh_plc_machine_combo(self):
|
||
"""响应式刷新 PLC 机台下拉框(由 machinesChanged 信号触发)"""
|
||
# 仅在 super 角色且控件存在时执行
|
||
if not hasattr(self, 'plc_machine_combo'):
|
||
return
|
||
|
||
machines = self.viewModel.machines
|
||
# 临时阻断信号,避免填充过程中多次触发
|
||
self.plc_machine_combo.blockSignals(True)
|
||
current_id = self.viewModel.currentMachineId
|
||
self.plc_machine_combo.clear()
|
||
selected_index = -1
|
||
for idx, m in enumerate(machines):
|
||
self.plc_machine_combo.addItem(m.get("label",""), m.get("id"))
|
||
if m.get("id") == current_id:
|
||
selected_index = idx
|
||
self.plc_machine_combo.blockSignals(False)
|
||
# 恢复选中项
|
||
if selected_index >= 0:
|
||
self.plc_machine_combo.setCurrentIndex(selected_index)
|
||
elif self.plc_machine_combo.count() > 0:
|
||
self.plc_machine_combo.setCurrentIndex(0)
|
||
|
||
def _refresh_phase_table(self):
|
||
"""响应式刷新样品阶段表格(由 phasesChanged 信号触发)"""
|
||
data = self.viewModel.phases
|
||
self.phase_table.setRowCount(len(data))
|
||
for i, p in enumerate(data):
|
||
self.phase_table.setItem(i, 0, QTableWidgetItem(str(i + 1)))
|
||
self.phase_table.setItem(i, 1, QTableWidgetItem(p.get("name", "")))
|
||
self.phase_table.setItem(i, 2, QTableWidgetItem(p.get("PN", "")))
|
||
self.phase_table.setItem(i, 3, QTableWidgetItem(p.get("description", "")))
|
||
self.phase_table.item(i,0).setData(Qt.UserRole, p.get("id"))
|
||
self.phase_table.resizeColumnsToContents()
|
||
|
||
def _refresh_user_table(self):
|
||
"""响应式刷新用户表格(由 usersChanged 信号触发)"""
|
||
# 仅在 super 角色且控件存在时执行
|
||
if not hasattr(self, 'user_table'):
|
||
return
|
||
|
||
data = self.viewModel.users
|
||
self.user_table.setRowCount(len(data))
|
||
for i, u in enumerate(data):
|
||
self.user_table.setItem(i, 0, QTableWidgetItem(str(i + 1)))
|
||
self.user_table.setItem(i, 1, QTableWidgetItem(u.get("username", "")))
|
||
self.user_table.setItem(i, 2, QTableWidgetItem(u.get("role", "")))
|
||
self.user_table.setItem(i, 3, QTableWidgetItem(u.get("password", "")))
|
||
self.user_table.setItem(i, 4, QTableWidgetItem(u.get("userid", "")))
|
||
self.user_table.setItem(i, 5, QTableWidgetItem(u.get("auth", "")))
|
||
self.user_table.setItem(i, 6, QTableWidgetItem(u.get("description", "")))
|
||
self.user_table.item(i,0).setData(Qt.UserRole, u.get("id"))
|
||
self.user_table.resizeColumnsToContents()
|
||
|
||
def _refresh_plc_form(self):
|
||
"""响应式刷新 PLC 表单(由 plcDataChanged 信号触发)"""
|
||
# 仅在 super 角色且控件存在时执行
|
||
if not hasattr(self, 'plc_address_input'):
|
||
return
|
||
|
||
print(f"Refreshing PLC form--更新PLC表单")
|
||
plc = self.viewModel.plcData
|
||
|
||
# 通用参数
|
||
self.plc_address_input.setText(str(plc.get("plcAddress", "")))
|
||
self.plc_com_input.setText(str(plc.get("com", "")))
|
||
self.plc_testtype_input.setText(str(plc.get("testType", "")))
|
||
|
||
# baudrate
|
||
baud = plc.get("baudrate", {})
|
||
if isinstance(baud, dict):
|
||
self.plc_baud_input.setText(json.dumps(baud, ensure_ascii=False))
|
||
else:
|
||
self.plc_baud_input.setText(str(baud))
|
||
|
||
# 通道启用状态(只读显示)
|
||
def set_status(ch_no, enabled):
|
||
label = self.ch_enable[ch_no]
|
||
if enabled:
|
||
label.setText("✓ 已启用")
|
||
label.setStyleSheet("color: green; font-weight: bold;")
|
||
else:
|
||
label.setText("✗ 未启用")
|
||
label.setStyleSheet("color: gray;")
|
||
set_status(1, plc.get("status1", False))
|
||
set_status(2, plc.get("status2", False))
|
||
set_status(3, plc.get("status3", False))
|
||
set_status(4, plc.get("status4", False))
|
||
|
||
# 通道参数
|
||
def fill_channel(ch_no, reg_data):
|
||
if isinstance(reg_data, dict):
|
||
data = reg_data
|
||
else:
|
||
data = {}
|
||
for key, le in self.ch_edits[ch_no].items():
|
||
le.setText(str(data.get(key, "")))
|
||
fill_channel(1, plc.get("register01",{}))
|
||
fill_channel(2, plc.get("register02",{}))
|
||
fill_channel(3, plc.get("register03",{}))
|
||
fill_channel(4, plc.get("register04",{}))
|
||
|
||
def _on_plc_machine_changed(self):
|
||
"""下拉框切换机台时,更新 ViewModel 的当前机台 ID(响应式触发 PLC 数据刷新)"""
|
||
idx = self.plc_machine_combo.currentIndex()
|
||
if idx >= 0:
|
||
print(f"Switching to machine index: {idx}")
|
||
machine_id = self.plc_machine_combo.itemData(idx)
|
||
self.viewModel.setCurrentMachineId(machine_id) # 设置后自动触发 plcDataChanged 信号
|
||
|
||
# ============ 原有事件处理(改为调用 ViewModel) ============
|
||
|
||
def _load_all_tabs(self):
|
||
self._load_machine_table()
|
||
self._load_phase_table()
|
||
self._load_user_table()
|
||
# 填充 PLC 机台选择
|
||
machines = self.model.load_machines()
|
||
# 临时阻断信号,避免填充过程中多次触发
|
||
self.plc_machine_combo.blockSignals(True)
|
||
self.plc_machine_combo.clear()
|
||
for m in machines:
|
||
# 用 label 作为显示文本,id 作为数据
|
||
self.plc_machine_combo.addItem(m.get("label",""), m.get("id"))
|
||
self.plc_machine_combo.blockSignals(False)
|
||
|
||
# 绑定信号(只绑定一次)
|
||
try:
|
||
self.plc_machine_combo.currentIndexChanged.disconnect()
|
||
except:
|
||
pass
|
||
self.plc_machine_combo.currentIndexChanged.connect(self._on_load_plc_for_index)
|
||
|
||
# 如果有机台,默认加载第一个
|
||
if self.plc_machine_combo.count() > 0:
|
||
self.plc_machine_combo.setCurrentIndex(0)
|
||
self._on_load_plc_for_index()
|
||
|
||
# 机台设置
|
||
def _load_machine_table(self):
|
||
data = self.model.load_machines()
|
||
self.machine_table.setRowCount(len(data))
|
||
for i, m in enumerate(data):
|
||
self.machine_table.setItem(i, 0, QTableWidgetItem(str(i + 1)))
|
||
self.machine_table.setItem(i, 1, QTableWidgetItem(m.get("label", "")))
|
||
self.machine_table.setItem(i, 2, QTableWidgetItem(m.get("SN", "")))
|
||
self.machine_table.setItem(i, 3, QTableWidgetItem(m.get("status", "")))
|
||
self.machine_table.setItem(i, 4, QTableWidgetItem(m.get("type", "")))
|
||
# 通道状态: 使用绿色复选框表示启用
|
||
cb1 = QCheckBox()
|
||
cb1.setChecked(str(m.get("status1", "")).lower() in ("1","true","enabled"))
|
||
cb1.setStyleSheet("QCheckBox::indicator:checked { background-color: #4CAF50; border: 2px solid #4CAF50; }")
|
||
self.machine_table.setCellWidget(i, 5, cb1)
|
||
cb2 = QCheckBox()
|
||
cb2.setChecked(str(m.get("status2", "")).lower() in ("1","true","enabled"))
|
||
cb2.setStyleSheet("QCheckBox::indicator:checked { background-color: #4CAF50; border: 2px solid #4CAF50; }")
|
||
self.machine_table.setCellWidget(i, 6, cb2)
|
||
cb3 = QCheckBox()
|
||
cb3.setChecked(str(m.get("status3", "")).lower() in ("1","true","enabled"))
|
||
cb3.setStyleSheet("QCheckBox::indicator:checked { background-color: #4CAF50; border: 2px solid #4CAF50; }")
|
||
self.machine_table.setCellWidget(i, 7, cb3)
|
||
cb4 = QCheckBox()
|
||
cb4.setChecked(str(m.get("status4", "")).lower() in ("1","true","enabled"))
|
||
cb4.setStyleSheet("QCheckBox::indicator:checked { background-color: #4CAF50; border: 2px solid #4CAF50; }")
|
||
self.machine_table.setCellWidget(i, 8, cb4)
|
||
self.machine_table.setItem(i, 9, QTableWidgetItem(m.get("description", "")))
|
||
self.machine_table.setItem(i, 10, QTableWidgetItem(m.get("com", "")))
|
||
# 将 id 缓存到行的第0列的 data 中
|
||
self.machine_table.item(i,0).setData(Qt.UserRole, m.get("id"))
|
||
self.machine_table.resizeColumnsToContents()
|
||
|
||
def _on_add_machine_row(self):
|
||
r = self.machine_table.rowCount()
|
||
self.machine_table.insertRow(r)
|
||
self.machine_table.setItem(r, 0, QTableWidgetItem(str(r + 1)))
|
||
self.machine_table.setItem(r, 1, QTableWidgetItem(""))
|
||
self.machine_table.setItem(r, 2, QTableWidgetItem(""))
|
||
self.machine_table.setItem(r, 3, QTableWidgetItem(""))
|
||
self.machine_table.setItem(r, 4, QTableWidgetItem(""))
|
||
# 通道启用复选框 - 绿色样式
|
||
cb1 = QCheckBox()
|
||
cb1.setStyleSheet("QCheckBox::indicator:checked { background-color: #4CAF50; border: 2px solid #4CAF50; }")
|
||
self.machine_table.setCellWidget(r, 5, cb1)
|
||
cb2 = QCheckBox()
|
||
cb2.setStyleSheet("QCheckBox::indicator:checked { background-color: #4CAF50; border: 2px solid #4CAF50; }")
|
||
self.machine_table.setCellWidget(r, 6, cb2)
|
||
cb3 = QCheckBox()
|
||
cb3.setStyleSheet("QCheckBox::indicator:checked { background-color: #4CAF50; border: 2px solid #4CAF50; }")
|
||
self.machine_table.setCellWidget(r, 7, cb3)
|
||
cb4 = QCheckBox()
|
||
cb4.setStyleSheet("QCheckBox::indicator:checked { background-color: #4CAF50; border: 2px solid #4CAF50; }")
|
||
self.machine_table.setCellWidget(r, 8, cb4)
|
||
self.machine_table.setItem(r, 9, QTableWidgetItem(""))
|
||
self.machine_table.setItem(r, 10, QTableWidgetItem(""))
|
||
|
||
def _on_save_machines(self):
|
||
"""保存机台设置(调用 ViewModel)
|
||
重要:只传入机台基本信息和通道启用状态,不传入 PLC 参数
|
||
"""
|
||
rows = self.machine_table.rowCount()
|
||
to_save = []
|
||
for i in range(rows):
|
||
def get(i,c):
|
||
item = self.machine_table.item(i,c)
|
||
return item.text().strip() if item else ""
|
||
def get_cb(i,c,value):
|
||
w = self.machine_table.cellWidget(i,c)
|
||
try:
|
||
return value if (w and isinstance(w, QCheckBox) and w.isChecked()) else ""
|
||
except Exception:
|
||
return ""
|
||
# 只传入机台设置页的字段,不包含 PLC 相关字段
|
||
m = {
|
||
"id": self.machine_table.item(i,0).data(Qt.UserRole),
|
||
"label": get(i,1),
|
||
"SN": get(i,2),
|
||
"status": get(i,3),
|
||
"type": get(i,4),
|
||
"testType": get(i,11), # 产品类型
|
||
"station1": get_cb(i,5,"01"), # 通道启用状态
|
||
"station2": get_cb(i,6,"02"),
|
||
"station3": get_cb(i,7,"03"),
|
||
"station4": get_cb(i,8,"04"),
|
||
"description": get(i,9),
|
||
"com": get(i,10),
|
||
# 不传入 PLC 相关字段,保持数据库中的原有值
|
||
# "station1", "station2", "station3", "station4",
|
||
# "plcAddress", "register01", "register02", "register03", "register04",
|
||
# "baudrate", "testType"
|
||
}
|
||
to_save.append(m)
|
||
self.viewModel.saveMachines(to_save) # 调用 ViewModel,自动触发响应式刷新
|
||
QMessageBox.information(self, "保存成功", "机台设置已保存")
|
||
|
||
def _on_delete_machine_row(self):
|
||
"""删除选中的机台行"""
|
||
selected_rows = self.machine_table.selectionModel().selectedRows()
|
||
if not selected_rows:
|
||
QMessageBox.warning(self, "提示", "请先选中要删除的行")
|
||
return
|
||
|
||
# 确认删除
|
||
reply = QMessageBox.question(
|
||
self,
|
||
"确认删除",
|
||
f"确定要删除选中的 {len(selected_rows)} 条机台记录吗?\n\n注意:此操作不可恢复!",
|
||
QMessageBox.Yes | QMessageBox.No,
|
||
QMessageBox.No
|
||
)
|
||
|
||
if reply == QMessageBox.No:
|
||
return
|
||
|
||
# 收集要删除的 ID
|
||
machine_ids = []
|
||
for index in selected_rows:
|
||
row = index.row()
|
||
id_item = self.machine_table.item(row, 0)
|
||
if id_item:
|
||
machine_id = id_item.data(Qt.UserRole)
|
||
if machine_id:
|
||
machine_ids.append(machine_id)
|
||
|
||
# 调用 ViewModel 删除
|
||
if machine_ids:
|
||
success = self.viewModel.deleteMachines(machine_ids)
|
||
if success:
|
||
QMessageBox.information(self, "成功", f"已删除 {len(machine_ids)} 条机台记录")
|
||
else:
|
||
QMessageBox.warning(self, "错误", "删除失败,请重试")
|
||
|
||
# 样品阶段设置(旧手动加载方法,已被响应式 _refresh_phase_table 取代)
|
||
def _load_phase_table(self):
|
||
"""已废弃,使用响应式 _refresh_phase_table 代替"""
|
||
pass
|
||
|
||
def _on_add_phase_row(self):
|
||
r = self.phase_table.rowCount()
|
||
self.phase_table.insertRow(r)
|
||
self.phase_table.setItem(r, 0, QTableWidgetItem(str(r + 1)))
|
||
for c in range(1, 4):
|
||
self.phase_table.setItem(r, c, QTableWidgetItem(""))
|
||
|
||
def _on_save_phases(self):
|
||
"""保存样品阶段设置(调用 ViewModel)"""
|
||
rows = self.phase_table.rowCount()
|
||
phases = []
|
||
for i in range(rows):
|
||
def get(i,c):
|
||
item = self.phase_table.item(i,c)
|
||
return item.text().strip() if item else ""
|
||
p = {
|
||
"id": self.phase_table.item(i,0).data(Qt.UserRole),
|
||
"name": get(i,1),
|
||
"PN": get(i,2),
|
||
"description": get(i,3),
|
||
}
|
||
phases.append(p)
|
||
self.viewModel.savePhases(phases) # 调用 ViewModel,自动触发响应式刷新
|
||
QMessageBox.information(self, "保存成功", "样品阶段设置已保存")
|
||
|
||
def _on_delete_phase_row(self):
|
||
"""删除选中的阶段行"""
|
||
selected_rows = self.phase_table.selectionModel().selectedRows()
|
||
if not selected_rows:
|
||
QMessageBox.warning(self, "提示", "请先选中要删除的行")
|
||
return
|
||
|
||
# 确认删除
|
||
reply = QMessageBox.question(
|
||
self,
|
||
"确认删除",
|
||
f"确定要删除选中的 {len(selected_rows)} 条阶段记录吗?\n\n注意:此操作不可恢复!",
|
||
QMessageBox.Yes | QMessageBox.No,
|
||
QMessageBox.No
|
||
)
|
||
|
||
if reply == QMessageBox.No:
|
||
return
|
||
|
||
# 收集要删除的 ID
|
||
phase_ids = []
|
||
for index in selected_rows:
|
||
row = index.row()
|
||
id_item = self.phase_table.item(row, 0)
|
||
if id_item:
|
||
phase_id = id_item.data(Qt.UserRole)
|
||
if phase_id:
|
||
phase_ids.append(phase_id)
|
||
|
||
# 调用 ViewModel 删除
|
||
if phase_ids:
|
||
success = self.viewModel.deletePhases(phase_ids)
|
||
if success:
|
||
QMessageBox.information(self, "成功", f"已删除 {len(phase_ids)} 条阶段记录")
|
||
else:
|
||
QMessageBox.warning(self, "错误", "删除失败,请重试")
|
||
|
||
# 用户及权限设置(旧手动加载方法,已被响应式 _refresh_user_table 取代)
|
||
def _load_user_table(self):
|
||
"""已废弃,使用响应式 _refresh_user_table 代替"""
|
||
pass
|
||
|
||
def _on_add_user_row(self):
|
||
r = self.user_table.rowCount()
|
||
self.user_table.insertRow(r)
|
||
self.user_table.setItem(r, 0, QTableWidgetItem(str(r + 1)))
|
||
for c in range(1, 7):
|
||
self.user_table.setItem(r, c, QTableWidgetItem(""))
|
||
|
||
def _on_save_users(self):
|
||
"""保存用户设置(调用 ViewModel)"""
|
||
rows = self.user_table.rowCount()
|
||
users = []
|
||
for i in range(rows):
|
||
def get(i,c):
|
||
item = self.user_table.item(i,c)
|
||
return item.text().strip() if item else ""
|
||
u = {
|
||
"id": self.user_table.item(i,0).data(Qt.UserRole),
|
||
"username": get(i,1),
|
||
"role": get(i,2),
|
||
"password": get(i,3),
|
||
"userid": get(i,4),
|
||
"auth": get(i,5),
|
||
"description": get(i,6),
|
||
}
|
||
users.append(u)
|
||
self.viewModel.saveUsers(users) # 调用 ViewModel,自动触发响应式刷新
|
||
QMessageBox.information(self, "保存成功", "用户及权限设置已保存")
|
||
|
||
def _on_delete_user_row(self):
|
||
"""删除选中的用户行"""
|
||
selected_rows = self.user_table.selectionModel().selectedRows()
|
||
if not selected_rows:
|
||
QMessageBox.warning(self, "提示", "请先选中要删除的行")
|
||
return
|
||
|
||
# 确认删除
|
||
reply = QMessageBox.question(
|
||
self,
|
||
"确认删除",
|
||
f"确定要删除选中的 {len(selected_rows)} 条用户记录吗?\n\n注意:此操作不可恢复!",
|
||
QMessageBox.Yes | QMessageBox.No,
|
||
QMessageBox.No
|
||
)
|
||
|
||
if reply == QMessageBox.No:
|
||
return
|
||
|
||
# 收集要删除的 ID
|
||
user_ids = []
|
||
for index in selected_rows:
|
||
row = index.row()
|
||
id_item = self.user_table.item(row, 0)
|
||
if id_item:
|
||
user_id = id_item.data(Qt.UserRole)
|
||
if user_id:
|
||
user_ids.append(user_id)
|
||
|
||
# 调用 ViewModel 删除
|
||
if user_ids:
|
||
success = self.viewModel.deleteUsers(user_ids)
|
||
if success:
|
||
QMessageBox.information(self, "成功", f"已删除 {len(user_ids)} 条用户记录")
|
||
else:
|
||
QMessageBox.warning(self, "错误", "删除失败,请重试")
|
||
|
||
# PLC: 旧的手动加载方法(已被响应式 _refresh_plc_form 取代,保留用于兼容)
|
||
def _on_load_plc_for_index(self):
|
||
"""已废弃,使用响应式 _refresh_plc_form 代替"""
|
||
pass
|
||
|
||
def _on_save_plc_for_index(self):
|
||
"""保存 PLC 设置(响应式,调用 ViewModel)"""
|
||
# 收集每通道参数为 dict
|
||
def collect(ch_no):
|
||
payload = {}
|
||
for key, le in self.ch_edits[ch_no].items():
|
||
val = le.text().strip()
|
||
if val != "":
|
||
payload[key] = val
|
||
return payload
|
||
|
||
# baudrate 从输入框解析,容错处理
|
||
baud_text = self.plc_baud_input.text().strip()
|
||
try:
|
||
baud_obj = json.loads(baud_text) if baud_text else {"baudrate": 19200, "stopbits": 1, "bytesize": 8, "parity": "E"}
|
||
except Exception:
|
||
baud_obj = {"baudrate": 19200, "stopbits": 1, "bytesize": 8, "parity": "E"}
|
||
|
||
# 构建更新数据(register01~04 和 baudrate 都为 dict,ViewModel 会自动转换为 JSON 字符串)
|
||
plc_updates = {
|
||
"plcAddress": self.plc_address_input.text().strip(),
|
||
"com": self.plc_com_input.text().strip(),
|
||
"baudrate": baud_obj, # dict 格式
|
||
"testType": self.plc_testtype_input.text().strip(),
|
||
# 通道启用状态不在此页修改,保持原值
|
||
"register01": collect(1), # dict 格式
|
||
"register02": collect(2), # dict 格式
|
||
"register03": collect(3), # dict 格式
|
||
"register04": collect(4), # dict 格式
|
||
}
|
||
|
||
# 调用 ViewModel 保存(自动触发响应式刷新)
|
||
success = self.viewModel.savePlcData(plc_updates)
|
||
if success:
|
||
QMessageBox.information(self, "保存成功", "PLC 设置已保存到机台")
|
||
else:
|
||
QMessageBox.warning(self, "保存失败", "请先选择机台") |