jiateng_ws/widgets/settings_widget.py
2025-06-28 13:02:34 +08:00

473 lines
21 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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()