feat: 更新相机控制库加载逻辑

This commit is contained in:
zhu-mengmeng 2025-06-28 13:02:34 +08:00
parent 074b656bbc
commit 968b4108b9
7 changed files with 132 additions and 57 deletions

View File

@ -3,6 +3,9 @@
import sys import sys
import copy import copy
import ctypes import ctypes
import platform
import os
import logging
from ctypes import * from ctypes import *
@ -11,15 +14,50 @@ from CameraParams_const import *
from CameraParams_header import * from CameraParams_header import *
from MvErrorDefine_const import * from MvErrorDefine_const import *
# Python3.8版本修改Dll加载策略, 默认不再搜索Path环境变量, 同时增加winmode参数以兼容旧版本 # 初始化全局变量
dllname = "MvCameraControl.dll" MvCamCtrldll = None
# 根据操作系统加载不同的库文件
system_name = platform.system()
try: try:
if system_name == "Windows":
dllname = "MvCameraControl.dll"
# Python3.8版本修改Dll加载策略, 默认不再搜索Path环境变量, 同时增加winmode参数以兼容旧版本
if "winmode" in ctypes.WinDLL.__init__.__code__.co_varnames: if "winmode" in ctypes.WinDLL.__init__.__code__.co_varnames:
MvCamCtrldll = WinDLL(dllname, winmode=0) MvCamCtrldll = WinDLL(dllname, winmode=0)
else: else:
MvCamCtrldll = WinDLL(dllname) 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:
logging.error(f"不支持的操作系统: {system_name}")
except Exception as e: except Exception as e:
print(f"加载MvCameraControl.dll失败: {e}") logging.error(f"加载相机控制库失败: {str(e)}")
MvCamCtrldll = None

Binary file not shown.

View File

@ -226,7 +226,8 @@ class MainWindowUI(QMainWindow):
# 上料区内容 - 这里可以添加更多控件 # 上料区内容 - 这里可以添加更多控件
self.material_content = QWidget() 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 = QVBoxLayout(self.material_content)
self.material_content_layout.setContentsMargins(0, 0, 0, 0) # 移除内边距以便相机画面填满 self.material_content_layout.setContentsMargins(0, 0, 0, 0) # 移除内边距以便相机画面填满
self.material_layout.addWidget(self.material_content) self.material_layout.addWidget(self.material_content)

View File

@ -108,12 +108,12 @@ class SettingsUI(QWidget):
self.exposure_label = QLabel("曝光时间:") self.exposure_label = QLabel("曝光时间:")
self.exposure_label.setFont(self.normal_font) self.exposure_label.setFont(self.normal_font)
self.exposure_slider = QSlider(Qt.Horizontal) self.exposure_slider = QSlider(Qt.Horizontal)
self.exposure_slider.setMinimum(1000) self.exposure_slider.setMinimum(0)
self.exposure_slider.setMaximum(50000) self.exposure_slider.setMaximum(100) # 使用0-100的范围在代码中映射到0-20000μs
self.exposure_slider.setValue(20000) self.exposure_slider.setValue(50) # 默认值设为50%对应10000μs
self.exposure_slider.setTickPosition(QSlider.TicksBelow) self.exposure_slider.setTickPosition(QSlider.TicksBelow)
self.exposure_slider.setTickInterval(5000) self.exposure_slider.setTickInterval(10)
self.exposure_value = QLabel("20000 μs") self.exposure_value = QLabel("10000 μs")
self.exposure_value.setFont(self.normal_font) self.exposure_value.setFont(self.normal_font)
self.exposure_value.setMinimumWidth(80) self.exposure_value.setMinimumWidth(80)

View File

@ -53,6 +53,12 @@ class CameraManager:
def enum_devices(self): def enum_devices(self):
"""枚举相机设备完全参考BasicDemo.py的enum_devices实现""" """枚举相机设备完全参考BasicDemo.py的enum_devices实现"""
try: try:
# 确保Hikvision SDK已正确加载
from camera.MvCameraControl_class import MvCamCtrldll
if MvCamCtrldll is None:
logging.error("相机SDK未正确加载无法枚举设备")
return []
# 确保先关闭任何已打开的相机 # 确保先关闭任何已打开的相机
if self.isOpen: if self.isOpen:
self.close_device() self.close_device()

View File

