jiateng_ws/widgets/camera_widget.py
2025-06-07 10:45:09 +08:00

298 lines
11 KiB
Python
Raw Permalink 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.

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