diff --git a/gui_config.py b/gui_config.py index ba6b104..49baa55 100644 --- a/gui_config.py +++ b/gui_config.py @@ -200,26 +200,37 @@ class ConfigGUI: def scan_cameras(self, max_index: int = 10): """扫描系统可用的采集卡索引,并填充下拉框""" import warnings + import sys + import io found = [] # 临时设置OpenCV日志级别(兼容不同版本) import os old_level = os.environ.get('OPENCV_LOG_LEVEL', '') - os.environ['OPENCV_LOG_LEVEL'] = 'ERROR' + os.environ['OPENCV_LOG_LEVEL'] = 'SILENT' # 尝试更严格的级别 + os.environ['OPENCV_IO_ENABLE_OPENEXR'] = '0' # 尝试设置日志级别(不同版本的OpenCV API不同) try: if hasattr(cv2, 'setLogLevel'): # OpenCV 4.x - if hasattr(cv2, 'LOG_LEVEL_ERROR'): + if hasattr(cv2, 'LOG_LEVEL_SILENT'): + cv2.setLogLevel(cv2.LOG_LEVEL_SILENT) + elif hasattr(cv2, 'LOG_LEVEL_ERROR'): cv2.setLogLevel(cv2.LOG_LEVEL_ERROR) elif hasattr(cv2, 'utils'): - # 某些版本的OpenCV - cv2.utils.setLogLevel(0) # 0=ERROR级别 + cv2.utils.setLogLevel(0) # 0=SILENT/ERROR级别 except Exception: - pass # 如果设置失败,继续执行 + pass + + # 重定向stderr来捕获OpenCV的错误输出 + old_stderr = sys.stderr + suppressed_output = io.StringIO() try: + # 暂时重定向stderr以抑制OpenCV的错误消息 + sys.stderr = suppressed_output + for idx in range(max_index + 1): cap = None try: @@ -245,11 +256,14 @@ class ConfigGUI: pass continue finally: + # 恢复stderr + sys.stderr = old_stderr # 恢复原来的日志级别 if old_level: os.environ['OPENCV_LOG_LEVEL'] = old_level else: os.environ.pop('OPENCV_LOG_LEVEL', None) + os.environ.pop('OPENCV_IO_ENABLE_OPENEXR', None) if not found: found = ["0"] # 至少给一个默认项,避免为空 @@ -320,29 +334,46 @@ class ConfigGUI: def save_config(self): """保存当前编辑的配置""" # 检查索引有效性 - if self.selected_index >= len(config_manager.config['groups']): - messagebox.showerror("错误", f"配置组索引 {self.selected_index} 无效") + if self.selected_index < 0 or self.selected_index >= len(config_manager.config['groups']): + messagebox.showerror("错误", f"请先选择一个有效的配置组") return False # 保存当前组的配置 group = config_manager.get_group_by_index(self.selected_index) - if group: - for key, var in self.config_vars.items(): - if not key.startswith('display_'): - try: - value_str = var.get().strip() - # 特殊处理:某些字段需要转换为整数 - if key in ['camera_index', 'camera_width', 'camera_height', 'serial_baudrate', 'move_velocity']: - try: - value = int(value_str) if value_str else group.get(key, 0) - except: + if not group: + messagebox.showerror("错误", f"配置组不存在") + return False + + for key, var in self.config_vars.items(): + if not key.startswith('display_'): + try: + value_str = var.get().strip() if var.get() else "" + + # 特殊处理:某些字段需要转换为整数 + if key in ['camera_index', 'camera_width', 'camera_height', 'serial_baudrate', 'move_velocity']: + try: + # 如果为空,使用当前值或默认值 + if value_str: + value = int(value_str) + else: + # 如果下拉框为空,尝试从当前配置获取 value = group.get(key, 0) - else: - value = value_str if value_str else group.get(key, '') - group[key] = value - except Exception as e: - print(f"保存字段 {key} 时出错: {e}") - pass + except ValueError: + # 转换失败,使用当前值 + value = group.get(key, 0) + print(f"⚠️ 字段 {key} 的值 '{value_str}' 无效,使用当前值 {value}") + else: + # 字符串字段 + value = value_str if value_str else group.get(key, '') + + group[key] = value + except Exception as e: + print(f"保存字段 {key} 时出错: {e}") + import traceback + traceback.print_exc() + # 保存失败时使用当前值 + if key in group: + pass # 保持原值不变 # 保存预览配置 display = config_manager.config.get('display', {}) @@ -384,28 +415,43 @@ class ConfigGUI: def save_config_silent(self): """静默保存配置(不显示消息框)""" # 保存当前组的配置 - if self.selected_index >= len(config_manager.config['groups']): - print(f"⚠️ 选中索引 {self.selected_index} 超出范围") + if self.selected_index < 0 or self.selected_index >= len(config_manager.config['groups']): + print(f"⚠️ 选中索引 {self.selected_index} 无效") return False group = config_manager.get_group_by_index(self.selected_index) - if group: - for key, var in self.config_vars.items(): - if not key.startswith('display_'): - try: - value_str = var.get().strip() - # 特殊处理:某些字段需要转换为整数 - if key in ['camera_index', 'camera_width', 'camera_height', 'serial_baudrate', 'move_velocity']: - try: - value = int(value_str) if value_str else group.get(key, 0) - except: + if not group: + print(f"⚠️ 配置组不存在") + return False + + for key, var in self.config_vars.items(): + if not key.startswith('display_'): + try: + value_str = var.get().strip() if var.get() else "" + + # 特殊处理:某些字段需要转换为整数 + if key in ['camera_index', 'camera_width', 'camera_height', 'serial_baudrate', 'move_velocity']: + try: + # 如果为空,使用当前值或默认值 + if value_str: + value = int(value_str) + else: + # 如果下拉框为空,尝试从当前配置获取 value = group.get(key, 0) - else: - value = value_str if value_str else group.get(key, '') - group[key] = value - except Exception as e: - print(f"保存字段 {key} 时出错: {e}") - pass + except ValueError: + # 转换失败,使用当前值 + value = group.get(key, 0) + print(f"⚠️ 字段 {key} 的值 '{value_str}' 无效,使用当前值 {value}") + else: + # 字符串字段 + value = value_str if value_str else group.get(key, '') + + group[key] = value + except Exception as e: + print(f"保存字段 {key} 时出错: {e}") + import traceback + traceback.print_exc() + # 保存失败时保持原值不变 # 保存预览配置 display = config_manager.config.get('display', {}) diff --git a/preview.py b/preview.py index b5c128d..e6cd3fd 100644 --- a/preview.py +++ b/preview.py @@ -9,16 +9,22 @@ import os from config import config_manager # 抑制OpenCV的警告信息(兼容不同版本) -os.environ['OPENCV_LOG_LEVEL'] = 'ERROR' +import sys +import io + +os.environ['OPENCV_LOG_LEVEL'] = 'SILENT' +os.environ['OPENCV_IO_ENABLE_OPENEXR'] = '0' + try: if hasattr(cv2, 'setLogLevel'): - # OpenCV 4.x - if hasattr(cv2, 'LOG_LEVEL_ERROR'): + if hasattr(cv2, 'LOG_LEVEL_SILENT'): + cv2.setLogLevel(cv2.LOG_LEVEL_SILENT) + elif hasattr(cv2, 'LOG_LEVEL_ERROR'): cv2.setLogLevel(cv2.LOG_LEVEL_ERROR) elif hasattr(cv2, 'utils'): - cv2.utils.setLogLevel(0) # 0=ERROR级别 + cv2.utils.setLogLevel(0) except Exception: - pass # 如果设置失败,继续执行 + pass class PreviewWindow: """采集卡预览窗口""" @@ -40,65 +46,75 @@ class PreviewWindow: print("⚠️ 没有活动的配置组,将尝试加载所有配置组") active_groups = self.config['groups'] - for i, group in enumerate(active_groups): - try: - cam_idx = group['camera_index'] - print(f" 尝试打开采集卡 {cam_idx} ({group['name']})...") - - cap = None - # 尝试多种后端打开 - backends_to_try = [ - (int(cam_idx), cv2.CAP_DSHOW), - (int(cam_idx), cv2.CAP_ANY), - (int(cam_idx), None), - ] - - for idx, backend in backends_to_try: - try: - with warnings.catch_warnings(): - warnings.filterwarnings('ignore') - if backend is not None: - cap = cv2.VideoCapture(idx, backend) - else: - cap = cv2.VideoCapture(idx) - - if cap.isOpened(): - # 测试读取一帧 - ret, test_frame = cap.read() - if ret and test_frame is not None: - break - else: - cap.release() - cap = None - except Exception: - if cap: - try: - cap.release() - except: - pass - cap = None - continue - - if cap and cap.isOpened(): - try: - cap.set(cv2.CAP_PROP_FRAME_WIDTH, group['camera_width']) - cap.set(cv2.CAP_PROP_FRAME_HEIGHT, group['camera_height']) - except Exception as e: - print(f" ⚠️ 设置分辨率失败: {e}") + # 重定向stderr来抑制OpenCV的错误输出 + old_stderr = sys.stderr + suppressed_output = io.StringIO() + + try: + sys.stderr = suppressed_output + + for i, group in enumerate(active_groups): + try: + cam_idx = group['camera_index'] + print(f" 尝试打开采集卡 {cam_idx} ({group['name']})...") - self.caps[i] = { - 'cap': cap, - 'group': group, - 'name': group['name'] - } - loaded_count += 1 - print(f" ✅ 采集卡 {cam_idx} 初始化成功") - else: - print(f" ❌ 采集卡 {cam_idx} 无法打开") - except Exception as e: - print(f" ❌ 采集卡 {group.get('camera_index', '?')} 初始化失败: {e}") - import traceback - traceback.print_exc() + cap = None + # 尝试多种后端打开 + backends_to_try = [ + (int(cam_idx), cv2.CAP_DSHOW), + (int(cam_idx), cv2.CAP_ANY), + (int(cam_idx), None), + ] + + for idx, backend in backends_to_try: + try: + with warnings.catch_warnings(): + warnings.filterwarnings('ignore') + if backend is not None: + cap = cv2.VideoCapture(idx, backend) + else: + cap = cv2.VideoCapture(idx) + + if cap.isOpened(): + # 测试读取一帧 + ret, test_frame = cap.read() + if ret and test_frame is not None: + break + else: + cap.release() + cap = None + except Exception: + if cap: + try: + cap.release() + except: + pass + cap = None + continue + + if cap and cap.isOpened(): + try: + cap.set(cv2.CAP_PROP_FRAME_WIDTH, group['camera_width']) + cap.set(cv2.CAP_PROP_FRAME_HEIGHT, group['camera_height']) + except Exception as e: + print(f" ⚠️ 设置分辨率失败: {e}") + + self.caps[i] = { + 'cap': cap, + 'group': group, + 'name': group['name'] + } + loaded_count += 1 + print(f" ✅ 采集卡 {cam_idx} 初始化成功") + else: + print(f" ❌ 采集卡 {cam_idx} 无法打开") + except Exception as e: + print(f" ❌ 采集卡 {group.get('camera_index', '?')} 初始化失败: {e}") + import traceback + traceback.print_exc() + finally: + # 恢复stderr + sys.stderr = old_stderr if loaded_count == 0: print("⚠️ 警告:没有成功加载任何采集卡!") diff --git a/utils/get_image.py b/utils/get_image.py index 2c6f64e..cefd3b2 100644 --- a/utils/get_image.py +++ b/utils/get_image.py @@ -45,16 +45,21 @@ import warnings # 抑制OpenCV的警告信息(兼容不同版本) import os -os.environ['OPENCV_LOG_LEVEL'] = 'ERROR' +import sys +import io +os.environ['OPENCV_LOG_LEVEL'] = 'SILENT' +os.environ['OPENCV_IO_ENABLE_OPENEXR'] = '0' + try: if hasattr(cv2, 'setLogLevel'): - # OpenCV 4.x - if hasattr(cv2, 'LOG_LEVEL_ERROR'): + if hasattr(cv2, 'LOG_LEVEL_SILENT'): + cv2.setLogLevel(cv2.LOG_LEVEL_SILENT) + elif hasattr(cv2, 'LOG_LEVEL_ERROR'): cv2.setLogLevel(cv2.LOG_LEVEL_ERROR) elif hasattr(cv2, 'utils'): - cv2.utils.setLogLevel(0) # 0=ERROR级别 + cv2.utils.setLogLevel(0) except Exception: - pass # 如果设置失败,继续执行 + pass class GetImage: def __init__(self, cam_index=0, width=1920, height=1080): @@ -71,32 +76,42 @@ class GetImage: (cam_index, None), # 默认后端 ] - for idx, backend in backends_to_try: - try: - with warnings.catch_warnings(): - warnings.filterwarnings('ignore', category=UserWarning) - if backend is not None: - self.cap = cv2.VideoCapture(idx, backend) - else: - self.cap = cv2.VideoCapture(idx) - - if self.cap.isOpened(): - # 测试读取一帧 - ret, test_frame = self.cap.read() - if ret and test_frame is not None: - print(f"✅ 采集卡 {cam_index} 打开成功") - break - else: - self.cap.release() + # 重定向stderr来抑制OpenCV的错误输出 + old_stderr = sys.stderr + suppressed_output = io.StringIO() + + try: + sys.stderr = suppressed_output + + for idx, backend in backends_to_try: + try: + with warnings.catch_warnings(): + warnings.filterwarnings('ignore', category=UserWarning) + if backend is not None: + self.cap = cv2.VideoCapture(idx, backend) + else: + self.cap = cv2.VideoCapture(idx) + + if self.cap.isOpened(): + # 测试读取一帧 + ret, test_frame = self.cap.read() + if ret and test_frame is not None: + print(f"✅ 采集卡 {cam_index} 打开成功") + break + else: + self.cap.release() + self.cap = None + except Exception as e: + if self.cap: + try: + self.cap.release() + except: + pass self.cap = None - except Exception as e: - if self.cap: - try: - self.cap.release() - except: - pass - self.cap = None - continue + continue + finally: + # 恢复stderr + sys.stderr = old_stderr if self.cap is None or not self.cap.isOpened(): print(f"❌ 无法打开采集卡 {cam_index}")