# -*- coding: utf-8 -*- """ 海康相机管理模块 本模块提供了完整的海康相机管理功能,包括: 1. 设备枚举和选择 2. 相机打开和关闭 3. 图像采集和显示 4. 曝光、增益等参数控制 5. 异常处理和重连机制 6. 相机参数控制界面 前置条件: 1. 已安装海康 MVS SDK 2. 已配置 Python 环境 3. 相机已正确连接 """ import sys import ctypes import threading import time import os import tkinter as tk from tkinter import ttk # 添加SDK路径 sys.path.append('Python/MvImport') from MvCameraControl_class import * from MvErrorDefine_const import * from CameraParams_header import * import cv2 import numpy as np # 枚举值定义 MV_EXPOSURE_AUTO_MODE_OFF = 0 MV_GAIN_AUTO_MODE_OFF = 0 MV_BALANCE_WHITE_AUTO_OFF = 0 class Camera: """ 海康相机管理类 功能: - 设备枚举和选择 - 相机打开和关闭 - 图像采集和显示 - 曝光、增益等参数控制 - 异常处理和重连机制 """ def __init__(self): """ 初始化相机管理器 """ # 初始化SDK MvCamera.MV_CC_Initialize() self.cam = None # 相机实例 self.device_list = MV_CC_DEVICE_INFO_LIST() # 设备列表 self.is_open = False # 相机是否打开 self.is_grabbing = False # 是否正在取流 self.b_exit = False # 退出标志 self.work_thread = None # 工作线程 self.current_exp = 5000.0 # 当前曝光时间(微秒) self.current_gain = 0.0 # 当前增益(dB) self.current_fps = 30.0 # 当前帧率 self.last_frame = None # 最后一帧图像 def to_hex_str(self, num): """ 将返回的错误码转换为十六进制显示 Args: num: 错误码 Returns: str: 十六进制字符串 """ cha_dic = {10: 'a', 11: 'b', 12: 'c', 13: 'd', 14: 'e', 15: 'f'} hex_str = "" if num < 0: num = num + 2 ** 32 while num >= 16: digit = num % 16 hex_str = cha_dic.get(digit, str(digit)) + hex_str num //= 16 hex_str = cha_dic.get(num, str(num)) + hex_str return hex_str def decoding_char(self, ctypes_char_array): """ 安全地从 ctypes 字符数组中解码出字符串 Args: ctypes_char_array: ctypes字符数组 Returns: str: 解码后的字符串 """ byte_str = memoryview(ctypes_char_array).tobytes() # 在第一个空字符处截断 null_index = byte_str.find(b'\x00') if null_index != -1: byte_str = byte_str[:null_index] # 多编码尝试解码 for encoding in ['gbk', 'utf-8', 'latin-1']: try: return byte_str.decode(encoding) except UnicodeDecodeError: continue # 如果所有编码都失败,使用替换策略 return byte_str.decode('latin-1', errors='replace') def enum_devices(self): """ 枚举相机设备 Returns: int: 设备数量 """ self.device_list = MV_CC_DEVICE_INFO_LIST() n_layer_type = (MV_GIGE_DEVICE | MV_USB_DEVICE) ret = MvCamera.MV_CC_EnumDevices(n_layer_type, self.device_list) if ret != 0: print(f"枚举设备失败! ret = :{self.to_hex_str(ret)}") return ret if self.device_list.nDeviceNum == 0: print("未找到设备") return ret print(f"找到 {self.device_list.nDeviceNum} 台设备!") dev_list = [] target_model_found = False for i in range(0, self.device_list.nDeviceNum): mvcc_dev_info = cast(self.device_list.pDeviceInfo[i], POINTER(MV_CC_DEVICE_INFO)).contents if mvcc_dev_info.nTLayerType == MV_GIGE_DEVICE: print(f"\ngige device: [{i}]") user_defined_name = self.decoding_char(mvcc_dev_info.SpecialInfo.stGigEInfo.chUserDefinedName) model_name = self.decoding_char(mvcc_dev_info.SpecialInfo.stGigEInfo.chModelName) print(f"device user define name: {user_defined_name}") print(f"device model name: {model_name}") # 检查是否为目标型号 if "MV-CS016-10UC" in model_name: print("✓ 找到目标相机型号: MV-CS016-10UC") target_model_found = True 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) print(f"current ip: {nip1}.{nip2}.{nip3}.{nip4} ") dev_list.append(f"[{i}]GigE: {user_defined_name} {model_name}({nip1}.{nip2}.{nip3}.{nip4})") elif mvcc_dev_info.nTLayerType == MV_USB_DEVICE: print(f"\nu3v device: [{i}]") user_defined_name = self.decoding_char(mvcc_dev_info.SpecialInfo.stUsb3VInfo.chUserDefinedName) model_name = self.decoding_char(mvcc_dev_info.SpecialInfo.stUsb3VInfo.chModelName) print(f"device user define name: {user_defined_name}") print(f"device model name: {model_name}") # 检查是否为目标型号 if "MV-CS016-10UC" in model_name: print("✓ 找到目标相机型号: MV-CS016-10UC") target_model_found = True str_serial_number = "" for per in mvcc_dev_info.SpecialInfo.stUsb3VInfo.chSerialNumber: if per == 0: break str_serial_number = str_serial_number + chr(per) print(f"user serial number: {str_serial_number}") dev_list.append(f"[{i}]USB: {user_defined_name} {model_name}({str_serial_number})") # 打印设备列表 print("\n设备列表:") for i, dev in enumerate(dev_list): print(f"{i}: {dev}") # 提示用户是否找到目标型号 if not target_model_found: print("\n⚠️ 警告: 未找到目标相机型号 MV-CS016-10UC") print("⚠️ 系统仍将尝试使用找到的设备,但可能无法正常工作") return self.device_list.nDeviceNum def open_device(self, n_index): """ 打开相机设备 Args: n_index: 设备索引 Returns: int: 错误码 """ if self.is_open: print("相机已经打开!") return MV_E_CALLORDER if n_index < 0 or n_index >= self.device_list.nDeviceNum: print("请选择有效的相机索引!") return MV_E_CALLORDER # 选择设备并创建句柄 st_device_list = cast(self.device_list.pDeviceInfo[n_index], POINTER(MV_CC_DEVICE_INFO)).contents self.cam = MvCamera() # 句柄占用保护 ret = self.cam.MV_CC_CreateHandle(st_device_list) if ret != 0: print(f"创建设备句柄失败! ret = {self.to_hex_str(ret)}") # 尝试清理可能的占用 try: self.cam.MV_CC_DestroyHandle() except: pass return ret # 打开设备 ret = self.cam.MV_CC_OpenDevice() if ret != 0: print(f"打开设备失败! ret = {self.to_hex_str(ret)}") try: self.cam.MV_CC_DestroyHandle() except: pass return ret print("设备打开成功!") self.is_open = True # 设置触发模式为off ret = self.cam.MV_CC_SetEnumValue("TriggerMode", MV_TRIGGER_MODE_OFF) if ret != 0: print(f"设置触发模式失败! ret = {self.to_hex_str(ret)}") # 设置像素格式为 BGR8 ret = self.cam.MV_CC_SetEnumValue("PixelFormat", PixelType_Gvsp_BGR8_Packed) if ret != 0: print(f"设置像素格式失败! ret = {self.to_hex_str(ret)}") # 设置连续采集模式 ret = self.cam.MV_CC_SetEnumValue("AcquisitionMode", 2) # Continuous if ret != 0: print(f"设置连续采集模式失败! ret = {self.to_hex_str(ret)}") # 防过曝设置 self.setup_anti_overexposure() return MV_OK def setup_anti_overexposure(self): """ 设置防过曝参数(点阵灯盘专用) """ # 关闭自动曝光,设置初始曝光时间(微秒) ret = self.cam.MV_CC_SetEnumValue("ExposureAuto", MV_EXPOSURE_AUTO_MODE_OFF) if ret != 0: print(f"设置曝光自动模式失败! ret = {self.to_hex_str(ret)}") # 点阵灯盘初始曝光时间:3000-5000微秒 initial_exp = 4000.0 ret = self.cam.MV_CC_SetFloatValue("ExposureTime", initial_exp) if ret != 0: print(f"设置曝光时间失败! ret = {self.to_hex_str(ret)}") else: self.current_exp = initial_exp print(f"初始曝光时间设置为: {initial_exp}μs") # 关闭自动增益 ret = self.cam.MV_CC_SetEnumValue("GainAuto", MV_GAIN_AUTO_MODE_OFF) if ret != 0: print(f"设置增益自动模式失败! ret = {self.to_hex_str(ret)}") ret = self.cam.MV_CC_SetFloatValue("Gain", 0.0) if ret != 0: print(f"设置增益失败! ret = {self.to_hex_str(ret)}") else: self.current_gain = 0.0 print("初始增益设置为: 0dB") # 关闭自动白平衡(识别绿色LED时必需) ret = self.cam.MV_CC_SetEnumValue("BalanceWhiteAuto", MV_BALANCE_WHITE_AUTO_OFF) if ret != 0: print(f"设置白平衡自动模式失败! ret = {self.to_hex_str(ret)}") else: print("自动白平衡已关闭") def set_exposure_time(self, microseconds): """ 设置曝光时间(微秒) Args: microseconds: 曝光时间(微秒) Returns: int: 错误码 """ if not self.is_open: return MV_E_CALLORDER ret = self.cam.MV_CC_SetFloatValue("ExposureTime", microseconds) if ret != 0: print(f"设置曝光时间失败! ret = {self.to_hex_str(ret)}") return ret self.current_exp = microseconds return MV_OK def set_gain(self, db): """ 设置增益(dB) Args: db: 增益值(dB) Returns: int: 错误码 """ if not self.is_open: return MV_E_CALLORDER ret = self.cam.MV_CC_SetFloatValue("Gain", db) if ret != 0: print(f"设置增益失败! ret = {self.to_hex_str(ret)}") return ret self.current_gain = db return MV_OK def set_frame_rate(self, fps): """ 设置帧率 Args: fps: 帧率值 Returns: int: 错误码 """ if not self.is_open: return MV_E_CALLORDER ret = self.cam.MV_CC_SetFloatValue("AcquisitionFrameRate", fps) if ret != 0: print(f"设置帧率失败! ret = {self.to_hex_str(ret)}") return ret self.current_fps = fps return MV_OK def set_roi(self, x, y, w, h): """ 设置ROI Args: x: X坐标 y: Y坐标 w: 宽度 h: 高度 Returns: int: 错误码 """ if not self.is_open: return MV_E_CALLORDER # 停止取流 if self.is_grabbing: self.stop_grabbing() # 设置ROI参数 ret = self.cam.MV_CC_SetIntValue("OffsetX", x) if ret != 0: print(f"设置OffsetX失败! ret = {self.to_hex_str(ret)}") return ret ret = self.cam.MV_CC_SetIntValue("OffsetY", y) if ret != 0: print(f"设置OffsetY失败! ret = {self.to_hex_str(ret)}") return ret ret = self.cam.MV_CC_SetIntValue("Width", w) if ret != 0: print(f"设置Width失败! ret = {self.to_hex_str(ret)}") return ret ret = self.cam.MV_CC_SetIntValue("Height", h) if ret != 0: print(f"设置Height失败! ret = {self.to_hex_str(ret)}") return ret # 重新开始取流 if self.is_grabbing: self.start_grabbing() return MV_OK def start_grabbing(self): """ 开始取流 Returns: int: 错误码 """ if not self.is_open: print("相机未打开!") return MV_E_CALLORDER if self.is_grabbing: print("已经开始取流!") return MV_E_CALLORDER self.b_exit = False ret = self.cam.MV_CC_StartGrabbing() if ret != 0: print(f"开始取流失败! ret = {self.to_hex_str(ret)}") return ret self.is_grabbing = True print("开始取流成功!") # 启动取图线程 self.work_thread = threading.Thread(target=self.work_thread_func) self.work_thread.daemon = True self.work_thread.start() return MV_OK def stop_grabbing(self): """ 停止取流 Returns: int: 错误码 """ if not self.is_open: print("相机未打开!") return MV_E_CALLORDER if not self.is_grabbing: print("未开始取流!") return MV_E_CALLORDER self.b_exit = True time.sleep(0.1) ret = self.cam.MV_CC_StopGrabbing() if ret != 0: print(f"停止取流失败! ret = {self.to_hex_str(ret)}") return ret self.is_grabbing = False print("停止取流成功!") return MV_OK def close_device(self): """ 关闭相机设备 """ if self.is_grabbing: self.stop_grabbing() if self.is_open: ret = self.cam.MV_CC_CloseDevice() if ret != 0: print(f"关闭设备失败! ret = {self.to_hex_str(ret)}") # 销毁句柄 self.cam.MV_CC_DestroyHandle() self.is_open = False print("设备关闭成功!") def work_thread_func(self): """ 取图线程函数 """ st_out_frame = MV_FRAME_OUT() memset(byref(st_out_frame), 0, sizeof(st_out_frame)) timeout_count = 0 # 超时计数器 max_timeout_count = 3 # 最大超时次数 while not self.b_exit: # 获取图像 ret = self.cam.MV_CC_GetImageBuffer(st_out_frame, 1000) if ret == 0: timeout_count = 0 # 重置超时计数器 # 打印图像信息 print(f"获取一帧图像: 宽度[{st_out_frame.stFrameInfo.nWidth}], 高度[{st_out_frame.stFrameInfo.nHeight}], 帧数[{st_out_frame.stFrameInfo.nFrameNum}]") # 转换为 OpenCV 格式 try: # 计算数据大小 data_size = st_out_frame.stFrameInfo.nWidth * st_out_frame.stFrameInfo.nHeight * 3 # 从缓冲区复制数据 frame_data = np.ctypeslib.as_array(st_out_frame.pBufAddr, shape=(data_size,)) frame = frame_data.reshape((st_out_frame.stFrameInfo.nHeight, st_out_frame.stFrameInfo.nWidth, 3)) # 保存最后一帧图像 self.last_frame = frame # 显示图像 cv2.imshow("Camera", frame) key = cv2.waitKey(1) & 0xFF if key == ord('q'): self.b_exit = True break except Exception as e: print(f"处理图像失败: {e}") finally: # 释放缓存 try: self.cam.MV_CC_FreeImageBuffer(st_out_frame) except Exception as e: print(f"释放图像缓存失败: {e}") else: print(f"获取图像失败! ret = {self.to_hex_str(ret)}") timeout_count += 1 time.sleep(0.1) # 连续超时3次,触发重连 if timeout_count >= max_timeout_count: print("连续超时,尝试重连相机...") self._reconnect_camera() timeout_count = 0 cv2.destroyAllWindows() def _reconnect_camera(self): """ 重连相机 """ try: # 停止取流 if self.is_grabbing: print("停止取流...") self.stop_grabbing() # 关闭设备 print("关闭设备...") self.close_device() # 重新枚举设备 print("重新枚举设备...") device_count = self.enum_devices() if device_count == 0: print("重连失败:未找到设备") return # 重新打开设备(默认使用索引0) print("重新打开设备...") ret = self.open_device(0) if ret != 0: print(f"重连失败:打开设备失败! ret = {self.to_hex_str(ret)}") return # 重新开始取流 print("重新开始取流...") ret = self.start_grabbing() if ret != 0: print(f"重连失败:开始取流失败! ret = {self.to_hex_str(ret)}") return print("相机重连成功!") except Exception as e: print(f"重连相机失败: {e}") def get_frame(self): """ 获取当前帧图像 Returns: tuple: (ret, frame) """ if not self.is_open or not self.is_grabbing: return False, None return True, self.last_frame def __del__(self): """ 析构函数,确保资源释放 """ self.close_device() # 反初始化SDK try: MvCamera.MV_CC_Finalize() print("SDK反初始化成功") except Exception as e: print(f"SDK反初始化失败: {e}") # SDK安装指南 """ 海康 MVS SDK 安装指南 1. 下载 MVS SDK - 访问海康官网:https://www.hikvision.com/cn/ - 搜索 "MVS SDK" 或 "机器视觉 SDK" - 下载对应操作系统的最新版本 2. 安装 SDK - 运行安装程序,按照默认设置安装 - 安装路径建议使用默认路径 3. 配置环境变量 - 将 SDK 安装目录下的 bin 文件夹添加到系统 PATH 环境变量 - 例如:C:\\Program Files\\MVS\\Runtime\\bin\\win64_x64 4. 配置 Python 环境 - 复制 SDK 安装目录下的 Python 绑定文件 - 从:C:\\Program Files\\MVS\\Development\\Samples\\Python\\MvImport - 到:项目目录\\Python\\MvImport 5. 安装依赖 - pip install opencv-python - pip install numpy 6. 测试安装 - 运行本脚本:python camera.py - 查看是否能成功枚举设备 7. 常见问题 - 找不到 MvCameraControl_class:检查 MvImport 目录是否正确 - 相机无法打开:检查相机是否被其他程序占用 - 图像获取失败:检查相机连接和参数设置 """ class CameraControlPanel: """ 相机控制面板类 功能: - 相机初始化和状态显示 - 曝光时间、增益、帧率等参数调节 - 相机控制(打开、关闭、开始取流、停止取流) - 操作日志显示 """ def __init__(self, root, camera): """ 初始化相机控制面板 Args: root: 父窗口 camera: 相机实例 """ self.root = root self.camera = camera self.root.title("相机控制面板") self.root.geometry("500x400") self.root.resizable(True, True) # 创建主框架 self.main_frame = ttk.Frame(self.root, padding="10") self.main_frame.pack(fill=tk.BOTH, expand=True) # 相机状态 self.status_var = tk.StringVar(value="未初始化") # 曝光时间变量 self.exp_var = tk.DoubleVar(value=5000.0) self.exp_var.trace("w", self.on_exp_change) # 增益变量 self.gain_var = tk.DoubleVar(value=0.0) self.gain_var.trace("w", self.on_gain_change) # 帧率变量 self.fps_var = tk.DoubleVar(value=30.0) self.fps_var.trace("w", self.on_fps_change) # 状态标签 self.create_status_section() # 相机控制 self.create_control_section() # 参数调节 self.create_param_section() # 日志区域 self.create_log_section() # 初始化相机 self.init_camera() def create_status_section(self): """ 创建状态显示区域 """ status_frame = ttk.LabelFrame(self.main_frame, text="相机状态", padding="10") status_frame.pack(fill=tk.X, pady=5) status_label = ttk.Label(status_frame, textvariable=self.status_var, font=("Arial", 12, "bold")) status_label.pack(anchor=tk.W) def create_control_section(self): """ 创建相机控制区域 """ control_frame = ttk.LabelFrame(self.main_frame, text="相机控制", padding="10") control_frame.pack(fill=tk.X, pady=5) # 按钮容器 button_frame = ttk.Frame(control_frame) button_frame.pack(fill=tk.X) # 初始化按钮 self.init_button = ttk.Button(button_frame, text="初始化相机", command=self.init_camera) self.init_button.pack(side=tk.LEFT, padx=5) # 打开按钮 self.open_button = ttk.Button(button_frame, text="打开相机", command=self.open_camera, state=tk.DISABLED) self.open_button.pack(side=tk.LEFT, padx=5) # 开始取流按钮 self.start_button = ttk.Button(button_frame, text="开始取流", command=self.start_grabbing, state=tk.DISABLED) self.start_button.pack(side=tk.LEFT, padx=5) # 停止取流按钮 self.stop_button = ttk.Button(button_frame, text="停止取流", command=self.stop_grabbing, state=tk.DISABLED) self.stop_button.pack(side=tk.LEFT, padx=5) # 关闭按钮 self.close_button = ttk.Button(button_frame, text="关闭相机", command=self.close_camera, state=tk.DISABLED) self.close_button.pack(side=tk.LEFT, padx=5) def create_param_section(self): """ 创建参数调节区域 """ param_frame = ttk.LabelFrame(self.main_frame, text="参数调节", padding="10") param_frame.pack(fill=tk.X, pady=5) # 曝光时间 exp_frame = ttk.Frame(param_frame) exp_frame.pack(fill=tk.X, pady=5) exp_label = ttk.Label(exp_frame, text="曝光时间 (μs):") exp_label.pack(side=tk.LEFT, padx=5) exp_scale = ttk.Scale(exp_frame, from_=100.0, to=50000.0, orient=tk.HORIZONTAL, variable=self.exp_var, length=200) exp_scale.pack(side=tk.LEFT, padx=5, expand=True, fill=tk.X) exp_entry = ttk.Entry(exp_frame, textvariable=self.exp_var, width=10) exp_entry.pack(side=tk.LEFT, padx=5) # 增益 gain_frame = ttk.Frame(param_frame) gain_frame.pack(fill=tk.X, pady=5) gain_label = ttk.Label(gain_frame, text="增益 (dB):") gain_label.pack(side=tk.LEFT, padx=5) gain_scale = ttk.Scale(gain_frame, from_=0.0, to=48.0, orient=tk.HORIZONTAL, variable=self.gain_var, length=200) gain_scale.pack(side=tk.LEFT, padx=5, expand=True, fill=tk.X) gain_entry = ttk.Entry(gain_frame, textvariable=self.gain_var, width=10) gain_entry.pack(side=tk.LEFT, padx=5) # 帧率 fps_frame = ttk.Frame(param_frame) fps_frame.pack(fill=tk.X, pady=5) fps_label = ttk.Label(fps_frame, text="帧率 (fps):") fps_label.pack(side=tk.LEFT, padx=5) fps_scale = ttk.Scale(fps_frame, from_=1.0, to=60.0, orient=tk.HORIZONTAL, variable=self.fps_var, length=200) fps_scale.pack(side=tk.LEFT, padx=5, expand=True, fill=tk.X) fps_entry = ttk.Entry(fps_frame, textvariable=self.fps_var, width=10) fps_entry.pack(side=tk.LEFT, padx=5) def create_log_section(self): """ 创建日志显示区域 """ log_frame = ttk.LabelFrame(self.main_frame, text="日志", padding="10") log_frame.pack(fill=tk.BOTH, expand=True, pady=5) # 创建文本框 self.log_text = tk.Text(log_frame, height=10, wrap=tk.WORD) self.log_text.pack(fill=tk.BOTH, expand=True) # 添加滚动条 scrollbar = ttk.Scrollbar(self.log_text, command=self.log_text.yview) scrollbar.pack(side=tk.RIGHT, fill=tk.Y) self.log_text.config(yscrollcommand=scrollbar.set) def log(self, message): """ 添加日志信息 Args: message: 日志消息 """ timestamp = time.strftime("%Y-%m-%d %H:%M:%S") log_message = f"[{timestamp}] {message}\n" self.log_text.insert(tk.END, log_message) self.log_text.see(tk.END) def init_camera(self): """ 初始化相机 """ try: self.log("正在初始化相机...") self.status_var.set("初始化中") # 枚举设备 device_count = self.camera.enum_devices() if device_count == 0: self.log("未找到相机设备") self.status_var.set("未找到设备") return self.log(f"找到 {device_count} 台相机设备") self.status_var.set("就绪") # 启用打开按钮 self.open_button.config(state=tk.NORMAL) self.log("相机初始化成功") except Exception as e: self.log(f"初始化失败: {e}") self.status_var.set("初始化失败") def open_camera(self): """ 打开相机 """ try: self.log("正在打开相机...") self.status_var.set("打开中") # 打开设备(默认使用索引0) ret = self.camera.open_device(0) if ret != 0: self.log(f"打开相机失败,错误码: {ret}") self.status_var.set("打开失败") return self.log("相机打开成功") self.status_var.set("已打开") # 更新参数显示 self.exp_var.set(self.camera.current_exp) self.gain_var.set(self.camera.current_gain) self.fps_var.set(self.camera.current_fps) # 启用控制按钮 self.start_button.config(state=tk.NORMAL) self.close_button.config(state=tk.NORMAL) self.open_button.config(state=tk.DISABLED) except Exception as e: self.log(f"打开相机失败: {e}") self.status_var.set("打开失败") def start_grabbing(self): """ 开始取流 """ try: self.log("正在开始取流...") self.status_var.set("取流中") ret = self.camera.start_grabbing() if ret != 0: self.log(f"开始取流失败,错误码: {ret}") self.status_var.set("取流失败") return self.log("开始取流成功") self.status_var.set("正在取流") # 更新按钮状态 self.start_button.config(state=tk.DISABLED) self.stop_button.config(state=tk.NORMAL) except Exception as e: self.log(f"开始取流失败: {e}") self.status_var.set("取流失败") def stop_grabbing(self): """ 停止取流 """ try: self.log("正在停止取流...") ret = self.camera.stop_grabbing() if ret != 0: self.log(f"停止取流失败,错误码: {ret}") return self.log("停止取流成功") self.status_var.set("已打开") # 更新按钮状态 self.stop_button.config(state=tk.DISABLED) self.start_button.config(state=tk.NORMAL) except Exception as e: self.log(f"停止取流失败: {e}") def close_camera(self): """ 关闭相机 """ try: self.log("正在关闭相机...") self.camera.close_device() self.log("相机关闭成功") self.status_var.set("已关闭") # 更新按钮状态 self.close_button.config(state=tk.DISABLED) self.stop_button.config(state=tk.DISABLED) self.start_button.config(state=tk.DISABLED) self.open_button.config(state=tk.NORMAL) except Exception as e: self.log(f"关闭相机失败: {e}") def on_exp_change(self, *args): """ 曝光时间变化回调 """ if self.camera.is_open: try: exp_value = self.exp_var.get() ret = self.camera.set_exposure_time(exp_value) if ret == 0: self.log(f"曝光时间设置为: {exp_value} μs") except Exception as e: self.log(f"设置曝光时间失败: {e}") def on_gain_change(self, *args): """ 增益变化回调 """ if self.camera.is_open: try: gain_value = self.gain_var.get() ret = self.camera.set_gain(gain_value) if ret == 0: self.log(f"增益设置为: {gain_value} dB") except Exception as e: self.log(f"设置增益失败: {e}") def on_fps_change(self, *args): """ 帧率变化回调 """ if self.camera.is_open: try: fps_value = self.fps_var.get() ret = self.camera.set_frame_rate(fps_value) if ret == 0: self.log(f"帧率设置为: {fps_value} fps") except Exception as e: self.log(f"设置帧率失败: {e}") if __name__ == "__main__": """ 测试相机功能 """ print("海康相机测试程序") print("按 Q 退出显示窗口") print("按 Ctrl+C 退出程序") # 创建相机实例 camera = Camera() try: # 创建控制界面 root = tk.Tk() app = CameraControlPanel(root, camera) # 运行主循环 root.mainloop() except KeyboardInterrupt: print("\n用户中断程序") except Exception as e: print(f"程序异常: {e}") finally: # 清理资源 camera.close_device() print("程序退出")