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,12 +738,7 @@ 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'
setattr(self, mission_var_name, channelPanel.taskLabel)
......@@ -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)
......@@ -1213,18 +1202,12 @@ class MainWindow(
def main():
"""独立运行入口,用于UI预览和调试"""
import sys
# 创建应用
app = QtWidgets.QApplication(sys.argv)
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):
"""
......
......@@ -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
......
......@@ -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,55 +63,96 @@ 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 = {}
for area_idx, area_name in area_names.items():
# 文件名:区域名称.csv(区域名称已包含通道信息,不需要重复)
# 文件名:区域名称.csv(区域名称已包含通道信息,不需要重复)
csv_filename = f"{area_name}.csv"
csv_filepath = os.path.join(save_liquid_data_path, csv_filename)
# 打开CSV文件(追加模式,增加缓冲区)
# 打开CSV文件(追加模式,增加缓冲区)
csv_file = open(csv_filepath, 'a', encoding='utf-8', buffering=8192) # 8KB缓冲
csv_files[area_idx] = csv_file
csv_writers[area_idx] = csv_file
# 存储线程不受 save_data_rate 限制,全速消费队列,避免数据丢失
# 存储线程不受 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() # 记录上次刷新时间
flush_interval = 0.5 # 批量刷新间隔(秒),每0.5秒刷新一次
last_flush_time = time.time() # 记录上次刷新时间
flush_interval = 0.5 # 批量刷新间隔(秒),每0.5秒刷新一次
while context.storage_flag:
try:
# 1. 保存检测结果到CSV(从存储数据队列读取)
try:
# 从独立的 storage_data 队列读取,避免与曲线线程竞争
# 从独立的 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
# 统一使用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")
# 不再立即刷新,改为批量刷新
# 不再立即刷新,改为批量刷新
data_count += 1
# 定时批量刷新到磁盘(每0.5秒一次)
# 定时批量刷新到磁盘(每0.5秒一次)
current_time_check = time.time()
if current_time_check - last_flush_time >= flush_interval:
......@@ -119,13 +160,16 @@ class StorageThread:
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