import sys import os import logging from ctypes import * from math import log10 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.QtCore import Qt, Signal USE_PYSIDE6 = True except ImportError: from PyQt5.QtWidgets import QWidget, QMessageBox from PyQt5.QtCore import Qt from PyQt5.QtCore import pyqtSignal as Signal USE_PYSIDE6 = False # 导入相机相关模块 from camera.MvCameraControl_class import MvCamera from camera.CamOperation_class import CameraOperation from camera.MvErrorDefine_const import * from camera.CameraParams_header import * from ui.settings_ui import SettingsUI # 导入相机管理器 from widgets.camera_manager import CameraManager class CameraSettingsWidget(SettingsUI): """相机设置控制器,管理相机设置并提供与主窗口相机显示部分的通信""" # 定义信号 signal_camera_connection = Signal(bool, str) # 相机连接状态信号 (是否连接, 错误消息) signal_camera_params_changed = Signal(float, float, float) # 相机参数变化信号 (曝光, 增益, 帧率) signal_camera_error = Signal(str) # 相机错误信号 def __init__(self, parent=None): super().__init__(parent) # 获取相机管理器实例 self.camera_manager = CameraManager.get_instance() # 初始化UI控件状态 self.update_controls() # 连接信号和槽 self.connect_signals() # 初始化相机参数范围 self.frame_rate_min = 1.0 self.frame_rate_max = 60.0 self.exposure_min = 20.0 self.exposure_max = 1000000.0 self.gain_min = 0.0 self.gain_max = 15.0 # 枚举设备 self.refresh_devices() def connect_signals(self): """连接信号和槽""" # 设备选择和刷新 self.refresh_button.clicked.connect(self.refresh_devices) # 相机控制按钮 self.open_button.clicked.connect(self.open_camera) self.close_button.clicked.connect(self.close_camera) self.test_button.clicked.connect(self.test_camera) # 参数滑块 self.exposure_slider.valueChanged.connect(self.update_exposure_value) self.gain_slider.valueChanged.connect(self.update_gain_value) self.framerate_slider.valueChanged.connect(self.update_frame_rate_value) # 参数操作按钮 self.get_params_button.clicked.connect(self.get_camera_params) self.set_params_button.clicked.connect(self.set_camera_params) self.save_camera_button.clicked.connect(self.save_camera_params) def refresh_devices(self): """刷新设备列表""" self.camera_combo.clear() # 枚举设备 devices_info = self.camera_manager.enum_devices() if not devices_info: self.camera_combo.addItem("未发现相机设备") self.camera_combo.setEnabled(False) self.open_button.setEnabled(False) return # 添加设备到下拉框 for device in devices_info: self.camera_combo.addItem(device["display"], device["index"]) self.camera_combo.setEnabled(True) self.open_button.setEnabled(True) # 更新控件状态 self.update_controls() def get_selected_device_index(self): """获取当前选中的设备索引""" if self.camera_combo.count() == 0: return -1 return self.camera_combo.currentData() def update_controls(self): """更新控件状态""" is_open = self.camera_manager.isOpen is_grabbing = self.camera_manager.isGrabbing # 设备选择和刷新 self.camera_combo.setEnabled(not is_open) self.refresh_button.setEnabled(not is_open) # 相机控制按钮 self.open_button.setEnabled(not is_open and self.camera_combo.count() > 0 and self.camera_combo.currentData() is not None) self.close_button.setEnabled(is_open) self.test_button.setEnabled(is_open and not is_grabbing) # 参数滑块 self.exposure_slider.setEnabled(is_open) self.gain_slider.setEnabled(is_open) self.framerate_slider.setEnabled(is_open) # 参数操作按钮 self.get_params_button.setEnabled(is_open) self.set_params_button.setEnabled(is_open) self.save_camera_button.setEnabled(is_open) def open_camera(self): """打开相机""" if self.camera_manager.isOpen: QMessageBox.warning(self, "错误", "相机已经打开!") return device_index = self.get_selected_device_index() if device_index < 0: QMessageBox.warning(self, "错误", "请先选择一个相机设备!") return success = self.camera_manager.open_device(device_index) if success: # 获取相机参数并更新UI self.get_camera_params() # 更新控件状态 self.update_controls() # 发送连接信号 self.signal_camera_connection.emit(True, "") else: # 发送连接失败信号 self.signal_camera_connection.emit(False, "打开相机失败") def close_camera(self): """关闭相机""" if not self.camera_manager.isOpen: return success = self.camera_manager.close_device() # 更新控件状态 self.update_controls() # 发送连接信号 if success: self.signal_camera_connection.emit(False, "") else: self.signal_camera_connection.emit(False, "关闭相机出错") def test_camera(self): """测试相机(在预览窗口显示图像)""" if not self.camera_manager.isOpen: QMessageBox.warning(self, "错误", "请先打开相机!") return if self.camera_manager.isGrabbing: # 停止预览 self.camera_manager.stop_grabbing() self.test_button.setText("开始预览") self.update_controls() else: # 获取预览窗口句柄 try: # 尝试使用PySide6方式获取窗口句柄 window_id = int(self.preview_frame.winId()) except: try: # 尝试使用PyQt5方式获取窗口句柄 window_id = self.preview_frame.winId().__int__() except: # 其他情况 window_id = int(self.preview_frame.winId()) # 开始预览 success = self.camera_manager.start_grabbing(window_id) if success: self.test_button.setText("停止预览") else: QMessageBox.warning(self, "错误", "开始预览失败!") # 更新控件状态 self.update_controls() def update_exposure_value(self, value): """更新曝光值显示""" # 将滑块值转换为实际曝光值(对数映射) min_log = log10(self.exposure_min) max_log = log10(self.exposure_max) log_range = max_log - min_log log_value = min_log + (value / 100.0) * log_range actual_value = 10 ** log_value # 更新显示 self.exposure_value.setText(f"{actual_value:.1f} μs") def update_gain_value(self, value): """更新增益值显示""" # 将滑块值转换为实际增益值 actual_value = self.gain_min + (value / 100.0) * (self.gain_max - self.gain_min) # 更新显示 self.gain_value.setText(f"{actual_value:.1f} dB") def update_frame_rate_value(self, value): """更新帧率值显示""" # 将滑块值转换为实际帧率值 actual_value = self.frame_rate_min + (value / 100.0) * (self.frame_rate_max - self.frame_rate_min) # 更新显示 self.framerate_value.setText(f"{actual_value:.1f} fps") def get_camera_params(self): """获取相机参数""" if not self.camera_manager.isOpen: QMessageBox.warning(self, "错误", "请先打开相机!") return # 获取参数 params = self.camera_manager.get_parameters() if not params: QMessageBox.warning(self, "错误", "获取相机参数失败!") return exposure_time, gain, frame_rate = params # 更新滑块值 # 曝光时间(对数映射) min_log = log10(self.exposure_min) max_log = log10(self.exposure_max) log_range = max_log - min_log log_value = log10(exposure_time) slider_value = int(((log_value - min_log) / log_range) * 100) self.exposure_slider.setValue(slider_value) # 增益 slider_value = int(((gain - self.gain_min) / (self.gain_max - self.gain_min)) * 100) self.gain_slider.setValue(slider_value) # 帧率 slider_value = int(((frame_rate - self.frame_rate_min) / (self.frame_rate_max - self.frame_rate_min)) * 100) self.framerate_slider.setValue(slider_value) # 发送参数变化信号 self.signal_camera_params_changed.emit(exposure_time, gain, frame_rate) def set_camera_params(self): """设置相机参数""" if not self.camera_manager.isOpen: QMessageBox.warning(self, "错误", "请先打开相机!") return # 从滑块获取参数值 # 曝光时间(对数映射) min_log = log10(self.exposure_min) max_log = log10(self.exposure_max) log_range = max_log - min_log log_value = min_log + (self.exposure_slider.value() / 100.0) * log_range exposure_time = 10 ** log_value # 增益 gain = self.gain_min + (self.gain_slider.value() / 100.0) * (self.gain_max - self.gain_min) # 帧率 frame_rate = self.frame_rate_min + (self.framerate_slider.value() / 100.0) * (self.frame_rate_max - self.frame_rate_min) # 设置参数 success = self.camera_manager.set_parameters(frame_rate, exposure_time, gain) if success: QMessageBox.information(self, "成功", "相机参数设置成功!") # 发送参数变化信号 self.signal_camera_params_changed.emit(exposure_time, gain, frame_rate) else: QMessageBox.warning(self, "错误", "相机参数设置失败!") def save_camera_params(self): """保存相机参数""" if not self.camera_manager.isOpen: QMessageBox.warning(self, "错误", "请先打开相机!") return # 实现保存参数到配置文件的功能 # TODO: 待实现 QMessageBox.information(self, "提示", "参数保存功能尚未实现") def closeEvent(self, event): """窗口关闭事件""" # 确保关闭相机 if self.camera_manager.isOpen: self.camera_manager.close_device() # 处理事件 super().closeEvent(event)