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