298 lines
11 KiB
Python
298 lines
11 KiB
Python
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() |