@ -113,11 +113,12 @@ class CameraSettingsWidget(QObject):
# 连接信号和槽 # 连接信号和槽
self.connect_signals() self.connect_signals()
# 初始化相机参数范围 # 初始化相机参数范围 - 注意:曝光参数的实际范围已改为线性映射
self.frame_rate_min = 1.0 self.frame_rate_min = 1.0
self.frame_rate_max = 60.0 self.frame_rate_max = 60.0
self.exposure_min = 20.0 # 以下曝光范围仅用于兼容旧代码实际的映射在update_exposure_value方法中实现
self.exposure_max = 1000000.0 self.exposure_min = 0.0 # 0 μs (最小值)
self.exposure_max = 20000.0 # 20000 μs (20ms) - 调整后的最大值
self.gain_min = 0.0 self.gain_min = 0.0
self.gain_max = 15.0 self.gain_max = 15.0
@ -333,7 +334,9 @@ class CameraSettingsWidget(QObject):
device_index = self.get_selected_device_index() device_index = self.get_selected_device_index()
if device_index < 0: if device_index < 0:
QMessageBox.warning(self, "错误", "请先选择一个有效的相机设备!") # 使用父组件作为消息框的父级
parent_widget = self.parent if hasattr(self, "parent") else None
QMessageBox.warning(parent_widget, "错误", "请先选择一个有效的相机设备!")
return return
self.open_button.setEnabled(False) self.open_button.setEnabled(False)
@ -346,9 +349,11 @@ class CameraSettingsWidget(QObject):
self.signal_camera_connection.emit(True, "") self.signal_camera_connection.emit(True, "")
else: else:
self.signal_camera_connection.emit(False, "打开相机失败") 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: 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: finally:
self.open_button.setText("打开相机") self.open_button.setText("打开相机")
self.update_controls() self.update_controls()
@ -376,18 +381,29 @@ class CameraSettingsWidget(QObject):
if self.camera_manager.start_grabbing(window_id): if self.camera_manager.start_grabbing(window_id):
self.test_button.setText("停止预览") self.test_button.setText("停止预览")
except Exception as e: 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() self.update_controls()
def update_exposure_value(self, value): def update_exposure_value(self, value):
"""更新曝光值显示""" """更新曝光值显示(使用线性映射)"""
min_log = log10(self.exposure_min) # 直接使用滑块值0-100映射到曝光范围1000-50000 μs
max_log = log10(self.exposure_max) # 使用UI中设置的实际曝光范围
log_range = max_log - min_log min_exp = 0 # 0 μs
log_value = min_log + (value / 100.0) * log_range max_exp = 50000
actual_value = 10 ** log_value
self.exposure_value.setText(f"{actual_value:.1f} μs") # 线性映射
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): def update_gain_value(self, value):
"""更新增益值显示""" """更新增益值显示"""
@ -410,13 +426,32 @@ class CameraSettingsWidget(QObject):
exposure_time, gain, frame_rate = params exposure_time, gain, frame_rate = params
min_log_exp = log10(self.exposure_min) # 使用线性映射计算曝光滑块值
max_log_exp = log10(self.exposure_max) min_exp = 0 # 0 μs
self.exposure_slider.setValue(int(((log10(exposure_time) - min_log_exp) / (max_log_exp - min_log_exp)) * 100)) 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.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)) 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) self.signal_camera_params_changed.emit(exposure_time, gain, frame_rate)
def set_camera_params(self): def set_camera_params(self):
@ -424,15 +459,25 @@ class CameraSettingsWidget(QObject):
if not self.camera_manager.isOpen: if not self.camera_manager.isOpen:
return return
min_log = log10(self.exposure_min) # 使用线性映射计算曝光时间
max_log = log10(self.exposure_max) min_exp = 0 # 0 μs
log_range = max_log - min_log max_exp = 50000 # 50000 μs
log_value = min_log + (self.exposure_slider.value() / 100.0) * log_range
exposure_time = 10 ** log_value
# 根据滑块值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) 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) 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.camera_manager.set_parameters(frame_rate, exposure_time, gain)
self.signal_camera_params_changed.emit(exposure_time, gain, frame_rate) self.signal_camera_params_changed.emit(exposure_time, gain, frame_rate)
@ -445,11 +490,13 @@ class CameraSettingsWidget(QObject):
gain = self.gain_slider.value() gain = self.gain_slider.value()
frame_rate = self.framerate_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): if self.camera_manager.save_params_to_config(exposure, gain, frame_rate):
QMessageBox.information(self, "成功", "相机参数已保存") QMessageBox.information(parent_widget, "成功", "相机参数已保存")
self.settings_changed.emit() self.settings_changed.emit()
else: else:
QMessageBox.critical(self, "错误", "保存相机参数失败") QMessageBox.critical(parent_widget, "错误", "保存相机参数失败")
def closeEvent(self, event): def closeEvent(self, event):
"""窗口关闭事件""" """窗口关闭事件"""

View File

@ -51,10 +51,8 @@ class SettingsWidget(SettingsUI):
self.camera_settings = CameraSettingsWidget(self) self.camera_settings = CameraSettingsWidget(self)
logging.info("相机设置组件创建成功") logging.info("相机设置组件创建成功")
# 确保相机设置组件正确初始化信号和槽手动调用一次connect_signals # 注意不再手动调用connect_signals因为它已经在CameraSettingsWidget的__init__中调用了
if hasattr(self.camera_settings, 'connect_signals'): logging.info("相机设置组件已在其初始化时连接信号,不再重复连接")
logging.info("手动调用相机设置控制器的connect_signals方法")
self.camera_settings.connect_signals()
# 添加后处理:直接给刷新按钮添加事件处理 # 添加后处理:直接给刷新按钮添加事件处理
if hasattr(self, 'refresh_button'): if hasattr(self, 'refresh_button'):
@ -182,23 +180,8 @@ class SettingsWidget(SettingsUI):
except Exception as e: except Exception as e:
logging.error(f"连接电量监控设置信号时出错: {e}") logging.error(f"连接电量监控设置信号时出错: {e}")
# 直接连接相机标签页中的刷新按钮 # 不再在这里连接刷新按钮,避免重复连接
try: logging.info("刷新按钮已在初始化时连接,不再重复连接")
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}")
# 数据库类型选择 # 数据库类型选择
self.db_type_combo.currentTextChanged.connect(self.update_db_ui_state) self.db_type_combo.currentTextChanged.connect(self.update_db_ui_state)