diff --git a/config/app_config.json b/config/app_config.json index c284556..d348eb8 100644 --- a/config/app_config.json +++ b/config/app_config.json @@ -3,7 +3,7 @@ "name": "腾智微丝产线包装系统", "version": "1.0.0", "features": { - "enable_serial_ports": false, + "enable_serial_ports": true, "enable_keyboard_listener": false, "enable_camera": false }, @@ -49,8 +49,8 @@ "default_framerate": 30 }, "modbus": { - "host": "192.168.2.88", - "port": "502" + "host": "localhost", + "port": "5020" }, "serial": { "keyboard": { @@ -78,6 +78,15 @@ "stable_threshold": 10, "stop_bits": 1, "timeout": 1 + }, + "scanner": { + "code": "scanner", + "data_bits": 8, + "parity": "N", + "port": "9600", + "ser": "COM3", + "stop_bits": 1, + "timeout": 1 } }, "electricity": { diff --git a/db/jtDB.db b/db/jtDB.db index e0c9969..f7cd1ac 100644 Binary files a/db/jtDB.db and b/db/jtDB.db differ diff --git a/from pymodbus.py b/from pymodbus.py index 1c44735..b5453bb 100644 --- a/from pymodbus.py +++ b/from pymodbus.py @@ -3,19 +3,19 @@ import time client = ModbusTcpClient('localhost', port=5020) client.connect() client.write_registers(address=11, values=[2248]) -client.write_registers(address=13, values=[0]) +client.write_registers(address=3, values=[0]) -client.write_registers(address=21, values=[0]) -time.sleep(2) -# client.write_registers(address=21, values=[1]) +# client.write_registers(address=20, values=[0]) +# time.sleep(2) +# client.write_registers(address=20, values=[1]) # client.write_registers(address=30, values=[25]) # client.write_registers(address=5, values=[16]) # 贴标完成 # client.write_registers(address=24, values=[1])s -client.write_registers(address=13, values=[1]) +# client.write_registers(address=13, values=[1]) -result = client.read_holding_registers(address=1, count=1) +result = client.read_holding_registers(address=3, count=1) print(result.registers[0],"123===") client.close() \ No newline at end of file diff --git a/ui/serial_settings_ui.py b/ui/serial_settings_ui.py index d3fa81c..efb6a21 100644 --- a/ui/serial_settings_ui.py +++ b/ui/serial_settings_ui.py @@ -28,7 +28,7 @@ class SerialSettingsUI(QWidget): enable_layout.addStretch() main_layout.addLayout(enable_layout) - # 创建串口设置组 + # # 创建串口设置组 serial_group = QGroupBox("串口设置") serial_layout = QGridLayout(serial_group) @@ -39,6 +39,7 @@ class SerialSettingsUI(QWidget): # 串口选择 mdz_port_layout = QHBoxLayout() self.mdz_port_combo = QComboBox() + self.mdz_port_combo.addItem("不使用", "") # 添加空选项 self.mdz_refresh_btn = QPushButton("刷新") mdz_port_layout.addWidget(self.mdz_port_combo) mdz_port_layout.addWidget(self.mdz_refresh_btn) @@ -85,6 +86,7 @@ class SerialSettingsUI(QWidget): # 串口选择 xj_port_layout = QHBoxLayout() self.xj_port_combo = QComboBox() + self.xj_port_combo.addItem("不使用", "") # 添加空选项 self.xj_refresh_btn = QPushButton("刷新") xj_port_layout.addWidget(self.xj_port_combo) xj_port_layout.addWidget(self.xj_refresh_btn) @@ -114,10 +116,47 @@ class SerialSettingsUI(QWidget): self.xj_parity_combo.addItem(parity[0], parity[1]) xj_layout.addRow("校验位:", self.xj_parity_combo) + # 扫码器串口设置 + scanner_group = QGroupBox("扫码器串口") + scanner_layout = QFormLayout(scanner_group) + + # 串口选择 + scanner_port_layout = QHBoxLayout() + self.scanner_port_combo = QComboBox() + self.scanner_port_combo.addItem("不使用", "") # 添加空选项 + self.scanner_refresh_btn = QPushButton("刷新") + scanner_port_layout.addWidget(self.scanner_port_combo) + scanner_port_layout.addWidget(self.scanner_refresh_btn) + scanner_layout.addRow("串口:", scanner_port_layout) + + # 波特率 + self.scanner_baud_combo = QComboBox() + for baud in ["1200", "2400", "4800", "9600", "19200", "38400", "57600", "115200"]: + self.scanner_baud_combo.addItem(baud) + scanner_layout.addRow("波特率:", self.scanner_baud_combo) + + # 数据位 + self.scanner_data_bits_combo = QComboBox() + for bits in ["5", "6", "7", "8"]: + self.scanner_data_bits_combo.addItem(bits) + scanner_layout.addRow("数据位:", self.scanner_data_bits_combo) + + # 停止位 + self.scanner_stop_bits_combo = QComboBox() + for bits in ["1", "1.5", "2"]: + self.scanner_stop_bits_combo.addItem(bits) + scanner_layout.addRow("停止位:", self.scanner_stop_bits_combo) + + # 校验位 + self.scanner_parity_combo = QComboBox() + for parity in [("无校验", "N"), ("奇校验", "O"), ("偶校验", "E")]: + self.scanner_parity_combo.addItem(parity[0], parity[1]) + scanner_layout.addRow("校验位:", self.scanner_parity_combo) # 将三个组添加到布局 serial_layout.addWidget(mdz_group, 0, 0) serial_layout.addWidget(xj_group, 0, 1) + serial_layout.addWidget(scanner_group, 1, 0) # 添加扫码器串口设置,放在第二行第一列 # 设置列伸缩因子,使两列等宽(比例1:1) serial_layout.setColumnStretch(0, 1) @@ -129,8 +168,10 @@ class SerialSettingsUI(QWidget): test_layout = QHBoxLayout() self.test_mdz_btn = QPushButton("测试米电阻串口") self.test_xj_btn = QPushButton("测试线径串口") + self.test_scanner_btn = QPushButton("测试扫码器串口") # 添加测试扫码器串口按钮 test_layout.addWidget(self.test_mdz_btn) test_layout.addWidget(self.test_xj_btn) + test_layout.addWidget(self.test_scanner_btn) # 添加到布局 test_layout.addStretch() main_layout.addLayout(test_layout) diff --git a/ui/settings_ui.py b/ui/settings_ui.py index 75db6d5..451cd96 100644 --- a/ui/settings_ui.py +++ b/ui/settings_ui.py @@ -486,23 +486,23 @@ class SettingsUI(QWidget): self.tab_widget.addTab(self.param_tab, "参数配置") def create_electricity_tab(self): - """创建电量监控选项卡""" - # 电量监控选项卡 + """创建电力监控选项卡""" + # 电力监控选项卡 self.electricity_tab = QWidget() self.electricity_layout = QVBoxLayout(self.electricity_tab) self.electricity_layout.setContentsMargins(0, 0, 0, 0) self.electricity_layout.setSpacing(0) # 添加一个临时提示标签,表示此处将由ElectricitySettingsUI替换 - self.electricity_placeholder = QLabel("正在加载电量监控设置...") + self.electricity_placeholder = QLabel("正在加载电力监控设置...") self.electricity_placeholder.setFont(self.normal_font) self.electricity_placeholder.setAlignment(Qt.AlignCenter) self.electricity_placeholder.setStyleSheet("color: #888888; padding: 20px;") self.electricity_layout.addWidget(self.electricity_placeholder) # 添加到选项卡 - self.tab_widget.addTab(self.electricity_tab, "电量监控") - + self.tab_widget.addTab(self.electricity_tab, "电力监控") + def create_pallet_type_tab(self): # 托盘类型设置选项卡 self.pallet_type_tab = QWidget() diff --git a/utils/serial_manager.py b/utils/serial_manager.py index 13744fe..0f01dce 100644 --- a/utils/serial_manager.py +++ b/utils/serial_manager.py @@ -54,7 +54,8 @@ class SerialManager: self.data = { 'mdz': 0, 'xj': 0, # 添加线径数据 - 'cz': 0 + 'cz': 0, + 'scanner': '' # 添加扫码器数据 } # 是否自动查询米电阻数据,默认为False,只通过PageUp键触发 @@ -102,6 +103,7 @@ class SerialManager: self.mdz_config = self.config.get_config('mdz') self.cz_config = self.config.get_config('cz') self.xj_config = self.config.get_config('xj') # 添加线径配置 + self.scanner_config = self.config.get_config('scanner') # 添加扫码器配置 # 检查操作系统类型,在macOS上处理COM端口名称问题 os_type = platform.system() @@ -118,7 +120,7 @@ class SerialManager: # 检查是否自动查询米电阻数据 self.auto_query_mdz = self.config.get_value('serial.keyboard.auto_query', False) - logging.info(f"已加载串口配置:mdz={self.mdz_config}, xj={self.xj_config}, cz={self.cz_config}, data_file={self.data_file}") + logging.info(f"已加载串口配置:mdz={self.mdz_config}, xj={self.xj_config}, cz={self.cz_config}, scanner={self.scanner_config}, data_file={self.data_file}") logging.info(f"米电阻自动查询: {'开启' if self.auto_query_mdz else '关闭'}") except Exception as e: logging.error(f"加载配置出错: {e}") @@ -126,6 +128,8 @@ class SerialManager: self.data_file = os.path.abspath('data.txt') self.mdz_config = {'port': 9600, 'ser': 'COM5'} self.cz_config = {'port': 9600, 'ser': 'COM2', 'stable_threshold': 10} + self.xj_config = {'port': 9600, 'ser': 'COM3'} + self.scanner_config = {'port': 9600, 'ser': 'COM4'} self.stable_threshold = 10 self.auto_query_mdz = False logging.info(f"使用默认配置,数据文件: {self.data_file}") @@ -194,90 +198,107 @@ class SerialManager: Args: port_name: 串口名称,如COM1 - port_type: 串口类型,'cz'表示称重,'mdz'表示米电阻, 'xj'表示线径 + port_type: 串口类型,'cz'表示称重,'mdz'表示米电阻, 'xj'表示线径, 'scanner'表示扫码器 baud_rate: 波特率,如果为None则从配置文件读取 data_bits: 数据位 stop_bits: 停止位 - parity: 校验位,N-无校验,E-偶校验,O-奇校验 + parity: 校验位,'N'表示无校验,'O'表示奇校验,'E'表示偶校验 timeout: 超时时间,单位秒 - callback: 数据回调函数,接收参数为(port_name, data) + callback: 回调函数,接收(port_name, data)作为参数 Returns: - 是否成功打开 + bool: 成功返回True,失败返回False """ + # 如果串口已经打开,先关闭 + if port_name in self.serial_ports and self.serial_ports[port_name]: + try: + self.close_port(port_name) + except Exception as e: + logging.error(f"关闭已打开的串口失败: {e}") + + # 配置串口参数 try: - # 如果波特率为None,从配置文件读取 + # 从配置读取波特率(如果未提供) if baud_rate is None: if port_type == 'cz' and self.cz_config: baud_rate = self.cz_config.get('port', 9600) elif port_type == 'mdz' and self.mdz_config: baud_rate = self.mdz_config.get('port', 9600) - elif port_type == 'xj' and self.xj_config: # 添加线径配置 + elif port_type == 'xj' and self.xj_config: baud_rate = self.xj_config.get('port', 9600) + elif port_type == 'scanner' and self.scanner_config: + baud_rate = self.scanner_config.get('port', 9600) else: baud_rate = 9600 # 默认波特率 - # 如果串口已经打开,先关闭 - if port_name in self.serial_ports: - self.close_port(port_name) - + # 转换校验位为PySerial常量 + if parity.upper() == 'N': + parity_constant = serial.PARITY_NONE + elif parity.upper() == 'O': + parity_constant = serial.PARITY_ODD + elif parity.upper() == 'E': + parity_constant = serial.PARITY_EVEN + else: + parity_constant = serial.PARITY_NONE + # 打开串口 - ser = serial.Serial( + self.serial_ports[port_name] = serial.Serial( port=port_name, baudrate=baud_rate, bytesize=data_bits, stopbits=stop_bits, - parity=parity, + parity=parity_constant, timeout=timeout ) - if not ser.is_open: - ser.open() - - # 存储串口对象 - self.serial_ports[port_name] = ser - logging.info(f"串行对象 for {port_name} 存储在 self.serial_ports 中. 当前活跃端口: {list(self.serial_ports.keys())}") + logging.info(f"打开串口成功: {port_name}, 类型: {port_type}, 波特率: {baud_rate}") # 设置回调 if callback: self.callbacks[port_name] = callback - # 启动读取线程 + # 创建并启动读取线程 self.running_flags[port_name] = True - # 根据串口类型选择不同的读取线程 if port_type == 'cz': + # 称重数据需要特殊处理 thread = threading.Thread(target=self._read_weight_thread, args=(port_name, self.stable_threshold)) - thread.daemon = True - thread.start() - self.read_threads[port_name] = thread elif port_type == 'mdz': + # 米电阻数据需要特殊处理 thread = threading.Thread(target=self._read_resistance_thread, args=(port_name,)) - thread.daemon = True - thread.start() - self.read_threads[port_name] = thread - elif port_type == 'xj': # 添加线径读取线程 + elif port_type == 'xj': + # 线径数据需要特殊处理 thread = threading.Thread(target=self._read_diameter_thread, args=(port_name,)) - thread.daemon = True - thread.start() - self.read_threads[port_name] = thread + elif port_type == 'scanner': + # 扫码器数据需要特殊处理 + thread = threading.Thread(target=self._read_scanner_thread, args=(port_name,)) else: - # 默认读取线程 + # 其他类型使用通用处理 thread = threading.Thread(target=self._read_thread, args=(port_name,)) - thread.daemon = True - thread.start() - self.read_threads[port_name] = thread + + thread.daemon = True + thread.start() + self.read_threads[port_name] = thread - logging.info(f"串口 {port_name} ({port_type}) 已打开,波特率={baud_rate}") return True - + except Exception as e: - logging.error(f"打开串口 {port_name} 失败: {str(e)}") - if port_name in self.serial_ports: # 清理,以防部分成功 - del self.serial_ports[port_name] - logging.info(f"打开 {port_name} 失败后, 当前活跃端口: {list(self.serial_ports.keys())}") - return False + logging.error(f"打开串口失败: {port_name}, 错误: {e}") + # 确保清理好资源 + if port_name in self.serial_ports: + try: + self.serial_ports[port_name].close() + except: + pass + self.serial_ports.pop(port_name, None) + # 停止相关线程 + self.running_flags[port_name] = False + if port_name in self.read_threads: + self.read_threads.pop(port_name, None) + + return False + def close_port(self, port_name: str) -> bool: """ 关闭串口 @@ -351,6 +372,35 @@ class SerialManager: logging.error(f"向串口 {port_name} 写入数据失败: {str(e)}") return False + def read_data(self, port_name: str, size: int = None) -> bytes: + """ + 从串口读取数据 + + Args: + port_name: 串口名称 + size: 要读取的字节数,如果为None则读取所有可用数据 + + Returns: + 读取的数据,如果失败则返回空字节 + """ + try: + if not self.is_port_open(port_name): + logging.error(f"尝试从未打开的串口 {port_name} 读取数据") + return b'' + + if size is None: + # 读取所有可用数据 + if self.serial_ports[port_name].in_waiting > 0: + return self.serial_ports[port_name].read(self.serial_ports[port_name].in_waiting) + return b'' + else: + # 读取指定数量的字节 + return self.serial_ports[port_name].read(size) + + except Exception as e: + logging.error(f"从串口 {port_name} 读取数据失败: {str(e)}") + return b'' + def _read_thread(self, port_name: str): """ 串口读取线程 @@ -534,7 +584,7 @@ class SerialManager: return # 构建数据字符串 - data_str = f"mdz:{self.data['mdz']}|cz:{self.data['cz']}|" + data_str = f"mdz:{self.data['mdz']}|cz:{self.data['cz']}|scanner:{self.data['scanner']}|" # 确保目录存在 data_dir = os.path.dirname(self.data_file) @@ -792,7 +842,7 @@ class SerialManager: """通知所有相关回调函数""" try: # 端口特定回调 (通常用于原始串口数据) - if port_name in self.callbacks and port_name not in ['mdz_data', 'xj_data']: # 避免重复处理 + if port_name in self.callbacks and port_name not in ['mdz_data', 'xj_data', 'scanner_data']: # 避免重复处理 try: # 假设这种回调期望原始的 value (可能是字节串,也可能是其他类型) self.callbacks[port_name](port_name, value) @@ -874,6 +924,37 @@ class SerialManager: else: logging.warning(f"回调失败: xj_data 中实际值为None. 初始 value: {value}") + # 全局回调, 特别处理 'scanner_data' + if 'scanner_data' in self.callbacks and port_name == 'scanner_data': + actual_scanner_value = None + source_info = "unknown" + + if isinstance(value, dict): + actual_scanner_value = value.get('value') + source_info = value.get('source', source_info) + elif isinstance(value, (str, bytes)): + if isinstance(value, bytes): + try: + actual_scanner_value = value.decode('utf-8').strip() + except: + actual_scanner_value = str(value) + else: + actual_scanner_value = value + + if actual_scanner_value is not None: + callback_data_str = f"扫码数据: {actual_scanner_value}" + try: + triggering_port = port_name if port_name not in ['scanner_data', 'scanner'] else self.scanner_config.get('ser', 'N/A') if self.scanner_config else 'N/A' + if source_info.startswith("mock"): + triggering_port = f"mock_{port_name}" + + self.callbacks['scanner_data'](triggering_port, callback_data_str.encode('utf-8')) + logging.info(f"通知 'scanner_data' 回调. 值: {actual_scanner_value}, 源: {source_info}, 触发源端口: {triggering_port}") + except Exception as e: + logging.error(f"调用全局回调 'scanner_data' 失败: {e}", exc_info=True) + else: + logging.warning(f"回调失败: scanner_data 中实际值为None. 初始 value: {value}") + except Exception as e: logging.error(f"通知回调失败: {e}", exc_info=True) @@ -893,14 +974,15 @@ class SerialManager: os_type = platform.system() if os_type == "Darwin" and ( (self.mdz_config and 'ser' in self.mdz_config and self.mdz_config['ser'] and self.mdz_config['ser'].startswith('COM')) or - (self.cz_config and 'ser' in self.cz_config and self.cz_config['ser'] and self.cz_config['ser'].startswith('COM')) + (self.cz_config and 'ser' in self.cz_config and self.cz_config['ser'] and self.cz_config['ser'].startswith('COM')) or + (self.scanner_config and 'ser' in self.scanner_config and self.scanner_config['ser'] and self.scanner_config['ser'].startswith('COM')) ): logging.warning("检测到在macOS系统上配置了Windows格式的COM端口,这些端口将无法正常打开") logging.warning("macOS上的串口通常是/dev/tty.*或/dev/cu.*格式") # 继续尝试打开,但不影响程序流程 # 尝试打开线径串口 - if self.cz_config and 'ser' in self.cz_config and self.cz_config['ser']: + if self.cz_config and 'ser' in self.cz_config and self.cz_config['ser'] and self.cz_config['ser'].strip(): port_name = self.cz_config['ser'] baud_rate = self.cz_config.get('port', 2400) @@ -917,10 +999,10 @@ class SerialManager: else: logging.info(f"线径串口 {port_name} 已经打开,无需重新打开") else: - logging.warning("线径串口未配置,跳过自动打开") + logging.warning("线径串口未配置或设置为不使用,跳过自动打开") # 尝试打开米电阻串口 - if self.mdz_config and 'ser' in self.mdz_config and self.mdz_config['ser']: + if self.mdz_config and 'ser' in self.mdz_config and self.mdz_config['ser'] and self.mdz_config['ser'].strip(): port_name = self.mdz_config['ser'] baud_rate = self.mdz_config.get('port', 9600) @@ -937,7 +1019,27 @@ class SerialManager: else: logging.info(f"米电阻串口 {port_name} 已经打开,无需重新打开") else: - logging.warning("米电阻串口未配置,跳过自动打开") + logging.warning("米电阻串口未配置或设置为不使用,跳过自动打开") + + # 尝试打开扫码器串口 + if self.scanner_config and 'ser' in self.scanner_config and self.scanner_config['ser'] and self.scanner_config['ser'].strip(): + port_name = self.scanner_config['ser'] + baud_rate = self.scanner_config.get('port', 9600) + + if not self.is_port_open(port_name): + try: + if self.open_port(port_name, 'scanner', baud_rate): + logging.info(f"自动打开扫码器串口 {port_name} 成功") + else: + logging.error(f"自动打开扫码器串口 {port_name} 失败") + success = False + except Exception as e: + logging.error(f"自动打开扫码器串口 {port_name} 时发生异常: {e}") + success = False + else: + logging.info(f"扫码器串口 {port_name} 已经打开,无需重新打开") + else: + logging.warning("扫码器串口未配置或设置为不使用,跳过自动打开") # 注意:不在这里启动键盘监听器,而是在MainWindow的handle_start方法中显式调用start_keyboard_listener @@ -1022,4 +1124,74 @@ class SerialManager: return False except Exception as e: logging.error(f"处理线径数据总体异常: {e}") + return False + + def _read_scanner_thread(self, port_name: str): + """ + 扫码器串口读取线程 + + Args: + port_name: 串口名称 + """ + try: + while self.running_flags.get(port_name, False): + if not self.is_port_open(port_name): + time.sleep(0.1) + continue + + # 检查是否有数据可读 + if self.serial_ports[port_name].in_waiting > 0: + response = self.serial_ports[port_name].read(self.serial_ports[port_name].in_waiting) + self._process_scanner_response(port_name, response) + + time.sleep(0.1) + + except Exception as e: + logging.error(f"扫码器串口 {port_name} 读取线程异常: {e}") + + def _process_scanner_response(self, port_name, response_bytes: bytes): + """处理扫码器响应数据""" + try: + if response_bytes: # 确保有响应数据 + try: + # 尝试解码为字符串 + scanner_value = response_bytes.decode('utf-8').strip() + + # 记录日志 + logging.info(f"[{port_name}] 扫码数据: {scanner_value}") + + # 更新数据 + self.data['scanner'] = scanner_value + + # 写入文件并通知回调 + self._write_data_to_file() + # 使用"扫码数据: xxx"格式通知回调 + callback_data = f"扫码数据: {scanner_value}".encode('utf-8') + if 'scanner_data' in self.callbacks: + self.callbacks['scanner_data'](port_name, callback_data) + return True + except Exception as e: + logging.error(f"处理扫码数据异常: {e}") + # 解码失败,尝试直接使用字节数据 + # 记录日志(十六进制字符串) + hex_str = ' '.join(f'{b:02X}' for b in response_bytes) + logging.warning(f"[{port_name}] 扫码数据(十六进制): {hex_str}") + + # 更新数据(使用十六进制字符串) + self.data['scanner'] = hex_str + + # 写入文件并通知回调 + self._write_data_to_file() + # 使用"扫码数据: xxx"格式通知回调 + callback_data = f"扫码数据: {hex_str}".encode('utf-8') + if 'scanner_data' in self.callbacks: + self.callbacks['scanner_data'](port_name, callback_data) + return True + else: + logging.warning("扫码响应数据为空") + + # 如果无法解析,则直接返回失败 + return False + except Exception as e: + logging.error(f"处理扫码数据总体异常: {e}") return False \ No newline at end of file diff --git a/widgets/main_window.py b/widgets/main_window.py index 848452b..36caf7b 100644 --- a/widgets/main_window.py +++ b/widgets/main_window.py @@ -205,6 +205,10 @@ class MainWindow(MainWindowUI): # 加载托盘号列表 self.load_pallet_codes() + + # 恢复开始按钮原始样式 + self.restore_start_button_style() + def get_axios_num(self,tray_id): """获取托盘号对应的轴号""" from dao.inspection_dao import InspectionDAO @@ -369,10 +373,13 @@ class MainWindow(MainWindowUI): # 更新串口管理器配置 self.serial_manager.reload_config() + # 重新打开已配置的串口 + self.serial_manager.auto_open_configured_ports() + # 重新加载托盘号 self.load_pallet_codes() - logging.info("设置已更新,重新加载配置") + logging.info("设置已更新,重新加载配置并重新打开串口") def handle_input(self): """处理上料按钮点击事件""" @@ -508,6 +515,49 @@ class MainWindow(MainWindowUI): logging.error(f"处理下料操作失败: {str(e)}") QMessageBox.critical(self, "错误", f"处理下料操作失败: {str(e)}") + def restore_start_button_style(self): + """恢复开始按钮的原始样式""" + try: + self.start_button.setStyleSheet(""" + QPushButton { + background-color: transparent; + color: black; + border: 1px solid #4caf50; + border-radius: 4px; + padding: 2px 10px; + min-height: 29px; + max-height: 29px; + } + QPushButton:hover { + background-color: #f0f0f0; + } + """) + except Exception as e: + logging.error(f"{str(e)}") + + def fill_start_button_style(self): + """填充开始按钮样式 - 绿色背景,白色字体""" + try: + # 填充按钮样式 - 绿色背景,白色字体,高度与下料按钮一致 + self.start_button.setStyleSheet(""" + QPushButton { + background-color: #4caf50; + color: white; + border: 1px solid #4caf50; + border-radius: 4px; + padding: 2px 10px; + min-height: 24px; + max-height: 24px; + } + QPushButton:hover { + background-color: #4caf50; + color: white; + } + """) + logging.info("已填充开始按钮样式") + except Exception as e: + logging.error(f"填充开始按钮样式失败: {str(e)}") + def handle_start(self): """ 处理开始按钮点击事件 @@ -522,11 +572,14 @@ class MainWindow(MainWindowUI): if self._current_unload_info and self._current_unload_num > 0: # 下料模式 - 开始下料操作 # 确保寄存器3(下料启动)设为1,寄存器4已在handle_output中设置了当前层数 + success2 = modbus.write_register_until_success(client, 2, 1) success3 = modbus.write_register_until_success(client, 3, 1) - if success3: + if success2 and success3: logging.info(f"开始下料操作:当前层数 {self._current_unload_num}/{self._total_unload_num}") QMessageBox.information(self, "操作提示", f"开始下料操作:当前第{self._current_unload_num}层") + # 填充按钮样式 + self.fill_start_button_style() else: QMessageBox.warning(self, "错误", "开始下料操作失败") else: @@ -538,8 +591,11 @@ class MainWindow(MainWindowUI): if success0 and success2: self._is_loading_active = True # 标记上料任务已开始 logging.info(f"开始上料操作:当前层数 {self._current_stow_num}") + # 填充按钮样式 + self.fill_start_button_style() else: QMessageBox.warning(self, "错误", "开始上料操作失败") + except Exception as e: logging.error(f"开始操作失败: {str(e)}") QMessageBox.critical(self, "错误", f"开始操作失败: {str(e)}") @@ -559,6 +615,8 @@ class MainWindow(MainWindowUI): if success3: logging.info(f"停止下料操作:当前层数 {self._current_unload_num}/{self._total_unload_num}") QMessageBox.information(self, "操作提示", "已停止下料操作") + # 恢复按钮原始样式 + self.restore_start_button_style() else: QMessageBox.warning(self, "错误", "停止下料操作失败") else: @@ -569,8 +627,11 @@ class MainWindow(MainWindowUI): self._is_loading_active = False # 标记上料任务已停止 logging.info("停止上料操作") QMessageBox.information(self, "操作提示", "已停止上料操作") + # 恢复按钮原始样式 + self.restore_start_button_style() else: QMessageBox.warning(self, "错误", "停止上料操作失败") + except Exception as e: logging.error(f"停止操作失败: {str(e)}") QMessageBox.critical(self, "错误", f"停止操作失败: {str(e)}") @@ -1943,34 +2004,45 @@ class MainWindow(MainWindowUI): def handle_register_change(self, address, value): """处理寄存器变化""" logging.info(f"[处理] 寄存器D{address}变化: {value}") - # 在这里可以添加通用寄存器变化处理逻辑 - pass - + # 这里可以添加通用寄存器变化处理逻辑 + @Slot(int, str) def handle_loading_feedback(self, status, desc): """处理上料信息反馈""" - message = desc # Default message - if status == 1: - modbus = ModbusUtils() - client = modbus.get_client() - # 睡 0.5 秒,用于延缓modbus 监听 - time.sleep(0.5) - modbus.write_register_until_success(client, 2, 0) + message = desc + try: + if status == 1: + modbus = ModbusUtils() + client = modbus.get_client() + # 睡 0.5 秒,用于延缓modbus 监听 + time.sleep(0.5) + modbus.write_register_until_success(client, 2, 0) + if self._current_stow_num > 0: + completed_layer_num = self._current_stow_num + self._current_stow_num -= 1 + if self._current_stow_num == 0: + self._is_loading_active = False # 任务完成,标记为非活动 + self._loading_info = None + logging.info("所有层拆垛完成,清空上料信息") + message = f"第 {completed_layer_num} 层(最后一层)拆垛完成!" + # 重置寄存器 0 和 2 为 0 + modbus.write_register_until_success(client, 0, 0) + modbus.write_register_until_success(client, 2, 0) + self.loading_feedback_signal.emit("input", message) + # 恢复开始按钮原始样式 + self.restore_start_button_style() + else: + logging.info(f"当前层拆垛完成,剩余层数: {self._current_stow_num}") + message = f"第 {completed_layer_num} 层拆垛完成。" + self.loading_feedback_signal.emit("input", message) + #通知寄存器,进行第几层拆垛 + modbus.write_register_until_success(client,0 ,self._current_stow_num) + except Exception as e: + logging.error(f"处理上料信息反馈失败: {str(e)}") + # 不在这里显示对话框,而是通过信号传递错误信息 + self.loading_feedback_signal.emit("error", f"处理上料信息反馈失败: {str(e)}") + finally: modbus.close_client(client) - - if self._current_stow_num > 0: - completed_layer_num = self._current_stow_num - self._current_stow_num -= 1 - if self._current_stow_num == 0: - self._is_loading_active = False # 任务完成,标记为非活动 - self._loading_info = None - logging.info("所有层拆垛完成,清空上料信息") - message = f"第 {completed_layer_num} 层(最后一层)拆垛完成!" - else: - logging.info(f"当前层拆垛完成,剩余层数: {self._current_stow_num}") - message = f"第 {completed_layer_num} 层拆垛完成。" - - self.loading_feedback_signal.emit("input", message) def _handle_loading_feedback_ui(self, status_type, desc): """在主线程中处理上料UI更新""" @@ -1996,6 +2068,8 @@ class MainWindow(MainWindowUI): client = modbus.get_client() try: + # 睡 0.5 秒,用于延缓modbus 监听 + time.sleep(0.5) # 临时重置寄存器3(下料启动)为0,等待用户下一次启动 modbus.write_register_until_success(client, 3, 0) @@ -2013,6 +2087,9 @@ class MainWindow(MainWindowUI): # 通过信号触发UI更新 - 显示前一层完成的消息 message = f"第{self._current_unload_num-1}层下料完成,请启动第{self._current_unload_num}层下料" self.unloading_feedback_signal.emit("output", message) + + # 恢复开始按钮原始样式 + self.restore_start_button_style() else: # 所有层都下料完成,重置寄存器和计数器 modbus.write_register_until_success(client, 3, 0) # 确保下料启动寄存器为0 @@ -2034,6 +2111,9 @@ class MainWindow(MainWindowUI): # 通过信号触发UI更新,而不是直接操作UI message = f"托盘 {tray_code} 的所有 {total_tier} 层下料已全部完成" self.unloading_feedback_signal.emit("output", message) + + # 恢复开始按钮原始样式 + self.restore_start_button_style() except Exception as e: logging.error(f"处理下料反馈时发生错误: {str(e)}") # 不在这里显示对话框,而是通过信号传递错误信息 @@ -2292,6 +2372,9 @@ class MainWindow(MainWindowUI): # 注册线径数据回调 self.serial_manager.callbacks['xj_data'] = self.on_diameter_data_received + # 注册扫码器数据回调 + self.serial_manager.callbacks['scanner_data'] = self.on_scanner_data_received + # 自动打开已配置的串口 self.serial_manager.auto_open_configured_ports() @@ -2402,6 +2485,33 @@ class MainWindow(MainWindowUI): except Exception as e: logging.error(f"处理线径数据失败: {str(e)}") + def on_scanner_data_received(self, port_name, data): + """扫码器数据接收回调函数 + + Args: + port_name: 串口名称 + data: 接收到的数据 + """ + try: + # 解析数据 + data_str = data.decode('utf-8') if isinstance(data, bytes) else str(data) + logging.info(f"收到扫码器数据: {data_str} 来自 {port_name}") + + # 提取扫码数据,格式为"扫码数据: xxx" + if "扫码数据:" in data_str: + gc_note = data_str.split("扫码数据:")[1].strip() + logging.info(f"提取到工程号: {gc_note}") + + # 设置工程号到输入框 + self.order_edit.setText(gc_note) + + # 模拟按下回车键,触发handle_order_enter方法 + self.handle_order_enter() + else: + logging.warning(f"收到的数据不包含扫码数据标记: {data_str}") + except Exception as e: + logging.error(f"处理扫码器数据失败: {str(e)}") + def set_inspection_value(self, data_type, config, value): """设置检验项目值到表格中 diff --git a/widgets/serial_settings_widget.py b/widgets/serial_settings_widget.py index c9cbd9e..5e5f632 100644 --- a/widgets/serial_settings_widget.py +++ b/widgets/serial_settings_widget.py @@ -32,6 +32,10 @@ class SerialSettingsWidget(SerialSettingsUI): self.xj_refresh_btn.clicked.connect(self.refresh_ports) self.test_xj_btn.clicked.connect(self.test_xj_port) + # 扫码器串口 + self.scanner_refresh_btn.clicked.connect(self.refresh_ports) + self.test_scanner_btn.clicked.connect(self.test_scanner_port) + # 保存按钮 self.save_btn.clicked.connect(self.save_settings) @@ -46,9 +50,18 @@ class SerialSettingsWidget(SerialSettingsUI): try: # 保存当前选择 current_mdz_port = self.mdz_port_combo.currentData() + current_xj_port = self.xj_port_combo.currentData() + current_scanner_port = self.scanner_port_combo.currentData() # 清空列表 self.mdz_port_combo.clear() + self.xj_port_combo.clear() + self.scanner_port_combo.clear() + + # 添加"不使用"选项 + self.mdz_port_combo.addItem("不使用", "") + self.xj_port_combo.addItem("不使用", "") + self.scanner_port_combo.addItem("不使用", "") # 获取可用串口 ports = list(serial.tools.list_ports.comports()) @@ -56,16 +69,41 @@ class SerialSettingsWidget(SerialSettingsUI): 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.xj_port_combo.addItem(port_desc, port_name) + + # 添加到扫码器下拉框 + self.scanner_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) + else: + # 如果之前没有选择,则设为"不使用" + self.mdz_port_combo.setCurrentIndex(0) + + if current_xj_port: + index = self.xj_port_combo.findData(current_xj_port) + if index >= 0: + self.xj_port_combo.setCurrentIndex(index) + else: + # 如果之前没有选择,则设为"不使用" + self.xj_port_combo.setCurrentIndex(0) + + if current_scanner_port: + index = self.scanner_port_combo.findData(current_scanner_port) + if index >= 0: + self.scanner_port_combo.setCurrentIndex(index) + else: + # 如果之前没有选择,则设为"不使用" + self.scanner_port_combo.setCurrentIndex(0) - - logging.info(f"已刷新串口列表,找到 {len(ports)} 个串口") except Exception as e: logging.error(f"刷新串口列表失败: {e}") @@ -155,6 +193,39 @@ class SerialSettingsWidget(SerialSettingsUI): if index >= 0: self.xj_parity_combo.setCurrentIndex(index) + # 加载扫码器设置 + scanner_config = self.config.get_config('scanner') + if scanner_config: + # 设置串口 + scanner_port = scanner_config.get('ser', '') + index = self.scanner_port_combo.findData(scanner_port) + if index >= 0: + self.scanner_port_combo.setCurrentIndex(index) + + # 设置波特率 + scanner_baud = str(scanner_config.get('port', '9600')) + index = self.scanner_baud_combo.findText(scanner_baud) + if index >= 0: + self.scanner_baud_combo.setCurrentIndex(index) + + # 设置数据位 + scanner_data_bits = str(scanner_config.get('data_bits', '8')) + index = self.scanner_data_bits_combo.findText(scanner_data_bits) + if index >= 0: + self.scanner_data_bits_combo.setCurrentIndex(index) + + # 设置停止位 + scanner_stop_bits = str(scanner_config.get('stop_bits', '1')) + index = self.scanner_stop_bits_combo.findText(scanner_stop_bits) + if index >= 0: + self.scanner_stop_bits_combo.setCurrentIndex(index) + + # 设置校验位 + scanner_parity = scanner_config.get('parity', 'N') + index = self.scanner_parity_combo.findData(scanner_parity) + if index >= 0: + self.scanner_parity_combo.setCurrentIndex(index) + logging.info("已加载串口设置") except Exception as e: logging.error(f"加载串口设置失败: {e}") @@ -180,7 +251,6 @@ class SerialSettingsWidget(SerialSettingsUI): mdz_query_interval = self.mdz_query_interval.value() mdz_config = { - 'ser': mdz_port, 'port': mdz_baud, 'data_bits': mdz_data_bits, 'stop_bits': mdz_stop_bits, @@ -189,6 +259,10 @@ class SerialSettingsWidget(SerialSettingsUI): 'query_interval': mdz_query_interval } + # 只有当用户选择了串口时才保存串口配置 + if mdz_port: + mdz_config['ser'] = mdz_port + self.config.set_config('mdz', mdz_config) # 保存线径设置 @@ -199,17 +273,37 @@ class SerialSettingsWidget(SerialSettingsUI): xj_parity = self.xj_parity_combo.currentData() xj_config = { - 'ser': xj_port, 'port': xj_baud, 'data_bits': xj_data_bits, 'stop_bits': xj_stop_bits, 'parity': xj_parity } + # 只有当用户选择了串口时才保存串口配置 + if xj_port: + xj_config['ser'] = xj_port + self.config.set_config('xj', xj_config) - + # 保存扫码器设置 + scanner_port = self.scanner_port_combo.currentData() + scanner_baud = int(self.scanner_baud_combo.currentText()) + scanner_data_bits = int(self.scanner_data_bits_combo.currentText()) + scanner_stop_bits = float(self.scanner_stop_bits_combo.currentText()) + scanner_parity = self.scanner_parity_combo.currentData() + scanner_config = { + 'port': scanner_baud, + 'data_bits': scanner_data_bits, + 'stop_bits': scanner_stop_bits, + 'parity': scanner_parity + } + + # 只有当用户选择了串口时才保存串口配置 + if scanner_port: + scanner_config['ser'] = scanner_port + + self.config.set_config('scanner', scanner_config) # 发送设置变更信号 self.settings_changed.emit() @@ -224,15 +318,16 @@ class SerialSettingsWidget(SerialSettingsUI): """测试米电阻串口""" try: port = self.mdz_port_combo.currentData() + + if not port: + QMessageBox.warning(self, "测试失败", "请先选择串口,当前设置为\"不使用\"") + return + 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) @@ -248,44 +343,46 @@ class SerialSettingsWidget(SerialSettingsUI): if query_cmd: try: # 转换查询指令为字节 - query_bytes = bytes.fromhex(query_cmd.replace(' ', '')) + cmd_bytes = bytes.fromhex(query_cmd.replace(' ', '')) + self.serial_manager.write_data(port, cmd_bytes) + time.sleep(0.1) # 等待响应 - # 发送查询指令 - self.serial_manager.write_data(port, query_bytes) - - # 等待一段时间 - time.sleep(0.5) - - # 关闭串口 - self.serial_manager.close_port(port) - - QMessageBox.information(self, "测试成功", f"米电阻串口 {port} 测试成功,已发送查询指令") + # 读取响应 + response = self.serial_manager.read_data(port) + if response: + # 将字节转换为十六进制字符串 + hex_str = ' '.join(f'{b:02X}' for b in response) + QMessageBox.information(self, "测试成功", f"串口打开成功,收到响应:\n{hex_str}") + else: + QMessageBox.information(self, "测试成功", "串口打开成功,但未收到响应") except Exception as e: - self.serial_manager.close_port(port) - QMessageBox.warning(self, "测试失败", f"发送查询指令失败: {e}") + QMessageBox.warning(self, "测试结果", f"串口打开成功,但发送指令失败: {e}") else: - self.serial_manager.close_port(port) - QMessageBox.information(self, "测试成功", f"米电阻串口 {port} 打开成功,但未发送查询指令") + QMessageBox.information(self, "测试成功", "串口打开成功") + + # 关闭串口 + self.serial_manager.close_port(port) else: - QMessageBox.warning(self, "测试失败", f"无法打开米电阻串口 {port}") + QMessageBox.critical(self, "测试失败", f"无法打开串口 {port}") + except Exception as e: logging.error(f"测试米电阻串口失败: {e}") - QMessageBox.warning(self, "测试失败", f"测试米电阻串口失败: {e}") - + QMessageBox.critical(self, "测试失败", f"测试米电阻串口失败: {e}") def test_xj_port(self): """测试线径串口""" try: port = self.xj_port_combo.currentData() + + if not port: + QMessageBox.warning(self, "测试失败", "请先选择串口,当前设置为\"不使用\"") + return + baud = int(self.xj_baud_combo.currentText()) data_bits = int(self.xj_data_bits_combo.currentText()) stop_bits = float(self.xj_stop_bits_combo.currentText()) parity = self.xj_parity_combo.currentData() - if not port: - QMessageBox.warning(self, "测试失败", "请选择串口") - return - # 关闭可能已经打开的串口 if self.serial_manager.is_port_open(port): self.serial_manager.close_port(port) @@ -296,15 +393,68 @@ class SerialSettingsWidget(SerialSettingsUI): ) if success: - # 等待一段时间 - time.sleep(2) + QMessageBox.information(self, "测试成功", f"串口 {port} 打开成功") # 关闭串口 self.serial_manager.close_port(port) - - QMessageBox.information(self, "测试成功", f"线径串口 {port} 测试成功") else: - QMessageBox.warning(self, "测试失败", f"无法打开线径串口 {port}") + QMessageBox.critical(self, "测试失败", f"无法打开串口 {port}") + except Exception as e: logging.error(f"测试线径串口失败: {e}") - QMessageBox.warning(self, "测试失败", f"测试线径串口失败: {e}") \ No newline at end of file + QMessageBox.critical(self, "测试失败", f"测试线径串口失败: {e}") + + def test_scanner_port(self): + """测试扫码器串口""" + try: + port = self.scanner_port_combo.currentData() + + if not port: + QMessageBox.warning(self, "测试失败", "请先选择串口,当前设置为\"不使用\"") + return + + baud = int(self.scanner_baud_combo.currentText()) + data_bits = int(self.scanner_data_bits_combo.currentText()) + stop_bits = float(self.scanner_stop_bits_combo.currentText()) + parity = self.scanner_parity_combo.currentData() + + # 关闭可能已经打开的串口 + if self.serial_manager.is_port_open(port): + self.serial_manager.close_port(port) + + # 尝试打开串口 + success = self.serial_manager.open_port( + port, 'scanner', baud, data_bits, stop_bits, parity, 1.0 + ) + + if success: + QMessageBox.information(self, "测试成功", f"串口 {port} 打开成功\n请触发扫码器进行扫描测试") + + # 尝试读取数据(短暂等待扫码器输入) + start_time = time.time() + timeout = 5.0 # 5秒超时 + + while time.time() - start_time < timeout: + response = self.serial_manager.read_data(port) + if response: + # 尝试将字节解码为字符串 + try: + text = response.decode('utf-8').strip() + QMessageBox.information(self, "测试成功", f"收到扫码数据:\n{text}") + break + except: + # 如果解码失败,显示十六进制 + hex_str = ' '.join(f'{b:02X}' for b in response) + QMessageBox.information(self, "测试成功", f"收到扫码数据 (十六进制):\n{hex_str}") + break + + time.sleep(0.1) # 短暂休眠,减少CPU占用 + + # 关闭串口 + self.serial_manager.close_port(port) + else: + QMessageBox.critical(self, "测试失败", f"无法打开串口 {port}") + + except Exception as e: + logging.error(f"测试扫码器串口失败: {e}") + QMessageBox.critical(self, "测试失败", f"测试扫码器串口失败: {e}") \ No newline at end of file diff --git a/widgets/settings_widget.py b/widgets/settings_widget.py index 6d00ee4..640dd8c 100644 --- a/widgets/settings_widget.py +++ b/widgets/settings_widget.py @@ -85,28 +85,28 @@ class SettingsWidget(SettingsUI): self.pallet_type_settings = PalletTypeSettingsWidget(self) self.plc_settings = PLCSettingsWidget(self) - # 创建电量监控设置组件 + # 创建电力监控设置组件 try: self.electricity_settings = ElectricitySettingsUI(self) - logging.info("电量监控设置组件创建成功") + logging.info("电力监控设置组件创建成功") - # 移除临时占位符标签并添加电量监控设置部件 + # 移除临时占位符标签并添加电力监控设置部件 if hasattr(self, 'electricity_placeholder'): - logging.info("移除电量监控临时占位符") + logging.info("移除电力监控临时占位符") self.electricity_layout.removeWidget(self.electricity_placeholder) self.electricity_placeholder.hide() self.electricity_placeholder.deleteLater() else: - logging.warning("未找到电量监控临时占位符标签") + logging.warning("未找到电力监控临时占位符标签") # 检查布局是否可用 if hasattr(self, 'electricity_layout'): - logging.info("添加电量监控设置部件到布局") + logging.info("添加电力监控设置部件到布局") self.electricity_layout.addWidget(self.electricity_settings) else: logging.error("无法找到electricity_layout布局") except Exception as e: - logging.error(f"初始化电量监控设置组件失败: {e}") + logging.error(f"初始化电力监控设置组件失败: {e}") # 移除临时占位符标签并添加检验设置部件 if hasattr(self, 'inspection_placeholder'): @@ -172,13 +172,13 @@ class SettingsWidget(SettingsUI): self.pallet_type_settings.settings_changed.connect(self.on_settings_changed) self.plc_settings.settings_changed.connect(self.on_settings_changed) - # 连接电量监控设置信号 + # 连接电力监控设置信号 if hasattr(self, 'electricity_settings'): try: self.electricity_settings.settings_changed.connect(self.on_settings_changed) - logging.info("已连接电量监控设置信号") + logging.info("已连接电力监控设置信号") except Exception as e: - logging.error(f"连接电量监控设置信号时出错: {e}") + logging.error(f"连接电力监控设置信号时出错: {e}") # 不再在这里连接刷新按钮,避免重复连接 logging.info("刷新按钮已在初始化时连接,不再重复连接") diff --git a/widgets/settings_window.py b/widgets/settings_window.py index 7e70ee6..7d8e5b7 100644 --- a/widgets/settings_window.py +++ b/widgets/settings_window.py @@ -28,54 +28,23 @@ class SettingsWindow(QDialog): 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, "串口设置") + # 应用相机刷新按钮修复 - try: - # 先尝试直接找到刷新按钮并添加事件处理 - logging.info("尝试直接查找刷新按钮并添加事件处理...") - from PySide6.QtWidgets import QPushButton - - # 在整个UI层次中查找刷新设备按钮 - refresh_buttons = [] - for widget in self.findChildren(QPushButton): - if widget.text() == "刷新设备": - refresh_buttons.append(widget) - - if refresh_buttons: - logging.info(f"找到 {len(refresh_buttons)} 个刷新设备按钮") - for i, button in enumerate(refresh_buttons): - try: - # 断开现有连接 - button.clicked.disconnect() - except Exception: - pass - - # 连接到相机设置的刷新方法 - if hasattr(self.settings_widget, 'camera_settings') and hasattr(self.settings_widget.camera_settings, 'refresh_devices'): - button.clicked.connect(self.settings_widget.camera_settings.refresh_devices) - logging.info(f"已将刷新按钮 #{i+1} 连接到refresh_devices方法") - - # 尝试调用一次 - self.settings_widget.camera_settings.refresh_devices() - logging.info("已手动调用refresh_devices方法初始化设备列表") - - # 使用导入的修复模块作为备选方案 - try: - # 使用相对导入,更可靠 - from . import refresh_devices_fix - refresh_devices_fix.fix_camera_refresh_button(self.settings_widget) - logging.info("已应用相机刷新按钮修复(通过相对导入)") - except ImportError: - # 如果相对导入失败,尝试绝对导入 - try: - import refresh_devices_fix - refresh_devices_fix.fix_camera_refresh_button(self.settings_widget) - logging.info("已应用相机刷新按钮修复(通过绝对导入)") - except ImportError: - # 如果绝对导入也失败,直接内联实现修复逻辑 - logging.warning("导入refresh_devices_fix失败,使用内联修复") - self._fix_camera_refresh_button() - except Exception as e: - logging.error(f"应用相机刷新按钮修复失败: {str(e)}") + self._fix_camera_refresh_button() + + # 添加按钮 + 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 _fix_camera_refresh_button(self): """内联实现的刷新按钮修复逻辑,用于导入模块失败的情况""" @@ -134,26 +103,6 @@ class SettingsWindow(QDialog): except Exception as e: logging.error(f"内联修复相机刷新按钮时发生错误: {str(e)}") - - # 添加串口设置到标签页 - self.serial_settings = SerialSettingsWidget(self) - self.settings_widget.tab_widget.addTab(self.serial_settings, "串口设置") - - # 添加电力监控设置到标签页 - self.electricity_settings = ElectricitySettingsUI(self) - self.settings_widget.tab_widget.addTab(self.electricity_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) - self.electricity_settings.settings_changed.connect(self.settings_changed.emit) - - logging.info("SettingsWindow初始化完成") def accept(self): """确认按钮处理,保存所有设置并发送设置变更信号"""