import sys import os import logging from ctypes import * from camera.CameraParams_const import * # 添加相机模块路径 sys.path.append(os.path.join(os.path.dirname(os.path.dirname(__file__)), "camera")) # 导入相机相关模块 from camera.MvCameraControl_class import MvCamera from camera.CamOperation_class import CameraOperation from camera.MvErrorDefine_const import * from camera.CameraParams_header import * # 导入MV_CC_DEVICE_INFO_LIST等类型 # 尝试从PySide6导入,如果失败则从PyQt5导入 try: from PySide6.QtWidgets import QWidget, QVBoxLayout, QHBoxLayout, QLabel, QPushButton, QComboBox, QMessageBox, QFrame from PySide6.QtCore import Signal USE_PYSIDE6 = True except ImportError: from PyQt5.QtWidgets import QWidget, QVBoxLayout, QHBoxLayout, QLabel, QPushButton, QComboBox, QMessageBox, QFrame from PyQt5.QtCore import pyqtSignal as Signal USE_PYSIDE6 = False class CameraWidget(QWidget): """海康威视工业相机控制组件""" # 定义信号 camera_connected = Signal(bool) camera_error = Signal(str) def __init__(self, parent=None): super().__init__(parent) # 初始化相机变量 self.deviceList = None self.cam = MvCamera() self.nSelCamIndex = 0 self.obj_cam_operation = None self.isOpen = False self.isGrabbing = False # 初始化UI self.init_ui() # 枚举相机设备 self.enum_devices() def init_ui(self): # 主布局 self.main_layout = QVBoxLayout(self) self.main_layout.setContentsMargins(5, 5, 5, 5) self.main_layout.setSpacing(5) # 相机选择区域 self.device_layout = QHBoxLayout() self.device_label = QLabel("相机:") self.device_combo = QComboBox() self.device_combo.setMinimumWidth(150) self.refresh_button = QPushButton("刷新") self.refresh_button.clicked.connect(self.enum_devices) self.device_layout.addWidget(self.device_label) self.device_layout.addWidget(self.device_combo) self.device_layout.addWidget(self.refresh_button) self.device_layout.addStretch() # 相机控制区域 self.control_layout = QHBoxLayout() self.open_button = QPushButton("打开相机") self.open_button.clicked.connect(self.open_device) self.close_button = QPushButton("关闭相机") self.close_button.clicked.connect(self.close_device) self.start_button = QPushButton("开始采集") self.start_button.clicked.connect(self.start_grabbing) self.stop_button = QPushButton("停止采集") self.stop_button.clicked.connect(self.stop_grabbing) self.control_layout.addWidget(self.open_button) self.control_layout.addWidget(self.close_button) self.control_layout.addWidget(self.start_button) self.control_layout.addWidget(self.stop_button) # 视频显示区域 self.display_frame = QFrame() self.display_frame.setFrameShape(QFrame.Box) self.display_frame.setLineWidth(1) self.display_frame.setMinimumHeight(200) self.display_frame.setStyleSheet("background-color: black;") # 将所有组件添加到主布局 self.main_layout.addLayout(self.device_layout) self.main_layout.addLayout(self.control_layout) self.main_layout.addWidget(self.display_frame, 1) # 显示区域占据剩余空间 # 初始化按钮状态 self.update_controls() def update_controls(self): """更新控件状态""" has_devices = self.device_combo.count() > 0 self.open_button.setEnabled(has_devices and not self.isOpen) self.close_button.setEnabled(self.isOpen) self.start_button.setEnabled(self.isOpen and not self.isGrabbing) self.stop_button.setEnabled(self.isOpen and self.isGrabbing) self.device_combo.setEnabled(not self.isOpen) self.refresh_button.setEnabled(not self.isOpen) def enum_devices(self): """枚举相机设备""" try: # 初始化SDK MvCamera.MV_CC_Initialize() # 清空设备列表 self.device_combo.clear() # 枚举设备 self.deviceList = MV_CC_DEVICE_INFO_LIST() n_layer_type = (MV_GIGE_DEVICE | MV_USB_DEVICE | MV_GENTL_CAMERALINK_DEVICE | MV_GENTL_CXP_DEVICE | MV_GENTL_XOF_DEVICE) ret = MvCamera.MV_CC_EnumDevices(n_layer_type, self.deviceList) if ret != 0: error_msg = f"枚举设备失败! 错误码: 0x{ret:x}" self.camera_error.emit(error_msg) logging.error(error_msg) return if self.deviceList.nDeviceNum == 0: logging.info("未找到相机设备") return logging.info(f"找到 {self.deviceList.nDeviceNum} 个相机设备") # 将设备添加到下拉列表 for i in range(0, self.deviceList.nDeviceNum): mvcc_dev_info = cast(self.deviceList.pDeviceInfo[i], POINTER(MV_CC_DEVICE_INFO)).contents if mvcc_dev_info.nTLayerType == MV_GIGE_DEVICE: # GigE相机 nip1 = ((mvcc_dev_info.SpecialInfo.stGigEInfo.nCurrentIp & 0xff000000) >> 24) nip2 = ((mvcc_dev_info.SpecialInfo.stGigEInfo.nCurrentIp & 0x00ff0000) >> 16) nip3 = ((mvcc_dev_info.SpecialInfo.stGigEInfo.nCurrentIp & 0x0000ff00) >> 8) nip4 = (mvcc_dev_info.SpecialInfo.stGigEInfo.nCurrentIp & 0x000000ff) ip = f"{nip1}.{nip2}.{nip3}.{nip4}" device_info = f"[{i}]GigE: {ip}" self.device_combo.addItem(device_info) elif mvcc_dev_info.nTLayerType == MV_USB_DEVICE: # USB相机 device_info = f"[{i}]USB" self.device_combo.addItem(device_info) else: # 其他类型相机 device_info = f"[{i}]Other" self.device_combo.addItem(device_info) # 更新控件状态 self.update_controls() except Exception as e: error_msg = f"枚举设备时发生异常: {str(e)}" self.camera_error.emit(error_msg) logging.error(error_msg) def get_selected_device_index(self): """获取选中的设备索引""" if self.device_combo.count() == 0: return -1 device_text = self.device_combo.currentText() start_idx = device_text.find('[') + 1 end_idx = device_text.find(']') if start_idx >= 0 and end_idx > start_idx: return int(device_text[start_idx:end_idx]) return 0 def open_device(self): """打开相机设备""" if self.isOpen: QMessageBox.warning(self, "错误", "相机已经打开!") return try: self.nSelCamIndex = self.get_selected_device_index() if self.nSelCamIndex < 0: QMessageBox.warning(self, "错误", "请选择一个相机!") return self.obj_cam_operation = CameraOperation(self.cam, self.deviceList, self.nSelCamIndex) ret = self.obj_cam_operation.Open_device() if ret != 0: error_msg = f"打开相机失败! 错误码: 0x{ret:x}" QMessageBox.warning(self, "错误", error_msg) self.isOpen = False logging.error(error_msg) else: # 设置连续模式 self.obj_cam_operation.Set_trigger_mode(False) # 获取参数 self.obj_cam_operation.Get_parameter() self.isOpen = True self.camera_connected.emit(True) logging.info("相机已打开") self.update_controls() except Exception as e: error_msg = f"打开相机时发生异常: {str(e)}" QMessageBox.warning(self, "错误", error_msg) logging.error(error_msg) self.isOpen = False self.update_controls() def close_device(self): """关闭相机设备""" if not self.isOpen: return try: if self.isGrabbing: self.stop_grabbing() ret = self.obj_cam_operation.Close_device() if ret != 0: error_msg = f"关闭相机失败! 错误码: 0x{ret:x}" QMessageBox.warning(self, "错误", error_msg) logging.error(error_msg) else: self.isOpen = False self.camera_connected.emit(False) logging.info("相机已关闭") self.update_controls() except Exception as e: error_msg = f"关闭相机时发生异常: {str(e)}" logging.error(error_msg) self.isOpen = False self.update_controls() def start_grabbing(self): """开始取图""" if not self.isOpen or self.isGrabbing: return try: # 根据使用的框架不同,获取窗口句柄的方式不同 if USE_PYSIDE6: window_id = int(self.display_frame.winId()) else: window_id = self.display_frame.winId() ret = self.obj_cam_operation.Start_grabbing(window_id) if ret != 0: error_msg = f"开始取图失败! 错误码: 0x{ret:x}" QMessageBox.warning(self, "错误", error_msg) logging.error(error_msg) else: self.isGrabbing = True logging.info("开始图像采集") self.update_controls() except Exception as e: error_msg = f"开始取图时发生异常: {str(e)}" QMessageBox.warning(self, "错误", error_msg) logging.error(error_msg) self.isGrabbing = False self.update_controls() def stop_grabbing(self): """停止取图""" if not self.isOpen or not self.isGrabbing: return try: ret = self.obj_cam_operation.Stop_grabbing() if ret != 0: error_msg = f"停止取图失败! 错误码: 0x{ret:x}" QMessageBox.warning(self, "错误", error_msg) logging.error(error_msg) else: self.isGrabbing = False logging.info("停止图像采集") self.update_controls() except Exception as e: error_msg = f"停止取图时发生异常: {str(e)}" logging.error(error_msg) self.isGrabbing = False self.update_controls() def closeEvent(self, event): """窗口关闭事件""" if self.isOpen: self.close_device() event.accept()