from PySide6.QtWidgets import QMessageBox, QVBoxLayout from PySide6.QtCore import Qt, Signal import logging import json import os from ui.settings_ui import SettingsUI from utils.sql_utils import SQLUtils from widgets.inspection_settings_widget import InspectionSettingsWidget from widgets.pallet_type_settings_widget import PalletTypeSettingsWidget from widgets.plc_settings_widget import PLCSettingsWidget from utils.config_loader import ConfigLoader from utils.app_mode import AppMode from widgets.camera_settings_widget import CameraSettingsWidget from widgets.serial_settings_widget import SerialSettingsWidget from ui.electricity_settings_ui import ElectricitySettingsUI class SettingsWidget(SettingsUI): # 定义信号 settings_changed = Signal() def __init__(self, parent=None): """初始化设置窗口""" super().__init__(parent) self.parent = parent logging.info("正在初始化SettingsWidget") # 先检查UI中的相机下拉框 if hasattr(self, 'camera_combo') and self.camera_combo is not None: logging.info(f"相机下拉框状态检查: 可见={self.camera_combo.isVisible()}, " f"尺寸={self.camera_combo.size().width()}x{self.camera_combo.size().height()}, " f"包含项={self.camera_combo.count()}") else: logging.error("无法找到相机下拉框(camera_combo)!") # 检查刷新按钮 if hasattr(self, 'refresh_button') and self.refresh_button is not None: logging.info(f"刷新按钮状态检查: 可见={self.refresh_button.isVisible()}, " f"尺寸={self.refresh_button.size().width()}x{self.refresh_button.size().height()}") else: logging.error("无法找到刷新按钮(refresh_button)!") # 创建子设置控制器 try: # 先添加一个测试项到下拉框 if hasattr(self, 'camera_combo'): self.camera_combo.clear() self.camera_combo.addItem("测试项 - 初始化前") logging.info(f"已添加测试项到下拉框,当前项数={self.camera_combo.count()}") self.camera_settings = CameraSettingsWidget(self) logging.info("相机设置组件创建成功") # 注意:不再手动调用connect_signals,因为它已经在CameraSettingsWidget的__init__中调用了 logging.info("相机设置组件已在其初始化时连接信号,不再重复连接") # 添加后处理:直接给刷新按钮添加事件处理 if hasattr(self, 'refresh_button'): logging.info("手动绑定SettingsWidget中的刷新按钮点击事件") try: self.refresh_button.clicked.disconnect() # 断开所有现有连接 except Exception as e: logging.warning(f"断开刷新按钮现有连接时出错: {e}") self.refresh_button.clicked.connect(self.camera_settings.refresh_devices) # 立即调用一次刷新方法 self.camera_settings.refresh_devices() logging.info("已刷新相机设备列表") # 再次检查相机下拉框状态 if hasattr(self, 'camera_combo'): logging.info(f"刷新后相机下拉框状态: 项目数={self.camera_combo.count()}, " f"当前文本={self.camera_combo.currentText() if self.camera_combo.count() > 0 else 'None'}") # 尝试手动向下拉框添加一项 self.camera_combo.addItem("测试项 - 手动添加") logging.info(f"手动添加后下拉框状态: 项目数={self.camera_combo.count()}") except Exception as e: logging.error(f"初始化相机设置组件失败: {e}") self.serial_settings = SerialSettingsWidget(self) self.inspection_settings = InspectionSettingsWidget(self) self.pallet_type_settings = PalletTypeSettingsWidget(self) self.plc_settings = PLCSettingsWidget(self) # 创建电力监控设置组件 try: self.electricity_settings = ElectricitySettingsUI(self) logging.info("电力监控设置组件创建成功") # 移除临时占位符标签并添加电力监控设置部件 if hasattr(self, 'electricity_placeholder'): logging.info("移除电力监控临时占位符") self.electricity_layout.removeWidget(self.electricity_placeholder) self.electricity_placeholder.hide() self.electricity_placeholder.deleteLater() else: logging.warning("未找到电力监控临时占位符标签") # 检查布局是否可用 if hasattr(self, 'electricity_layout'): logging.info("添加电力监控设置部件到布局") self.electricity_layout.addWidget(self.electricity_settings) else: logging.error("无法找到electricity_layout布局") except Exception as e: logging.error(f"初始化电力监控设置组件失败: {e}") # 移除临时占位符标签并添加检验设置部件 if hasattr(self, 'inspection_placeholder'): logging.info("移除临时占位符") self.inspection_layout.removeWidget(self.inspection_placeholder) self.inspection_placeholder.hide() self.inspection_placeholder.deleteLater() else: logging.warning("未找到临时占位符标签") # 检查布局是否可用 if hasattr(self, 'inspection_layout'): logging.info("添加检验设置部件到布局") self.inspection_layout.addWidget(self.inspection_settings) else: logging.error("无法找到inspection_layout布局") # 创建托盘类型设置部件 logging.info("创建PalletTypeSettingsWidget实例") self.pallet_type_settings = PalletTypeSettingsWidget() # 移除临时占位符标签并添加托盘类型设置部件 if hasattr(self, 'pallet_type_placeholder'): logging.info("移除托盘类型临时占位符") self.pallet_type_layout.removeWidget(self.pallet_type_placeholder) self.pallet_type_placeholder.hide() self.pallet_type_placeholder.deleteLater() else: logging.warning("未找到托盘类型临时占位符标签") # 检查布局是否可用 if hasattr(self, 'pallet_type_layout'): logging.info("添加托盘类型设置部件到布局") self.pallet_type_layout.addWidget(self.pallet_type_settings) else: logging.error("无法找到pallet_type_layout布局") # 加载配置文件 self.config_loader = ConfigLoader.get_instance() # 连接信号 self.connect_signals() # 初始化数据库类型UI状态 self.load_db_config() # 初始化运行模式状态 self.load_app_mode() logging.info("SettingsWidget初始化完成") def connect_signals(self): """连接信号和槽""" # 连接子设置控制器的信号 if hasattr(self, 'camera_settings'): try: self.camera_settings.settings_changed.connect(self.on_settings_changed) except Exception as e: logging.error(f"连接相机设置信号时出错: {e}") self.serial_settings.settings_changed.connect(self.on_settings_changed) self.inspection_settings.settings_changed.connect(self.on_settings_changed) 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("已连接电力监控设置信号") except Exception as e: logging.error(f"连接电力监控设置信号时出错: {e}") # 不再在这里连接刷新按钮,避免重复连接 logging.info("刷新按钮已在初始化时连接,不再重复连接") # 数据库类型选择 self.db_type_combo.currentTextChanged.connect(self.update_db_ui_state) # 更新当前使用的数据源 self.current_source_combo.currentTextChanged.connect(self.update_default_source) # 按钮动作 self.test_conn_button.clicked.connect(self.test_connection) self.save_db_button.clicked.connect(self.save_db_settings) # 检验配置变更信号 self.inspection_settings.signal_configs_changed.connect(self.handle_inspection_configs_changed) # 托盘类型配置变更信号 self.pallet_type_settings.signal_pallet_types_changed.connect(self.handle_pallet_types_changed) # 返回按钮 if hasattr(self, 'back_button'): self.back_button.clicked.connect(self.back_to_main) # 运行模式单选按钮 self.standalone_mode_radio.toggled.connect(self.on_app_mode_changed) self.api_mode_radio.toggled.connect(self.on_app_mode_changed) def load_app_mode(self): """加载应用运行模式设置""" try: # 获取当前运行模式 current_mode = AppMode.get_mode() # 设置对应的单选按钮 if current_mode == 'api': self.api_mode_radio.setChecked(True) else: # 默认为standalone self.standalone_mode_radio.setChecked(True) logging.info(f"已加载应用运行模式: {current_mode}") except Exception as e: logging.error(f"加载应用运行模式失败: {e}") # 默认选择单机模式 self.standalone_mode_radio.setChecked(True) def on_app_mode_changed(self, checked): """处理运行模式变更""" if not checked: # 忽略未选中状态的信号 return try: if self.standalone_mode_radio.isChecked(): AppMode.set_mode('standalone') logging.info("已将应用运行模式设置为: 单机模式") elif self.api_mode_radio.isChecked(): AppMode.set_mode('api') logging.info("已将应用运行模式设置为: 接口模式") except Exception as e: logging.error(f"设置应用运行模式失败: {e}") def load_db_config(self): """加载数据库配置""" try: # 获取默认数据源 default_source = self.config_loader.get_value('database.default', 'sqlite').lower() # 设置当前使用的数据源组合框 self._update_source_combo_items() # 更新组合框项目 index = self.current_source_combo.findText(default_source.capitalize(), Qt.MatchFixedString) if index >= 0: self.current_source_combo.setCurrentIndex(index) # 默认选择当前使用的数据源类型 index = self.db_type_combo.findText(default_source.capitalize(), Qt.MatchFixedString) if index >= 0: self.db_type_combo.setCurrentIndex(index) # 更新UI状态 self.update_db_ui_state() except Exception as e: logging.error(f"加载数据库配置失败: {e}") def _update_source_combo_items(self): """更新数据源下拉框项目""" try: # 清空当前项目 self.current_source_combo.clear() # 获取所有配置的数据源 sources = self.config_loader.get_value('database.sources', {}) # 添加数据源到下拉框 for source_name in sources.keys(): # 处理特殊情况:postgresql显示为PostgreSQL display_name = source_name.capitalize() self.current_source_combo.addItem(display_name, source_name) except Exception as e: logging.error(f"更新数据源下拉框失败: {e}") # 添加默认项 self.current_source_combo.addItem("SQLite", "sqlite") def update_db_ui_state(self): """根据选择的数据库类型更新UI状态""" db_type = self.db_type_combo.currentText().lower() # 处理特殊情况:PostgreSQL对应postgresql if db_type == "postgresql": db_type = "postgresql" # 加载选定类型的数据源配置 config_path = f"database.sources.{db_type}" db_config = {} if db_type == "sqlite": # SQLite模式下,只需要数据库文件路径和说明 path = self.config_loader.get_value(f"{config_path}.path", "db/jtDB.db") description = self.config_loader.get_value(f"{config_path}.description", "") self.host_input.setEnabled(False) self.host_input.setText("") self.user_input.setEnabled(False) self.user_input.setText("") self.password_input.setEnabled(False) self.password_input.setText("") self.port_input.setEnabled(False) self.port_input.setText("") self.database_input.setEnabled(True) self.database_input.setText(path) self.desc_input.setText(description) elif db_type == "postgresql": # PostgreSQL模式下,需要完整的连接信息 host = self.config_loader.get_value(f"{config_path}.host", "localhost") port = self.config_loader.get_value(f"{config_path}.port", "5432") user = self.config_loader.get_value(f"{config_path}.user", "postgres") password = self.config_loader.get_value(f"{config_path}.password", "") name = self.config_loader.get_value(f"{config_path}.name", "jtDB") description = self.config_loader.get_value(f"{config_path}.description", "") self.host_input.setEnabled(True) self.host_input.setText(host) self.user_input.setEnabled(True) self.user_input.setText(user) self.password_input.setEnabled(True) self.password_input.setText(password) self.port_input.setEnabled(True) self.port_input.setText(port) self.database_input.setEnabled(True) self.database_input.setText(name) self.desc_input.setText(description) elif db_type == "mysql": # MySQL模式下,需要完整的连接信息 host = self.config_loader.get_value(f"{config_path}.host", "localhost") port = self.config_loader.get_value(f"{config_path}.port", "3306") user = self.config_loader.get_value(f"{config_path}.user", "root") password = self.config_loader.get_value(f"{config_path}.password", "") name = self.config_loader.get_value(f"{config_path}.name", "jtDB") description = self.config_loader.get_value(f"{config_path}.description", "") self.host_input.setEnabled(True) self.host_input.setText(host) self.user_input.setEnabled(True) self.user_input.setText(user) self.password_input.setEnabled(True) self.password_input.setText(password) self.port_input.setEnabled(True) self.port_input.setText(port) self.database_input.setEnabled(True) self.database_input.setText(name) self.desc_input.setText(description) def update_default_source(self): """更新默认使用的数据源""" default_source = self.current_source_combo.currentText().lower() self.config_loader.set_value('database.default', default_source) logging.info(f"已更新默认使用的数据源为: {default_source}") def get_db_type(self): """获取当前选择的数据库类型""" return self.db_type_combo.currentText().lower() def get_connection_params(self): """获取数据库连接参数""" db_type = self.get_db_type() params = { "database": self.database_input.text().strip() } if db_type != "sqlite": params.update({ "host": self.host_input.text().strip(), "user": self.user_input.text().strip(), "password": self.password_input.text().strip(), "port": self.port_input.text().strip() }) return db_type, params def test_connection(self): """测试数据库连接""" db_type, params = self.get_connection_params() try: # 创建数据库连接 db = SQLUtils(db_type, **params) # 测试连接 db.cursor.execute("SELECT 1") # 关闭连接 db.close() # 显示成功消息 QMessageBox.information(self, "连接成功", "数据库连接测试成功!") logging.info(f"数据库连接测试成功,类型: {db_type}") except Exception as e: # 显示错误消息 QMessageBox.critical(self, "连接失败", f"数据库连接测试失败!\n\n错误: {str(e)}") logging.error(f"数据库连接测试失败,类型: {db_type}, 错误: {str(e)}") def save_db_settings(self): """保存数据库设置""" db_type = self.get_db_type() params = self.get_connection_params()[1] desc = self.desc_input.text().strip() try: # 构建要保存的配置数据 config_path = f"database.sources.{db_type}" if db_type == "sqlite": self.config_loader.set_value(f"{config_path}.path", params["database"]) self.config_loader.set_value(f"{config_path}.description", desc) else: self.config_loader.set_value(f"{config_path}.host", params["host"]) self.config_loader.set_value(f"{config_path}.port", params["port"]) self.config_loader.set_value(f"{config_path}.user", params["user"]) self.config_loader.set_value(f"{config_path}.password", params["password"]) self.config_loader.set_value(f"{config_path}.name", params["database"]) self.config_loader.set_value(f"{config_path}.description", desc) # 更新数据源下拉框 self._update_source_combo_items() # 构建要显示的消息 settings_info = f"数据库类型: {db_type}\n" for key, value in params.items(): if key != "password": settings_info += f"{key}: {value}\n" else: settings_info += f"{key}: {'*' * len(value)}\n" settings_info += f"说明: {desc}" # 显示成功消息 QMessageBox.information(self, "设置已保存", f"数据库设置已保存!\n\n{settings_info}") logging.info(f"数据库设置已保存,类型: {db_type}") except Exception as e: # 显示错误消息 QMessageBox.critical(self, "保存失败", f"保存数据库设置失败!\n\n错误: {str(e)}") logging.error(f"保存数据库设置失败: {str(e)}") def handle_inspection_configs_changed(self): """处理检验配置变更""" logging.info("检验配置已更新") # 如果有父窗口,通知父窗口更新检验配置 if self.parent and hasattr(self.parent, 'update_inspection_columns'): self.parent.update_inspection_columns() def handle_pallet_types_changed(self): """处理托盘类型配置变更""" logging.info("托盘类型配置已更新") # 如果有父窗口,通知父窗口更新托盘类型 if self.parent and hasattr(self.parent, 'update_pallet_types'): self.parent.update_pallet_types() def back_to_main(self): """返回主页""" if self.parent and hasattr(self.parent, 'show_main_page'): self.parent.show_main_page() def on_settings_changed(self): """处理设置变更信号""" logging.info("设置已变更,发送settings_changed信号") # 发送信号 self.settings_changed.emit()