From 968b4108b9a50a09406956582f9f5dd9e12628b3 Mon Sep 17 00:00:00 2001 From: zhu-mengmeng <15588200382@163.com> Date: Sat, 28 Jun 2025 13:02:34 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=9B=B4=E6=96=B0=E7=9B=B8=E6=9C=BA?= =?UTF-8?q?=E6=8E=A7=E5=88=B6=E5=BA=93=E5=8A=A0=E8=BD=BD=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- camera/MvCameraControl_class.py | 50 ++++++++++++++-- db/jtDB.db | Bin 106496 -> 106496 bytes ui/main_window_ui.py | 3 +- ui/settings_ui.py | 10 ++-- widgets/camera_manager.py | 6 ++ widgets/camera_settings_widget.py | 95 ++++++++++++++++++++++-------- widgets/settings_widget.py | 25 ++------ 7 files changed, 132 insertions(+), 57 deletions(-) diff --git a/camera/MvCameraControl_class.py b/camera/MvCameraControl_class.py index bc12318..0f9d2b8 100644 --- a/camera/MvCameraControl_class.py +++ b/camera/MvCameraControl_class.py @@ -3,6 +3,9 @@ import sys import copy import ctypes +import platform +import os +import logging from ctypes import * @@ -11,15 +14,50 @@ from CameraParams_const import * from CameraParams_header import * from MvErrorDefine_const import * -# Python3.8版本修改Dll加载策略, 默认不再搜索Path环境变量, 同时增加winmode参数以兼容旧版本 -dllname = "MvCameraControl.dll" +# 初始化全局变量 +MvCamCtrldll = None + +# 根据操作系统加载不同的库文件 +system_name = platform.system() try: - if "winmode" in ctypes.WinDLL.__init__.__code__.co_varnames: - MvCamCtrldll = WinDLL(dllname, winmode=0) + if system_name == "Windows": + dllname = "MvCameraControl.dll" + # Python3.8版本修改Dll加载策略, 默认不再搜索Path环境变量, 同时增加winmode参数以兼容旧版本 + if "winmode" in ctypes.WinDLL.__init__.__code__.co_varnames: + MvCamCtrldll = WinDLL(dllname, winmode=0) + else: + MvCamCtrldll = WinDLL(dllname) + logging.info(f"Windows系统: 成功加载 {dllname}") + elif system_name == "Darwin": # macOS + # macOS通常使用.dylib或.so文件,尝试多种可能的库名 + possible_libs = [ + "libMvCameraControl.dylib", + "/Library/Frameworks/MvCameraControl.framework/MvCameraControl", + "libMvCameraControl.so", + "MvCameraControl.so" + ] + + lib_loaded = False + for lib in possible_libs: + try: + MvCamCtrldll = CDLL(lib) + logging.info(f"macOS系统: 成功加载 {lib}") + lib_loaded = True + break + except OSError: + continue + + if not lib_loaded: + logging.error("macOS系统: 无法加载相机控制库,请确保已安装Hikvision SDK并设置正确的库路径") + elif system_name == "Linux": + dllname = "libMvCameraControl.so" + MvCamCtrldll = CDLL(dllname) + logging.info(f"Linux系统: 成功加载 {dllname}") else: - MvCamCtrldll = WinDLL(dllname) + logging.error(f"不支持的操作系统: {system_name}") except Exception as e: - print(f"加载MvCameraControl.dll失败: {e}") + logging.error(f"加载相机控制库失败: {str(e)}") + MvCamCtrldll = None diff --git a/db/jtDB.db b/db/jtDB.db index 2fe13471f6a26eea604841e855d20d3cda6b3217..e0c99697c4ecd3d4a4310ad36f6e40089af287f4 100644 GIT binary patch delta 434 zcmZoTz}9epZGtr8sfjYqjHfmx{Iz3z-5h7XJ;+Of&z6M^%Bk5 zEDXksMg~Tvx&~&tM&=5JrdCEKR;K2X63tmyq>Ld_nOLNZAW|7vqzoZa>F84RK+6mu za%t#trdEcQ5UErwQWg-Y6f9Ea5UFG=Qf3gTB$!k^!qbLkRwf2OPbb3Ukfcl@+7hrz Yfpx`Wk%ER|99Ai?uGsDEvluH`0XJV;B>(^b delta 59 zcmV-B0L1@*zy^T829O&8ypbG30lcwb{XPNRgI+(kUOxe*Py;jygqH_!0co=l5EKdn R7=vAxQJMi0x0;#(W(AOc6ixsD diff --git a/ui/main_window_ui.py b/ui/main_window_ui.py index 4dc4d16..62ffca8 100644 --- a/ui/main_window_ui.py +++ b/ui/main_window_ui.py @@ -226,7 +226,8 @@ class MainWindowUI(QMainWindow): # 上料区内容 - 这里可以添加更多控件 self.material_content = QWidget() - self.material_content.setStyleSheet("background-color: black;") # 黑色背景适合显示相机画面 + # 使用透明背景,让相机画面可以正常显示 + self.material_content.setStyleSheet("background-color: transparent;") self.material_content_layout = QVBoxLayout(self.material_content) self.material_content_layout.setContentsMargins(0, 0, 0, 0) # 移除内边距以便相机画面填满 self.material_layout.addWidget(self.material_content) diff --git a/ui/settings_ui.py b/ui/settings_ui.py index 6668dbd..75db6d5 100644 --- a/ui/settings_ui.py +++ b/ui/settings_ui.py @@ -108,12 +108,12 @@ class SettingsUI(QWidget): self.exposure_label = QLabel("曝光时间:") self.exposure_label.setFont(self.normal_font) self.exposure_slider = QSlider(Qt.Horizontal) - self.exposure_slider.setMinimum(1000) - self.exposure_slider.setMaximum(50000) - self.exposure_slider.setValue(20000) + self.exposure_slider.setMinimum(0) + self.exposure_slider.setMaximum(100) # 使用0-100的范围,在代码中映射到0-20000μs + self.exposure_slider.setValue(50) # 默认值设为50%,对应10000μs self.exposure_slider.setTickPosition(QSlider.TicksBelow) - self.exposure_slider.setTickInterval(5000) - self.exposure_value = QLabel("20000 μs") + self.exposure_slider.setTickInterval(10) + self.exposure_value = QLabel("10000 μs") self.exposure_value.setFont(self.normal_font) self.exposure_value.setMinimumWidth(80) diff --git a/widgets/camera_manager.py b/widgets/camera_manager.py index 95ebb7f..f0fd3fc 100644 --- a/widgets/camera_manager.py +++ b/widgets/camera_manager.py @@ -53,6 +53,12 @@ class CameraManager: def enum_devices(self): """枚举相机设备,完全参考BasicDemo.py的enum_devices实现""" try: + # 确保Hikvision SDK已正确加载 + from camera.MvCameraControl_class import MvCamCtrldll + if MvCamCtrldll is None: + logging.error("相机SDK未正确加载,无法枚举设备") + return [] + # 确保先关闭任何已打开的相机 if self.isOpen: self.close_device() diff --git a/widgets/camera_settings_widget.py b/widgets/camera_settings_widget.py index 7e15836..52b4899 100644 --- a/widgets/camera_settings_widget.py +++ b/widgets/camera_settings_widget.py @@ -113,11 +113,12 @@ class CameraSettingsWidget(QObject): # 连接信号和槽 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 + # 以下曝光范围仅用于兼容旧代码,实际的映射在update_exposure_value方法中实现 + self.exposure_min = 0.0 # 0 μs (最小值) + self.exposure_max = 20000.0 # 20000 μs (20ms) - 调整后的最大值 self.gain_min = 0.0 self.gain_max = 15.0 @@ -333,7 +334,9 @@ class CameraSettingsWidget(QObject): device_index = self.get_selected_device_index() if device_index < 0: - QMessageBox.warning(self, "错误", "请先选择一个有效的相机设备!") + # 使用父组件作为消息框的父级 + parent_widget = self.parent if hasattr(self, "parent") else None + QMessageBox.warning(parent_widget, "错误", "请先选择一个有效的相机设备!") return self.open_button.setEnabled(False) @@ -346,9 +349,11 @@ class CameraSettingsWidget(QObject): self.signal_camera_connection.emit(True, "") else: self.signal_camera_connection.emit(False, "打开相机失败") - QMessageBox.warning(self, "错误", "打开相机失败") + parent_widget = self.parent if hasattr(self, "parent") else None + QMessageBox.warning(parent_widget, "错误", "打开相机失败") except Exception as e: - QMessageBox.critical(self, "错误", f"打开相机时发生异常: {str(e)}") + parent_widget = self.parent if hasattr(self, "parent") else None + QMessageBox.critical(parent_widget, "错误", f"打开相机时发生异常: {str(e)}") finally: self.open_button.setText("打开相机") self.update_controls() @@ -376,18 +381,29 @@ class CameraSettingsWidget(QObject): if self.camera_manager.start_grabbing(window_id): self.test_button.setText("停止预览") except Exception as e: - QMessageBox.warning(self, "错误", f"开始预览失败: {e}") + parent_widget = self.parent if hasattr(self, "parent") else None + QMessageBox.warning(parent_widget, "错误", f"开始预览失败: {e}") 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") + """更新曝光值显示(使用线性映射)""" + # 直接使用滑块值(0-100)映射到曝光范围(1000-50000 μs) + # 使用UI中设置的实际曝光范围 + min_exp = 0 # 0 μs + max_exp = 50000 + + # 线性映射 + actual_value = min_exp + (value * (max_exp - min_exp) / 100.0) + + # 防止溢出 + if actual_value > max_exp: + actual_value = max_exp + logging.warning(f"曝光值过大,已限制为{actual_value}μs") + + # 显示整数值,μs级别不需要小数点 + self.exposure_value.setText(f"{int(actual_value)} μs") + logging.debug(f"曝光滑块值: {value}, 映射后曝光值: {int(actual_value)}μs") def update_gain_value(self, value): """更新增益值显示""" @@ -410,13 +426,32 @@ class CameraSettingsWidget(QObject): exposure_time, gain, frame_rate = params - min_log_exp = log10(self.exposure_min) - max_log_exp = log10(self.exposure_max) - self.exposure_slider.setValue(int(((log10(exposure_time) - min_log_exp) / (max_log_exp - min_log_exp)) * 100)) + # 使用线性映射计算曝光滑块值 + min_exp = 0 # 0 μs + max_exp = 20000 # 20000 μs + # 将获取到的曝光时间限制在有效范围内 + if exposure_time < min_exp: + exposure_time = min_exp + elif exposure_time > max_exp: + exposure_time = max_exp + + # 线性映射到滑块值(0-100) + exposure_slider_value = int(((exposure_time - min_exp) / (max_exp - min_exp)) * 100) + + # 确保滑块值在有效范围内 + if exposure_slider_value < 0: + exposure_slider_value = 0 + elif exposure_slider_value > 100: + exposure_slider_value = 100 + + self.exposure_slider.setValue(exposure_slider_value) + + # 其他参数仍使用原始映射方法 self.gain_slider.setValue(int(((gain - self.gain_min) / (self.gain_max - self.gain_min)) * 100)) self.framerate_slider.setValue(int(((frame_rate - self.frame_rate_min) / (self.frame_rate_max - self.frame_rate_min)) * 100)) + logging.debug(f"获取相机参数: 曝光时间={exposure_time}μs -> 滑块值={exposure_slider_value}, 增益={gain}dB, 帧率={frame_rate}fps") self.signal_camera_params_changed.emit(exposure_time, gain, frame_rate) def set_camera_params(self): @@ -424,15 +459,25 @@ class CameraSettingsWidget(QObject): if not self.camera_manager.isOpen: 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 + # 使用线性映射计算曝光时间 + min_exp = 0 # 0 μs + max_exp = 50000 # 50000 μs + # 根据滑块值(0-100)线性映射到曝光时间 + slider_value = self.exposure_slider.value() + exposure_time = min_exp + (slider_value * (max_exp - min_exp) / 100.0) + + # 确保曝光时间在有效范围内 + if exposure_time < min_exp: + exposure_time = min_exp + elif exposure_time > max_exp: + exposure_time = max_exp + + # 增益和帧率保持原来的映射方式 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) + logging.debug(f"设置相机参数: 曝光滑块值={slider_value} -> 曝光时间={exposure_time}μs, 增益={gain}dB, 帧率={frame_rate}fps") self.camera_manager.set_parameters(frame_rate, exposure_time, gain) self.signal_camera_params_changed.emit(exposure_time, gain, frame_rate) @@ -445,11 +490,13 @@ class CameraSettingsWidget(QObject): gain = self.gain_slider.value() frame_rate = self.framerate_slider.value() + parent_widget = self.parent if hasattr(self, "parent") else None + if self.camera_manager.save_params_to_config(exposure, gain, frame_rate): - QMessageBox.information(self, "成功", "相机参数已保存") + QMessageBox.information(parent_widget, "成功", "相机参数已保存") self.settings_changed.emit() else: - QMessageBox.critical(self, "错误", "保存相机参数失败") + QMessageBox.critical(parent_widget, "错误", "保存相机参数失败") def closeEvent(self, event): """窗口关闭事件""" diff --git a/widgets/settings_widget.py b/widgets/settings_widget.py index 54f8b33..6d00ee4 100644 --- a/widgets/settings_widget.py +++ b/widgets/settings_widget.py @@ -51,10 +51,8 @@ class SettingsWidget(SettingsUI): self.camera_settings = CameraSettingsWidget(self) logging.info("相机设置组件创建成功") - # 确保相机设置组件正确初始化信号和槽(手动调用一次connect_signals) - if hasattr(self.camera_settings, 'connect_signals'): - logging.info("手动调用相机设置控制器的connect_signals方法") - self.camera_settings.connect_signals() + # 注意:不再手动调用connect_signals,因为它已经在CameraSettingsWidget的__init__中调用了 + logging.info("相机设置组件已在其初始化时连接信号,不再重复连接") # 添加后处理:直接给刷新按钮添加事件处理 if hasattr(self, 'refresh_button'): @@ -182,23 +180,8 @@ class SettingsWidget(SettingsUI): except Exception as e: logging.error(f"连接电量监控设置信号时出错: {e}") - # 直接连接相机标签页中的刷新按钮 - try: - if hasattr(self, 'camera_tab') and hasattr(self, 'refresh_button') and hasattr(self, 'camera_settings'): - 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() - except Exception as e: - logging.error(f"连接刷新按钮时出错: {e}") + # 不再在这里连接刷新按钮,避免重复连接 + logging.info("刷新按钮已在初始化时连接,不再重复连接") # 数据库类型选择 self.db_type_combo.currentTextChanged.connect(self.update_db_ui_state)