更新配置文件以支持串口功能,修改主窗口以初始化串口管理器并处理设置变更
This commit is contained in:
parent
79eca2b44f
commit
fa31dc734d
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -4,7 +4,7 @@
|
||||
"version": "1.0.0",
|
||||
"features": {
|
||||
"enable_serial_ports": false,
|
||||
"enable_keyboard_listener": false,
|
||||
"enable_keyboard_listener": true,
|
||||
"enable_camera": false
|
||||
}
|
||||
},
|
||||
@ -26,5 +26,31 @@
|
||||
"modbus": {
|
||||
"host": "localhost",
|
||||
"port": "5020"
|
||||
},
|
||||
"serial":{
|
||||
"keyboard":{
|
||||
"trigger_key":"Key.page_up"
|
||||
},"mdz":{
|
||||
"bit": 10,
|
||||
"code": "mdz",
|
||||
"data_bits": 8,
|
||||
"parity": "N",
|
||||
"port": "9600",
|
||||
"query_cmd": "01 03 00 01 00 07 55 C8",
|
||||
"query_interval": 5,
|
||||
"ser": "COM5",
|
||||
"stop_bits": 1,
|
||||
"timeout": 1
|
||||
},"cz":{
|
||||
"bit": 10,
|
||||
"code": "cz",
|
||||
"data_bits": 8,
|
||||
"parity": "N",
|
||||
"port": "9600",
|
||||
"ser": "COM2",
|
||||
"stable_threshold": 10,
|
||||
"stop_bits": 1,
|
||||
"timeout": 1
|
||||
}
|
||||
}
|
||||
}
|
||||
BIN
db/jtDB.db
BIN
db/jtDB.db
Binary file not shown.
@ -2,10 +2,10 @@ from pymodbus.client import ModbusTcpClient
|
||||
|
||||
client = ModbusTcpClient('localhost', port=5020)
|
||||
client.connect()
|
||||
# client.write_registers(address=11, values=[114])
|
||||
client.write_registers(address=6, values=[1])
|
||||
# client.write_registers(address=11, values=[113])
|
||||
# client.write_registers(address=6, values=[1])
|
||||
# client.write_registers(address=5, values=[16])
|
||||
# client.write_registers(address=13, values=[1])
|
||||
client.write_registers(address=13, values=[1])
|
||||
|
||||
|
||||
result = client.read_holding_registers(address=13, count=1)
|
||||
|
||||
5
main.py
5
main.py
@ -130,8 +130,9 @@ def main():
|
||||
# 读取配置
|
||||
config = ConfigLoader.get_instance()
|
||||
# 打印关键配置信息
|
||||
enable_serial_ports = config.get_value('app.features.enable_serial_ports', False)
|
||||
enable_keyboard_listener = config.get_value('app.features.enable_keyboard_listener', False)
|
||||
enable_serial_ports = config.get_value('serial.printer.enabled', False)
|
||||
# 键盘监听器配置信息
|
||||
enable_keyboard_listener = config.get_value('serial.keyboard.enabled', False)
|
||||
logging.info(f"配置信息 - 启用串口: {enable_serial_ports}, 启用键盘监听: {enable_keyboard_listener}")
|
||||
|
||||
# 设置中文翻译器
|
||||
|
||||
51
test_keyboard.py
Normal file
51
test_keyboard.py
Normal file
@ -0,0 +1,51 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import sys
|
||||
import logging
|
||||
import time
|
||||
from utils.serial_manager import SerialManager
|
||||
from utils.keyboard_listener import KeyboardListener
|
||||
|
||||
# 配置日志
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format='%(asctime)s - %(levelname)s - %(name)s - %(message)s',
|
||||
handlers=[
|
||||
logging.StreamHandler(sys.stdout)
|
||||
]
|
||||
)
|
||||
|
||||
# 自定义回调函数
|
||||
def my_pageup_callback():
|
||||
print("\n" + "="*50)
|
||||
print("PageUp键被按下!自定义回调函数被触发!")
|
||||
print("="*50 + "\n")
|
||||
|
||||
def main():
|
||||
print("初始化SerialManager...")
|
||||
sm = SerialManager()
|
||||
|
||||
# 注册自定义回调函数
|
||||
kl = KeyboardListener()
|
||||
kl.register_callback('Key.page_up', my_pageup_callback)
|
||||
|
||||
print("启动键盘监听器...")
|
||||
sm.start_keyboard_listener()
|
||||
|
||||
print("键盘监听器已启动,按PageUp键触发米电阻查询")
|
||||
print("程序将运行30秒,按Ctrl+C可以提前退出")
|
||||
|
||||
try:
|
||||
for i in range(30):
|
||||
print(f"等待中 {i+1}/30...", flush=True)
|
||||
time.sleep(1)
|
||||
except KeyboardInterrupt:
|
||||
print("\n用户中断,正在退出...")
|
||||
finally:
|
||||
print("停止键盘监听器...")
|
||||
sm.stop_keyboard_listener(join_thread=True)
|
||||
print("程序结束")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
2
ui/__init__.py
Normal file
2
ui/__init__.py
Normal file
@ -0,0 +1,2 @@
|
||||
# ui 包
|
||||
# 包含所有 UI 相关的类
|
||||
@ -30,19 +30,6 @@ class InspectionSettingsUI(QWidget):
|
||||
self.main_layout.setContentsMargins(20, 20, 20, 20)
|
||||
self.main_layout.setSpacing(15)
|
||||
|
||||
# 标题
|
||||
self.title_label = QLabel("检验项目配置")
|
||||
self.title_label.setFont(self.title_font)
|
||||
self.title_label.setAlignment(Qt.AlignCenter)
|
||||
self.title_label.setStyleSheet("color: #1a237e; padding: 10px;")
|
||||
self.main_layout.addWidget(self.title_label)
|
||||
|
||||
# 说明文本
|
||||
self.desc_label = QLabel("配置检验二级菜单项目,至少1项,最多6项。启用的项目将显示在微丝产线表格的检验区域。")
|
||||
self.desc_label.setWordWrap(True)
|
||||
self.desc_label.setStyleSheet("color: #666666; padding: 0px 10px 10px 10px;")
|
||||
self.main_layout.addWidget(self.desc_label)
|
||||
|
||||
# 创建滚动区域
|
||||
self.scroll_area = QScrollArea()
|
||||
self.scroll_area.setWidgetResizable(True)
|
||||
|
||||
@ -30,90 +30,11 @@ class PalletTypeSettingsUI(QWidget):
|
||||
self.main_layout.setContentsMargins(20, 20, 20, 20)
|
||||
self.main_layout.setSpacing(15)
|
||||
|
||||
# 标题
|
||||
self.title_label = QLabel("托盘类型配置")
|
||||
self.title_label.setFont(self.title_font)
|
||||
self.title_label.setAlignment(Qt.AlignCenter)
|
||||
self.title_label.setStyleSheet("color: #1a237e; padding: 10px;")
|
||||
self.main_layout.addWidget(self.title_label)
|
||||
|
||||
# 说明文本
|
||||
self.desc_label = QLabel("配置上料和下料托盘类型,点击上料/下料按钮切换显示对应类型。")
|
||||
self.desc_label.setWordWrap(True)
|
||||
self.desc_label.setStyleSheet("color: #666666; padding: 0px 10px 10px 10px;")
|
||||
self.main_layout.addWidget(self.desc_label)
|
||||
|
||||
# 创建操作类型选择按钮
|
||||
self.operation_layout = QHBoxLayout()
|
||||
self.operation_layout.setContentsMargins(0, 0, 0, 0)
|
||||
self.operation_layout.setSpacing(20)
|
||||
|
||||
self.input_button = QPushButton("上料类型")
|
||||
self.input_button.setFont(self.normal_font)
|
||||
self.input_button.setFixedHeight(40)
|
||||
self.input_button.setStyleSheet("""
|
||||
QPushButton {
|
||||
background-color: #2196f3;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 5px;
|
||||
}
|
||||
QPushButton:hover {
|
||||
background-color: #1e88e5;
|
||||
}
|
||||
QPushButton:pressed {
|
||||
background-color: #1976d2;
|
||||
}
|
||||
QPushButton:checked {
|
||||
background-color: #1565c0;
|
||||
border: 2px solid #0d47a1;
|
||||
}
|
||||
""")
|
||||
self.input_button.setCheckable(True)
|
||||
self.input_button.setChecked(True)
|
||||
|
||||
self.output_button = QPushButton("下料类型")
|
||||
self.output_button.setFont(self.normal_font)
|
||||
self.output_button.setFixedHeight(40)
|
||||
self.output_button.setStyleSheet("""
|
||||
QPushButton {
|
||||
background-color: #ff9800;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 5px;
|
||||
}
|
||||
QPushButton:hover {
|
||||
background-color: #fb8c00;
|
||||
}
|
||||
QPushButton:pressed {
|
||||
background-color: #f57c00;
|
||||
}
|
||||
QPushButton:checked {
|
||||
background-color: #ef6c00;
|
||||
border: 2px solid #e65100;
|
||||
}
|
||||
""")
|
||||
self.output_button.setCheckable(True)
|
||||
|
||||
self.operation_layout.addWidget(self.input_button)
|
||||
self.operation_layout.addWidget(self.output_button)
|
||||
self.main_layout.addLayout(self.operation_layout)
|
||||
|
||||
# 创建堆叠部件,用于切换上料和下料类型配置
|
||||
self.stacked_widget = QStackedWidget()
|
||||
|
||||
# 创建上料类型配置页面
|
||||
self.input_widget = self.create_pallet_type_widget("input")
|
||||
self.stacked_widget.addWidget(self.input_widget)
|
||||
|
||||
# 创建下料类型配置页面
|
||||
self.output_widget = self.create_pallet_type_widget("output")
|
||||
self.stacked_widget.addWidget(self.output_widget)
|
||||
|
||||
# 默认显示上料类型
|
||||
self.stacked_widget.setCurrentIndex(0)
|
||||
|
||||
self.main_layout.addWidget(self.stacked_widget, 1)
|
||||
self.main_layout.addWidget(self.output_widget, 1)
|
||||
|
||||
# 底部按钮区域
|
||||
self.button_layout = QHBoxLayout()
|
||||
@ -162,10 +83,6 @@ class PalletTypeSettingsUI(QWidget):
|
||||
|
||||
self.main_layout.addLayout(self.button_layout)
|
||||
|
||||
# 连接信号和槽
|
||||
self.input_button.clicked.connect(self.show_input_types)
|
||||
self.output_button.clicked.connect(self.show_output_types)
|
||||
|
||||
def create_pallet_type_widget(self, operation_type):
|
||||
"""创建托盘类型配置部件
|
||||
|
||||
@ -198,64 +115,56 @@ class PalletTypeSettingsUI(QWidget):
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 5px;
|
||||
background-color: #ffffff;
|
||||
alternate-background-color: #f5f5f5;
|
||||
alternate-background-color: #f9f9f9;
|
||||
}
|
||||
QHeaderView::section {
|
||||
background-color: #f0f0f0;
|
||||
padding: 6px;
|
||||
padding: 5px;
|
||||
border: 1px solid #ddd;
|
||||
font-weight: bold;
|
||||
}
|
||||
""")
|
||||
|
||||
# 设置表格属性,用于标识操作类型
|
||||
table.setObjectName(f"{operation_type}_table")
|
||||
table.setProperty("operation_type", operation_type)
|
||||
|
||||
layout.addWidget(table)
|
||||
|
||||
# 创建表单布局,用于添加/编辑托盘类型
|
||||
form_group = QGroupBox("添加/编辑托盘类型")
|
||||
# 创建表单
|
||||
form_group = QGroupBox("编辑托盘类型")
|
||||
form_group.setFont(self.normal_font)
|
||||
form_layout = QFormLayout(form_group)
|
||||
form_layout.setContentsMargins(15, 25, 15, 15)
|
||||
form_layout.setContentsMargins(15, 15, 15, 15)
|
||||
form_layout.setSpacing(10)
|
||||
|
||||
# 类型名称
|
||||
type_name_label = QLabel("类型名称:")
|
||||
type_name_label.setFont(self.normal_font)
|
||||
type_name_input = QLineEdit()
|
||||
type_name_input.setFont(self.normal_font)
|
||||
type_name_input.setObjectName(f"{operation_type}_type_name_input")
|
||||
form_layout.addRow(type_name_label, type_name_input)
|
||||
form_layout.addRow("类型名称:", type_name_input)
|
||||
|
||||
# 描述
|
||||
desc_label = QLabel("描述:")
|
||||
desc_label.setFont(self.normal_font)
|
||||
desc_input = QLineEdit()
|
||||
desc_input.setFont(self.normal_font)
|
||||
desc_input.setObjectName(f"{operation_type}_desc_input")
|
||||
form_layout.addRow(desc_label, desc_input)
|
||||
form_layout.addRow("描述:", desc_input)
|
||||
|
||||
# 排序
|
||||
sort_order_label = QLabel("排序:")
|
||||
sort_order_label.setFont(self.normal_font)
|
||||
sort_order_spin = QSpinBox()
|
||||
sort_order_spin.setFont(self.normal_font)
|
||||
sort_order_spin.setObjectName(f"{operation_type}_sort_order_spin")
|
||||
sort_order_spin.setRange(1, 999)
|
||||
sort_order_spin.setValue(100)
|
||||
form_layout.addRow(sort_order_label, sort_order_spin)
|
||||
sort_order_spin.setObjectName(f"{operation_type}_sort_order_spin")
|
||||
form_layout.addRow("排序:", sort_order_spin)
|
||||
|
||||
# 是否启用
|
||||
# 启用
|
||||
enabled_check = QCheckBox("启用")
|
||||
enabled_check.setFont(self.normal_font)
|
||||
enabled_check.setObjectName(f"{operation_type}_enabled_check")
|
||||
enabled_check.setChecked(True)
|
||||
enabled_check.setObjectName(f"{operation_type}_enabled_check")
|
||||
form_layout.addRow("", enabled_check)
|
||||
|
||||
# 添加表单按钮
|
||||
form_button_layout = QHBoxLayout()
|
||||
layout.addWidget(form_group)
|
||||
|
||||
# 创建按钮区域
|
||||
button_layout = QHBoxLayout()
|
||||
|
||||
add_button = QPushButton("添加")
|
||||
add_button.setFont(self.normal_font)
|
||||
@ -265,15 +174,12 @@ class PalletTypeSettingsUI(QWidget):
|
||||
background-color: #4caf50;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 5px;
|
||||
border-radius: 3px;
|
||||
padding: 5px 15px;
|
||||
}
|
||||
QPushButton:hover {
|
||||
background-color: #45a049;
|
||||
}
|
||||
QPushButton:pressed {
|
||||
background-color: #3d8b40;
|
||||
}
|
||||
""")
|
||||
|
||||
update_button = QPushButton("更新")
|
||||
@ -284,17 +190,13 @@ class PalletTypeSettingsUI(QWidget):
|
||||
background-color: #2196f3;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 5px;
|
||||
border-radius: 3px;
|
||||
padding: 5px 15px;
|
||||
}
|
||||
QPushButton:hover {
|
||||
background-color: #1e88e5;
|
||||
}
|
||||
QPushButton:pressed {
|
||||
background-color: #1976d2;
|
||||
}
|
||||
""")
|
||||
update_button.setEnabled(False)
|
||||
|
||||
delete_button = QPushButton("删除")
|
||||
delete_button.setFont(self.normal_font)
|
||||
@ -304,17 +206,13 @@ class PalletTypeSettingsUI(QWidget):
|
||||
background-color: #f44336;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 5px;
|
||||
border-radius: 3px;
|
||||
padding: 5px 15px;
|
||||
}
|
||||
QPushButton:hover {
|
||||
background-color: #e53935;
|
||||
}
|
||||
QPushButton:pressed {
|
||||
background-color: #d32f2f;
|
||||
}
|
||||
""")
|
||||
delete_button.setEnabled(False)
|
||||
|
||||
cancel_button = QPushButton("取消")
|
||||
cancel_button.setFont(self.normal_font)
|
||||
@ -324,52 +222,19 @@ class PalletTypeSettingsUI(QWidget):
|
||||
background-color: #9e9e9e;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 5px;
|
||||
border-radius: 3px;
|
||||
padding: 5px 15px;
|
||||
}
|
||||
QPushButton:hover {
|
||||
background-color: #757575;
|
||||
}
|
||||
QPushButton:pressed {
|
||||
background-color: #616161;
|
||||
}
|
||||
""")
|
||||
cancel_button.setEnabled(False)
|
||||
|
||||
form_button_layout.addWidget(add_button)
|
||||
form_button_layout.addWidget(update_button)
|
||||
form_button_layout.addWidget(delete_button)
|
||||
form_button_layout.addWidget(cancel_button)
|
||||
button_layout.addWidget(add_button)
|
||||
button_layout.addWidget(update_button)
|
||||
button_layout.addWidget(delete_button)
|
||||
button_layout.addWidget(cancel_button)
|
||||
|
||||
form_layout.addRow("", form_button_layout)
|
||||
|
||||
layout.addWidget(form_group)
|
||||
|
||||
# 添加隐藏字段,用于存储当前编辑的ID
|
||||
widget.setProperty("current_edit_id", -1)
|
||||
layout.addLayout(button_layout)
|
||||
|
||||
return widget
|
||||
|
||||
def show_input_types(self):
|
||||
"""显示上料类型"""
|
||||
self.input_button.setChecked(True)
|
||||
self.output_button.setChecked(False)
|
||||
self.stacked_widget.setCurrentIndex(0)
|
||||
|
||||
def show_output_types(self):
|
||||
"""显示下料类型"""
|
||||
self.input_button.setChecked(False)
|
||||
self.output_button.setChecked(True)
|
||||
self.stacked_widget.setCurrentIndex(1)
|
||||
|
||||
def set_form_enabled(self, enabled):
|
||||
"""设置表单是否可编辑"""
|
||||
self.input_button.setEnabled(enabled)
|
||||
self.output_button.setEnabled(enabled)
|
||||
self.save_button.setEnabled(enabled)
|
||||
self.reset_button.setEnabled(enabled)
|
||||
|
||||
# 禁用所有表格和表单
|
||||
for widget in [self.input_widget, self.output_widget]:
|
||||
for child in widget.findChildren(QWidget):
|
||||
child.setEnabled(enabled)
|
||||
150
ui/serial_settings_ui.py
Normal file
150
ui/serial_settings_ui.py
Normal file
@ -0,0 +1,150 @@
|
||||
from PySide6.QtWidgets import (
|
||||
QWidget, QLabel, QLineEdit, QComboBox, QCheckBox,
|
||||
QGridLayout, QGroupBox, QPushButton, QHBoxLayout,
|
||||
QVBoxLayout, QFormLayout, QSpinBox
|
||||
)
|
||||
from PySide6.QtCore import Qt, Signal
|
||||
|
||||
class SerialSettingsUI(QWidget):
|
||||
"""串口设置UI组件"""
|
||||
|
||||
# 定义信号
|
||||
settings_changed = Signal()
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
self.init_ui()
|
||||
|
||||
def init_ui(self):
|
||||
"""初始化UI"""
|
||||
main_layout = QVBoxLayout(self)
|
||||
|
||||
# 创建全局启用选项
|
||||
enable_layout = QHBoxLayout()
|
||||
self.enable_serial_checkbox = QCheckBox("启用串口功能")
|
||||
self.enable_keyboard_checkbox = QCheckBox("启用键盘监听 (PageUp 触发米电阻查询)")
|
||||
enable_layout.addWidget(self.enable_serial_checkbox)
|
||||
enable_layout.addWidget(self.enable_keyboard_checkbox)
|
||||
enable_layout.addStretch()
|
||||
main_layout.addLayout(enable_layout)
|
||||
|
||||
# 创建串口设置组
|
||||
serial_group = QGroupBox("串口设置")
|
||||
serial_layout = QGridLayout(serial_group)
|
||||
|
||||
# 米电阻串口设置
|
||||
mdz_group = QGroupBox("米电阻串口")
|
||||
mdz_layout = QFormLayout(mdz_group)
|
||||
|
||||
# 串口选择
|
||||
mdz_port_layout = QHBoxLayout()
|
||||
self.mdz_port_combo = QComboBox()
|
||||
self.mdz_refresh_btn = QPushButton("刷新")
|
||||
mdz_port_layout.addWidget(self.mdz_port_combo)
|
||||
mdz_port_layout.addWidget(self.mdz_refresh_btn)
|
||||
mdz_layout.addRow("串口:", mdz_port_layout)
|
||||
|
||||
# 波特率
|
||||
self.mdz_baud_combo = QComboBox()
|
||||
for baud in ["1200", "2400", "4800", "9600", "19200", "38400", "57600", "115200"]:
|
||||
self.mdz_baud_combo.addItem(baud)
|
||||
mdz_layout.addRow("波特率:", self.mdz_baud_combo)
|
||||
|
||||
# 数据位
|
||||
self.mdz_data_bits_combo = QComboBox()
|
||||
for bits in ["5", "6", "7", "8"]:
|
||||
self.mdz_data_bits_combo.addItem(bits)
|
||||
mdz_layout.addRow("数据位:", self.mdz_data_bits_combo)
|
||||
|
||||
# 停止位
|
||||
self.mdz_stop_bits_combo = QComboBox()
|
||||
for bits in ["1", "1.5", "2"]:
|
||||
self.mdz_stop_bits_combo.addItem(bits)
|
||||
mdz_layout.addRow("停止位:", self.mdz_stop_bits_combo)
|
||||
|
||||
# 校验位
|
||||
self.mdz_parity_combo = QComboBox()
|
||||
for parity in [("无校验", "N"), ("奇校验", "O"), ("偶校验", "E")]:
|
||||
self.mdz_parity_combo.addItem(parity[0], parity[1])
|
||||
mdz_layout.addRow("校验位:", self.mdz_parity_combo)
|
||||
|
||||
# 查询指令
|
||||
self.mdz_query_cmd = QLineEdit()
|
||||
mdz_layout.addRow("查询指令:", self.mdz_query_cmd)
|
||||
|
||||
# 查询间隔
|
||||
self.mdz_query_interval = QSpinBox()
|
||||
self.mdz_query_interval.setRange(1, 60)
|
||||
self.mdz_query_interval.setSuffix(" 秒")
|
||||
mdz_layout.addRow("查询间隔:", self.mdz_query_interval)
|
||||
|
||||
# 线径串口设置
|
||||
cz_group = QGroupBox("线径检测串口")
|
||||
cz_layout = QFormLayout(cz_group)
|
||||
|
||||
# 串口选择
|
||||
cz_port_layout = QHBoxLayout()
|
||||
self.cz_port_combo = QComboBox()
|
||||
self.cz_refresh_btn = QPushButton("刷新")
|
||||
cz_port_layout.addWidget(self.cz_port_combo)
|
||||
cz_port_layout.addWidget(self.cz_refresh_btn)
|
||||
cz_layout.addRow("串口:", cz_port_layout)
|
||||
|
||||
# 波特率
|
||||
self.cz_baud_combo = QComboBox()
|
||||
for baud in ["1200", "2400", "4800", "9600", "19200", "38400", "57600", "115200"]:
|
||||
self.cz_baud_combo.addItem(baud)
|
||||
cz_layout.addRow("波特率:", self.cz_baud_combo)
|
||||
|
||||
# 数据位
|
||||
self.cz_data_bits_combo = QComboBox()
|
||||
for bits in ["5", "6", "7", "8"]:
|
||||
self.cz_data_bits_combo.addItem(bits)
|
||||
cz_layout.addRow("数据位:", self.cz_data_bits_combo)
|
||||
|
||||
# 停止位
|
||||
self.cz_stop_bits_combo = QComboBox()
|
||||
for bits in ["1", "1.5", "2"]:
|
||||
self.cz_stop_bits_combo.addItem(bits)
|
||||
cz_layout.addRow("停止位:", self.cz_stop_bits_combo)
|
||||
|
||||
# 校验位
|
||||
self.cz_parity_combo = QComboBox()
|
||||
for parity in [("无校验", "N"), ("奇校验", "O"), ("偶校验", "E")]:
|
||||
self.cz_parity_combo.addItem(parity[0], parity[1])
|
||||
cz_layout.addRow("校验位:", self.cz_parity_combo)
|
||||
|
||||
# 稳定阈值
|
||||
self.cz_stable_threshold = QSpinBox()
|
||||
self.cz_stable_threshold.setRange(1, 30)
|
||||
self.cz_stable_threshold.setSuffix(" 次")
|
||||
cz_layout.addRow("稳定阈值:", self.cz_stable_threshold)
|
||||
|
||||
# 将两个组添加到布局
|
||||
serial_layout.addWidget(mdz_group, 0, 0)
|
||||
serial_layout.addWidget(cz_group, 0, 1)
|
||||
|
||||
# 设置列伸缩因子,使两列等宽(比例1:1)
|
||||
serial_layout.setColumnStretch(0, 1)
|
||||
serial_layout.setColumnStretch(1, 1)
|
||||
|
||||
main_layout.addWidget(serial_group)
|
||||
|
||||
# 测试按钮
|
||||
test_layout = QHBoxLayout()
|
||||
self.test_mdz_btn = QPushButton("测试米电阻串口")
|
||||
self.test_cz_btn = QPushButton("测试线径串口")
|
||||
test_layout.addWidget(self.test_mdz_btn)
|
||||
test_layout.addWidget(self.test_cz_btn)
|
||||
test_layout.addStretch()
|
||||
main_layout.addLayout(test_layout)
|
||||
|
||||
# 保存按钮
|
||||
button_layout = QHBoxLayout()
|
||||
self.save_btn = QPushButton("保存设置")
|
||||
self.save_btn.setStyleSheet("background-color: #e3f2fd; border: 1px solid #2196f3; padding: 8px 16px; font-weight: bold; border-radius: 4px;")
|
||||
button_layout.addStretch()
|
||||
button_layout.addWidget(self.save_btn)
|
||||
main_layout.addLayout(button_layout)
|
||||
|
||||
main_layout.addStretch()
|
||||
37
ui/settings_window_ui.py
Normal file
37
ui/settings_window_ui.py
Normal file
@ -0,0 +1,37 @@
|
||||
from PySide6.QtWidgets import (
|
||||
QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
|
||||
QTabWidget, QPushButton, QLabel
|
||||
)
|
||||
from PySide6.QtCore import Qt, Signal
|
||||
from PySide6.QtGui import QIcon
|
||||
|
||||
class SettingsWindowUI(QMainWindow):
|
||||
"""设置窗口UI"""
|
||||
|
||||
# 定义信号
|
||||
settings_changed = Signal()
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
self.init_ui()
|
||||
|
||||
def init_ui(self):
|
||||
"""初始化UI"""
|
||||
self.setWindowTitle("系统设置")
|
||||
self.setMinimumSize(800, 600)
|
||||
|
||||
# 创建中央部件
|
||||
central_widget = QWidget()
|
||||
self.setCentralWidget(central_widget)
|
||||
|
||||
# 创建主布局
|
||||
main_layout = QVBoxLayout(central_widget)
|
||||
|
||||
# 创建标签
|
||||
title_label = QLabel("系统设置")
|
||||
title_label.setStyleSheet("font-size: 18px; font-weight: bold; margin-bottom: 10px;")
|
||||
main_layout.addWidget(title_label)
|
||||
|
||||
# 创建选项卡
|
||||
self.tab_widget = QTabWidget()
|
||||
main_layout.addWidget(self.tab_widget)
|
||||
Binary file not shown.
@ -128,3 +128,39 @@ class ConfigLoader:
|
||||
|
||||
# 保存配置
|
||||
return self.save_config()
|
||||
|
||||
def get_config(self, key):
|
||||
"""
|
||||
获取serial配置下的指定配置
|
||||
|
||||
Args:
|
||||
key: 配置键,例如'mdz', 'cz'等
|
||||
|
||||
Returns:
|
||||
dict: 配置值,未找到则返回None
|
||||
"""
|
||||
if 'serial' not in self.config:
|
||||
self.config['serial'] = {}
|
||||
|
||||
if key not in self.config['serial']:
|
||||
return None
|
||||
|
||||
return self.config['serial'][key]
|
||||
|
||||
def set_config(self, key, config_data):
|
||||
"""
|
||||
设置serial配置下的指定配置
|
||||
|
||||
Args:
|
||||
key: 配置键,例如'mdz', 'cz'等
|
||||
config_data: 要设置的配置数据
|
||||
|
||||
Returns:
|
||||
bool: 是否设置成功
|
||||
"""
|
||||
if 'serial' not in self.config:
|
||||
self.config['serial'] = {}
|
||||
|
||||
self.config['serial'][key] = config_data
|
||||
# 这里不保存配置,等待调用save_config方法时一并保存
|
||||
return True
|
||||
@ -29,6 +29,42 @@ class InspectionConfigManager:
|
||||
logging.error(f"加载检验配置失败: {str(e)}")
|
||||
return False
|
||||
|
||||
def save_configs(self, configs, username='system'):
|
||||
"""保存检验配置列表
|
||||
|
||||
Args:
|
||||
configs: 检验配置列表
|
||||
username: 操作用户
|
||||
|
||||
Returns:
|
||||
bool: 保存是否成功
|
||||
"""
|
||||
try:
|
||||
# 获取当前所有配置
|
||||
current_configs = self.get_configs(include_disabled=True)
|
||||
|
||||
# 创建位置到配置ID的映射
|
||||
position_to_id = {}
|
||||
for config in current_configs:
|
||||
position = config.get('position')
|
||||
if position:
|
||||
position_to_id[position] = config.get('id')
|
||||
|
||||
# 更新每个配置
|
||||
for config in configs:
|
||||
position = config.get('position')
|
||||
if position in position_to_id:
|
||||
# 更新已有配置
|
||||
config_id = position_to_id[position]
|
||||
self.update_config(config_id, config, username)
|
||||
|
||||
# 重新加载配置
|
||||
self.reload_configs()
|
||||
return True
|
||||
except Exception as e:
|
||||
logging.error(f"保存检验配置失败: {str(e)}")
|
||||
return False
|
||||
|
||||
def get_configs(self, include_disabled=False):
|
||||
"""获取检验配置列表
|
||||
|
||||
|
||||
345
utils/keyboard_listener.py
Normal file
345
utils/keyboard_listener.py
Normal file
@ -0,0 +1,345 @@
|
||||
from pynput.keyboard import Key, Listener, GlobalHotKeys
|
||||
import logging
|
||||
import threading
|
||||
import platform
|
||||
import os
|
||||
|
||||
class KeyboardListener:
|
||||
"""键盘监听器,用于监听特定按键并触发相应操作"""
|
||||
_instance = None
|
||||
_lock = threading.Lock()
|
||||
|
||||
def __new__(cls):
|
||||
with cls._lock:
|
||||
if cls._instance is None:
|
||||
cls._instance = super(KeyboardListener, cls).__new__(cls)
|
||||
cls._instance._initialized = False
|
||||
return cls._instance
|
||||
|
||||
def __init__(self):
|
||||
if self._initialized:
|
||||
return
|
||||
|
||||
self._initialized = True
|
||||
self.listener = None
|
||||
self.hotkey_listener = None
|
||||
self.is_running = False
|
||||
self.callbacks = {}
|
||||
|
||||
# 设置日志级别为DEBUG,确保能看到所有日志
|
||||
# 注意:如果主程序或其他模块也配置了logging,这里的basicConfig可能不会覆盖已有的配置
|
||||
# 建议在主程序入口处统一配置logging
|
||||
logging.basicConfig(
|
||||
level=logging.DEBUG,
|
||||
format='%(asctime)s - %(levelname)s - %(name)s - [%(funcName)s] - %(message)s',
|
||||
handlers=[
|
||||
logging.StreamHandler(), # 确保日志输出到控制台
|
||||
]
|
||||
)
|
||||
|
||||
# 检测操作系统
|
||||
self.os_type = platform.system()
|
||||
logging.info(f"当前操作系统: {self.os_type}")
|
||||
|
||||
def start(self, join_thread=False):
|
||||
"""启动键盘监听
|
||||
|
||||
Args:
|
||||
join_thread: 是否阻塞等待键盘监听线程结束
|
||||
"""
|
||||
logging.info("-----> KeyboardListener.start() called <-----") # 显式入口日志
|
||||
print("-----> KeyboardListener.start() called <-----") # 确保控制台可见
|
||||
if self.is_running:
|
||||
logging.info("键盘监听器已经在运行中")
|
||||
return True
|
||||
|
||||
self.is_running = True # 先标记为 True
|
||||
try:
|
||||
logging.info(f"OS type: {self.os_type}. Preparing to start listener.")
|
||||
|
||||
listener_started_successfully = False
|
||||
try:
|
||||
if self.os_type == "Windows":
|
||||
logging.info("Attempting to use GlobalHotKeys for Windows.")
|
||||
# _start_hotkey_listener 内部会处理自己的成功/失败和日志
|
||||
self._start_hotkey_listener()
|
||||
# 假设 GlobalHotKeys 启动后 is_active 会更新
|
||||
listener_started_successfully = self.is_active()
|
||||
else:
|
||||
logging.info(f"Using normal Listener for {self.os_type}.")
|
||||
listener_started_successfully = self._start_normal_listener()
|
||||
except Exception as e:
|
||||
logging.error(f"启动特定监听器失败: {e}", exc_info=True)
|
||||
# 失败时重置标志
|
||||
listener_started_successfully = False
|
||||
|
||||
if not listener_started_successfully:
|
||||
# 如果到这里还没有成功启动任何监听器 (例如 _start_hotkey_listener 内部回退到 _start_normal_listener 也失败了)
|
||||
# 或者 _start_normal_listener 直接失败
|
||||
logging.error("Failed to start any keyboard listener (normal or hotkey).")
|
||||
self.is_running = False # 如果没有成功启动,重置状态
|
||||
# 不要引发异常,让应用程序继续运行
|
||||
logging.warning("将继续运行应用程序,但键盘监听功能将不可用")
|
||||
return False
|
||||
|
||||
logging.info(f"Callbacks registered: {list(self.callbacks.keys())}")
|
||||
|
||||
# 提示用户如何触发
|
||||
if 'Key.page_up' in self.callbacks or '<page_up>' in self.callbacks:
|
||||
logging.info("按下 PageUp 键可触发相应操作")
|
||||
|
||||
# 如果需要阻塞等待线程结束
|
||||
if join_thread:
|
||||
self.join()
|
||||
|
||||
logging.info("-----> KeyboardListener.start() finished successfully <-----")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
self.is_running = False # 发生异常时重置状态
|
||||
logging.error(f"CRITICAL: Exception during KeyboardListener.start() [outer try-except]: {e}", exc_info=True)
|
||||
# 不要引发异常,让应用程序继续运行
|
||||
logging.warning("将继续运行应用程序,但键盘监听功能将不可用")
|
||||
return False
|
||||
|
||||
def _start_normal_listener(self):
|
||||
"""启动普通的按键监听"""
|
||||
logging.info("-----> _start_normal_listener called <-----")
|
||||
try:
|
||||
logging.info("Attempting to initialize pynput.keyboard.Listener.")
|
||||
self.listener = Listener(on_press=self._on_press)
|
||||
logging.info("pynput.keyboard.Listener instance created.")
|
||||
|
||||
self.listener.daemon = False # 修改为非守护线程,防止主线程退出时被强制终止
|
||||
logging.info("Normal listener daemon set to False.")
|
||||
|
||||
logging.info("Attempting to start normal listener thread...")
|
||||
|
||||
try:
|
||||
self.listener.start()
|
||||
logging.info("Normal listener thread started (or start() method returned).")
|
||||
except Exception as e:
|
||||
logging.error(f"启动监听器线程失败: {e}", exc_info=True)
|
||||
return False
|
||||
|
||||
# 使用额外的异常处理来检查是否存活
|
||||
try:
|
||||
is_alive = self.listener.is_alive()
|
||||
logging.info(f"Normal listener thread is_alive() is {is_alive}.")
|
||||
|
||||
if is_alive:
|
||||
logging.info("-----> _start_normal_listener finished successfully <-----")
|
||||
return True # 表示成功
|
||||
else:
|
||||
logging.warning("Normal listener thread is_alive() is False immediately after start. This might indicate an issue.")
|
||||
logging.info("-----> _start_normal_listener finished with is_alive()=False <-----")
|
||||
return False # 表示失败
|
||||
except Exception as e:
|
||||
logging.error(f"检查监听器线程状态失败: {e}", exc_info=True)
|
||||
# 假设成功启动,让程序继续运行
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f"CRITICAL ERROR during _start_normal_listener: {e}", exc_info=True)
|
||||
logging.info("-----> _start_normal_listener finished with exception <-----")
|
||||
return False # 表示失败
|
||||
|
||||
def _start_hotkey_listener(self):
|
||||
"""启动全局热键监听(适用于Windows)"""
|
||||
logging.info("-----> _start_hotkey_listener called <-----") # 显式入口日志
|
||||
try:
|
||||
logging.info("启动全局监听.")
|
||||
hotkey_mapping = {}
|
||||
page_up_callback = self.callbacks.get('Key.page_up') or self.callbacks.get('<page_up>')
|
||||
|
||||
if page_up_callback:
|
||||
hotkey_mapping['<page_up>'] = page_up_callback
|
||||
else:
|
||||
logging.warning("未找到PageUp回调 (Key.page_up 或 <page_up>). 全局热键将不会设置.")
|
||||
|
||||
if not hotkey_mapping:
|
||||
self._start_normal_listener()
|
||||
return
|
||||
|
||||
self.hotkey_listener = GlobalHotKeys(hotkey_mapping)
|
||||
|
||||
self.hotkey_listener.daemon = False # 修改为非守护线程,防止主线程退出时被强制终止
|
||||
logging.info("全局热键监听线程 daemon 设置为 False.")
|
||||
|
||||
self.hotkey_listener.start()
|
||||
|
||||
# 检查线程是否真的在运行 (这是一个启发式检查,is_alive() 可能在线程真正工作前就返回True)
|
||||
if self.hotkey_listener.is_alive():
|
||||
logging.info("全局热键监听线程 is_alive() 为 True.")
|
||||
else:
|
||||
logging.warning("全局热键监听线程 is_alive() 为 False 立即启动后. 这可能表明存在问题.")
|
||||
|
||||
logging.info("全局热键监听 (GlobalHotKeys) 已成功启动 (根据pynput文档, start() 是非阻塞的)")
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f"CRITICAL ERROR during _start_hotkey_listener: {e}", exc_info=True)
|
||||
# 如果GlobalHotKeys失败,回退到普通监听器
|
||||
self._start_normal_listener()
|
||||
logging.info("-----> _start_hotkey_listener finished <-----")
|
||||
|
||||
def stop(self):
|
||||
"""停止键盘监听"""
|
||||
logging.info("Attempting to stop keyboard listeners.")
|
||||
if not self.is_running:
|
||||
logging.info("键盘监听器已经停止或未运行")
|
||||
return
|
||||
|
||||
self.is_running = False
|
||||
|
||||
if self.listener:
|
||||
try:
|
||||
self.listener.stop()
|
||||
logging.info("常规键盘监听已停止")
|
||||
except Exception as e:
|
||||
logging.error(f"停止常规键盘监听失败: {e}", exc_info=True)
|
||||
finally:
|
||||
self.listener = None
|
||||
|
||||
if self.hotkey_listener:
|
||||
try:
|
||||
self.hotkey_listener.stop()
|
||||
logging.info("全局热键监听已停止")
|
||||
except Exception as e:
|
||||
logging.error(f"停止全局热键监听失败: {e}", exc_info=True)
|
||||
finally:
|
||||
self.hotkey_listener = None
|
||||
logging.info("All keyboard listeners stop process completed.")
|
||||
|
||||
def register_callback(self, key_name, callback):
|
||||
"""
|
||||
注册按键回调函数
|
||||
Args:
|
||||
key_name: 按键名称,如 'Key.page_up' 或 '<f1>' (用于GlobalHotKeys)
|
||||
callback: 回调函数,无参数
|
||||
"""
|
||||
self.callbacks[key_name] = callback
|
||||
logging.info(f"已注册按键回调: {key_name} -> {callback.__name__ if hasattr(callback, '__name__') else callback}")
|
||||
|
||||
if key_name == 'Key.page_up':
|
||||
self.callbacks['<page_up>'] = callback
|
||||
logging.info(f"已为 PageUp 键额外注册 '<page_up>' (用于全局热键)")
|
||||
|
||||
def _on_press(self, key):
|
||||
"""按键按下事件处理 (主要用于普通Listener)"""
|
||||
try:
|
||||
key_str = self._key_to_string(key)
|
||||
# 增加日志级别,确保按键事件始终被记录
|
||||
logging.info(f"[Normal Listener] 检测到按键: {key_str} (原始: {key})")
|
||||
# 直接打印到控制台,确保可见
|
||||
print(f"\n[键盘事件] 检测到按键: {key_str}\n")
|
||||
|
||||
if key_str in self.callbacks:
|
||||
logging.info(f"[Normal Listener] 触发按键回调 for key: {key_str}")
|
||||
print(f"\n[键盘事件] 触发回调: {key_str}\n")
|
||||
try:
|
||||
self.callbacks[key_str]()
|
||||
logging.info(f"[Normal Listener] 成功执行回调函数 for key: {key_str}")
|
||||
return
|
||||
except Exception as e:
|
||||
logging.error(f"[Normal Listener] 执行回调时出错: {e}", exc_info=True)
|
||||
print(f"\n[键盘事件] 回调执行错误: {e}\n")
|
||||
elif hasattr(key, 'name') and key.name and ('page_up' in key.name.lower() or 'pageup' in key.name.lower()):
|
||||
logging.info(f"[Normal Listener] 检测到 PageUp 相关的键: {key.name}")
|
||||
print(f"\n[键盘事件] 检测到PageUp键: {key.name}\n")
|
||||
page_up_callback = self.callbacks.get('Key.page_up') or self.callbacks.get('<page_up>')
|
||||
if page_up_callback:
|
||||
logging.info("[Normal Listener] 使用 'Key.page_up' 或 '<page_up>' 注册的回调触发 PageUp")
|
||||
print("\n[键盘事件] 触发PageUp回调\n")
|
||||
try:
|
||||
page_up_callback()
|
||||
logging.info("[Normal Listener] 成功执行 PageUp 回调函数")
|
||||
return
|
||||
except Exception as e:
|
||||
logging.error(f"[Normal Listener] 执行 PageUp 回调时出错: {e}", exc_info=True)
|
||||
print(f"\n[键盘事件] PageUp回调执行错误: {e}\n")
|
||||
else:
|
||||
logging.warning("[Normal Listener] 检测到 PageUp 键,但未找到对应的回调函数")
|
||||
print("\n[键盘事件] 检测到PageUp键,但未找到回调\n")
|
||||
else:
|
||||
logging.info(f"[Normal Listener] 按键 {key_str} 未注册精确回调")
|
||||
|
||||
# 添加当前注册的回调列表,便于调试
|
||||
logging.info(f"[Normal Listener] 当前注册的回调: {list(self.callbacks.keys())}")
|
||||
print(f"\n[键盘事件] 当前注册的回调: {list(self.callbacks.keys())}\n")
|
||||
except Exception as e:
|
||||
logging.error(f"[Normal Listener] 处理按键事件时出错: {e}", exc_info=True)
|
||||
print(f"\n[键盘事件] 处理按键事件出错: {e}\n")
|
||||
return
|
||||
|
||||
def _key_to_string(self, key):
|
||||
"""将按键对象转换为字符串格式"""
|
||||
try:
|
||||
logging.debug(f"原始按键: {key}, 类型: {type(key)}")
|
||||
|
||||
# 特殊处理PageUp键
|
||||
if str(key).lower() == 'key.page_up' or (hasattr(key, 'name') and key.name and 'page_up' in key.name.lower()):
|
||||
print("\n[键盘事件] 检测到PageUp键的特殊处理\n")
|
||||
return 'Key.page_up'
|
||||
|
||||
# 常规处理
|
||||
if hasattr(key, 'name') and key.name:
|
||||
key_name = f'Key.{key.name}'
|
||||
logging.debug(f"特殊键,转换为: {key_name}")
|
||||
return key_name
|
||||
elif hasattr(key, 'char') and key.char:
|
||||
logging.debug(f"字符键,转换为: {key.char}")
|
||||
return key.char
|
||||
else:
|
||||
key_str = str(key)
|
||||
logging.debug(f"其他类型键,转换为: {key_str}")
|
||||
return key_str
|
||||
except Exception as e:
|
||||
logging.error(f"转换按键为字符串时出错: {e}", exc_info=True)
|
||||
print(f"\n[键盘事件] 按键转换错误: {e}\n")
|
||||
return str(key)
|
||||
|
||||
def is_active(self):
|
||||
"""检查监听器是否处于活动状态"""
|
||||
normal_listener_active = self.listener and self.listener.is_alive()
|
||||
hotkey_listener_active = self.hotkey_listener and self.hotkey_listener.is_alive()
|
||||
active = self.is_running and (normal_listener_active or hotkey_listener_active)
|
||||
|
||||
logging.debug(f"键盘监听器状态: is_running={self.is_running}, normal_active={normal_listener_active}, hotkey_active={hotkey_listener_active}, combined_active={active}")
|
||||
return active
|
||||
|
||||
def trigger_test_event(self):
|
||||
"""测试方法:手动触发PageUp回调"""
|
||||
page_up_callback = self.callbacks.get('Key.page_up') or self.callbacks.get('<page_up>')
|
||||
|
||||
if page_up_callback:
|
||||
logging.info("手动触发 PageUp 回调进行测试")
|
||||
print("[测试] 手动触发PageUp回调")
|
||||
try:
|
||||
page_up_callback()
|
||||
return True
|
||||
except Exception as e:
|
||||
logging.error(f"测试触发回调时出错: {e}", exc_info=True)
|
||||
else:
|
||||
logging.error("未找到用于测试的 PageUp 回调 (未注册 Key.page_up 或 <page_up>)")
|
||||
print("[测试] 未找到PageUp回调")
|
||||
return False
|
||||
|
||||
def join(self, timeout=None):
|
||||
"""等待键盘监听线程结束
|
||||
|
||||
Args:
|
||||
timeout: 超时时间,如果为None则一直等待
|
||||
"""
|
||||
logging.info("等待键盘监听线程结束...")
|
||||
try:
|
||||
if self.listener and self.listener.is_alive():
|
||||
self.listener.join(timeout)
|
||||
logging.info("常规键盘监听线程已结束或超时")
|
||||
|
||||
if self.hotkey_listener and self.hotkey_listener.is_alive():
|
||||
self.hotkey_listener.join(timeout)
|
||||
logging.info("热键监听线程已结束或超时")
|
||||
except Exception as e:
|
||||
logging.error(f"等待键盘监听线程结束时出错: {e}", exc_info=True)
|
||||
|
||||
logging.info("键盘监听线程join操作完成")
|
||||
@ -89,6 +89,23 @@ class PalletTypeManager:
|
||||
self.reload_pallet_types()
|
||||
return result
|
||||
|
||||
def add_pallet_type(self, data, username='system'):
|
||||
"""添加托盘类型(create_pallet_type的别名)
|
||||
|
||||
Args:
|
||||
data: 托盘类型数据
|
||||
username: 操作用户
|
||||
|
||||
Returns:
|
||||
bool: 添加是否成功
|
||||
"""
|
||||
try:
|
||||
result = self.create_pallet_type(data, username)
|
||||
return result is not None
|
||||
except Exception as e:
|
||||
logging.error(f"添加托盘类型失败: {str(e)}")
|
||||
return False
|
||||
|
||||
def update_pallet_type(self, pallet_type_id, data, username='system'):
|
||||
"""更新托盘类型
|
||||
|
||||
@ -135,6 +152,37 @@ class PalletTypeManager:
|
||||
if result:
|
||||
self.reload_pallet_types()
|
||||
return result
|
||||
|
||||
def update_pallet_type_status(self, pallet_type_id, enabled, username='system'):
|
||||
"""更新托盘类型状态(toggle_pallet_type的别名)
|
||||
|
||||
Args:
|
||||
pallet_type_id: 托盘类型ID
|
||||
enabled: 是否启用
|
||||
username: 操作用户
|
||||
|
||||
Returns:
|
||||
bool: 操作是否成功
|
||||
"""
|
||||
return self.toggle_pallet_type(pallet_type_id, enabled, username)
|
||||
|
||||
def save_all_pallet_types(self, username='system'):
|
||||
"""保存所有托盘类型(实际上是一个空操作,因为每次修改都会立即保存)
|
||||
|
||||
Args:
|
||||
username: 操作用户
|
||||
|
||||
Returns:
|
||||
bool: 操作是否成功
|
||||
"""
|
||||
try:
|
||||
# 实际上每次修改都会立即保存,这里只是为了提供一个统一的接口
|
||||
logging.info("保存所有托盘类型(空操作)")
|
||||
return True
|
||||
except Exception as e:
|
||||
logging.error(f"保存所有托盘类型失败: {str(e)}")
|
||||
return False
|
||||
|
||||
def get_pallet_type_by_type(self, pallet_type):
|
||||
"""根据托盘类型值获取托盘数据
|
||||
|
||||
@ -150,6 +198,7 @@ class PalletTypeManager:
|
||||
return result
|
||||
else:
|
||||
return None
|
||||
|
||||
def get_pallet_type_by_pallet_id(self, pallet_id):
|
||||
"""根据托盘号获取托盘类型
|
||||
|
||||
|
||||
1140
utils/serial_manager.py
Normal file
1140
utils/serial_manager.py
Normal file
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@ -8,11 +8,11 @@ from camera.CameraParams_const import *
|
||||
sys.path.append(os.path.join(os.path.dirname(os.path.dirname(__file__)), "camera"))
|
||||
|
||||
try:
|
||||
from PySide6.QtWidgets import QWidget, QMessageBox
|
||||
from PySide6.QtWidgets import QMessageBox
|
||||
from PySide6.QtCore import Qt, Signal
|
||||
USE_PYSIDE6 = True
|
||||
except ImportError:
|
||||
from PyQt5.QtWidgets import QWidget, QMessageBox
|
||||
from PyQt5.QtWidgets import QMessageBox
|
||||
from PyQt5.QtCore import Qt
|
||||
from PyQt5.QtCore import pyqtSignal as Signal
|
||||
USE_PYSIDE6 = False
|
||||
@ -35,6 +35,7 @@ class CameraSettingsWidget(SettingsUI):
|
||||
signal_camera_connection = Signal(bool, str) # 相机连接状态信号 (是否连接, 错误消息)
|
||||
signal_camera_params_changed = Signal(float, float, float) # 相机参数变化信号 (曝光, 增益, 帧率)
|
||||
signal_camera_error = Signal(str) # 相机错误信号
|
||||
settings_changed = Signal() # 设置变更信号,与 SettingsWindow 兼容
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
@ -305,14 +306,26 @@ class CameraSettingsWidget(SettingsUI):
|
||||
QMessageBox.warning(self, "错误", "相机参数设置失败!")
|
||||
|
||||
def save_camera_params(self):
|
||||
"""保存相机参数"""
|
||||
"""保存相机参数到配置文件"""
|
||||
if not self.camera_manager.isOpen:
|
||||
QMessageBox.warning(self, "错误", "请先打开相机!")
|
||||
return
|
||||
|
||||
# 实现保存参数到配置文件的功能
|
||||
# TODO: 待实现
|
||||
QMessageBox.information(self, "提示", "参数保存功能尚未实现")
|
||||
# 获取当前参数
|
||||
exposure = self.exposure_slider.value()
|
||||
gain = self.gain_slider.value()
|
||||
frame_rate = self.framerate_slider.value()
|
||||
|
||||
# 保存到配置文件
|
||||
success = self.camera_manager.save_params_to_config(exposure, gain, frame_rate)
|
||||
|
||||
if success:
|
||||
QMessageBox.information(self, "成功", "相机参数已保存到配置文件")
|
||||
self.settings_changed.emit() # 发送设置变更信号
|
||||
logging.info(f"相机参数已保存: 曝光={exposure}μs, 增益={gain}dB, 帧率={frame_rate}fps")
|
||||
else:
|
||||
QMessageBox.critical(self, "错误", "保存相机参数失败")
|
||||
logging.error("保存相机参数失败")
|
||||
|
||||
def closeEvent(self, event):
|
||||
"""窗口关闭事件"""
|
||||
|
||||
@ -13,6 +13,7 @@ class InspectionSettingsWidget(InspectionSettingsUI):
|
||||
|
||||
# 定义信号
|
||||
signal_configs_changed = Signal() # 配置变更信号
|
||||
settings_changed = Signal() # 设置变更信号,与 SettingsWindow 兼容
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
@ -343,42 +344,25 @@ class InspectionSettingsWidget(InspectionSettingsUI):
|
||||
if not self.validate_form():
|
||||
return
|
||||
|
||||
# 设置表单禁用,避免保存过程中的错误操作
|
||||
self.set_form_enabled(False)
|
||||
|
||||
# 收集更新数据
|
||||
for i in range(6):
|
||||
position = i + 1
|
||||
config_id = self.config_ids[i]
|
||||
|
||||
# 获取表单数据
|
||||
data = self.get_form_data(position)
|
||||
configs = []
|
||||
for position in range(1, 7):
|
||||
group = self.config_groups[position - 1]
|
||||
if group.isChecked():
|
||||
config = self.get_form_data(position)
|
||||
configs.append(config)
|
||||
|
||||
# 检查配置ID是否存在
|
||||
if config_id is not None:
|
||||
# 更新已有配置
|
||||
result = self.inspection_manager.update_config(config_id, data)
|
||||
if not result:
|
||||
raise Exception(f"更新检验项目 {position} 失败")
|
||||
else:
|
||||
# 新建配置(不应该进入这个分支,因为默认已经创建了6个配置)
|
||||
logging.warning(f"检验项目 {position} 不存在,需要在初始化时创建")
|
||||
# 保存配置
|
||||
success = self.inspection_manager.save_configs(configs)
|
||||
|
||||
# 重新加载配置
|
||||
self.inspection_manager.reload_configs()
|
||||
|
||||
# 恢复表单可用
|
||||
self.set_form_enabled(True)
|
||||
|
||||
# 显示成功消息
|
||||
QMessageBox.information(self, "保存成功", "检验配置已保存成功!")
|
||||
|
||||
# 发送配置变更信号
|
||||
if success:
|
||||
QMessageBox.information(self, "成功", "检验配置已保存")
|
||||
self.signal_configs_changed.emit()
|
||||
|
||||
logging.info("已保存检验配置")
|
||||
self.settings_changed.emit() # 发送设置变更信号
|
||||
logging.info("检验配置已保存")
|
||||
else:
|
||||
QMessageBox.critical(self, "错误", "保存检验配置失败")
|
||||
logging.error("保存检验配置失败")
|
||||
except Exception as e:
|
||||
logging.error(f"保存检验配置失败: {str(e)}")
|
||||
QMessageBox.critical(self, "错误", f"保存检验配置失败: {str(e)}")
|
||||
# 恢复表单可用
|
||||
self.set_form_enabled(True)
|
||||
@ -38,7 +38,8 @@ from widgets.camera_settings_widget import CameraSettingsWidget
|
||||
from utils.inspection_config_manager import InspectionConfigManager
|
||||
# 导入托盘类型管理器
|
||||
from utils.pallet_type_manager import PalletTypeManager
|
||||
|
||||
# 导入串口管理
|
||||
from utils.serial_manager import SerialManager
|
||||
|
||||
class MainWindow(MainWindowUI):
|
||||
"""主窗口"""
|
||||
@ -159,6 +160,9 @@ class MainWindow(MainWindowUI):
|
||||
self.statusBar().addPermanentWidget(QLabel(" "))
|
||||
logging.info(f"主窗口已创建,用户: {user_name}")
|
||||
|
||||
# 初始化串口管理器
|
||||
self.serial_manager = SerialManager()
|
||||
|
||||
def add_pallet_type_selectors(self):
|
||||
"""添加托盘类型选择下拉框"""
|
||||
# 创建上料托盘类型选择下拉框
|
||||
@ -288,15 +292,29 @@ class MainWindow(MainWindowUI):
|
||||
|
||||
def show_settings_page(self):
|
||||
"""显示设置页面"""
|
||||
# 延迟创建设置组件
|
||||
if not hasattr(self, 'settings_widget'):
|
||||
from widgets.settings_widget import SettingsWidget
|
||||
self.settings_widget = SettingsWidget(self)
|
||||
self.stacked_widget.addWidget(self.settings_widget)
|
||||
# 创建设置窗口
|
||||
if not hasattr(self, 'settings_window'):
|
||||
from widgets.settings_window import SettingsWindow
|
||||
self.settings_window = SettingsWindow(self)
|
||||
# 连接设置改变信号
|
||||
self.settings_window.settings_changed.connect(self.on_settings_changed)
|
||||
|
||||
# 切换到设置页面
|
||||
self.stacked_widget.setCurrentWidget(self.settings_widget)
|
||||
logging.info("显示设置页面")
|
||||
# 显示设置窗口
|
||||
self.settings_window.show()
|
||||
logging.info("显示设置窗口")
|
||||
|
||||
def on_settings_changed(self):
|
||||
"""设置改变时触发"""
|
||||
# 重新加载配置
|
||||
from utils.config_loader import ConfigLoader
|
||||
config_loader = ConfigLoader.get_instance()
|
||||
config_loader.load_config()
|
||||
self.config = self.load_config() # 重新加载配置到 self.config
|
||||
|
||||
# 更新串口管理器配置
|
||||
self.serial_manager.reload_config()
|
||||
|
||||
logging.info("设置已更新,重新加载配置")
|
||||
|
||||
def handle_input(self):
|
||||
"""处理上料按钮点击事件"""
|
||||
@ -498,6 +516,9 @@ class MainWindow(MainWindowUI):
|
||||
# 启动Modbus监控
|
||||
self.setup_modbus_monitor()
|
||||
|
||||
# 启动串口监听
|
||||
self.serial_manager.auto_open_configured_ports()
|
||||
|
||||
success0 = modbus.write_register_until_success(client, 0, int(stow_num))
|
||||
success1 = modbus.write_register_until_success(client, 1, int(pallet_type))
|
||||
success2 = modbus.write_register_until_success(client, 2, 1)
|
||||
@ -515,6 +536,7 @@ class MainWindow(MainWindowUI):
|
||||
finally:
|
||||
modbus.close_client(client)
|
||||
|
||||
|
||||
def handle_stop(self):
|
||||
"""处理停止按钮点击事件,并关闭 modbus 监控"""
|
||||
modbus = ModbusUtils()
|
||||
@ -534,6 +556,9 @@ class MainWindow(MainWindowUI):
|
||||
if hasattr(self, 'modbus_monitor'):
|
||||
logging.info("停止Modbus监控")
|
||||
self.modbus_monitor.stop()
|
||||
# 停止串口监听
|
||||
self.serial_manager.stop_keyboard_listener()
|
||||
self.serial_manager.close_all_ports()
|
||||
|
||||
def handle_camera_status(self, is_connected, message):
|
||||
"""处理相机状态变化"""
|
||||
@ -579,9 +604,14 @@ class MainWindow(MainWindowUI):
|
||||
# 停止相机显示
|
||||
self.camera_display.stop_display()
|
||||
|
||||
# 停止串口监听
|
||||
self.serial_manager.stop_keyboard_listener()
|
||||
self.serial_manager.close_all_ports()
|
||||
|
||||
# 接受关闭事件
|
||||
event.accept()
|
||||
|
||||
|
||||
def handle_order_enter(self):
|
||||
"""处理工程号输入框按下回车事件"""
|
||||
logging.info("工程号输入框按下回车事件")
|
||||
@ -1729,8 +1759,14 @@ class MainWindow(MainWindowUI):
|
||||
"""处理NG信号, 删除当前在处理的数据(也就是第一条数据)"""
|
||||
if ng == 1:
|
||||
# 获取当前选中的行或第一个数据行,并删除
|
||||
try:
|
||||
order_id = self.process_table.item(2, 1).text().strip()
|
||||
tray_id = self.tray_edit.currentText()
|
||||
except Exception as e:
|
||||
logging.error(f"处理NG信号时发生错误: {str(e)}")
|
||||
order_id = ""
|
||||
tray_id = ""
|
||||
|
||||
self.inspection_manager.delete_inspection_data(order_id, tray_id)
|
||||
# 触发重新查询,更新数据
|
||||
self.load_finished_inspection_data()
|
||||
|
||||
@ -9,6 +9,7 @@ class PalletTypeSettingsWidget(PalletTypeSettingsUI):
|
||||
|
||||
# 定义信号
|
||||
signal_pallet_types_changed = Signal()
|
||||
settings_changed = Signal() # 设置变更信号,与 SettingsWindow 兼容
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
@ -29,22 +30,6 @@ class PalletTypeSettingsWidget(PalletTypeSettingsUI):
|
||||
self.save_button.clicked.connect(self.save_all_pallet_types)
|
||||
self.reset_button.clicked.connect(self.load_pallet_types)
|
||||
|
||||
# 上料类型表格和按钮
|
||||
input_table = self.input_widget.findChild(QTableWidget, "input_table")
|
||||
input_table.itemSelectionChanged.connect(lambda: self.handle_table_selection("input"))
|
||||
|
||||
input_add_button = self.input_widget.findChild(QPushButton, "input_add_button")
|
||||
input_add_button.clicked.connect(lambda: self.add_pallet_type("input"))
|
||||
|
||||
input_update_button = self.input_widget.findChild(QPushButton, "input_update_button")
|
||||
input_update_button.clicked.connect(lambda: self.update_pallet_type("input"))
|
||||
|
||||
input_delete_button = self.input_widget.findChild(QPushButton, "input_delete_button")
|
||||
input_delete_button.clicked.connect(lambda: self.delete_pallet_type("input"))
|
||||
|
||||
input_cancel_button = self.input_widget.findChild(QPushButton, "input_cancel_button")
|
||||
input_cancel_button.clicked.connect(lambda: self.cancel_edit("input"))
|
||||
|
||||
# 下料类型表格和按钮
|
||||
output_table = self.output_widget.findChild(QTableWidget, "output_table")
|
||||
output_table.itemSelectionChanged.connect(lambda: self.handle_table_selection("output"))
|
||||
@ -67,9 +52,6 @@ class PalletTypeSettingsWidget(PalletTypeSettingsUI):
|
||||
# 重新加载数据
|
||||
self.pallet_type_manager.reload_pallet_types()
|
||||
|
||||
# 加载上料类型
|
||||
self.load_operation_pallet_types("input")
|
||||
|
||||
# 加载下料类型
|
||||
self.load_operation_pallet_types("output")
|
||||
|
||||
@ -82,7 +64,7 @@ class PalletTypeSettingsWidget(PalletTypeSettingsUI):
|
||||
"""加载指定操作类型的托盘类型数据
|
||||
|
||||
Args:
|
||||
operation_type: 操作类型 (input/output)
|
||||
operation_type: 操作类型 (output)
|
||||
"""
|
||||
# 获取表格
|
||||
table = self.get_table_by_operation_type(operation_type)
|
||||
@ -126,14 +108,12 @@ class PalletTypeSettingsWidget(PalletTypeSettingsUI):
|
||||
"""根据操作类型获取表格
|
||||
|
||||
Args:
|
||||
operation_type: 操作类型 (input/output)
|
||||
operation_type: 操作类型 (output)
|
||||
|
||||
Returns:
|
||||
QTableWidget: 表格部件
|
||||
"""
|
||||
if operation_type == "input":
|
||||
return self.input_widget.findChild(QTableWidget, "input_table")
|
||||
elif operation_type == "output":
|
||||
if operation_type == "output":
|
||||
return self.output_widget.findChild(QTableWidget, "output_table")
|
||||
return None
|
||||
|
||||
@ -141,12 +121,12 @@ class PalletTypeSettingsWidget(PalletTypeSettingsUI):
|
||||
"""获取表单值
|
||||
|
||||
Args:
|
||||
operation_type: 操作类型 (input/output)
|
||||
operation_type: 操作类型 (output)
|
||||
|
||||
Returns:
|
||||
dict: 表单值
|
||||
"""
|
||||
widget = self.input_widget if operation_type == "input" else self.output_widget
|
||||
widget = self.output_widget
|
||||
|
||||
type_name_input = widget.findChild(QLineEdit, f"{operation_type}_type_name_input")
|
||||
desc_input = widget.findChild(QLineEdit, f"{operation_type}_desc_input")
|
||||
@ -165,10 +145,10 @@ class PalletTypeSettingsWidget(PalletTypeSettingsUI):
|
||||
"""设置表单值
|
||||
|
||||
Args:
|
||||
operation_type: 操作类型 (input/output)
|
||||
operation_type: 操作类型 (output)
|
||||
values: 表单值
|
||||
"""
|
||||
widget = self.input_widget if operation_type == "input" else self.output_widget
|
||||
widget = self.output_widget
|
||||
|
||||
type_name_input = widget.findChild(QLineEdit, f"{operation_type}_type_name_input")
|
||||
desc_input = widget.findChild(QLineEdit, f"{operation_type}_desc_input")
|
||||
@ -184,9 +164,9 @@ class PalletTypeSettingsWidget(PalletTypeSettingsUI):
|
||||
"""重置表单
|
||||
|
||||
Args:
|
||||
operation_type: 操作类型 (input/output)
|
||||
operation_type: 操作类型 (output)
|
||||
"""
|
||||
widget = self.input_widget if operation_type == "input" else self.output_widget
|
||||
widget = self.output_widget
|
||||
|
||||
# 重置表单值
|
||||
self.set_form_values(operation_type, {
|
||||
@ -199,35 +179,31 @@ class PalletTypeSettingsWidget(PalletTypeSettingsUI):
|
||||
# 重置当前编辑ID
|
||||
widget.setProperty("current_edit_id", -1)
|
||||
|
||||
# 重置按钮状态
|
||||
add_button = widget.findChild(QPushButton, f"{operation_type}_add_button")
|
||||
update_button = widget.findChild(QPushButton, f"{operation_type}_update_button")
|
||||
delete_button = widget.findChild(QPushButton, f"{operation_type}_delete_button")
|
||||
cancel_button = widget.findChild(QPushButton, f"{operation_type}_cancel_button")
|
||||
|
||||
add_button.setEnabled(True)
|
||||
update_button.setEnabled(False)
|
||||
delete_button.setEnabled(False)
|
||||
cancel_button.setEnabled(False)
|
||||
|
||||
def handle_table_selection(self, operation_type):
|
||||
"""处理表格选择事件
|
||||
|
||||
Args:
|
||||
operation_type: 操作类型 (input/output)
|
||||
operation_type: 操作类型 (output)
|
||||
"""
|
||||
# 获取表格
|
||||
table = self.get_table_by_operation_type(operation_type)
|
||||
if not table:
|
||||
return
|
||||
|
||||
# 获取选中行
|
||||
selected_rows = table.selectionModel().selectedRows()
|
||||
if not selected_rows:
|
||||
selected_items = table.selectedItems()
|
||||
if not selected_items:
|
||||
return
|
||||
|
||||
# 获取选中行数据
|
||||
row = selected_rows[0].row()
|
||||
pallet_type_id = table.item(row, 0).data(Qt.UserRole)
|
||||
# 获取行数据
|
||||
row = selected_items[0].row()
|
||||
|
||||
# 获取ID
|
||||
id_item = table.item(row, 0)
|
||||
if not id_item:
|
||||
return
|
||||
|
||||
pallet_type_id = id_item.data(Qt.UserRole)
|
||||
|
||||
# 获取托盘类型数据
|
||||
pallet_type = self.pallet_type_manager.get_pallet_type_by_id(pallet_type_id)
|
||||
@ -238,33 +214,22 @@ class PalletTypeSettingsWidget(PalletTypeSettingsUI):
|
||||
self.set_form_values(operation_type, pallet_type)
|
||||
|
||||
# 设置当前编辑ID
|
||||
widget = self.input_widget if operation_type == "input" else self.output_widget
|
||||
widget = self.output_widget
|
||||
widget.setProperty("current_edit_id", pallet_type_id)
|
||||
|
||||
# 设置按钮状态
|
||||
add_button = widget.findChild(QPushButton, f"{operation_type}_add_button")
|
||||
update_button = widget.findChild(QPushButton, f"{operation_type}_update_button")
|
||||
delete_button = widget.findChild(QPushButton, f"{operation_type}_delete_button")
|
||||
cancel_button = widget.findChild(QPushButton, f"{operation_type}_cancel_button")
|
||||
|
||||
add_button.setEnabled(False)
|
||||
update_button.setEnabled(True)
|
||||
delete_button.setEnabled(True)
|
||||
cancel_button.setEnabled(True)
|
||||
|
||||
def validate_form(self, operation_type):
|
||||
"""验证表单
|
||||
|
||||
Args:
|
||||
operation_type: 操作类型 (input/output)
|
||||
operation_type: 操作类型 (output)
|
||||
|
||||
Returns:
|
||||
bool: 表单是否有效
|
||||
bool: 验证是否通过
|
||||
"""
|
||||
values = self.get_form_values(operation_type)
|
||||
|
||||
if not values['type_name']:
|
||||
QMessageBox.warning(self, "警告", "托盘类型名称不能为空")
|
||||
QMessageBox.warning(self, "警告", "请输入类型名称")
|
||||
return False
|
||||
|
||||
return True
|
||||
@ -273,148 +238,172 @@ class PalletTypeSettingsWidget(PalletTypeSettingsUI):
|
||||
"""添加托盘类型
|
||||
|
||||
Args:
|
||||
operation_type: 操作类型 (input/output)
|
||||
operation_type: 操作类型 (output)
|
||||
"""
|
||||
# 验证表单
|
||||
if not self.validate_form(operation_type):
|
||||
return
|
||||
|
||||
try:
|
||||
# 获取表单值
|
||||
values = self.get_form_values(operation_type)
|
||||
|
||||
# 创建托盘类型
|
||||
result = self.pallet_type_manager.create_pallet_type(values)
|
||||
if result:
|
||||
logging.info(f"添加托盘类型成功: {values['type_name']}")
|
||||
# 添加托盘类型
|
||||
success = self.pallet_type_manager.add_pallet_type(values)
|
||||
|
||||
if success:
|
||||
# 重新加载数据
|
||||
self.load_operation_pallet_types(operation_type)
|
||||
|
||||
# 重置表单
|
||||
self.reset_form(operation_type)
|
||||
|
||||
# 发送信号
|
||||
self.signal_pallet_types_changed.emit()
|
||||
self.settings_changed.emit() # 发送设置变更信号
|
||||
|
||||
logging.info(f"已添加{operation_type}托盘类型: {values['type_name']}")
|
||||
else:
|
||||
QMessageBox.critical(self, "错误", "添加托盘类型失败")
|
||||
except Exception as e:
|
||||
logging.error(f"添加托盘类型失败: {str(e)}")
|
||||
QMessageBox.critical(self, "错误", f"添加托盘类型失败: {str(e)}")
|
||||
QMessageBox.critical(self, "错误", f"添加{operation_type}托盘类型失败")
|
||||
logging.error(f"添加{operation_type}托盘类型失败: {values['type_name']}")
|
||||
|
||||
def update_pallet_type(self, operation_type):
|
||||
"""更新托盘类型
|
||||
|
||||
Args:
|
||||
operation_type: 操作类型 (input/output)
|
||||
operation_type: 操作类型 (output)
|
||||
"""
|
||||
if not self.validate_form(operation_type):
|
||||
return
|
||||
|
||||
try:
|
||||
# 获取当前编辑ID
|
||||
widget = self.input_widget if operation_type == "input" else self.output_widget
|
||||
widget = self.output_widget
|
||||
pallet_type_id = widget.property("current_edit_id")
|
||||
|
||||
if pallet_type_id < 0:
|
||||
QMessageBox.warning(self, "警告", "请先选择要编辑的托盘类型")
|
||||
return
|
||||
|
||||
# 验证表单
|
||||
if not self.validate_form(operation_type):
|
||||
return
|
||||
|
||||
# 获取表单值
|
||||
values = self.get_form_values(operation_type)
|
||||
|
||||
# 更新托盘类型
|
||||
result = self.pallet_type_manager.update_pallet_type(pallet_type_id, values)
|
||||
if result:
|
||||
logging.info(f"更新托盘类型成功: {values['type_name']}")
|
||||
success = self.pallet_type_manager.update_pallet_type(pallet_type_id, values)
|
||||
|
||||
if success:
|
||||
# 重新加载数据
|
||||
self.load_operation_pallet_types(operation_type)
|
||||
|
||||
# 重置表单
|
||||
self.reset_form(operation_type)
|
||||
|
||||
# 发送信号
|
||||
self.signal_pallet_types_changed.emit()
|
||||
self.settings_changed.emit() # 发送设置变更信号
|
||||
|
||||
logging.info(f"已更新{operation_type}托盘类型: {values['type_name']}")
|
||||
else:
|
||||
QMessageBox.critical(self, "错误", "更新托盘类型失败")
|
||||
except Exception as e:
|
||||
logging.error(f"更新托盘类型失败: {str(e)}")
|
||||
QMessageBox.critical(self, "错误", f"更新托盘类型失败: {str(e)}")
|
||||
QMessageBox.critical(self, "错误", f"更新{operation_type}托盘类型失败")
|
||||
logging.error(f"更新{operation_type}托盘类型失败: {values['type_name']}")
|
||||
|
||||
def delete_pallet_type(self, operation_type):
|
||||
"""删除托盘类型
|
||||
|
||||
Args:
|
||||
operation_type: 操作类型 (input/output)
|
||||
operation_type: 操作类型 (output)
|
||||
"""
|
||||
try:
|
||||
# 获取当前编辑ID
|
||||
widget = self.input_widget if operation_type == "input" else self.output_widget
|
||||
pallet_type_id = widget.property("current_edit_id")
|
||||
if pallet_type_id < 0:
|
||||
# 获取选中行
|
||||
table = self.get_table_by_operation_type(operation_type)
|
||||
if not table:
|
||||
return
|
||||
|
||||
selected_items = table.selectedItems()
|
||||
if not selected_items:
|
||||
QMessageBox.warning(self, "警告", "请先选择要删除的托盘类型")
|
||||
return
|
||||
|
||||
# 确认删除
|
||||
pallet_type = self.pallet_type_manager.get_pallet_type_by_id(pallet_type_id)
|
||||
if not pallet_type:
|
||||
QMessageBox.warning(self, "警告", "找不到要删除的托盘类型")
|
||||
# 获取托盘类型ID
|
||||
row = selected_items[0].row()
|
||||
id_item = table.item(row, 0)
|
||||
if not id_item:
|
||||
return
|
||||
|
||||
reply = QMessageBox.question(self, "确认删除",
|
||||
f"确定要删除托盘类型 '{pallet_type['type_name']}' 吗?",
|
||||
pallet_type_id = id_item.data(Qt.UserRole)
|
||||
type_name = id_item.text()
|
||||
|
||||
# 确认删除
|
||||
reply = QMessageBox.question(self, "确认删除", f"确定要删除{operation_type}托盘类型 [{type_name}] 吗?",
|
||||
QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
|
||||
|
||||
if reply != QMessageBox.Yes:
|
||||
return
|
||||
|
||||
# 删除托盘类型
|
||||
result = self.pallet_type_manager.delete_pallet_type(pallet_type_id)
|
||||
if result:
|
||||
logging.info(f"删除托盘类型成功: {pallet_type['type_name']}")
|
||||
success = self.pallet_type_manager.delete_pallet_type(pallet_type_id)
|
||||
|
||||
if success:
|
||||
# 重新加载数据
|
||||
self.load_operation_pallet_types(operation_type)
|
||||
|
||||
# 重置表单
|
||||
self.reset_form(operation_type)
|
||||
|
||||
# 发送信号
|
||||
self.signal_pallet_types_changed.emit()
|
||||
self.settings_changed.emit() # 发送设置变更信号
|
||||
|
||||
logging.info(f"已删除{operation_type}托盘类型: {type_name}")
|
||||
else:
|
||||
QMessageBox.critical(self, "错误", "删除托盘类型失败")
|
||||
except Exception as e:
|
||||
logging.error(f"删除托盘类型失败: {str(e)}")
|
||||
QMessageBox.critical(self, "错误", f"删除托盘类型失败: {str(e)}")
|
||||
QMessageBox.critical(self, "错误", f"删除{operation_type}托盘类型失败")
|
||||
logging.error(f"删除{operation_type}托盘类型失败: {type_name}")
|
||||
|
||||
def cancel_edit(self, operation_type):
|
||||
"""取消编辑
|
||||
|
||||
Args:
|
||||
operation_type: 操作类型 (input/output)
|
||||
operation_type: 操作类型 (output)
|
||||
"""
|
||||
# 重置表单
|
||||
self.reset_form(operation_type)
|
||||
|
||||
# 取消表格选择
|
||||
# 清除表格选择
|
||||
table = self.get_table_by_operation_type(operation_type)
|
||||
if table:
|
||||
table.clearSelection()
|
||||
|
||||
def toggle_pallet_type(self, pallet_type_id, enabled):
|
||||
"""启用或禁用托盘类型
|
||||
"""切换托盘类型启用状态
|
||||
|
||||
Args:
|
||||
pallet_type_id: 托盘类型ID
|
||||
enabled: 是否启用
|
||||
"""
|
||||
try:
|
||||
# 更新启用状态
|
||||
result = self.pallet_type_manager.toggle_pallet_type(pallet_type_id, enabled)
|
||||
if result:
|
||||
logging.info(f"更新托盘类型启用状态成功: {pallet_type_id} -> {enabled}")
|
||||
# 更新托盘类型启用状态
|
||||
success = self.pallet_type_manager.update_pallet_type_status(pallet_type_id, enabled)
|
||||
|
||||
if success:
|
||||
# 发送信号
|
||||
self.signal_pallet_types_changed.emit()
|
||||
self.settings_changed.emit() # 发送设置变更信号
|
||||
|
||||
logging.info(f"已{('启用' if enabled else '禁用')}托盘类型: {pallet_type_id}")
|
||||
else:
|
||||
QMessageBox.critical(self, "错误", "更新托盘类型启用状态失败")
|
||||
except Exception as e:
|
||||
logging.error(f"更新托盘类型启用状态失败: {str(e)}")
|
||||
QMessageBox.critical(self, "错误", f"更新托盘类型启用状态失败: {str(e)}")
|
||||
QMessageBox.critical(self, "错误", f"更新托盘类型状态失败")
|
||||
logging.error(f"更新托盘类型状态失败: {pallet_type_id}")
|
||||
|
||||
def save_all_pallet_types(self):
|
||||
"""保存所有托盘类型"""
|
||||
# 目前没有需要批量保存的操作,所有操作都是实时保存的
|
||||
QMessageBox.information(self, "提示", "托盘类型配置已保存")
|
||||
# 保存所有托盘类型
|
||||
success = self.pallet_type_manager.save_all_pallet_types()
|
||||
|
||||
if success:
|
||||
QMessageBox.information(self, "成功", "所有托盘类型已保存")
|
||||
|
||||
# 发送信号
|
||||
self.signal_pallet_types_changed.emit()
|
||||
self.settings_changed.emit() # 发送设置变更信号
|
||||
|
||||
logging.info("已保存所有托盘类型")
|
||||
else:
|
||||
QMessageBox.critical(self, "错误", "保存托盘类型失败")
|
||||
logging.error("保存托盘类型失败")
|
||||
324
widgets/serial_settings_widget.py
Normal file
324
widgets/serial_settings_widget.py
Normal file
@ -0,0 +1,324 @@
|
||||
import os
|
||||
import json
|
||||
import logging
|
||||
import serial.tools.list_ports
|
||||
import time
|
||||
from PySide6.QtWidgets import QMessageBox
|
||||
from ui.serial_settings_ui import SerialSettingsUI
|
||||
from utils.config_loader import ConfigLoader
|
||||
from utils.serial_manager import SerialManager
|
||||
|
||||
class SerialSettingsWidget(SerialSettingsUI):
|
||||
"""串口设置组件"""
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
self.config = ConfigLoader.get_instance()
|
||||
self.serial_manager = SerialManager()
|
||||
|
||||
# 连接信号
|
||||
self.mdz_refresh_btn.clicked.connect(self.refresh_ports)
|
||||
self.cz_refresh_btn.clicked.connect(self.refresh_ports)
|
||||
self.test_mdz_btn.clicked.connect(self.test_mdz_port)
|
||||
self.test_cz_btn.clicked.connect(self.test_cz_port)
|
||||
self.save_btn.clicked.connect(self.save_settings)
|
||||
|
||||
# 初始化串口列表
|
||||
self.refresh_ports()
|
||||
|
||||
# 加载设置
|
||||
self.load_settings()
|
||||
|
||||
def refresh_ports(self):
|
||||
"""刷新串口列表"""
|
||||
try:
|
||||
# 保存当前选择
|
||||
current_mdz_port = self.mdz_port_combo.currentData()
|
||||
current_cz_port = self.cz_port_combo.currentData()
|
||||
|
||||
# 清空列表
|
||||
self.mdz_port_combo.clear()
|
||||
self.cz_port_combo.clear()
|
||||
|
||||
# 获取可用串口
|
||||
ports = list(serial.tools.list_ports.comports())
|
||||
|
||||
for port in ports:
|
||||
port_name = port.device
|
||||
port_desc = f"{port_name} ({port.description})"
|
||||
self.mdz_port_combo.addItem(port_desc, port_name)
|
||||
self.cz_port_combo.addItem(port_desc, port_name)
|
||||
|
||||
# 恢复之前的选择
|
||||
if current_mdz_port:
|
||||
index = self.mdz_port_combo.findData(current_mdz_port)
|
||||
if index >= 0:
|
||||
self.mdz_port_combo.setCurrentIndex(index)
|
||||
|
||||
if current_cz_port:
|
||||
index = self.cz_port_combo.findData(current_cz_port)
|
||||
if index >= 0:
|
||||
self.cz_port_combo.setCurrentIndex(index)
|
||||
|
||||
logging.info(f"已刷新串口列表,找到 {len(ports)} 个串口")
|
||||
except Exception as e:
|
||||
logging.error(f"刷新串口列表失败: {e}")
|
||||
QMessageBox.warning(self, "刷新失败", f"刷新串口列表失败: {e}")
|
||||
|
||||
def load_settings(self):
|
||||
"""加载设置"""
|
||||
try:
|
||||
# 加载全局设置
|
||||
enable_serial = self.config.get_value('app.features.enable_serial_ports', False)
|
||||
enable_keyboard = self.config.get_value('app.features.enable_keyboard_listener', False)
|
||||
|
||||
self.enable_serial_checkbox.setChecked(enable_serial)
|
||||
self.enable_keyboard_checkbox.setChecked(enable_keyboard)
|
||||
|
||||
# 加载米电阻设置
|
||||
mdz_config = self.config.get_config('mdz')
|
||||
if mdz_config:
|
||||
# 设置串口
|
||||
mdz_port = mdz_config.get('ser', '')
|
||||
index = self.mdz_port_combo.findData(mdz_port)
|
||||
if index >= 0:
|
||||
self.mdz_port_combo.setCurrentIndex(index)
|
||||
|
||||
# 设置波特率
|
||||
mdz_baud = str(mdz_config.get('port', '9600'))
|
||||
index = self.mdz_baud_combo.findText(mdz_baud)
|
||||
if index >= 0:
|
||||
self.mdz_baud_combo.setCurrentIndex(index)
|
||||
|
||||
# 设置数据位
|
||||
mdz_data_bits = str(mdz_config.get('data_bits', '8'))
|
||||
index = self.mdz_data_bits_combo.findText(mdz_data_bits)
|
||||
if index >= 0:
|
||||
self.mdz_data_bits_combo.setCurrentIndex(index)
|
||||
|
||||
# 设置停止位
|
||||
mdz_stop_bits = str(mdz_config.get('stop_bits', '1'))
|
||||
index = self.mdz_stop_bits_combo.findText(mdz_stop_bits)
|
||||
if index >= 0:
|
||||
self.mdz_stop_bits_combo.setCurrentIndex(index)
|
||||
|
||||
# 设置校验位
|
||||
mdz_parity = mdz_config.get('parity', 'N')
|
||||
index = self.mdz_parity_combo.findData(mdz_parity)
|
||||
if index >= 0:
|
||||
self.mdz_parity_combo.setCurrentIndex(index)
|
||||
|
||||
# 设置查询指令
|
||||
mdz_query_cmd = mdz_config.get('query_cmd', '01 03 00 01 00 07 55 C8')
|
||||
self.mdz_query_cmd.setText(mdz_query_cmd)
|
||||
|
||||
# 设置查询间隔
|
||||
mdz_query_interval = mdz_config.get('query_interval', 5)
|
||||
self.mdz_query_interval.setValue(mdz_query_interval)
|
||||
|
||||
# 加载称重设置
|
||||
cz_config = self.config.get_config('cz')
|
||||
if cz_config:
|
||||
# 设置串口
|
||||
cz_port = cz_config.get('ser', '')
|
||||
index = self.cz_port_combo.findData(cz_port)
|
||||
if index >= 0:
|
||||
self.cz_port_combo.setCurrentIndex(index)
|
||||
|
||||
# 设置波特率
|
||||
cz_baud = str(cz_config.get('port', '9600'))
|
||||
index = self.cz_baud_combo.findText(cz_baud)
|
||||
if index >= 0:
|
||||
self.cz_baud_combo.setCurrentIndex(index)
|
||||
|
||||
# 设置数据位
|
||||
cz_data_bits = str(cz_config.get('data_bits', '8'))
|
||||
index = self.cz_data_bits_combo.findText(cz_data_bits)
|
||||
if index >= 0:
|
||||
self.cz_data_bits_combo.setCurrentIndex(index)
|
||||
|
||||
# 设置停止位
|
||||
cz_stop_bits = str(cz_config.get('stop_bits', '1'))
|
||||
index = self.cz_stop_bits_combo.findText(cz_stop_bits)
|
||||
if index >= 0:
|
||||
self.cz_stop_bits_combo.setCurrentIndex(index)
|
||||
|
||||
# 设置校验位
|
||||
cz_parity = cz_config.get('parity', 'N')
|
||||
index = self.cz_parity_combo.findData(cz_parity)
|
||||
if index >= 0:
|
||||
self.cz_parity_combo.setCurrentIndex(index)
|
||||
|
||||
# 设置稳定阈值
|
||||
cz_stable_threshold = cz_config.get('stable_threshold', 10)
|
||||
self.cz_stable_threshold.setValue(cz_stable_threshold)
|
||||
|
||||
logging.info("已加载串口设置")
|
||||
except Exception as e:
|
||||
logging.error(f"加载串口设置失败: {e}")
|
||||
QMessageBox.warning(self, "加载失败", f"加载串口设置失败: {e}")
|
||||
|
||||
def save_settings(self):
|
||||
"""保存设置"""
|
||||
try:
|
||||
# 保存全局设置
|
||||
enable_serial = self.enable_serial_checkbox.isChecked()
|
||||
enable_keyboard = self.enable_keyboard_checkbox.isChecked()
|
||||
|
||||
self.config.set_value('app.features.enable_serial_ports', enable_serial)
|
||||
self.config.set_value('app.features.enable_keyboard_listener', enable_keyboard)
|
||||
|
||||
# 保存米电阻设置
|
||||
mdz_config = {}
|
||||
mdz_config['ser'] = self.mdz_port_combo.currentData()
|
||||
mdz_config['port'] = int(self.mdz_baud_combo.currentText())
|
||||
mdz_config['data_bits'] = int(self.mdz_data_bits_combo.currentText())
|
||||
mdz_config['stop_bits'] = float(self.mdz_stop_bits_combo.currentText())
|
||||
mdz_config['parity'] = self.mdz_parity_combo.currentData()
|
||||
mdz_config['query_cmd'] = self.mdz_query_cmd.text()
|
||||
mdz_config['query_interval'] = self.mdz_query_interval.value()
|
||||
mdz_config['code'] = 'mdz'
|
||||
mdz_config['bit'] = 10
|
||||
mdz_config['timeout'] = 1
|
||||
|
||||
self.config.set_config('mdz', mdz_config)
|
||||
|
||||
# 保存称重设置
|
||||
cz_config = {}
|
||||
cz_config['ser'] = self.cz_port_combo.currentData()
|
||||
cz_config['port'] = int(self.cz_baud_combo.currentText())
|
||||
cz_config['data_bits'] = int(self.cz_data_bits_combo.currentText())
|
||||
cz_config['stop_bits'] = float(self.cz_stop_bits_combo.currentText())
|
||||
cz_config['parity'] = self.cz_parity_combo.currentData()
|
||||
cz_config['stable_threshold'] = self.cz_stable_threshold.value()
|
||||
cz_config['code'] = 'cz'
|
||||
cz_config['bit'] = 10
|
||||
cz_config['timeout'] = 1
|
||||
|
||||
self.config.set_config('cz', cz_config)
|
||||
|
||||
# 保存键盘设置
|
||||
keyboard_config = {}
|
||||
keyboard_config['enabled'] = enable_keyboard
|
||||
keyboard_config['trigger_key'] = 'Key.page_up'
|
||||
|
||||
self.config.set_config('keyboard', keyboard_config)
|
||||
|
||||
# 保存配置文件
|
||||
self.config.save_config()
|
||||
|
||||
# 重新加载串口管理器的配置
|
||||
self.serial_manager.reload_config()
|
||||
|
||||
# 根据键盘监听设置立即启动或停止键盘监听
|
||||
if enable_keyboard:
|
||||
logging.info("键盘监听已启用,正在启动键盘监听...")
|
||||
self.serial_manager.start_keyboard_listener()
|
||||
|
||||
# 添加测试触发,确认键盘监听是否正常工作
|
||||
if hasattr(self.serial_manager, 'keyboard_listener') and self.serial_manager.keyboard_listener:
|
||||
if self.serial_manager.keyboard_listener.is_active():
|
||||
logging.info("键盘监听已成功启动,尝试手动触发测试事件")
|
||||
self.serial_manager.keyboard_listener.trigger_test_event()
|
||||
else:
|
||||
logging.warning("键盘监听启动失败,未处于活动状态")
|
||||
else:
|
||||
logging.info("键盘监听已禁用,正在停止键盘监听...")
|
||||
self.serial_manager.stop_keyboard_listener()
|
||||
|
||||
# 发送设置改变信号
|
||||
self.settings_changed.emit()
|
||||
|
||||
logging.info("已保存串口设置")
|
||||
QMessageBox.information(self, "保存成功", "串口设置已保存")
|
||||
except Exception as e:
|
||||
logging.error(f"保存串口设置失败: {e}")
|
||||
QMessageBox.warning(self, "保存失败", f"保存串口设置失败: {e}")
|
||||
|
||||
def test_mdz_port(self):
|
||||
"""测试米电阻串口"""
|
||||
try:
|
||||
port = self.mdz_port_combo.currentData()
|
||||
baud = int(self.mdz_baud_combo.currentText())
|
||||
data_bits = int(self.mdz_data_bits_combo.currentText())
|
||||
stop_bits = float(self.mdz_stop_bits_combo.currentText())
|
||||
parity = self.mdz_parity_combo.currentData()
|
||||
|
||||
if not port:
|
||||
QMessageBox.warning(self, "测试失败", "请选择串口")
|
||||
return
|
||||
|
||||
# 关闭可能已经打开的串口
|
||||
if self.serial_manager.is_port_open(port):
|
||||
self.serial_manager.close_port(port)
|
||||
|
||||
# 尝试打开串口
|
||||
success = self.serial_manager.open_port(
|
||||
port, 'mdz', baud, data_bits, stop_bits, parity, 1.0
|
||||
)
|
||||
|
||||
if success:
|
||||
# 尝试发送查询指令
|
||||
query_cmd = self.mdz_query_cmd.text()
|
||||
if query_cmd:
|
||||
try:
|
||||
# 转换查询指令为字节
|
||||
query_bytes = bytes.fromhex(query_cmd.replace(' ', ''))
|
||||
|
||||
# 发送查询指令
|
||||
self.serial_manager.write_data(port, query_bytes)
|
||||
|
||||
# 等待一段时间
|
||||
time.sleep(0.5)
|
||||
|
||||
# 关闭串口
|
||||
self.serial_manager.close_port(port)
|
||||
|
||||
QMessageBox.information(self, "测试成功", f"米电阻串口 {port} 测试成功,已发送查询指令")
|
||||
except Exception as e:
|
||||
self.serial_manager.close_port(port)
|
||||
QMessageBox.warning(self, "测试失败", f"发送查询指令失败: {e}")
|
||||
else:
|
||||
self.serial_manager.close_port(port)
|
||||
QMessageBox.information(self, "测试成功", f"米电阻串口 {port} 打开成功,但未发送查询指令")
|
||||
else:
|
||||
QMessageBox.warning(self, "测试失败", f"无法打开米电阻串口 {port}")
|
||||
except Exception as e:
|
||||
logging.error(f"测试米电阻串口失败: {e}")
|
||||
QMessageBox.warning(self, "测试失败", f"测试米电阻串口失败: {e}")
|
||||
|
||||
def test_cz_port(self):
|
||||
"""测试线径串口"""
|
||||
try:
|
||||
port = self.cz_port_combo.currentData()
|
||||
baud = int(self.cz_baud_combo.currentText())
|
||||
data_bits = int(self.cz_data_bits_combo.currentText())
|
||||
stop_bits = float(self.cz_stop_bits_combo.currentText())
|
||||
parity = self.cz_parity_combo.currentData()
|
||||
|
||||
if not port:
|
||||
QMessageBox.warning(self, "测试失败", "请选择串口")
|
||||
return
|
||||
|
||||
# 关闭可能已经打开的串口
|
||||
if self.serial_manager.is_port_open(port):
|
||||
self.serial_manager.close_port(port)
|
||||
|
||||
# 尝试打开串口
|
||||
success = self.serial_manager.open_port(
|
||||
port, 'cz', baud, data_bits, stop_bits, parity, 1.0
|
||||
)
|
||||
|
||||
if success:
|
||||
# 等待一段时间
|
||||
time.sleep(2)
|
||||
|
||||
# 关闭串口
|
||||
self.serial_manager.close_port(port)
|
||||
|
||||
QMessageBox.information(self, "测试成功", f"线径串口 {port} 测试成功")
|
||||
else:
|
||||
QMessageBox.warning(self, "测试失败", f"无法打开线径串口 {port}")
|
||||
except Exception as e:
|
||||
logging.error(f"测试线径串口失败: {e}")
|
||||
QMessageBox.warning(self, "测试失败", f"测试线径串口失败: {e}")
|
||||
51
widgets/settings_window.py
Normal file
51
widgets/settings_window.py
Normal file
@ -0,0 +1,51 @@
|
||||
import logging
|
||||
from PySide6.QtCore import Signal
|
||||
from PySide6.QtWidgets import QDialog, QVBoxLayout, QDialogButtonBox
|
||||
from widgets.serial_settings_widget import SerialSettingsWidget
|
||||
from widgets.settings_widget import SettingsWidget
|
||||
|
||||
class SettingsWindow(QDialog):
|
||||
"""设置窗口,直接使用SettingsWidget中的标签页"""
|
||||
|
||||
# 定义信号
|
||||
settings_changed = Signal()
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
logging.info("正在初始化SettingsWindow")
|
||||
|
||||
# 设置窗口标题和大小
|
||||
self.setWindowTitle("系统设置")
|
||||
self.resize(900, 700)
|
||||
self.setModal(True)
|
||||
|
||||
# 创建主布局
|
||||
main_layout = QVBoxLayout(self)
|
||||
main_layout.setContentsMargins(10, 10, 10, 10)
|
||||
|
||||
# 创建设置部件
|
||||
self.settings_widget = SettingsWidget(self)
|
||||
main_layout.addWidget(self.settings_widget)
|
||||
|
||||
# 添加串口设置到标签页
|
||||
self.serial_settings = SerialSettingsWidget(self)
|
||||
self.settings_widget.tab_widget.addTab(self.serial_settings, "串口设置")
|
||||
|
||||
# 添加按钮
|
||||
self.button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
|
||||
self.button_box.accepted.connect(self.accept)
|
||||
self.button_box.rejected.connect(self.reject)
|
||||
main_layout.addWidget(self.button_box)
|
||||
|
||||
# 连接信号
|
||||
self.serial_settings.settings_changed.connect(self.settings_changed.emit)
|
||||
|
||||
logging.info("SettingsWindow初始化完成")
|
||||
|
||||
def accept(self):
|
||||
"""确认按钮处理,保存所有设置并发送设置变更信号"""
|
||||
# 通知设置已变更
|
||||
self.settings_changed.emit()
|
||||
|
||||
# 调用父类方法关闭对话框
|
||||
super().accept()
|
||||
Loading…
Reference in New Issue
Block a user