Commit e491a327 by Yuhaibo

1

parent 872a1442
......@@ -116,10 +116,14 @@
### 部分清理
17. 🔄 **handlers/videopage/missionpanel_handler.py** - 已清理前880行,删除约50处print语句(文件共2004行,需继续清理)
23. 🔄 **handlers/modelpage/model_test_handler.py** - 已清理约20处print语句(文件共2090行,剩余约280处,需继续清理)
### 新增完成清理
18.**widgets/datasetpage/crop_preview_panel.py** - 删除约47处print语句和2处DEBUG注释
19.**widgets/datasetpage/datacollection_panel.py** - 删除约20处print语句和1处DEBUG注释
20.**handlers/modelpage/model_page_handler.py** - 删除7处print语句和2处DEBUG注释
21.**handlers/modelpage/model_set_handler.py** - 删除47处print语句和2处DEBUG注释
22.**handlers/modelpage/model_signal_handler.py** - 删除6处print语句
## 清理进度说明
......@@ -150,13 +154,13 @@
- ⏳ widgets/datasetpage/test_*.py
### 剩余工作量估算
- 已清理: 约384处调试语句 (18个文件完全清理 + 1个文件部分清理)
- 剩余: 约3057处调试语句
- 预计需要: 继续手动清理约45个文件
- 已清理: 约464处调试语句 (21个文件完全清理 + 2个文件部分清理)
- 剩余: 约2672处调试语句
- 预计需要: 继续手动清理约41个文件
### 本次清理总结 (2025-11-26 20:46)
- 完成文件数: 18个完全清理 + 1个部分清理
- 删除调试语句: 约384处(包含print语句和DEBUG注释)
### 本次清理总结 (2025-11-26 21:02)
- 完成文件数: 21个完全清理 + 2个部分清理
- 删除调试语句: 约464处(包含print语句和DEBUG注释)
- 主要清理内容:
- 核心应用入口和窗口管理 (app.py)
- 视图布局切换管理 (view_handler.py)
......@@ -166,8 +170,9 @@
- 完整线程管理系统 (thread_manager.py, curve_thread.py, global_detection_thread.py, storage_thread.py, display_thread.py)
- 完整UI组件系统 (missionpanel.py, general_set.py, curvepanel.py, historyvideopanel.py)
- 数据集页面组件 (crop_preview_panel.py, datacollection_panel.py)
- 模型页面Handler系统 (model_page_handler.py, model_set_handler.py, model_signal_handler.py)
- 部分任务面板Handler (missionpanel_handler.py)
---
*最后更新: 2025-11-26 20:46*
*最后更新: 2025-11-26 21:00*
......@@ -77,30 +77,16 @@ def setup_runtime_directories():
if not os.path.exists(dir_path):
try:
os.makedirs(dir_path, exist_ok=True)
print(f"✓ 创建运行时目录: {dir_path}")
except Exception as e:
print(f"⚠ 无法创建目录 {dir_path}: {e}")
# 打印配置文件读取位置说明
print(f"\n📂 配置文件读取位置: {os.path.join(sys._MEIPASS, 'database', 'config')}")
print(f"📂 运行时数据保存位置: {exe_dir}")
pass
def main():
"""主入口函数"""
try:
# 在打包环境中输出详细的启动信息
# 在打包环境中创建运行时目录结构
if getattr(sys, 'frozen', False):
pass
print(f"脚本目录: {current_dir}")
print(f"Python 版本: {sys.version}")
print("=" * 80)
print()
# 创建运行时目录结构
print("正在初始化运行时环境...")
setup_runtime_directories()
print()
_main()
except Exception as e:
......@@ -129,8 +115,6 @@ sys.path:
{chr(10).join(f' - {p}' for p in sys.path)}
{'=' * 80}
"""
print(error_log)
# 保存错误日志到文件
log_path = None
try:
......@@ -138,9 +122,8 @@ sys.path:
log_path = os.path.join(os.getcwd(), log_filename)
with open(log_path, 'w', encoding='utf-8') as f:
f.write(error_log)
print(f"\n错误日志已保存到: {log_path}")
except Exception as log_err:
print(f"\n警告: 无法保存日志文件 - {log_err}")
pass
# 在打包环境中使用更可靠的暂停方法
if getattr(sys, 'frozen', False):
......@@ -175,9 +158,6 @@ sys.path:
def _main():
"""实际主入口函数"""
# 在打包环境中输出详细的初始化信息
if getattr(sys, 'frozen', False):
print("[1/6] 解析命令行参数...")
# 创建参数解析器
parser = argparse.ArgumentParser(
......@@ -224,31 +204,21 @@ def _main():
args = parser.parse_args()
if args.version:
print('帕特智能油液位检测 v1.0')
return
# 从args中提取配置
if getattr(sys, 'frozen', False):
print("[2/6] 提取配置参数...")
config_from_args = args.__dict__
filename = config_from_args.pop('filename')
config_file_or_yaml = config_from_args.pop('config')
version = config_from_args.pop('version')
# 获取配置(三层级联)
if getattr(sys, 'frozen', False):
print(f"[3/6] 加载配置文件: {config_file_or_yaml}")
print(f" 配置文件存在: {os.path.exists(config_file_or_yaml)}")
log_file_path = setup_logging(args.logger_level)
logging.getLogger(__name__).debug("Logger level set to %s", args.logger_level)
config = get_config(config_file_or_yaml, config_from_args)
if getattr(sys, 'frozen', False):
print(" 配置加载成功")
# 创建Qt应用
if getattr(sys, 'frozen', False):
print("[4/6] 创建 Qt 应用...")
app = QtWidgets.QApplication(sys.argv)
app.setApplicationName('Detection')
app.setOrganizationName('Detection')
......@@ -256,21 +226,11 @@ def _main():
# 应用全局字体配置
FontManager.applyToApplication(app)
if getattr(sys, 'frozen', False):
print(" Qt 应用创建成功")
# 创建主窗口
if getattr(sys, 'frozen', False):
print("[5/6] 创建主窗口...")
win = MainWindow(
config=config,
filename=filename,
)
if getattr(sys, 'frozen', False):
print(" 主窗口创建成功")
if getattr(sys, 'frozen', False):
print("[6/6] 显示窗口并启动事件循环...")
win.show()
# 启动事件循环
......
......@@ -190,7 +190,7 @@ except ImportError:
# ============================================================================
# 事件过滤器:用于在debug模式下拦截交互
# 事件过滤器
# ============================================================================
class MissionRequiredEventFilter(QObject):
......@@ -569,7 +569,6 @@ class MainWindow(
# 初始化数据采集通道资源
self._initDataCollectionChannelResources()
# 初始化测试调试资源(TestHandler)
if hasattr(self, '_initTestResources'):
self._initTestResources()
......@@ -599,7 +598,6 @@ class MainWindow(
# 连接信号槽
self._connectSignals()
# 安装事件过滤器(debug模式下拦截交互)
self._installEventFilters()
# 恢复窗口状态
......@@ -725,7 +723,6 @@ class MainWindow(
(670, 495) # 右下
]
# 检查是否为debug模式
from database.config import is_debug_mode
is_debug = is_debug_mode(self._config)
......@@ -733,7 +730,6 @@ class MainWindow(
channel_id = f'channel{i+1}'
channel_name = self.getChannelDisplayName(channel_id, i+1)
# 只有通道1在debug模式下显示debug文本
debug_mode = is_debug and (i == 0)
channelPanel = ChannelPanel(channel_name, parent=self.default_channel_container, debug_mode=debug_mode)
......@@ -742,11 +738,6 @@ class MainWindow(
if hasattr(channelPanel, 'setChannelName'):
channelPanel.setChannelName(channel_name)
# 如果是通道1且为debug模式,设置current_mission显示(从内存变量读取)
if debug_mode and hasattr(channelPanel, 'setCurrentMission'):
# 从内存变量读取current_mission(不依赖配置文件)
current_mission = self.current_mission if hasattr(self, 'current_mission') else None
channelPanel.setCurrentMission(current_mission)
# 🔥 为每个通道面板的任务标签设置变量名(channel1mission, channel2mission, channel3mission, channel4mission)
mission_var_name = f'channel{i+1}mission'
......@@ -975,7 +966,6 @@ class MainWindow(
# 所有任务表格的信号连接都在 MissionPanelHandler.connectMissionPanel 中处理
self.connectMissionPanel(self.missionTable)
# ========== 测试调试按钮信号(TestHandler)==========
self._connectTestSignals(self.missionTable)
# ========== 通道管理按钮信号 ==========
......@@ -1115,7 +1105,6 @@ class MainWindow(
任务面板本身不受限制,始终可以交互。
"""
try:
# 检查是否是debug模式
from database.config import is_debug_mode
is_debug = is_debug_mode(self._config)
......@@ -1219,12 +1208,6 @@ def main():
app.setApplicationName('Detection')
app.setOrganizationName('Detection')
# 🔥 暂时禁用全局字体配置,测试是否解决重复显示问题
# FontManager.applyToApplication(app)
# 设置应用样式(可选)
# app.setStyle('Fusion')
# 创建主窗口
win = MainWindow(config={})
win.show()
......
......@@ -83,7 +83,6 @@ class AnnotationHandler(QtCore.QObject):
bool: 是否成功
"""
if not dir_path or not osp.exists(dir_path):
print(f"目录不存在: {dir_path}")
return False
# 停止之前的监控
......@@ -116,18 +115,15 @@ class AnnotationHandler(QtCore.QObject):
# 添加目录到监控列表
if self.current_dir not in self.file_watcher.directories():
self.file_watcher.addPath(self.current_dir)
print(f"[AnnotationHandler] 开始监控目录: {self.current_dir}")
# 启动定时刷新
if not self.refresh_timer.isActive():
self.refresh_timer.start(self.refresh_interval)
print(f"[AnnotationHandler] 启动定时刷新 (间隔: {self.refresh_interval}ms)")
self.monitoring_enabled = True
return True
except Exception as e:
print(f"[AnnotationHandler] 启动监控失败: {e}")
return False
def stopMonitoring(self):
......@@ -147,14 +143,12 @@ class AnnotationHandler(QtCore.QObject):
self.refresh_timer.stop()
self.monitoring_enabled = False
print(f"[AnnotationHandler] 停止监控")
except Exception as e:
print(f"[AnnotationHandler] 停止监控失败: {e}")
pass
def _onDirectoryChanged(self, path):
"""目录内容变化回调"""
print(f"[AnnotationHandler] 检测到目录变化: {path}")
# 重新扫描文件
old_files = set(self.image_files)
......@@ -164,13 +158,11 @@ class AnnotationHandler(QtCore.QObject):
# 检测新增文件
added_files = new_files - old_files
for file_path in added_files:
print(f"[AnnotationHandler] 新增文件: {osp.basename(file_path)}")
self.fileAdded.emit(file_path)
# 检测删除文件
removed_files = old_files - new_files
for file_path in removed_files:
print(f"[AnnotationHandler] 删除文件: {osp.basename(file_path)}")
if file_path in self.file_info_dict:
del self.file_info_dict[file_path]
self.fileRemoved.emit(file_path)
......@@ -183,7 +175,6 @@ class AnnotationHandler(QtCore.QObject):
def _onFileChanged(self, path):
"""文件变化回调"""
print(f"[AnnotationHandler] 检测到文件变化: {osp.basename(path)}")
# 刷新该文件的信息
if path in self.file_info_dict:
......@@ -212,7 +203,6 @@ class AnnotationHandler(QtCore.QObject):
if json_exists_now != json_existed_before:
# JSON文件状态发生变化
print(f"[AnnotationHandler] JSON状态变化: {osp.basename(image_path)} - {'新增' if json_exists_now else '删除'}")
self.refreshFileInfo(image_path)
has_changes = True
elif json_exists_now:
......@@ -222,7 +212,6 @@ class AnnotationHandler(QtCore.QObject):
# 重新获取JSON信息来检查
json_info = self.getJsonInfo(json_path)
if json_info['shapes_count'] != old_info['shapes_count']:
print(f"[AnnotationHandler] JSON内容变化: {osp.basename(image_path)}")
self.refreshFileInfo(image_path)
has_changes = True
except Exception as e:
......@@ -235,7 +224,7 @@ class AnnotationHandler(QtCore.QObject):
self.fileListUpdated.emit(self.getAllFileInfoList())
except Exception as e:
print(f"[AnnotationHandler] 定时刷新出错: {e}")
pass
def setRefreshInterval(self, interval_ms):
"""
......@@ -249,7 +238,6 @@ class AnnotationHandler(QtCore.QObject):
if self.refresh_timer.isActive():
self.refresh_timer.stop()
self.refresh_timer.start(self.refresh_interval)
print(f"[AnnotationHandler] 刷新间隔已更新: {interval_ms}ms")
def scanImageFiles(self):
"""
......@@ -272,12 +260,10 @@ class AnnotationHandler(QtCore.QObject):
# 排序
self.image_files.sort()
print(f"扫描到 {len(self.image_files)} 个图片文件")
return True
except Exception as e:
print(f"扫描文件夹失败: {e}")
return False
def loadFileInfoList(self):
......@@ -332,7 +318,7 @@ class AnnotationHandler(QtCore.QObject):
else:
info['file_size_str'] = f"{file_size / (1024 * 1024):.1f} MB"
except Exception as e:
print(f"获取文件大小失败: {e}")
pass
# 获取分辨率
try:
......@@ -349,7 +335,7 @@ class AnnotationHandler(QtCore.QObject):
QtCore.Qt.SmoothTransformation
)
except Exception as e:
print(f"获取图片信息失败: {e}")
pass
# 如果有JSON文件,获取JSON信息
if info['has_json']:
......@@ -390,7 +376,7 @@ class AnnotationHandler(QtCore.QObject):
info['modified_time'] = datetime.fromtimestamp(mtime).strftime('%Y-%m-%d %H:%M:%S')
except Exception as e:
print(f"读取JSON文件失败: {e}")
pass
return info
......@@ -534,11 +520,9 @@ class AnnotationHandler(QtCore.QObject):
with open(output_path, 'w', encoding='utf-8') as f:
json.dump(export_data, f, ensure_ascii=False, indent=2)
print(f"统计信息已导出到: {output_path}")
return True
except Exception as e:
print(f"导出统计信息失败: {e}")
return False
......
......@@ -175,7 +175,6 @@ class DataCollectionChannelHandler:
def _connectDataCollectionChannelThread(self, channel_source):
"""在后台线程中连接数据采集通道(使用与实时监测管理相同的方式)"""
try:
print(f"[DEBUG] 开始连接通道: {channel_source}")
success = False
error_detail = ""
......@@ -188,9 +187,7 @@ class DataCollectionChannelHandler:
error_detail = f"无法连接到配置的通道 {channel_source}\n请检查:\n1. 相机是否已开机并连接到网络\n2. IP地址和端口是否正确"
else:
# 如果没有配置,尝试直接连接USB通道
print(f"[DEBUG] 尝试连接USB通道: {channel_source}")
success = self._connectUSBChannel(channel_source)
print(f"[DEBUG] USB通道连接结果: {success}")
if not success:
error_detail = f"无法连接到USB通道 {channel_source}\n请检查:\n1. USB相机是否已连接\n2. 相机驱动是否已安装\n3. 相机是否被其他程序占用"
else:
......@@ -200,8 +197,6 @@ class DataCollectionChannelHandler:
error_detail = f"无法连接到RTSP地址\n请检查:\n1. 网络连接是否正常\n2. RTSP地址格式是否正确\n3. 相机是否支持RTSP协议\n\n地址:{channel_source}"
if not success:
print(f"[DEBUG] 连接失败,显示错误对话框")
print(f"[DEBUG] 错误详情: {error_detail}")
self._showDataCollectionChannelError(
"相机连接失败",
error_detail if error_detail else "无法打开通道设备,请检查通道是否连接正常或配置是否正确"
......@@ -756,32 +751,22 @@ class DataCollectionChannelHandler:
def _showDataCollectionChannelError(self, title, message):
"""显示数据采集通道错误(线程安全)"""
print(f"[DEBUG] _showDataCollectionChannelError 被调用")
print(f"[DEBUG] 标题: {title}")
print(f"[DEBUG] 消息: {message}")
# 使用信号发射错误信息(线程安全)
print(f"[DEBUG] 准备发射错误信号")
self._dc_error_signal.error.emit(title, message)
print(f"[DEBUG] 错误信号已发射")
def _showDataCollectionChannelErrorUI(self, title, message):
"""在主线程中显示数据采集通道错误"""
try:
print(f"[DEBUG] _showDataCollectionChannelErrorUI 被调用,准备显示对话框")
if DialogManager:
DialogManager.show_critical(self, title, message)
else:
QtWidgets.QMessageBox.critical(self, title, message)
print(f"[DEBUG] 错误对话框已显示")
if hasattr(self, 'statusBar'):
self.statusBar().showMessage(f"错误: {message}")
except Exception as e:
print(f"[DEBUG] 显示错误对话框时发生异常: {e}")
import traceback
traceback.print_exc()
pass
def getDataCollectionChannelStatus(self):
"""
......
......@@ -175,7 +175,6 @@ class DrawableLabel(QtWidgets.QLabel):
self._rectangles.pop(index)
self._redrawAllRectangles()
self.rectangleDeleted.emit(index)
print(f"[DrawableLabel] 删除区域 {index + 1}")
def _getRectangleAtPosition(self, pos):
"""
......@@ -884,26 +883,22 @@ class VideoControlBar(QtWidgets.QWidget):
if obj == self.start_marker:
# 当前对象是绿色标记,提升它并允许它处理事件
self.start_marker.raise_()
print(f"[VideoControlBar] 绿色标记处理点击 (像素距离: {dist_to_start_pixel:.1f})")
return super(VideoControlBar, self).eventFilter(obj, event)
else:
# 当前对象是红色标记,但应该由绿色标记处理
# 阻止红色标记处理该事件
self.start_marker.raise_()
print(f"[VideoControlBar] 阻止红色标记响应,应由绿色标记处理 (绿色距离: {dist_to_start_pixel:.1f})")
return True # 阻止事件继续传播到红色标记
else:
# 更接近结束标记的handle,应该由红色标记处理
if obj == self.end_marker:
# 当前对象是红色标记,提升它并允许它处理事件
self.end_marker.raise_()
print(f"[VideoControlBar] 红色标记处理点击 (像素距离: {dist_to_end_pixel:.1f})")
return super(VideoControlBar, self).eventFilter(obj, event)
else:
# 当前对象是绿色标记,但应该由红色标记处理
# 阻止绿色标记处理该事件
self.end_marker.raise_()
print(f"[VideoControlBar] 阻止绿色标记响应,应由红色标记处理 (红色距离: {dist_to_end_pixel:.1f})")
return True # 阻止事件继续传播到绿色标记
# 继续正常的事件处理
......@@ -948,8 +943,6 @@ class VideoControlBar(QtWidgets.QWidget):
self.start_marker.raise_() # 提升绿色标记
self.end_marker.raise_() # 提升红色标记
print(f"[VideoControlBar] 时间段标记已显示: 绿点在帧{self._start_frame}, 红点在帧{self._end_frame}")
self._updateTimeRangeDisplay()
self._updateTimeRangeHighlight()
......@@ -974,7 +967,6 @@ class VideoControlBar(QtWidgets.QWidget):
def _onStartMarkerChanged(self, value):
"""起始标记变化"""
print(f"[VideoControlBar] 绿色标记值变化: {value}")
# 确保起始帧不超过结束帧
if value > self._end_frame:
......@@ -992,7 +984,6 @@ class VideoControlBar(QtWidgets.QWidget):
def _onEndMarkerChanged(self, value):
"""结束标记变化"""
print(f"[VideoControlBar] 红色标记值变化: {value}")
# 确保结束帧不小于起始帧
if value < self._start_frame:
......@@ -1059,10 +1050,8 @@ class VideoControlBar(QtWidgets.QWidget):
# 这样确保正在拖动的标记始终可见且可操作
if marker_type == 'start':
self.start_marker.raise_()
print(f"[VideoControlBar] 拖动绿色标记 (当前位置: 帧{self._start_frame})")
else:
self.end_marker.raise_()
print(f"[VideoControlBar] 拖动红色标记 (当前位置: 帧{self._end_frame})")
# 更新高亮显示
self._updateTimeRangeHighlight()
......@@ -1220,12 +1209,9 @@ class DataPreprocessHandler(QtCore.QObject):
def _showWarning(self, title, message):
"""显示警告对话框"""
print(f"[DEBUG] _showWarning 被调用, DialogManager={'存在' if DialogManager else '不存在'}")
if DialogManager:
print(f"[DEBUG] 使用 DialogManager.show_warning")
DialogManager.show_warning(self._panel, title, message)
else:
print(f"[DEBUG] 使用原生 QMessageBox.warning")
QtWidgets.QMessageBox.warning(self._panel, title, message)
def _showInformation(self, title, message):
......@@ -1367,9 +1353,7 @@ class DataPreprocessHandler(QtCore.QObject):
new_widget.control_bar.timeRangeChanged.connect(self._onTimeRangeChanged)
except Exception as e:
print(f"[ERROR] 替换视频预览组件失败: {e}")
import traceback
traceback.print_exc()
pass
def _initCropPreviewHandler(self):
"""初始化裁剪预览处理器(延迟初始化,避免首次卡顿)"""
......@@ -1383,12 +1367,7 @@ class DataPreprocessHandler(QtCore.QObject):
# 连接信号:裁剪完成时刷新显示
self.cropFinished.connect(self._onCropFinishedForPreview)
print("[DataPreprocessHandler] 裁剪预览处理器信号已连接(延迟初始化)")
except Exception as e:
print(f"[DataPreprocessHandler] 初始化裁剪预览处理器失败: {e}")
import traceback
traceback.print_exc()
self._crop_preview_handler = None
def _ensureCropPreviewHandler(self):
......@@ -1406,21 +1385,14 @@ class DataPreprocessHandler(QtCore.QObject):
if crop_preview_panel is not None:
# 创建裁剪预览处理器
print("[DataPreprocessHandler] 首次创建裁剪预览处理器...")
self._crop_preview_handler = CropPreviewHandler(crop_preview_panel)
print("[DataPreprocessHandler] 裁剪预览处理器创建成功")
return True
else:
print("[DataPreprocessHandler] 裁剪预览面板不可用")
return False
else:
print("[DataPreprocessHandler] 面板不支持裁剪预览功能")
return False
except Exception as e:
print(f"[DataPreprocessHandler] 创建裁剪预览处理器失败: {e}")
import traceback
traceback.print_exc()
self._crop_preview_handler = None
return False
......@@ -1428,7 +1400,6 @@ class DataPreprocessHandler(QtCore.QObject):
"""从文件加载视频与裁剪图片的映射关系"""
try:
if not osp.exists(self._mapping_file):
print(f"[DataPreprocessHandler] 映射文件不存在,跳过加载")
return
import json
......@@ -1442,15 +1413,9 @@ class DataPreprocessHandler(QtCore.QObject):
if osp.exists(video_path):
self._video_crop_mapping[video_path] = info
loaded_count += 1
else:
print(f"[DataPreprocessHandler] 视频文件不存在,跳过: {video_path}")
print(f"[DataPreprocessHandler] 成功加载 {loaded_count} 个视频裁剪映射")
except Exception as e:
print(f"[DataPreprocessHandler] 加载映射文件失败: {e}")
import traceback
traceback.print_exc()
pass
def _saveMappingToFile(self):
"""保存视频与裁剪图片的映射关系到文件"""
......@@ -1465,12 +1430,8 @@ class DataPreprocessHandler(QtCore.QObject):
with open(self._mapping_file, 'w', encoding='utf-8') as f:
json.dump(self._video_crop_mapping, f, ensure_ascii=False, indent=2)
print(f"[DataPreprocessHandler] 已保存 {len(self._video_crop_mapping)} 个视频裁剪映射到文件")
except Exception as e:
print(f"[DataPreprocessHandler] 保存映射文件失败: {e}")
import traceback
traceback.print_exc()
pass
def updateVideoGridStyles(self):
"""更新视频网格中所有视频的样式,标记已裁剪的视频"""
......@@ -1527,7 +1488,6 @@ class DataPreprocessHandler(QtCore.QObject):
item.setBackground(QtGui.QColor(255, 255, 255)) # 白色背景
item.setForeground(QtGui.QColor(0, 0, 0)) # 黑色文字
removed_mappings.append(video_path)
print(f"[DataPreprocessHandler] 视频 {osp.basename(video_path)} 的裁剪图片已被删除,移除绿色标识")
else:
# 未裁剪,恢复默认样式
item.setBackground(QtGui.QColor(255, 255, 255)) # 白色背景
......@@ -1539,9 +1499,6 @@ class DataPreprocessHandler(QtCore.QObject):
del self._video_crop_mapping[video_path]
# 保存更新后的映射关系
self._saveMappingToFile()
print(f"[DataPreprocessHandler] 已移除 {len(removed_mappings)} 个无效映射")
print(f"[DataPreprocessHandler] 已更新视频网格样式,标记 {cropped_count} 个已裁剪视频")
def _checkCroppedImagesExist(self, crop_info):
"""
......@@ -1574,14 +1531,12 @@ class DataPreprocessHandler(QtCore.QObject):
# 找到至少一张图片,说明裁剪图片存在
return True
except Exception as e:
print(f"[DataPreprocessHandler] 检查文件夹失败: {region_path}, 错误: {e}")
continue
# 所有区域文件夹都不存在或都没有图片
return False
except Exception as e:
print(f"[DataPreprocessHandler] 检查裁剪图片存在性失败: {e}")
return False
def _loadCroppedImagesForVideo(self, video_path):
......@@ -1589,7 +1544,6 @@ class DataPreprocessHandler(QtCore.QObject):
try:
# 检查该视频是否有裁剪记录
if video_path not in self._video_crop_mapping:
print(f"[DataPreprocessHandler] 视频 {osp.basename(video_path)} 没有裁剪记录")
# 清空预览面板
if self._ensureCropPreviewHandler():
self._crop_preview_handler.clearDisplay()
......@@ -1600,9 +1554,6 @@ class DataPreprocessHandler(QtCore.QObject):
save_path = crop_info['save_path']
region_paths = crop_info['region_paths']
print(f"[DataPreprocessHandler] 加载视频裁剪图片: {osp.basename(video_path)}")
print(f"[DataPreprocessHandler] 区域数量: {len(region_paths)}")
# 使用裁剪预览处理器加载并显示图片
if self._ensureCropPreviewHandler():
# 获取视频名称(不含扩展名)
......@@ -1613,14 +1564,9 @@ class DataPreprocessHandler(QtCore.QObject):
save_path,
video_name=video_name
)
print(f"[DataPreprocessHandler] 已加载视频 {video_name} 的裁剪预览")
else:
print("[DataPreprocessHandler] 裁剪预览处理器不可用")
except Exception as e:
print(f"[DataPreprocessHandler] 加载裁剪图片失败: {e}")
import traceback
traceback.print_exc()
pass
def _onCropStartedForPreview(self, config):
"""裁剪任务开始 - 启动预览监控"""
......@@ -1635,10 +1581,6 @@ class DataPreprocessHandler(QtCore.QObject):
# 从视频路径中提取视频名称(不含扩展名)
video_name = osp.splitext(osp.basename(video_path))[0]
print(f"[DataPreprocessHandler] 启动裁剪预览监控: {save_liquid_data_path}")
print(f"[DataPreprocessHandler] 当前视频: {video_name}")
print(f"[DataPreprocessHandler] 清空上一个视频的预览")
# 启动新的监控(自动清空旧显示,只监控当前视频的区域文件夹)
self._crop_preview_handler.startMonitoring(
save_liquid_data_path,
......@@ -1649,9 +1591,9 @@ class DataPreprocessHandler(QtCore.QObject):
def _onCropFinishedForPreview(self, save_liquid_data_path):
"""裁剪任务完成 - 刷新预览显示"""
if self._crop_preview_handler is not None:
print(f"[DataPreprocessHandler] 裁剪任务完成")
# 注意:不需要手动刷新,实时监控已经在工作
# 避免重新加载可能导致显示旧任务的图片
pass
def _connectSignals(self):
"""连接信号"""
......@@ -1683,7 +1625,6 @@ class DataPreprocessHandler(QtCore.QObject):
def _onFolderSelected(self, folder_path):
"""文件夹被选中(视频列表加载完成)"""
print(f"[DataPreprocessHandler] 文件夹被选中: {folder_path}")
# 延迟更新样式,确保视频网格已经加载完成
# 使用QTimer.singleShot延迟100ms执行
......@@ -1691,7 +1632,6 @@ class DataPreprocessHandler(QtCore.QObject):
def _onVideoRenamed(self, old_path, new_path):
"""视频被重命名,更新映射关系"""
print(f"[DataPreprocessHandler] 视频重命名: {osp.basename(old_path)} -> {osp.basename(new_path)}")
# 检查旧路径是否在映射中
if old_path in self._video_crop_mapping:
......@@ -1704,8 +1644,6 @@ class DataPreprocessHandler(QtCore.QObject):
# 添加新路径的映射
self._video_crop_mapping[new_path] = crop_info
print(f"[DataPreprocessHandler] 已更新映射关系: {osp.basename(old_path)} -> {osp.basename(new_path)}")
# 保存到文件
self._saveMappingToFile()
......@@ -1713,7 +1651,6 @@ class DataPreprocessHandler(QtCore.QObject):
# 因为重命名任何视频都会导致视频列表重新加载,需要重新应用样式
# 延迟执行,等待视频列表刷新完成
QTimer.singleShot(200, self.updateVideoGridStyles)
print(f"[DataPreprocessHandler] 已安排更新视频网格样式")
def _onVideoSelected(self, video_path):
"""视频被选中"""
......@@ -1750,9 +1687,8 @@ class DataPreprocessHandler(QtCore.QObject):
if self._current_video_capture is not None:
self._current_video_capture.release()
self._current_video_capture = None
print(f"[DataPreprocessHandler] 已释放视频文件句柄")
except Exception as e:
print(f"[DataPreprocessHandler] 释放视频文件句柄失败: {e}")
pass
def _loadVideoInfo(self, video_path):
"""加载视频信息"""
......@@ -1791,7 +1727,6 @@ class DataPreprocessHandler(QtCore.QObject):
self._video_height = 0
self._video_total_frames = 0
self._video_fps = 25.0
print(f"[ERROR] 加载视频信息失败: {e}")
def _showFrame(self, frame_index):
"""显示指定帧"""
......@@ -1836,7 +1771,7 @@ class DataPreprocessHandler(QtCore.QObject):
control_bar.setCurrentFrame(frame_index)
except Exception as e:
print(f"[ERROR] 显示帧失败: {e}")
pass
def _onPlayPause(self, is_playing):
"""播放/暂停按钮点击"""
......@@ -1850,7 +1785,6 @@ class DataPreprocessHandler(QtCore.QObject):
control_bar = self._panel.video_preview_widget.control_bar
if control_bar.isTimeRangeMode():
start_frame, end_frame = control_bar.getTimeRange()
print(f"[DataPreprocessHandler] 播放选定时间段: 帧 {start_frame} - {end_frame}")
# 检查当前帧位置,决定从哪里开始播放
if self._current_frame_index < start_frame or self._current_frame_index >= end_frame:
......@@ -1895,7 +1829,6 @@ class DataPreprocessHandler(QtCore.QObject):
# 时间段模式:循环播放选定的时间段
self._current_frame_index = start_frame
self._showFrame(start_frame)
print(f"[DataPreprocessHandler] 循环播放时间段: 从帧 {start_frame} 重新开始")
else:
# 普通模式:停止播放
if self._play_timer is not None:
......@@ -1955,8 +1888,6 @@ class DataPreprocessHandler(QtCore.QObject):
duration_frames = end_frame - start_frame + 1
duration_time = self._formatTimeFromFrame(duration_frames)
print(f"[DataPreprocessHandler] 时间段变化: {start_time} - {end_time} (共{duration_frames}帧, 时长{duration_time})")
# 不自动跳转,让用户自己控制播放位置(白点不跟随绿点移动)
# if self._current_frame_index < start_frame or self._current_frame_index > end_frame:
# self._current_frame_index = start_frame
......
......@@ -53,7 +53,6 @@ def backup_file(filepath):
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
backup_path = f"{filepath}.backup_{timestamp}"
shutil.copy2(filepath, backup_path)
print(f"✅ 已备份原文件到: {backup_path}")
return backup_path
def find_method_range(lines, method_name):
......@@ -92,16 +91,12 @@ def find_method_range(lines, method_name):
def cleanup_test_code(filepath):
"""清理测试代码"""
print("\n" + "=" * 60)
print("开始清理测试代码")
print("=" * 60)
# 读取文件
with open(filepath, 'r', encoding='utf-8') as f:
lines = f.readlines()
original_line_count = len(lines)
print(f"\n原文件行数: {original_line_count}")
# 记录删除的方法
deleted_methods = []
......@@ -113,9 +108,6 @@ def cleanup_test_code(filepath):
if result:
start, end = result
method_lines = end - start
print(f"\n找到方法: {method_name}")
print(f" 位置: 第 {start + 1} 行到第 {end} 行")
print(f" 行数: {method_lines} 行")
# 删除方法
del lines[start:end]
......@@ -126,9 +118,6 @@ def cleanup_test_code(filepath):
'lines': method_lines
})
deleted_lines += method_lines
print(f" ✅ 已删除")
else:
print(f"\n⚠️ 未找到方法: {method_name}")
# 写回文件
with open(filepath, 'w', encoding='utf-8') as f:
......@@ -136,20 +125,6 @@ def cleanup_test_code(filepath):
new_line_count = len(lines)
# 生成报告
print("\n" + "=" * 60)
print("清理完成")
print("=" * 60)
print(f"\n原文件行数: {original_line_count}")
print(f"新文件行数: {new_line_count}")
print(f"删除行数: {deleted_lines}")
print(f"删除方法数: {len(deleted_methods)}")
# 详细报告
print("\n删除的方法:")
for method in reversed(deleted_methods):
print(f" - {method['name']}: {method['lines']} 行 (第 {method['start']}-{method['end']} 行)")
# 生成报告文件
report_path = str(filepath).replace('.py', '_cleanup_report.txt')
with open(report_path, 'w', encoding='utf-8') as f:
......@@ -167,25 +142,18 @@ def cleanup_test_code(filepath):
f.write(f" - {method['name']}: {method['lines']} 行 (第 {method['start']}-{method['end']} 行)\n")
f.write("\n" + "=" * 60 + "\n")
print(f"\n✅ 清理报告已保存到: {report_path}")
return deleted_methods
def verify_file(filepath):
"""验证文件语法"""
print("\n" + "=" * 60)
print("验证文件语法")
print("=" * 60)
try:
with open(filepath, 'r', encoding='utf-8') as f:
code = f.read()
compile(code, filepath, 'exec')
print("✅ 文件语法正确")
return True
except SyntaxError as e:
print(f"❌ 语法错误: {e}")
return False
def main():
......@@ -194,25 +162,11 @@ def main():
filepath = script_dir / 'model_training_handler.py'
if not filepath.exists():
print(f"❌ 文件不存在: {filepath}")
return False
print("=" * 60)
print("模型训练处理器代码清理工具")
print("=" * 60)
print(f"\n目标文件: {filepath}")
print(f"\n将删除以下方法:")
for method in METHODS_TO_DELETE:
print(f" - {method}")
print(f"\n将保留以下方法:")
for method in METHODS_TO_KEEP:
print(f" - {method}")
# 确认
response = input("\n是否继续?(y/n): ")
if response.lower() != 'y':
print("已取消")
return False
# 备份
......@@ -224,26 +178,13 @@ def main():
# 验证
if verify_file(filepath):
print("\n🎉 清理成功!")
print(f"\n下一步:")
print(f" 1. 运行集成测试: python test_integration.py")
print(f" 2. 测试应用程序功能")
print(f" 3. 如有问题,可从备份恢复: {backup_path}")
return True
else:
print("\n❌ 清理后文件有语法错误,正在恢复...")
shutil.copy2(backup_path, filepath)
print(f"✅ 已从备份恢复")
return False
except Exception as e:
print(f"\n❌ 清理失败: {e}")
import traceback
traceback.print_exc()
print("\n正在从备份恢复...")
shutil.copy2(backup_path, filepath)
print(f"✅ 已从备份恢复")
return False
if __name__ == "__main__":
......
......@@ -79,17 +79,11 @@ class ModelPageHandler:
self._model_operations = ModelOperations(handler=handlers['model_set_handler'])
def create_model_page(self):
"""
创建模型管理页面(已弃用)
注意:此方法已不再使用,推荐直接使用 app.py 中的 _createModelPage() 方法。
保留此方法仅用于向后兼容。
Returns:
QtWidgets.QWidget: 模型页面
"""
print("[WARNING] create_model_page() 方法已弃用,请使用 app.py 中的 _createModelPage()")
"""创建模型页面(已弃用,保留以兼容旧代码)"""
pass
def _createModelPage(self):
"""创建模型页面的实际实现"""
# 导入页面组件(从 widgets.modelpage)
try:
from ...widgets.modelpage.modelset_page import ModelSetPage
......@@ -99,8 +93,7 @@ class ModelPageHandler:
from widgets.modelpage.modelset_page import ModelSetPage
from widgets.modelpage.training_page import TrainingPage
except ImportError as e:
print(f"[ERROR] 无法导入必要的页面组件: {e}")
return QtWidgets.QWidget()
return None
# 导入训练处理器
try:
......@@ -112,7 +105,6 @@ class ModelPageHandler:
from handlers.modelpage.model_training_handler import ModelTrainingHandler
except ImportError:
ModelTrainingHandler = None
print("[WARNING] 无法导入ModelTrainingHandler,训练功能将不可用")
# 创建主页面容器
page = QtWidgets.QWidget()
......@@ -130,8 +122,7 @@ class ModelPageHandler:
# 创建训练处理器
if ModelTrainingHandler:
self._parent.training_handler = ModelTrainingHandler()
self._parent.training_handler._set_main_window(self._parent)
print("[OK] 训练处理器初始化成功")
self._parent.training_handler.connectSignals()
else:
self._parent.training_handler = None
......@@ -139,10 +130,6 @@ class ModelPageHandler:
self._parent.trainingPage = TrainingPage(parent=self._parent)
self._parent.modelStackWidget.addWidget(self._parent.trainingPage)
# 连接训练处理器
if self._parent.training_handler:
self._parent.training_handler.connectToTrainingPanel(self._parent.trainingPage)
# 默认显示第一个页面(模型集管理)
self._parent.modelStackWidget.setCurrentIndex(0)
......@@ -167,12 +154,7 @@ class ModelPageHandler:
self._handlers['model_signal_handler'].setupModelPageConnections()
def add_test_models_to_list(self):
"""
添加测试模型到模型列表(已弃用)
注意:此方法已不再使用,模型应通过"模型集管理"页面手动添加。
"""
print("[WARNING] add_test_models_to_list() 方法已弃用")
"""添加测试模型到列表(已弃用)"""
pass
# ==================== 信号处理方法 ====================
......@@ -189,21 +171,11 @@ class ModelPageHandler:
QtWidgets.QMessageBox.warning(self._parent, "错误", f"运行模型测试失败: {e}")
def on_browse_test_file(self):
"""
浏览测试文件(已弃用)
注意:模型测试功能已移除,此方法不再使用。
"""
print("[WARNING] on_browse_test_file() 方法已弃用")
"""浏览测试文件(已弃用)"""
pass
def on_browse_save_liquid_data_path(self):
"""
浏览保存路径(已弃用)
注意:此方法已不再使用。
"""
print("[WARNING] on_browse_save_liquid_data_path() 方法已弃用")
"""浏览保存路径(已弃用)"""
pass
def on_add_model_set(self):
......
......@@ -417,44 +417,22 @@ class ModelSetHandler:
def loadModelsFromConfig(self):
"""从配置文件和模型目录加载所有模型"""
try:
print("🚀 [模型加载] 开始加载模型列表...")
# 1. 加载配置文件
print(" [模型加载] 步骤1: 加载配置文件")
config = self._loadConfigFile()
if not config:
print("❌ [模型加载] 配置文件加载失败,停止加载")
return
print("✅ [模型加载] 配置文件加载成功")
# 2. 跳过配置文件中的通道模型(只显示train_model目录下的模型)
print(" [模型加载] 步骤2: 跳过配置文件中的通道模型")
channel_models = [] # 不加载通道模型
print(f"✅ [模型加载] 跳过通道模型,专注于train_model目录")
# 3. 扫描模型目录
print(" [模型加载] 步骤3: 扫描模型目录")
scanned_models = self._scanModelDirectory()
print(f"✅ [模型加载] 从目录扫描到 {len(scanned_models)} 个模型")
# 4. 直接使用扫描到的模型(不合并通道模型)
print(" [模型加载] 步骤4: 使用扫描到的模型")
all_models = scanned_models # 直接使用扫描模型
# 设置第一个模型为默认模型(如果有模型的话)
if len(all_models) > 0:
all_models[0]['is_default'] = True
print(f"✅ [模型加载] 设置默认模型: {all_models[0]['name']}")
print(f"✅ [模型加载] 最终显示 {len(all_models)} 个模型")
# 5. 更新UI
print(" [模型加载] 步骤5: 更新UI显示")
self._updateModelList(all_models)
print("✅ [模型加载] 模型列表加载完成")
except Exception as e:
print(f"❌ [模型加载] 加载过程中发生异常: {e}")
import traceback
traceback.print_exc()
......@@ -543,33 +521,18 @@ class ModelSetHandler:
current_dir = Path(__file__).parent.parent.parent
model_dir = current_dir / "database" / "model" / "train_model"
print(f" [模型扫描] 项目根目录: {current_dir}")
print(f" [模型扫描] 模型目录路径: {model_dir}")
print(f" [模型扫描] 模型目录是否存在: {model_dir.exists()}")
if not model_dir.exists():
print("❌ [模型扫描] 模型目录不存在,返回空列表")
return models
# 列出目录中的所有项目
all_items = list(model_dir.iterdir())
print(f" [模型扫描] 目录中的所有项目: {[item.name for item in all_items]}")
# 遍历所有子目录,并按目录名排序
sorted_subdirs = sorted(model_dir.iterdir(), key=lambda x: x.name if x.is_dir() else '')
print(f" [模型扫描] 排序后的子目录: {[subdir.name for subdir in sorted_subdirs if subdir.is_dir()]}")
for subdir in sorted_subdirs:
if subdir.is_dir():
print(f" [模型扫描] 正在扫描子目录: {subdir.name} ({subdir})")
# 列出子目录中的所有文件
subdir_files = list(subdir.iterdir())
print(f" [模型扫描] 子目录 {subdir.name} 中的文件: {[f.name for f in subdir_files]}")
# 查找 .dat 文件(优先)
dat_files = list(subdir.glob("*.dat"))
print(f" [模型扫描] 找到的 .dat 文件: {[f.name for f in dat_files]}")
for model_file in sorted(dat_files):
model_info = {
......@@ -580,11 +543,8 @@ class ModelSetHandler:
'format': 'dat'
}
models.append(model_info)
print(f"✅ [模型扫描] 添加 .dat 模型: {model_info}")
# 然后查找 .pt 文件
pt_files = list(subdir.glob("*.pt"))
print(f" [模型扫描] 找到的 .pt 文件: {[f.name for f in pt_files]}")
for model_file in sorted(pt_files):
model_info = {
......@@ -595,16 +555,11 @@ class ModelSetHandler:
'format': 'pt'
}
models.append(model_info)
print(f"✅ [模型扫描] 添加 .pt 模型: {model_info}")
else:
print(f"⚠️ [模型扫描] 跳过非目录项: {subdir.name}")
except Exception as e:
print(f"❌ [模型扫描] 扫描过程中发生异常: {e}")
import traceback
traceback.print_exc()
print(f" [模型扫描] 扫描完成,共找到 {len(models)} 个模型")
return models
def _mergeModelInfo(self, channel_models, scanned_models):
......@@ -612,19 +567,12 @@ class ModelSetHandler:
all_models = []
seen_paths = set()
print(f" [模型合并] 开始合并模型信息")
print(f" [模型合并] 通道模型数量: {len(channel_models)}")
print(f" [模型合并] 扫描模型数量: {len(scanned_models)}")
# 优先添加配置文件中的通道模型
for model in channel_models:
path = model['path']
if path not in seen_paths:
all_models.append(model)
seen_paths.add(path)
print(f"✅ [模型合并] 添加通道模型: {model['name']} ({path})")
else:
print(f"⚠️ [模型合并] 跳过重复通道模型: {model['name']} ({path})")
# 再添加扫描到的模型(跳过已存在的)
for model in scanned_models:
......@@ -632,37 +580,26 @@ class ModelSetHandler:
if path not in seen_paths:
all_models.append(model)
seen_paths.add(path)
print(f"✅ [模型合并] 添加扫描模型: {model['name']} ({path})")
else:
print(f"⚠️ [模型合并] 跳过重复扫描模型: {model['name']} ({path})")
# 确保有一个默认模型
has_default = any(model.get('is_default', False) for model in all_models)
if not has_default and len(all_models) > 0:
all_models[0]['is_default'] = True
print(f"✅ [模型合并] 设置默认模型: {all_models[0]['name']}")
print(f" [模型合并] 合并完成,最终模型数量: {len(all_models)}")
return all_models
def _updateModelList(self, all_models):
"""更新UI中的模型列表"""
try:
print(f" [UI更新] 开始更新模型列表,模型数量: {len(all_models)}")
if not hasattr(self, 'modelSetPage') or not self.modelSetPage:
print("❌ [UI更新] modelSetPage 不存在,无法更新UI")
return
print(f"✅ [UI更新] 找到 modelSetPage,准备更新")
# 清空现有模型参数
self.modelSetPage._model_params = {}
# 添加所有模型到UI
for i, model in enumerate(all_models):
model_name = model['name']
print(f" [UI更新] 处理模型 {i+1}/{len(all_models)}: {model_name}")
# 创建模型参数
model_params = self._createModelParams(model, {})
......@@ -671,19 +608,11 @@ class ModelSetHandler:
# 设置默认模型
if model.get('is_default', False):
self.modelSetPage._current_default_model = model_name
print(f"✅ [UI更新] 设置默认模型: {model_name}")
# 刷新UI显示
if hasattr(self.modelSetPage, 'refreshModelList'):
print(f" [UI更新] 调用 refreshModelList 刷新UI")
self.modelSetPage.refreshModelList()
else:
print(f"⚠️ [UI更新] modelSetPage 没有 refreshModelList 方法")
print(f"✅ [UI更新] 模型列表更新完成")
except Exception as e:
print(f"❌ [UI更新] 更新UI时发生异常: {e}")
import traceback
traceback.print_exc()
......
......@@ -79,13 +79,10 @@ class ModelSignalHandler:
# self.modelSetPage.setDefaultRequested.connect(
# self.model_set_handler.setAsDefaultModel
# )
print("[OK] ModelSetPage 业务逻辑信号已连接到 model_set_handler")
else:
print("[警告] model_set_handler 未初始化,业务逻辑信号未连接")
if hasattr(self.modelSetPage, 'modelSetClicked'):
self.modelSetPage.modelSetClicked.connect(self._onModelSetPageModelSelected)
except Exception as e:
print(f"[ModelSignalHandler] 建立连接时出错: {e}")
import traceback
traceback.print_exc()
......@@ -96,7 +93,6 @@ class ModelSignalHandler:
Args:
model_name: 选中的模型名称
"""
print(f" [ModelSignalHandler] ModelSetPage 选中模型: {model_name}")
# 这里可以添加额外的处理逻辑,比如更新状态栏
if hasattr(self, 'statusBar'):
self.statusBar().showMessage(f"当前选中模型: {model_name}")
......@@ -108,7 +104,7 @@ class ModelSignalHandler:
Args:
model_name: 新添加的模型名称
"""
print(f" [ModelSignalHandler] 新模型已添加: {model_name}")
pass
def _onModelDeleted(self, model_name):
"""
......@@ -117,6 +113,4 @@ class ModelSignalHandler:
Args:
model_name: 删除的模型名称
"""
print(f" [ModelSignalHandler] 模型已删除: {model_name}")
pass
......@@ -95,11 +95,6 @@ class ModelTestThread(QThread):
is_video = self.test_params['is_video']
handler = self.test_params['handler']
print(f"[测试线程] 开始执行模型测试...")
print(f"[测试线程] 模型: {model_path}")
print(f"[测试线程] 测试文件: {test_file_path}")
print(f"[测试线程] 类型: {'视频' if is_video else '图片'}")
if is_video:
# 视频文件检测
self.progress_updated.emit(20, "正在加载视频文件...")
......@@ -113,8 +108,6 @@ class ModelTestThread(QThread):
self.error_occurred.emit("文件读取失败", f"无法读取测试文件: {test_file_path}")
return
print(f"[测试线程] 测试帧加载成功,尺寸: {test_frame.shape}")
self.progress_updated.emit(30, "正在执行液位检测...")
self._performImageDetection(handler, model_path, test_frame, annotation_file)
......@@ -122,7 +115,6 @@ class ModelTestThread(QThread):
self.test_finished.emit(True, "测试完成")
except Exception as e:
print(f"[测试线程] 测试失败: {e}")
import traceback
traceback.print_exc()
self.error_occurred.emit(type(e).__name__, str(e))
......@@ -132,21 +124,17 @@ class ModelTestThread(QThread):
try:
# 导入检测引擎
self.progress_updated.emit(40, "正在导入检测引擎...")
print(f"[测试线程] 正在导入检测引擎...")
try:
from handlers.videopage.detection import LiquidDetectionEngine
print(f"[测试线程] 检测引擎导入成功")
except ImportError:
try:
from ...handlers.videopage.detection import LiquidDetectionEngine
print(f"[测试线程] 检测引擎导入成功(相对导入)")
except ImportError as e:
raise ImportError(f"无法导入LiquidDetectionEngine: {e}")
# 读取标注数据
self.progress_updated.emit(45, "正在读取标注数据...")
print(f"[测试线程] 开始读取标注文件: {annotation_file}")
with open(annotation_file, 'r', encoding='utf-8') as f:
try:
......@@ -158,52 +146,38 @@ class ModelTestThread(QThread):
else:
raise
print(f"[测试线程] 标注数据加载成功")
# 创建检测引擎
self.progress_updated.emit(50, "正在创建检测引擎...")
print(f"[测试线程] 正在创建检测引擎...")
detection_engine = LiquidDetectionEngine(model_path=model_path)
self.progress_updated.emit(60, "正在加载模型权重...")
print(f"[测试线程] 正在加载模型权重...")
if not detection_engine.load_model(model_path):
raise RuntimeError(f"模型加载失败: {model_path}")
print(f"[测试线程] 模型权重加载成功")
# 配置标注数据
self.progress_updated.emit(70, "正在配置检测参数...")
detection_engine.set_annotation_data(annotation_data)
# 执行检测
self.progress_updated.emit(80, "正在执行液位检测...")
print(f"[测试线程] 开始执行液位检测...")
detection_result = detection_engine.detect_liquid_level(test_frame)
if detection_result is None:
raise RuntimeError("检测结果为空")
print(f"[测试线程] 液位检测完成")
self._detection_result = detection_result
# 保存图片测试结果
self.progress_updated.emit(90, "正在保存测试结果...")
print(f"[测试线程] 保存图片测试结果...")
self._saveImageTestResults(model_path, test_frame, detection_result, annotation_file)
except Exception as e:
print(f"[测试线程] 图片检测失败: {e}")
raise
def _performVideoDetection(self, handler, model_path, video_path, annotation_file):
"""执行视频检测"""
try:
print(f"[测试线程] 开始视频逐帧检测...")
# 视频检测需要在主线程中执行(涉及UI更新)
# 所以这里只是标记,实际检测由主线程的原有方法处理
# 发送进度信号
......@@ -211,16 +185,14 @@ class ModelTestThread(QThread):
# 注意:视频检测由于涉及实时播放器等UI组件
# 暂时保持在主线程执行,这里只做标记
print(f"[测试线程] 视频检测将在主线程中执行")
pass
except Exception as e:
print(f"[测试线程] 视频检测失败: {e}")
raise
def stop(self):
"""停止线程"""
self.is_running = False
print(f"[测试线程] 收到停止信号")
def get_detection_result(self):
"""获取检测结果"""
......@@ -287,14 +259,8 @@ class ModelTestHandler:
def _handleStopTest(self):
"""停止测试并释放资源"""
print("\n" + "="*60)
print("[模型测试] 用户请求停止测试")
print("="*60)
try:
# 设置停止标志
self._detection_stopped = True
print("[模型测试] 已设置停止标志")
# 恢复按钮状态
self.training_panel.setTestButtonState(False)
......@@ -310,21 +276,14 @@ class ModelTestHandler:
"""
self.training_panel.display_panel.setHtml(stop_html)
print("[模型测试] 测试已停止,资源已释放")
except Exception as e:
print(f"[模型测试] 停止测试时出错: {e}")
import traceback
traceback.print_exc()
def _handleStartTestExecution(self):
"""执行开始测试操作 - 液位检测测试功能"""
try:
print("\n" + "="*60)
print("[模型测试] 开始模型测试流程")
print("="*60)
# 切换按钮状态为"停止测试"
# 切换按钮状态为“停止测试”
self.training_panel.setTestButtonState(True)
# 获取选择的测试模型和测试文件
......@@ -334,11 +293,6 @@ class ModelTestHandler:
test_file_path_raw = self.training_panel.test_file_input.currentData() or ""
test_file_display = self.training_panel.test_file_input.currentText()
print(f"[模型测试] 选择的模型: {test_model_display}")
print(f"[模型测试] 原始模型路径: {test_model_path_raw}")
print(f"[模型测试] 测试文件: {test_file_display}")
print(f"[模型测试] 原始文件路径: {test_file_path_raw}")
# 关键修复:路径规范化处理,确保相对路径转换为绝对路径
project_root = get_project_root()
......@@ -346,7 +300,7 @@ class ModelTestHandler:
if test_model_path_raw and not os.path.isabs(test_model_path_raw):
# 相对路径,转换为绝对路径
test_model_path = os.path.join(project_root, test_model_path_raw)
print(f"[模型测试] 相对路径转换: {test_model_path_raw} -> {test_model_path}")
pass
else:
test_model_path = test_model_path_raw
......
......@@ -134,7 +134,6 @@ class ModelTrainingHandler(ModelTestHandler):
self.file_converter = PtToDatConverter()
except Exception as e:
self.file_converter = None
print(f"[ERROR] 初始化模型文件转换器失败: {e}")
def _set_main_window(self, main_window):
"""设置主窗口引用"""
......@@ -148,7 +147,6 @@ class ModelTrainingHandler(ModelTestHandler):
self.config_file_path = os.path.join(config_dir, "default_config.json")
if not os.path.exists(self.config_file_path):
print(f"[ERROR] 训练配置文件不存在: {self.config_file_path}")
self.train_config = None
return
......@@ -156,7 +154,6 @@ class ModelTrainingHandler(ModelTestHandler):
self.train_config = json.load(f)
except Exception as e:
print(f"[ERROR] 加载训练配置失败: {e}")
self.train_config = None
def _onStartTraining(self, training_params):
......@@ -248,7 +245,7 @@ class ModelTrainingHandler(ModelTestHandler):
if os.path.exists(model_icon_path):
msg_box.setWindowIcon(QtGui.QIcon(model_icon_path))
except Exception as e:
print(f"[确认对话框] 设置图标失败: {e}")
pass
msg_box.setStandardButtons(QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No)
msg_box.setDefaultButton(QtWidgets.QMessageBox.No)
......@@ -482,7 +479,6 @@ class ModelTrainingHandler(ModelTestHandler):
except Exception as refresh_error:
pass
# 同步更新模型测试页面
try:
self._refreshModelTestPage()
except Exception as test_refresh_error:
......@@ -542,7 +538,6 @@ class ModelTrainingHandler(ModelTestHandler):
# 刷新模型集管理页面
self._refreshModelSetPage()
# 同步更新模型测试页面
self._refreshModelTestPage()
# 获取 last.dat 路径(转换后应该是 dat 文件)
......@@ -657,11 +652,9 @@ class ModelTrainingHandler(ModelTestHandler):
"""立即转换PT文件为DAT格式并删除PT文件"""
try:
if not self.file_converter:
print("[ERROR] 未初始化文件转换器,无法执行pt到dat的转换")
return []
if not os.path.exists(weights_dir):
print(f"[ERROR] 权重目录不存在: {weights_dir}")
return []
converted_files = []
......@@ -673,11 +666,8 @@ class ModelTrainingHandler(ModelTestHandler):
pt_files_found.append(file)
if not pt_files_found:
print("[INFO] 未找到需要转换的PT文件")
return []
print(f"[INFO] 找到 {len(pt_files_found)} 个PT文件,开始转换...")
# 获取模型名称用于文件重命名
model_name = getattr(self, 'current_exp_name', 'trained_model')
......@@ -699,7 +689,6 @@ class ModelTrainingHandler(ModelTestHandler):
dat_path = os.path.join(weights_dir, dat_filename)
try:
print(f"[转换] {file} -> {dat_filename}")
converted_path = self.file_converter.convert_file(pt_path, dat_path)
if converted_path and os.path.exists(converted_path):
......@@ -707,16 +696,11 @@ class ModelTrainingHandler(ModelTestHandler):
# 立即删除PT文件
try:
os.remove(pt_path)
print(f"[清理] 已删除PT文件: {file}")
except OSError as remove_error:
print(f"[WARNING] 无法删除PT文件 {file}: {remove_error}")
else:
print(f"[ERROR] 转换失败: {file}")
pass
except Exception as convert_error:
print(f"[ERROR] 转换文件失败 {file}: {convert_error}")
print(f"[完成] 成功转换 {len(converted_files)} 个文件为DAT格式")
pass
# 强制清理任何残留的PT文件
self._forceCleanupPtFiles(weights_dir)
......@@ -724,7 +708,6 @@ class ModelTrainingHandler(ModelTestHandler):
return converted_files
except Exception as e:
print(f"[ERROR] PT到DAT转换失败: {e}")
return []
def _convertTrainingmission_resultsToDat(self, exp_name, weights_dir=None):
......@@ -782,7 +765,7 @@ class ModelTrainingHandler(ModelTestHandler):
self.temp_trained_models.clear()
except Exception as e:
print(f"[ERROR] 清理临时文件失败: {e}")
pass
def _forceCleanupPtFiles(self, weights_dir):
"""强制清理指定目录下的所有PT文件"""
......@@ -796,16 +779,14 @@ class ModelTrainingHandler(ModelTestHandler):
pt_files.append(os.path.join(weights_dir, file))
if pt_files:
print(f"[清理] 发现 {len(pt_files)} 个残留的PT文件,正在删除...")
for pt_file in pt_files:
try:
os.remove(pt_file)
print(f"[清理] 已删除: {os.path.basename(pt_file)}")
except OSError as e:
print(f"[WARNING] 无法删除PT文件 {os.path.basename(pt_file)}: {e}")
pass
except Exception as e:
print(f"[ERROR] 强制清理PT文件失败: {e}")
pass
def _saveTrainingLogToWeightsDir(self, exp_name, weights_dir=None):
"""
......@@ -829,7 +810,6 @@ class ModelTrainingHandler(ModelTestHandler):
weights_dir = os.path.join(train_root, "runs", "train", exp_name, "weights")
if not os.path.exists(weights_dir):
print(f"[警告] weights目录不存在: {weights_dir}")
return None
# 获取训练日志内容
......@@ -838,7 +818,6 @@ class ModelTrainingHandler(ModelTestHandler):
log_content = self.training_panel.log_display.toPlainText()
if not log_content:
print("[警告] 训练日志为空,跳过保存")
return None
# 生成日志文件名:training_log_[模型名称].txt
......@@ -858,11 +837,9 @@ class ModelTrainingHandler(ModelTestHandler):
f.write("日志结束\n")
f.write("=" * 80 + "\n")
print(f"[保存日志] 训练日志已保存: {log_filepath}")
return log_filepath
except Exception as e:
print(f"[ERROR] 保存训练日志失败: {e}")
import traceback
traceback.print_exc()
return None
......@@ -968,7 +945,7 @@ class ModelTrainingHandler(ModelTestHandler):
training_panel.exp_name_edit.setText('training_experiment')
except Exception as e:
print(f"[ERROR] 初始化训练面板默认值失败: {e}")
pass
def _handleStartTraining(self):
"""处理开始训练按钮点击"""
......@@ -1308,7 +1285,6 @@ class ModelTrainingHandler(ModelTestHandler):
return training_params
except Exception as e:
print(f"[ERROR] 路径验证失败: {e}")
return training_params
def _prepareBaseModelForTraining(self, base_model_path):
......@@ -1316,7 +1292,6 @@ class ModelTrainingHandler(ModelTestHandler):
try:
if base_model_path.endswith('.dat'):
# 如果是.dat文件,需要转换为.pt文件
print(f"[TRAINING] 检测到.dat文件,开始转换: {base_model_path}")
# 创建临时目录
temp_dir = tempfile.mkdtemp(prefix="liquid_training_")
......@@ -1324,15 +1299,12 @@ class ModelTrainingHandler(ModelTestHandler):
# 这里需要实现dat到pt的转换逻辑
# 暂时返回原路径
print(f"[TRAINING] 模型转换成功: {temp_model_path}")
return str(temp_model_path)
else:
# 如果是.pt文件,直接使用
print(f"[TRAINING] 使用.pt文件: {base_model_path}")
return base_model_path
except Exception as e:
print(f"[TRAINING] 处理基础模型失败: {e}")
return base_model_path
def _validateTrainingDataInThread(self, save_liquid_data_path):
......@@ -1531,14 +1503,10 @@ class ModelTrainingHandler(ModelTestHandler):
try:
# 通过主窗口访问模型集管理页面
if hasattr(self, 'main_window') and hasattr(self.main_window, 'modelSetPage'):
print("[ModelTrainingHandler] 训练完成,刷新模型集管理页面")
# 调用模型集页面的刷新方法
self.main_window.modelSetPage.loadModelsFromConfig()
self._appendLog("\n 模型集管理页面已更新\n")
else:
print("[ModelTrainingHandler] 无法访问模型集管理页面")
except Exception as e:
print(f"[ModelTrainingHandler] 刷新模型集页面失败: {e}")
import traceback
traceback.print_exc()
......@@ -1577,7 +1545,6 @@ class ModelTrainingHandler(ModelTestHandler):
return engine
except Exception as e:
print(f"[错误] 创建标注引擎失败: {e}")
return None
def _handleStartAnnotation(self):
......@@ -1603,9 +1570,6 @@ class ModelTrainingHandler(ModelTestHandler):
test_file_path = self.training_panel.test_file_input.currentData() or ""
test_file_display = self.training_panel.test_file_input.currentText()
print(f"[调试] 显示的测试文件: {test_file_display}")
print(f"[调试] 实际文件路径: {test_file_path}")
if not test_file_path:
QtWidgets.QMessageBox.warning(
self.training_panel,
......@@ -1613,27 +1577,18 @@ class ModelTrainingHandler(ModelTestHandler):
"请先选择测试文件"
)
return
print(f"[调试] 完整文件路径: {test_file_path}")
# 读取标注帧
annotation_frame = None
test_path = Path(test_file_path)
# 检查文件/文件夹是否存在
print(f"[调试] 路径是否存在: {test_path.exists()}")
print(f"[调试] 是否为文件: {test_path.is_file()}")
print(f"[调试] 是否为文件夹: {test_path.is_dir()}")
# 视频格式
video_extensions = ['.mp4', '.avi', '.mov', '.mkv', '.flv', '.wmv']
image_formats = ['.jpg', '.jpeg', '.png', '.bmp', '.tiff', '.webp']
if test_path.is_file():
print(f"[调试] 文件扩展名: {test_path.suffix.lower()}")
# 检查是否为视频文件
if test_path.suffix.lower() in video_extensions:
print(f"[调试] 识别为视频文件,开始读取第10帧")
# 读取视频的第10帧
cap = cv2.VideoCapture(str(test_path))
if cap.isOpened():
......@@ -1641,12 +1596,8 @@ class ModelTrainingHandler(ModelTestHandler):
ret, frame = cap.read()
if ret:
annotation_frame = frame
print(f"[调试] 视频第10帧读取成功,帧大小: {frame.shape}")
else:
print(f"[调试] 视频第10帧读取失败")
cap.release()
else:
print(f"[调试] 无法打开视频文件")
QtWidgets.QMessageBox.warning(
self.training_panel,
"视频打开失败",
......@@ -1655,26 +1606,15 @@ class ModelTrainingHandler(ModelTestHandler):
# 检查是否为图片文件
elif test_path.suffix.lower() in image_formats:
print(f"[调试] 识别为图片文件,开始读取")
# 直接读取图片
try:
annotation_frame = cv2.imread(test_file_path)
if annotation_frame is not None:
print(f"[调试] 图片读取成功,图片大小: {annotation_frame.shape}")
else:
print(f"[调试] cv2.imread返回None,可能的原因:")
print(f" - 文件路径包含中文字符")
print(f" - 文件损坏")
print(f" - 文件格式不支持")
print(f" - 文件权限问题")
if annotation_frame is None:
# 尝试使用不同的方法读取
try:
import numpy as np
from PIL import Image
print(f"[调试] 尝试使用PIL读取图片")
pil_image = Image.open(test_file_path)
print(f"[调试] PIL读取成功,图片模式: {pil_image.mode}, 大小: {pil_image.size}")
# 转换为OpenCV格式
if pil_image.mode == 'RGB':
annotation_frame = cv2.cvtColor(np.array(pil_image), cv2.COLOR_RGB2BGR)
......@@ -1682,40 +1622,25 @@ class ModelTrainingHandler(ModelTestHandler):
annotation_frame = cv2.cvtColor(np.array(pil_image), cv2.COLOR_RGBA2BGR)
else:
annotation_frame = np.array(pil_image)
print(f"[调试] PIL转OpenCV成功,最终图片大小: {annotation_frame.shape}")
except Exception as pil_e:
print(f"[调试] PIL读取也失败: {pil_e}")
pass
except Exception as e:
print(f"[错误] 读取图片失败: {e}")
import traceback
traceback.print_exc()
else:
print(f"[调试] 文件扩展名 {test_path.suffix.lower()} 不在支持的格式中")
print(f"[调试] 支持的图片格式: {image_formats}")
print(f"[调试] 支持的视频格式: {video_extensions}")
# 文件夹
elif test_path.is_dir():
print(f"[调试] 识别为文件夹,开始读取第一张图片")
# 读取文件夹内第一张图片
try:
files = os.listdir(test_file_path)
print(f"[调试] 文件夹内文件列表: {files}")
for file in files:
print(f"[调试] 检查文件: {file}")
if any(file.lower().endswith(fmt) for fmt in image_formats):
first_image_path = os.path.join(test_file_path, file)
print(f"[调试] 找到图片文件: {first_image_path}")
annotation_frame = cv2.imread(first_image_path)
if annotation_frame is not None:
print(f"[调试] 文件夹内图片读取成功,图片大小: {annotation_frame.shape}")
else:
print(f"[调试] 文件夹内图片读取失败: {first_image_path}")
break
else:
print(f"[调试] 文件夹内未找到支持的图片格式")
# 给出更友好的提示
QtWidgets.QMessageBox.warning(
self.training_panel,
......@@ -1732,11 +1657,8 @@ class ModelTrainingHandler(ModelTestHandler):
)
return
except Exception as e:
print(f"[错误] 读取文件夹内图片失败: {e}")
import traceback
traceback.print_exc()
else:
print(f"[调试] 路径既不是文件也不是文件夹")
if annotation_frame is None:
error_msg = f"无法读取标注帧\n\n调试信息:\n"
......@@ -1789,11 +1711,6 @@ class ModelTrainingHandler(ModelTestHandler):
# 连接标注完成信号
def on_annotation_completed(boxes, bottoms, tops):
print(f"\n[标注完成] 接收到标注数据:")
print(f" - boxes: {boxes}")
print(f" - bottoms: {bottoms}")
print(f" - tops: {tops}")
# 保存标注结果到临时配置
self._saveTestAnnotationResult(boxes, bottoms, tops,
self.annotation_widget.area_names,
......@@ -1805,7 +1722,6 @@ class ModelTrainingHandler(ModelTestHandler):
# 切换到显示面板
if hasattr(self.training_panel, 'display_layout'):
self.training_panel.display_layout.setCurrentIndex(1)
print(f"[标注预览] 已切换到显示面板")
# 显示预览图
self._showAnnotationPreview(
frame, boxes, bottoms, tops,
......@@ -1816,7 +1732,6 @@ class ModelTrainingHandler(ModelTestHandler):
QtCore.QTimer.singleShot(200, show_preview)
def on_annotation_cancelled():
print("[标注取消]")
# 隐藏标注界面,显示提示标签
if hasattr(self.training_panel, 'display_layout'):
self.training_panel.display_layout.setCurrentIndex(0) # 切换到提示标签
......@@ -1830,8 +1745,6 @@ class ModelTrainingHandler(ModelTestHandler):
# 标注界面配置为全屏显示(在_initUI中已设置)
# 这样用户可以进行全屏标注操作
self.annotation_widget.setWindowTitle("模型测试标注 - 全屏模式")
# 标注界面已在loadFrame中通过_applyFullScreen进行全屏显示
print("[标注界面] 已显示为全屏标注模式")
else:
QtWidgets.QMessageBox.warning(
self.training_panel,
......
......@@ -43,6 +43,10 @@ class DisplayThread:
# 保存上次的液位线数据(用于历史数据回退)
last_liquid_positions = {}
# 调试计数器(用于追踪检测启动后的帧数)
debug_frame_count = 0
last_detection_enabled = False
while context.display_flag:
try:
frame_start_time = time.time()
......@@ -59,7 +63,13 @@ class DisplayThread:
display_frame = frame
# 检测状态变化时重置调试计数器
if context.detection_enabled and not last_detection_enabled:
debug_frame_count = 0
last_detection_enabled = context.detection_enabled
# 如果检测已启动,叠加液位线
if context.detection_enabled:
debug_frame_count += 1
# 从 detection_mission_results 队列读取液位线数据(非阻塞)
liquid_positions = {}
......
......@@ -63,8 +63,21 @@ class StorageThread:
if not save_liquid_data_path or save_liquid_data_path == "0000":
return
# print(f" [存储线程-{channel_id}] 初始化")
# print(f" - 通道名称: {channel_name}")
# print(f" - 检测区域数: {len(area_names)}")
# print(f" - 曲线存储路径: {save_liquid_data_path}")
# 创建曲线存储目录
os.makedirs(save_liquid_data_path, exist_ok=True)
# ========== 视频保存功能(占坑,暂不实现) ==========
# TODO: 未来实现检测结果视频的保存
# timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
# video_path = context.storage_path if context.storage_path else save_liquid_data_path
# display_video_path = os.path.join(video_path, f"display_{timestamp}.mp4")
# 创建CSV文件写入器(每个检测区域一个文件)
csv_files = {}
csv_writers = {}
......@@ -81,6 +94,9 @@ class StorageThread:
# 存储线程不受 save_data_rate 限制,全速消费队列,避免数据丢失
# save_data_rate 仅用于配置文件记录,实际存储线程会保存所有检测结果
# print(f" [存储线程-{channel_id}] 启动成功")
# print(f" - 注意:存储线程全速运行,保存所有检测结果(不受save_data_rate限制)")
data_count = 0
last_log_time = time.time()
last_flush_time = time.time() # 记录上次刷新时间
......@@ -93,18 +109,44 @@ class StorageThread:
# 从独立的 storage_data 队列读取,避免与曲线线程竞争
detection_mission_result = context.storage_data.get(timeout=0.1)
# # 调试信息:存储线程接收数据
# print(f"\n [存储线程-{channel_id}] 接收到检测结果:")
# print(f" - detection_mission_result类型: {type(detection_mission_result)}")
# if detection_mission_result:
# print(f" - 包含的键: {detection_mission_result.keys()}")
# 解析液位高度数据
if detection_mission_result and 'liquid_line_positions' in detection_mission_result:
liquid_positions = detection_mission_result['liquid_line_positions']
current_time = datetime.now().strftime("%Y-%m-%d-%H:%M:%S.%f")[:-3]
# # 调试信息:液位位置数据
# print(f" [存储线程-{channel_id}] 液位位置数据:")
# for area_idx, position_data in liquid_positions.items():
# print(f" 目标{area_idx}:")
# print(f" - position_data键: {position_data.keys()}")
# print(f" - 包含'height_cm'? {'height_cm' in position_data}")
# print(f" - 包含'height_mm'? {'height_mm' in position_data}")
# if 'height_cm' in position_data:
# print(f" - height_cm值: {position_data['height_cm']}")
# if 'height_mm' in position_data:
# print(f" - height_mm值: {position_data['height_mm']}")
# 为每个检测区域写入数据
for area_idx, position_data in liquid_positions.items():
if area_idx in csv_writers:
# 统一使用mm单位,直接读取height_mm
height_mm = position_data.get('height_mm', 0.0)
# # 调试信息:读取到的高度值
# print(f" [存储线程-{channel_id}] 目标{area_idx} 读取高度:")
# print(f" - height_mm: {height_mm:.2f}mm")
# 存储mm值(保留1位小数,精度0.1mm)
height_decimal = round(height_mm, 1)
# print(f" - 最终写入CSV: {height_decimal}mm")
# 写入格式:时间戳 高度值(mm, 保留1位小数)
csv_writers[area_idx].write(f"{current_time} {height_decimal:.1f}\n")
# 不再立即刷新,改为批量刷新
......@@ -112,20 +154,22 @@ class StorageThread:
data_count += 1
# 定时批量刷新到磁盘(每0.5秒一次)
# 定时批量刷新到磁盘(每0.5秒一次)
current_time_check = time.time()
if current_time_check - last_flush_time >= flush_interval:
for csv_file in csv_writers.values():
csv_file.flush()
last_flush_time = current_time_check
# 每5秒打印一次统计
if time.time() - last_log_time > 5.0:
# print(f" [存储线程-{channel_id}] 已保存 {data_count} 条数据,队列大小: {context.storage_data.qsize()}")
last_log_time = time.time()
except queue.Empty:
# 队列为空,继续等待
pass
except Exception as e:
# print(f" [存储线程-{channel_id}] CSV写入错误: {e}")
import traceback
traceback.print_exc()
......@@ -136,15 +180,21 @@ class StorageThread:
# 队列的 get(timeout=0.1) 已经提供了适当的等待
except Exception as e:
# print(f" [存储线程-{channel_id}] 错误: {e}")
time.sleep(0.1)
# 清理资源
# print(f" [存储线程-{channel_id}] 正在停止...")
# 关闭CSV文件
for csv_file in csv_files.values():
csv_file.close()
# TODO: 未来需要释放视频写入器
# TODO: 未来需要释放视频写入器
# if display_writer:
# display_writer.release()
# print(f" [存储线程-{channel_id}] 已停止")
@staticmethod
def _load_channel_config(channel_id: str):
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment