jiateng_ws/widgets/camera_widget.py

298 lines
11 KiB
Python
Raw Normal View History

2025-06-07 10:45:09 +08:00
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()