Commit 872a1442 by Yuhaibo

1

parent dde5f108
......@@ -1311,30 +1311,15 @@ class MainWindow(
def showVideoPage(self):
"""显示实时检测管理页面"""
print(f"\n{'='*60}")
print(f"[showVideoPage] ========== 切换到实时检测管理页面 ==========")
print(f"{'='*60}")
self.stackedWidget.setCurrentWidget(self.videoPage)
# 🔥 确保切换到默认布局(videoLayoutStack 索引0)
if hasattr(self, 'videoLayoutStack'):
print(f"[showVideoPage] videoLayoutStack 当前索引: {self.videoLayoutStack.currentIndex()}")
print(f"[showVideoPage] 切换 videoLayoutStack 到索引 0(默认布局)...")
self.videoLayoutStack.setCurrentIndex(0)
self._video_layout_mode = 0
print(f"[showVideoPage] 切换后索引: {self.videoLayoutStack.currentIndex()}")
# 显示当前控件信息
current_widget = self.videoLayoutStack.currentWidget()
print(f"[showVideoPage] 当前控件: {current_widget}")
if current_widget:
print(f" - 可见性: {current_widget.isVisible()}")
print(f" - 大小: {current_widget.size()}")
# 🔥 确保所有通道面板在默认布局中可见
if hasattr(self, 'channelPanels'):
print(f"[showVideoPage] 确保所有通道面板可见...")
for i, channel_panel in enumerate(self.channelPanels):
if hasattr(self, 'default_channel_positions') and i < len(self.default_channel_positions):
# 确保通道面板在正确的父容器中
......@@ -1345,10 +1330,8 @@ class MainWindow(
# 显示通道面板
channel_panel.show()
print(f" - 通道{i+1}: 可见性={channel_panel.isVisible()}, 父容器={channel_panel.parent()}")
self.statusBar().showMessage(self.tr("当前页面: 实时检测管理"))
print(f"[showVideoPage] ========== 切换完成 ==========\n")
def showModelPage(self):
"""显示模型管理页面"""
......
......@@ -112,10 +112,14 @@
13.**widgets/videopage/missionpanel.py** - 删除约20处print语句
14.**widgets/videopage/general_set.py** - 删除约30处print语句
15.**widgets/videopage/curvepanel.py** - 删除4处print语句
16.**widgets/videopage/historyvideopanel.py** - 删除约15处print语句
16.**widgets/videopage/historyvideopanel.py** - 删除约16处print语句和3处DEBUG注释
### 部分清理
17. 🔄 **handlers/videopage/missionpanel_handler.py** - 已清理前330行,删除约20处print语句(文件共2050行,需继续清理)
17. 🔄 **handlers/videopage/missionpanel_handler.py** - 已清理前880行,删除约50处print语句(文件共2004行,需继续清理)
### 新增完成清理
18.**widgets/datasetpage/crop_preview_panel.py** - 删除约47处print语句和2处DEBUG注释
19.**widgets/datasetpage/datacollection_panel.py** - 删除约20处print语句和1处DEBUG注释
## 清理进度说明
......@@ -146,13 +150,13 @@
- ⏳ widgets/datasetpage/test_*.py
### 剩余工作量估算
- 已清理: 约283处调试语句 (16个文件完全清理 + 1个文件部分清理)
- 剩余: 约3158处调试语句
- 预计需要: 继续手动清理约47个文件
- 已清理: 约384处调试语句 (18个文件完全清理 + 1个文件部分清理)
- 剩余: 约3057处调试语句
- 预计需要: 继续手动清理约45个文件
### 本次清理总结 (2025-11-26 20:30)
- 完成文件数: 16个完全清理 + 1个部分清理
- 删除调试语句: 约283处
### 本次清理总结 (2025-11-26 20:46)
- 完成文件数: 18个完全清理 + 1个部分清理
- 删除调试语句: 约384处(包含print语句和DEBUG注释)
- 主要清理内容:
- 核心应用入口和窗口管理 (app.py)
- 视图布局切换管理 (view_handler.py)
......@@ -161,8 +165,9 @@
- 曲线面板Handler (curvepanel_handler.py)
- 完整线程管理系统 (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 (missionpanel_handler.py)
---
*最后更新: 2025-11-26 20:30*
*最后更新: 2025-11-26 20:46*
......@@ -43,10 +43,6 @@ class DisplayThread:
# 保存上次的液位线数据(用于历史数据回退)
last_liquid_positions = {}
# 调试计数器(用于追踪检测启动后的帧数)
debug_frame_count = 0
last_detection_enabled = False
while context.display_flag:
try:
frame_start_time = time.time()
......@@ -63,13 +59,7 @@ 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,96 +63,55 @@ 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:
......@@ -160,16 +119,13 @@ 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()
......@@ -180,21 +136,15 @@ 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: 未来需要释放视频写入器
# if display_writer:
# display_writer.release()
# print(f" [存储线程-{channel_id}] 已停止")
# TODO: 未来需要释放视频写入器
@staticmethod
def _load_channel_config(channel_id: str):
......
# -*- coding: utf-8 -*-
"""
模型加载诊断脚本
用于测试检测线程的模型配置加载是否正常
"""
import os
import sys
import yaml
# 添加项目根目录到路径
current_dir = os.path.dirname(os.path.abspath(__file__))
project_root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(current_dir))))
sys.path.insert(0, project_root)
from detection_thread import DetectionThread
def test_model_config_loading():
"""测试模型配置加载"""
print("=" * 80)
print("模型配置加载测试")
print("=" * 80)
# 测试各个通道
for channel_id in ['channel1', 'channel2', 'channel3', 'channel4']:
print(f"\n{'='*80}")
print(f"测试 {channel_id}")
print('='*80)
# 加载模型配置
model_config = DetectionThread._load_model_config(channel_id)
if model_config:
print(f"[OK] [{channel_id}] 模型配置加载成功")
print(f" 配置内容:")
for key, value in model_config.items():
if key == 'model_path':
print(f" - {key}: {value}")
# 检查文件是否存在
if os.path.exists(value):
file_size = os.path.getsize(value) / (1024 * 1024) # MB
print(f" [OK] 文件存在 ({file_size:.2f} MB)")
else:
print(f" [ERROR] 文件不存在!")
else:
print(f" - {key}: {value}")
else:
print(f"[ERROR] [{channel_id}] 模型配置加载失败")
# 加载标注配置
print(f"\n尝试加载 {channel_id} 的标注配置...")
annotation_config = DetectionThread._load_annotation_config(channel_id)
if annotation_config:
print(f"[OK] [{channel_id}] 标注配置加载成功")
boxes = annotation_config.get('boxes', [])
print(f" - 检测区域数: {len(boxes)}")
print(f" - 实际高度: {annotation_config.get('actual_heights', [])}")
else:
print(f"[ERROR] [{channel_id}] 标注配置加载失败")
def test_default_config_structure():
"""测试 default_config.yaml 的结构"""
print("\n" + "=" * 80)
print("检查 default_config.yaml 配置结构")
print("=" * 80)
config_file = os.path.join(project_root, 'database', 'config', 'default_config.yaml')
if not os.path.exists(config_file):
print(f" 配置文件不存在: {config_file}")
return
print(f" 配置文件存在: {config_file}\n")
with open(config_file, 'r', encoding='utf-8') as f:
config = yaml.safe_load(f)
# 检查各通道的模型路径配置
print("检查各通道模型路径配置:")
for i in range(1, 5):
channel_id = f'channel{i}'
model_path_key = f'{channel_id}_model_path'
if model_path_key in config:
model_path = config[model_path_key]
print(f" {model_path_key}: {model_path}")
# 转换为绝对路径
if not os.path.isabs(model_path):
model_path = model_path.replace('/', os.sep).replace('\\', os.sep)
full_path = os.path.join(project_root, model_path)
full_path = os.path.normpath(full_path)
else:
full_path = model_path
# 检查文件是否存在
if os.path.exists(full_path):
file_size = os.path.getsize(full_path) / (1024 * 1024) # MB
print(f" 文件存在: {full_path} ({file_size:.2f} MB)")
else:
print(f" 文件不存在: {full_path}")
else:
print(f" {model_path_key}: 未配置")
# 检查全局模型配置
print("\n检查全局模型配置:")
if 'model' in config:
model_config = config['model']
for key, value in model_config.items():
print(f" - {key}: {value}")
else:
print(" 未找到全局 model 配置")
# 检查GPU配置
print("\n检查GPU配置:")
print(f" - gpu_enabled: {config.get('gpu_enabled', '未配置')}")
print(f" - default_device: {config.get('default_device', '未配置')}")
print(f" - batch_processing_enabled: {config.get('batch_processing_enabled', '未配置')}")
print(f" - default_batch_size: {config.get('default_batch_size', '未配置')}")
if __name__ == '__main__':
print(f"项目根目录: {project_root}\n")
test_default_config_structure()
print("\n")
test_model_config_loading()
print("\n" + "=" * 80)
print("测试完成")
print("=" * 80)
......@@ -43,7 +43,6 @@ except ImportError:
try:
from datasetpage import AnnotationHandler
except ImportError as e:
print(f"Warning: AnnotationHandler not available: {e}")
AnnotationHandler = None
# 导入labelme
......@@ -57,7 +56,6 @@ try:
from labelme.config import get_config as labelme_get_config # 重命名避免冲突
LABELME_AVAILABLE = True
except ImportError as e:
print(f"Warning: Labelme not available: {e}")
LABELME_AVAILABLE = False
LabelmeMainWindow = None
labelme_get_config = None
......@@ -298,7 +296,6 @@ class AnnotationTool(QtWidgets.QWidget):
def _initLabelme(self):
"""初始化并嵌入Labelme标注工具"""
if not LABELME_AVAILABLE:
print("Labelme未安装,无法初始化标注工具")
return
try:
......@@ -381,8 +378,6 @@ class AnnotationTool(QtWidgets.QWidget):
padding: 5px;
}
""")
print("[AnnotationTool] 已优化形状列表显示")
# 优化唯一标签列表的显示
if hasattr(self.labelme_widget, 'uniqLabelList'):
......@@ -412,8 +407,6 @@ class AnnotationTool(QtWidgets.QWidget):
margin: 2px 0px;
}
""")
print("[AnnotationTool] 已优化标签列表显示")
# 调整dock面板的大小 - 这是关键!
if hasattr(self.labelme_widget, 'shape_dock'):
......@@ -425,17 +418,14 @@ class AnnotationTool(QtWidgets.QWidget):
current_width = shape_dock.width()
if current_width < 260:
shape_dock.resize(260, shape_dock.height())
print(f"[AnnotationTool] 已调整形状面板宽度: {shape_dock.width()}px")
# 也调整标签dock面板(如果显示的话)
if hasattr(self.labelme_widget, 'label_dock'):
label_dock = self.labelme_widget.label_dock
label_dock.setMinimumWidth(scale_w(220)) # 🔥 响应式宽度
print(f"[AnnotationTool] 已调整标签面板宽度")
except Exception as e:
print(f"[AnnotationTool] 优化标签显示失败: {e}")
pass
def _connectLabelmeActions(self):
"""连接工具栏按钮到labelme的功能"""
......@@ -458,7 +448,7 @@ class AnnotationTool(QtWidgets.QWidget):
self.labelme_widget.adjustScale
)
except Exception as e:
print(f"连接labelme操作失败: {e}")
pass
def _connectSignals(self):
"""连接UI信号和槽"""
......@@ -521,8 +511,6 @@ class AnnotationTool(QtWidgets.QWidget):
if not dir_path or not osp.exists(dir_path):
return
print(f"检测到labelme打开目录: {dir_path}")
# 使用handler加载目录
if self.annotation_handler:
self.annotation_handler.setDirectory(dir_path)
......@@ -708,15 +696,12 @@ class AnnotationTool(QtWidgets.QWidget):
json_path: JSON标注文件路径(可选)
"""
if self.labelme_widget is None:
print("Labelme未初始化")
return
try:
# 使用labelme的loadFile方法加载图片
self.labelme_widget.loadFile(image_path)
print(f"已加载图片: {image_path}")
except Exception as e:
print(f"加载图片失败: {e}")
import traceback
traceback.print_exc()
......@@ -796,45 +781,6 @@ if __name__ == "__main__":
annotation_tool.lbl_total_stats.setText(f"总数: {total}")
annotation_tool.lbl_annotated_stats.setText(f"已标注: {annotated}")
# 打印测试说明
print("\n" + "="*70)
print(" AnnotationTool 数据标注工具组件测试程序")
print("="*70)
print(" 界面布局(两栏):")
print("\n 左侧面板 - 标注数据列表:")
print(" - 搜索框:支持按图片名称搜索")
print(" - 筛选选项:全部/已标注/未标注/待审核")
print(" - 标注列表:显示图片缩略图和基本信息")
print(" - 操作按钮:编辑、删除、导出")
print(" - 底部统计:总数、已标注数量")
print("\n 右侧面板 - Labelme标注工具区域:")
print(" - Labelme已完整嵌入到右侧面板")
print(" - 工具栏:放大、缩小、适应窗口(已连接到labelme功能)")
print(" - 包含labelme的全部标注功能:")
print(" * 多边形/矩形标注")
print(" * 标签管理")
print(" * 文件列表")
print(" * 标注列表")
print("\n 布局特点:")
print(" - 使用 QSplitter 实现可调整宽度的两栏布局")
print(" - 默认比例: 左:右 = 1:3")
print(" - 可以拖拽分割线调整各面板宽度")
print("\n Labelme集成完成:")
print(" - self.labelme_widget: 嵌入的labelme主窗口实例")
print(" - self.labelme_container: labelme的容器")
print(" - self.labelme_layout: 容器的布局管理器")
print(" - loadImageForAnnotation(): 加载图片到labelme进行标注")
print("\n 使用方法:")
print(" - 左侧列表选择图片")
print(" - 调用 loadImageForAnnotation(image_path) 加载到labelme")
print(" - 在右侧labelme面板中进行标注")
print(" - Labelme保持独立性,所有原生功能完整保留")
print("\n 说明:")
print(" - Labelme已完整嵌入,功能独立完整")
print(" - 左侧面板按钮功能可后续完善")
print(" - 界面风格统一,适配现有系统")
print("="*70 + "\n")
window.show()
sys.exit(app.exec_())
......@@ -341,7 +341,7 @@ class CropConfigDialog(QtWidgets.QDialog):
self.format_combo.setCurrentText(saved_format)
except Exception as e:
print(f" 加载设置失败: {e}")
pass
def _saveSettings(self):
"""保存当前设置"""
......@@ -352,7 +352,7 @@ class CropConfigDialog(QtWidgets.QDialog):
settings.setValue("file_prefix", self.prefix_edit.text())
settings.setValue("image_format", self.format_combo.currentText())
except Exception as e:
print(f" 保存设置失败: {e}")
pass
def _onBrowse(self):
"""浏览文件夹"""
......@@ -441,8 +441,6 @@ class CropConfigDialog(QtWidgets.QDialog):
self.format_combo.setCurrentText(config['image_format'])
# ==================== 独立运行测试 ====================
if __name__ == "__main__":
"""独立运行测试"""
import sys
......@@ -455,54 +453,7 @@ if __name__ == "__main__":
default_frequency=10
)
# 打印测试说明
print("\n" + "="*70)
print(" 裁剪配置对话框测试程序")
print("="*70)
print(" 功能列表:")
print("\n 保存路径设置:")
print(" - 文本框显示当前路径")
print(" - 浏览按钮打开文件夹选择对话框")
print(" - 自动验证路径有效性")
print("\n 裁剪频率设置:")
print(" - SpinBox 设置裁剪间隔(1-1000帧)")
print(" - 快捷按钮:每帧、每5帧、每10帧、每30帧、每60帧")
print(" - 实时显示频率说明")
print("\n 文件命名设置:")
print(" - 设置文件名前缀")
print(" - 选择图片格式(jpg/png/bmp/tiff)")
print(" - 显示命名示例")
print("\n 设置持久化:")
print(" - 使用 QSettings 自动保存设置")
print(" - 下次打开自动恢复上次配置")
print("\n 界面特点:")
print(" - 分组布局,层次清晰")
print(" - 图标和emoji装饰")
print(" - 现代化的配色和圆角设计")
print(" - 响应式布局")
print("="*70 + "\n")
# 显示对话框
mission_result = dialog.exec_()
# 打印结果
if mission_result == QtWidgets.QDialog.Accepted:
config = dialog.getConfig()
print("\n 用户点击了【确定】按钮")
print(" 配置信息:")
print(f" 保存路径: {config['save_liquid_data_path']}")
print(f" 裁剪频率: 每 {config['crop_frequency']} 帧")
print(f" 文件前缀: {config['file_prefix']}")
print(f" 图片格式: {config['image_format']}")
print(f"\n 文件命名示例:")
print(f" {config['file_prefix']}_0001.{config['image_format']}")
print(f" {config['file_prefix']}_0002.{config['image_format']}")
print(f" {config['file_prefix']}_0003.{config['image_format']}")
print(" ...")
else:
print("\n 用户点击了【取消】按钮")
print("\n" + "="*70 + "\n")
sys.exit(0)
......@@ -252,10 +252,6 @@ class CropPreviewPanel(QtWidgets.QWidget):
auto_refresh: 是否自动刷新图片列表(默认True)
video_name: 视频名称,如果提供则只显示该视频的区域文件夹
"""
print(f"[CropPreview] ========== 设置保存路径 ==========")
print(f"[CropPreview] 保存路径: {save_liquid_data_path}")
print(f"[CropPreview] 视频名称: {video_name}")
print(f"[CropPreview] 自动刷新: {auto_refresh}")
self._save_liquid_data_path = save_liquid_data_path
self._video_name = video_name # 保存视频名称
......@@ -283,16 +279,12 @@ class CropPreviewPanel(QtWidgets.QWidget):
# 只在需要时加载图片
if auto_refresh:
self.refreshImages()
print(f"[CropPreview] 找到 {len([p for p in self._region_paths if p])} 个区域文件夹")
print(f"[CropPreview] ========== 设置完成 ==========")
def _findRegionPaths(self):
"""查找所有区域文件夹(支持新旧命名格式,可根据视频名称过滤)"""
self._region_paths = []
if not self._save_liquid_data_path or not osp.exists(self._save_liquid_data_path):
print(f"[CropPreview] 保存路径无效或不存在")
return
try:
......@@ -300,10 +292,6 @@ class CropPreviewPanel(QtWidgets.QWidget):
subdirs = [d for d in os.listdir(self._save_liquid_data_path)
if osp.isdir(osp.join(self._save_liquid_data_path, d))]
print(f"[CropPreview] 扫描目录: {self._save_liquid_data_path}")
print(f"[CropPreview] 发现子文件夹: {subdirs}")
print(f"[CropPreview] 当前视频名称过滤: {self._video_name}")
# 筛选出区域文件夹
region_folders = []
for subdir in subdirs:
......@@ -312,22 +300,14 @@ class CropPreviewPanel(QtWidgets.QWidget):
# 匹配格式:视频名_区域X
if subdir.startswith(f"{self._video_name}_") and '区域' in subdir:
region_folders.append(subdir)
print(f"[CropPreview] 匹配视频文件夹: {subdir}")
else:
print(f"[CropPreview] 跳过文件夹(不匹配当前视频): {subdir}")
else:
# 未指定视频名称时,查找所有区域文件夹
if '区域' in subdir or 'region' in subdir.lower():
region_folders.append(subdir)
print(f"[CropPreview] 匹配区域文件夹: {subdir}")
# 按文件夹名排序
region_folders.sort()
if self._video_name:
print(f"[CropPreview] ==> 只显示视频 '{self._video_name}' 的区域文件夹")
print(f"[CropPreview] ==> 最终找到 {len(region_folders)} 个区域文件夹: {region_folders}")
# 初始化所有区域为None
for i in range(3):
self._region_paths.append(None)
......@@ -345,14 +325,8 @@ class CropPreviewPanel(QtWidgets.QWidget):
if 0 <= region_index < 3:
region_path = osp.join(self._save_liquid_data_path, folder)
self._region_paths[region_index] = region_path
print(f"[CropPreview] 区域{region_num} -> {folder} (索引{region_index})")
else:
print(f"[CropPreview] 跳过无效区域编号: {folder}")
else:
print(f"[CropPreview] 无法解析区域编号: {folder}")
except Exception as e:
print(f"[CropPreview] 查找区域文件夹失败: {e}")
import traceback
traceback.print_exc()
# 降级到旧的查找方式
......@@ -360,7 +334,6 @@ class CropPreviewPanel(QtWidgets.QWidget):
region_path = osp.join(self._save_liquid_data_path, f"region{i+1}")
if osp.exists(region_path):
self._region_paths.append(region_path)
print(f"[CropPreview] (旧格式) 找到区域文件夹: region{i+1}")
else:
self._region_paths.append(None)
......@@ -382,8 +355,6 @@ class CropPreviewPanel(QtWidgets.QWidget):
# 更新统计
self._updateStats()
print(f"[CropPreview] 刷新完成")
def _loadRegionImages(self, region_index, region_path):
"""
......@@ -428,7 +399,6 @@ class CropPreviewPanel(QtWidgets.QWidget):
font = placeholder_item.font()
font.setPointSize(10)
placeholder_item.setFont(font)
print(f"[CropPreview] 区域{region_index+1} 等待图片生成...")
return
# 添加到网格(只显示最新的100张,避免加载过多)
......@@ -458,10 +428,8 @@ class CropPreviewPanel(QtWidgets.QWidget):
# 设置文本居中对齐
item.setTextAlignment(Qt.AlignCenter)
print(f"[CropPreview] 区域{region_index+1} 加载 {len(files)} 张图片 (显示最新{len(display_files)}张)")
except Exception as e:
print(f"[CropPreview] 加载区域{region_index+1}图片失败: {e}")
pass
def _generateThumbnail(self, image_path):
"""
......@@ -490,7 +458,6 @@ class CropPreviewPanel(QtWidgets.QWidget):
return scaled_pixmap
except Exception as e:
print(f"[CropPreview] 生成缩略图失败: {e}")
return None
def clearImages(self):
......@@ -522,8 +489,6 @@ class CropPreviewPanel(QtWidgets.QWidget):
# 更新统计
self._updateStats()
print(f"[CropPreview] 清空图片列表,准备新任务")
def addImage(self, region_index, image_path):
"""
......@@ -597,7 +562,6 @@ class CropPreviewPanel(QtWidgets.QWidget):
self._current_region = index
self._updateStats()
self.regionChanged.emit(index)
print(f"[CropPreview] 切换到区域 {index+1}")
def _onImageDoubleClicked(self, item):
"""图片被双击 - 显示大图"""
......@@ -710,14 +674,11 @@ class CropPreviewPanel(QtWidgets.QWidget):
# 更新统计
self._updateStats()
print(f"[CropPreview] 已删除区域 {current_region + 1} 的文件")
except Exception as e:
if DialogManager:
DialogManager.show_critical(self, "错误", f"删除失败: {str(e)}")
else:
QtWidgets.QMessageBox.critical(self, "错误", f"删除失败: {str(e)}")
print(f"[CropPreview] 删除区域文件失败: {e}")
import traceback
traceback.print_exc()
......@@ -799,7 +760,6 @@ class ImageViewDialog(QtWidgets.QDialog):
except Exception as e:
self.image_label.setText(f"加载失败: {e}")
print(f"[ImageViewDialog] 加载图片失败: {e}")
if __name__ == "__main__":
......
......@@ -101,12 +101,9 @@ class DataCollectionPanel(QtWidgets.QWidget):
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, title, message)
else:
print(f"[DEBUG] 使用原生 QMessageBox.warning")
QtWidgets.QMessageBox.warning(self, title, message)
def _showInformation(self, title, message):
......@@ -261,10 +258,10 @@ class DataCollectionPanel(QtWidgets.QWidget):
from ..responsive_layout import scale_spacing
channel_select_layout.setSpacing(scale_spacing(5))
channel_label = QtWidgets.QLabel("选择通道:")
channel_label.setFixedWidth(scale_w(75)) # 🔥 响应式宽度
channel_label.setFixedWidth(scale_w(75)) # 响应式宽度
self.channel_combo = QtWidgets.QComboBox()
self.channel_combo.setFixedWidth(scale_w(90)) # 🔥 响应式宽度
self.channel_combo.setFixedWidth(scale_w(90)) # 响应式宽度
# 向右移动5px
from ..responsive_layout import scale_margin
margin = scale_margin(5)
......@@ -389,7 +386,7 @@ class DataCollectionPanel(QtWidgets.QWidget):
# 右侧:通道预览区域 - 响应式布局
self.channel_preview = QtWidgets.QLabel()
self.channel_preview.setFixedSize(scale_w(640), scale_h(480)) # 🔥 响应式尺寸
self.channel_preview.setFixedSize(scale_w(640), scale_h(480)) # 响应式尺寸
self.channel_preview.setAlignment(QtCore.Qt.AlignCenter)
self.channel_preview.setStyleSheet("""
QLabel {
......@@ -455,10 +452,8 @@ class DataCollectionPanel(QtWidgets.QWidget):
if osp.exists(path) and osp.isdir(path):
self._root_path = path
self._loadFolders()
pass
return True
else:
pass
return False
def getRootPath(self):
......@@ -719,7 +714,7 @@ class DataCollectionPanel(QtWidgets.QWidget):
return
# 检查是否为视频文件
video_extensions = ['.mp4', '.avi', '.mov', '.mkv', '.wmv', '.flv', '.webm', '.m4v']
video_extensions = ['.mp4', '.avi', '.mov', '.mkv', '.flv', '.wmv', '.mpg', '.mpeg']
file_ext = osp.splitext(file_name)[1].lower()
if file_ext in video_extensions:
......@@ -902,56 +897,38 @@ class DataCollectionPanel(QtWidgets.QWidget):
def _onDeleteFile(self, item):
"""删除文件"""
print("\n" + "="*60)
print("[调试] _onDeleteFile 方法被调用")
print("="*60)
if not item:
print("[调试] item 为空,返回")
return
# 获取文件路径
file_path = item.data(Qt.UserRole)
print(f"[调试] 文件路径: {file_path}")
if not file_path or not osp.exists(file_path):
print("[调试] 文件不存在,显示警告")
self._showWarning("警告", "文件不存在")
return
file_name = osp.basename(file_path)
print(f"[调试] 文件名: {file_name}")
# 确认删除 - 使用警告图标的统一对话框
print("[调试] 显示确认删除对话框(使用 _showQuestionWarning)")
# 确认删除
if self._showQuestionWarning(
"确认删除",
f"确定要删除文件 '{file_name}' 吗?\n\n"
f"文件将被移动到回收站"
f"确定要删除文件 '{file_name}' 吗?\n\n文件将被移动到回收站"
):
print("[调试] 用户确认删除")
try:
# 使用Windows回收站删除
if delete_file_to_recycle_bin:
delete_file_to_recycle_bin(file_path)
print(f"[调试] 文件已移动到回收站: {file_path}")
else:
raise ImportError("回收站工具未导入")
# 刷新内容列表
if self._current_folder:
self._loadFolderContent(self._current_folder)
print(" [调试] 已刷新文件夹内容")
print(" [调试] 显示成功提示(使用 _showInformation)")
self._showInformation("成功", f"文件已删除\n{file_name}")
except Exception as e:
print(f"[调试] 删除文件失败: {e}")
print("[调试] 显示错误提示(使用 _showCritical)")
self._showCritical("错误", f"删除文件失败:\n{str(e)}")
else:
print("[调试] 用户取消删除")
pass
def _onAddFolder(self):
"""新增文件夹"""
......@@ -1020,8 +997,6 @@ class DataCollectionPanel(QtWidgets.QWidget):
# 发射信号
self.folderAdded.emit(folder_path)
except Exception as e:
self._showCritical("错误", f"创建文件夹失败: {e}")
......@@ -1050,7 +1025,7 @@ class DataCollectionPanel(QtWidgets.QWidget):
elif ext in ['.jpg', '.jpeg', '.png', '.bmp', '.gif', '.tiff']:
image_count += 1
except Exception as e:
print(f"[WARNING] 统计文件夹内容失败: {e}")
pass
# 构建文件信息提示
file_info = []
......@@ -1065,7 +1040,7 @@ class DataCollectionPanel(QtWidgets.QWidget):
else:
message = f"确定要删除文件夹 '{folder_name}' 吗?\n\n文件夹为空\n\n将被移动到回收站"
# 确认删除 - 使用警告图标的统一对话框
# 确认删除
if self._showQuestionWarning("确认删除", message):
try:
# 使用Windows回收站删除
......@@ -1087,8 +1062,6 @@ class DataCollectionPanel(QtWidgets.QWidget):
# 发射信号
self.folderDeleted.emit(folder_path)
except Exception as e:
self._showCritical("错误", f"删除文件夹失败: {e}")
......@@ -1346,7 +1319,6 @@ def _loadRTSPConfig(self):
config_path = os.path.join(project_root, 'database', 'config', 'default_config.yaml')
if not os.path.exists(config_path):
print(f"[WARNING] 配置文件不存在: {config_path}")
return None
# 读取 YAML 配置文件
......@@ -1373,11 +1345,9 @@ def _loadRTSPConfig(self):
} if channels else None
except Exception as e:
print(f"[WARNING] 无法获取项目根目录或读取配置: {e}")
return None
except Exception as e:
print(f"[ERROR] 加载RTSP配置失败: {e}")
return None
# 获取选择的通道方法
......
......@@ -68,20 +68,14 @@ class IntegratedDatasetPage(QtWidgets.QWidget):
def _initHandlers(self):
"""初始化处理器"""
print("\n[IntegratedDatasetPage] 初始化处理器...")
# 数据预处理处理器
self.preprocess_handler = DataPreprocessHandler(self.preprocess_panel)
print(" ✓ 数据预处理处理器已创建")
# 裁剪预览处理器
self.preview_handler = CropPreviewHandler(self.preview_panel)
print(" ✓ 裁剪预览处理器已创建")
def _connectSignals(self):
"""连接信号"""
print("\n[IntegratedDatasetPage] 连接信号...")
# === 预处理面板信号 ===
# 文件夹被选中
......@@ -119,31 +113,22 @@ class IntegratedDatasetPage(QtWidgets.QWidget):
# 切换区域
self.preview_panel.regionChanged.connect(self._onRegionChanged)
print(" ✓ 信号连接完成")
# ========== 数据预处理面板槽函数 ==========
def _onFolderSelected(self, folder_path):
"""文件夹被选中"""
print(f"\n[Folder] 选中: {osp.basename(folder_path)}")
pass
def _onVideoSelected(self, video_path):
"""视频被选中"""
print(f"\n[Video] 选中: {osp.basename(video_path)}")
pass
# ========== 数据预处理处理器槽函数 ==========
def _onCropStarted(self, config):
"""裁剪任务开始"""
save_liquid_data_path = config.get('save_liquid_data_path', '')
crop_frequency = config.get('crop_frequency', 1)
print(f"\n{'='*60}")
print(f"[Crop] 裁剪任务开始")
print(f" 保存路径: {save_liquid_data_path}")
print(f" 裁剪频率: 每 {crop_frequency} 帧")
print(f"{'='*60}")
# 启动预览监控
self.preview_handler.startMonitoring(save_liquid_data_path)
......@@ -153,16 +138,10 @@ class IntegratedDatasetPage(QtWidgets.QWidget):
def _onCropProgress(self, progress):
"""裁剪进度更新"""
if progress % 10 == 0: # 每10%打印一次
print(f"[Crop] 进度: {progress}%")
pass
def _onCropFinished(self, save_liquid_data_path):
"""裁剪任务完成"""
print(f"\n{'='*60}")
print(f"[Crop] 裁剪任务完成!")
print(f" 保存路径: {save_liquid_data_path}")
print(f"{'='*60}\n")
# 刷新预览显示
self.preview_handler.refreshPanel()
......@@ -175,8 +154,6 @@ class IntegratedDatasetPage(QtWidgets.QWidget):
def _onCropError(self, error_msg):
"""裁剪错误"""
print(f"\n[ERROR] 裁剪失败: {error_msg}")
# 显示错误提示
QtWidgets.QMessageBox.critical(
self, "裁剪失败",
......@@ -187,21 +164,21 @@ class IntegratedDatasetPage(QtWidgets.QWidget):
def _onNewImageDetected(self, region_index, image_path):
"""检测到新图片"""
print(f"[Preview] 新图片: 区域{region_index+1} - {osp.basename(image_path)}")
pass
def _onFolderChanged(self, folder_path):
"""监控文件夹变化"""
print(f"[Preview] 文件夹变化: {folder_path}")
pass
# ========== 预览面板槽函数 ==========
def _onImageSelected(self, image_path):
"""图片被选中"""
print(f"[Preview] 图片选中: {osp.basename(image_path)}")
pass
def _onRegionChanged(self, region_index):
"""切换区域"""
print(f"[Preview] 切换到区域{region_index+1}")
pass
def main():
......@@ -226,41 +203,6 @@ def main():
except:
pass
# 打印使用说明
print("\n" + "="*70)
print(" 裁剪图片预览集成测试程序")
print("="*70)
print("\n布局说明:")
print(" 左侧面板 - 数据预处理")
print(" • 目录管理:查看和管理视频文件夹")
print(" • 视频预览:预览选中的视频")
print(" • 区域裁剪:配置裁剪参数并执行裁剪")
print("\n 右侧面板 - 裁剪预览")
print(" • 实时监控:自动检测新生成的裁剪图片")
print(" • 多区域显示:支持最多3个裁剪区域")
print(" • 图片预览:网格显示缩略图,双击查看大图")
print("\n使用流程:")
print(" 1. 在左侧选择包含视频的文件夹")
print(" 2. 点击视频文件进行预览")
print(" 3. 点击'区域裁剪'按钮配置裁剪参数")
print(" 4. 在视频预览区域画框选择裁剪区域(最多3个)")
print(" 5. 按 C 键确认裁剪配置")
print(" 6. 在弹出的对话框中设置保存路径和参数")
print(" 7. 点击'开始裁剪'执行任务")
print(" 8. 右侧预览面板会实时显示裁剪生成的图片")
print("\n快捷键:")
print(" • 画框模式:鼠标拖动绘制裁剪区域")
print(" • C 键:确认当前裁剪配置")
print(" • R 键:重置所有裁剪框")
print("\n功能特性:")
print(" ✓ 实时监控文件夹变化")
print(" ✓ 自动更新图片显示")
print(" ✓ 支持多区域独立显示")
print(" ✓ 双击图片查看原始大小")
print(" ✓ 显示详细统计信息")
print("="*70 + "\n")
# 显示窗口
window.show()
# 运行应用
......
......@@ -24,10 +24,6 @@ from annotationtool import AnnotationTool
def test_labelme_integration():
"""测试labelme集成"""
print("\n" + "="*70)
print(" Labelme集成测试")
print("="*70)
# 创建应用
app = QtWidgets.QApplication(sys.argv)
......@@ -37,22 +33,10 @@ def test_labelme_integration():
window.resize(1600, 900)
# 创建标注工具
print("\n[1/4] 创建AnnotationTool实例...")
annotation_tool = AnnotationTool()
window.setCentralWidget(annotation_tool)
# 检查labelme是否成功初始化
print("\n[2/4] 检查Labelme初始化状态...")
if annotation_tool.labelme_widget is not None:
print(" Labelme已成功嵌入")
print(f" - Labelme实例类型: {type(annotation_tool.labelme_widget).__name__}")
print(f" - Labelme容器: {type(annotation_tool.labelme_container).__name__}")
else:
print(" Labelme未能初始化")
print(" 请检查labelme是否已安装: pip install labelme")
# 添加测试数据
print("\n[3/4] 添加测试数据到列表...")
test_images = [
{
"name": "sample_001.jpg",
......@@ -81,8 +65,6 @@ def test_labelme_integration():
item.setText(f" {img_data['name']}\n{img_data['resolution']}")
item.setData(Qt.UserRole, img_data)
print(f" 已添加 {len(test_images)} 个测试图片")
# 更新统计
annotation_tool.lbl_total_stats.setText(f"总数: {len(test_images)}")
annotation_tool.lbl_annotated_stats.setText("已标注: 0")
......@@ -90,7 +72,6 @@ def test_labelme_integration():
# 连接列表点击事件(演示如何加载图片)
def on_item_clicked(item):
data = item.data(Qt.UserRole)
print(f"\n 点击了图片: {data['name']}")
# 更新详细信息
annotation_tool.lbl_image_name.setText(data["name"])
......@@ -102,36 +83,10 @@ def test_labelme_integration():
# 如果图片存在,加载到labelme
if osp.exists(data["path"]):
print(f" 加载图片到labelme: {data['path']}")
annotation_tool.loadImageForAnnotation(data["path"])
else:
print(f" ️ 图片不存在: {data['path']}")
print(" (这是测试数据,实际使用时需要提供真实图片路径)")
annotation_tool.annotation_list.itemClicked.connect(on_item_clicked)
# 显示使用说明
print("\n[4/4] 启动测试窗口...")
print("\n" + "-"*70)
print(" 使用说明")
print("-"*70)
print("\n 界面布局:")
print(" 左侧: 标注数据列表")
print(" 右侧: Labelme标注工具")
print("\n 测试步骤:")
print(" 1. 查看右侧是否显示labelme界面")
print(" 2. 点击左侧列表中的图片项")
print(" 3. 观察详细信息是否更新")
print(" 4. 测试工具栏按钮(放大、缩小、适应窗口)")
print(" 5. 如果有真实图片,可以在labelme中进行标注")
print("\n 加载自定义图片:")
print(" 在右侧labelme中点击 '打开' 或 '打开目录' 加载图片")
print("\n 快捷键:")
print(" - Ctrl+O: 打开图片")
print(" - Ctrl+S: 保存标注")
print(" - 更多快捷键请参考labelme工具栏")
print("-"*70)
# 显示窗口
window.show()
......@@ -157,9 +112,6 @@ def test_labelme_integration():
"详细错误信息请查看控制台。"
)
print("\n 测试窗口已启动")
print("="*70 + "\n")
sys.exit(app.exec_())
......
......@@ -560,16 +560,11 @@ if __name__ == "__main__":
panel = TrainingPanel()
window.setCentralWidget(panel)
# 连接信号测试
def on_start_training(params):
print(f"开始训练:")
for key, value in params.items():
print(f" {key}: {value}")
panel.append_training_log("训练已开始...\n")
panel.set_training_active(True)
def on_stop_training():
print("停止训练")
panel.append_training_log("训练已停止\n")
panel.set_training_active(False)
......
......@@ -270,7 +270,6 @@ class VideoBrowser(QtWidgets.QWidget):
path: 根目录路径
"""
if not osp.exists(path):
print(f" 路径不存在: {path}")
return False
self._root_path = path
......@@ -283,7 +282,6 @@ class VideoBrowser(QtWidgets.QWidget):
# 展开根节点
self.tree_view.expand(root_index)
print(f" 设置根目录: {path}")
return True
def getRootPath(self):
......@@ -316,7 +314,7 @@ class VideoBrowser(QtWidgets.QWidget):
if ext in self._video_extensions:
videos.append(file_path)
except Exception as e:
print(f" 获取视频列表失败: {e}")
return []
return videos
......@@ -362,13 +360,10 @@ class VideoBrowser(QtWidgets.QWidget):
# 发送信号
self.videoSelected.emit(video_files[0])
print(f" 加载视频文件: {video_count} 个,当前: {osp.basename(video_files[0])}")
else:
print(f" 文件夹中没有视频文件")
self.preview_label.setText("该文件夹中没有视频文件")
except Exception as e:
print(f" 加载视频失败: {e}")
self.stats_label.setText("加载失败")
def _clearVideoInfo(self):
......@@ -412,8 +407,6 @@ class VideoBrowser(QtWidgets.QWidget):
# 启用按钮
self.btn_play.setEnabled(True)
self.btn_open_folder.setEnabled(True)
print(f" 更新视频信息: {file_name}")
# ========== 槽函数 ==========
......@@ -423,7 +416,6 @@ class VideoBrowser(QtWidgets.QWidget):
return
folder_path = self.file_model.filePath(index)
print(f" 点击文件夹: {folder_path}")
# 加载该文件夹中的视频
self._loadVideosFromFolder(folder_path)
......@@ -447,7 +439,6 @@ class VideoBrowser(QtWidgets.QWidget):
current_folder = self.getCurrentFolder()
if current_folder:
self._loadVideosFromFolder(current_folder)
print(" 刷新文件夹")
def _onSelectRoot(self):
"""选择根目录"""
......@@ -475,10 +466,7 @@ class VideoBrowser(QtWidgets.QWidget):
elif os.name == 'posix': # macOS 或 Linux
import subprocess
subprocess.Popen(['xdg-open', self._current_video_path])
print(f"▶ 播放视频: {osp.basename(self._current_video_path)}")
except Exception as e:
print(f" 播放视频失败: {e}")
QtWidgets.QMessageBox.warning(
self,
"播放失败",
......@@ -499,10 +487,8 @@ class VideoBrowser(QtWidgets.QWidget):
elif os.name == 'posix': # macOS 或 Linux
import subprocess
subprocess.Popen(['xdg-open', folder_path])
print(f" 打开文件夹: {folder_path}")
except Exception as e:
print(f" 打开文件夹失败: {e}")
pass
if __name__ == "__main__":
......@@ -520,52 +506,19 @@ if __name__ == "__main__":
video_browser = VideoBrowser()
window.setCentralWidget(video_browser)
# 连接信号测试
def on_folder_selected(folder_path):
print(f" 选中文件夹: {folder_path}")
pass
def on_video_selected(video_path):
print(f" 选中视频: {video_path}")
pass
def on_video_played(video_path):
print(f"▶ 播放视频: {video_path}")
pass
video_browser.folderSelected.connect(on_folder_selected)
video_browser.videoSelected.connect(on_video_selected)
video_browser.videoPlayed.connect(on_video_played)
# 打印测试说明
print("\n" + "="*70)
print(" VideoBrowser 视频浏览器测试程序")
print("="*70)
print(" 组件功能:")
print("\n 左侧面板 - 文件夹浏览:")
print(" - 显示目录树结构")
print(" - 点击文件夹自动加载第一个视频")
print(" - 双击文件夹展开/折叠")
print(" - 点击'选择根目录'切换根路径")
print(" - 点击'刷新'重新加载")
print(" - 显示统计信息(文件夹数、视频数)")
print("\n 右侧面板 - 视频预览:")
print(" - 视频预览区域(黑色背景)")
print(" - 自动显示第一个视频的信息")
print(" - 支持格式: .mp4, .avi, .mkv, .mov, .wmv, .flv, .webm, .m4v")
print(" - 点击'播放'按钮使用系统播放器打开")
print(" - 点击'打开文件夹'在资源管理器中查看")
print("\n 视频信息:")
print(" - 文件名")
print(" - 文件大小(MB/GB)")
print(" - 完整路径")
print("\n 信号:")
print(" - folderSelected: 文件夹被选中")
print(" - videoSelected: 视频被自动加载")
print(" - videoPlayed: 视频开始播放")
print("\n 提示:")
print(" - 可以通过代码设置自定义根目录")
print(" - 支持拖拽分割器调整左右面板大小")
print(" - 点击文件夹自动加载该文件夹中的第一个视频")
print("="*70 + "\n")
window.show()
sys.exit(app.exec_())
......@@ -340,7 +340,6 @@ if __name__ == "__main__":
video_clipper = VideoClipper()
window.setCentralWidget(video_clipper)
# 添加测试数据到照片目录
test_folders = [
("项目A", 25),
("项目B", 18),
......@@ -370,39 +369,8 @@ if __name__ == "__main__":
video_clipper.photo_tree.setColumnWidth(0, 150)
video_clipper.photo_tree.setColumnWidth(1, 60)
# 更新统计信息
video_clipper.lbl_photo_stats.setText(f"照片: {sum(c for _, c in test_folders)} | 文件夹: {len(test_folders)}")
# 打印测试说明
print("\n" + "="*70)
print(" VideoClipper 视频裁剪器组件测试程序")
print("="*70)
print(" 界面布局(三栏):")
print("\n 左侧面板 - 照片目录:")
print(" - 树形结构显示文件夹")
print(" - 显示每个文件夹的照片数量")
print(" - 刷新按钮")
print(" - 底部统计信息")
print("\n 中间面板 - 裁剪图片展示:")
print(" - 滚动区域显示裁剪的图片")
print(" - 清空和保存按钮")
print(" - 底部统计裁剪数量")
print("\n 右侧面板 - 视频预览:")
print(" - 视频预览显示区(黑色背景)")
print(" - 视频信息卡片(文件名、大小、时长、分辨率)")
print(" - 控制按钮(选择视频、播放/暂停、裁剪当前帧)")
print(" - 进度条")
print(" - 时间显示(当前时间 / 总时长)")
print("\n 布局特点:")
print(" - 使用 QSplitter 实现可调整宽度的三栏布局")
print(" - 默认比例: 左:中:右 = 1:2:2")
print(" - 可以拖拽分割线调整各面板宽度")
print("\n 说明:")
print(" - 当前为纯UI界面,不包含功能实现")
print(" - 所有按钮和控件已预留,可后续添加功能")
print(" - 界面风格统一,适配现有系统")
print("="*70 + "\n")
window.show()
sys.exit(app.exec_())
......@@ -203,7 +203,6 @@ if __name__ == "__main__":
menubar = MenuBar(main_window)
main_window.setMenuBar(menubar)
# 添加测试菜单项(带图标)
menubar.addFileAction(
"open", "打开",
lambda: print("打开文件"),
......
......@@ -964,12 +964,10 @@ if __name__ == '__main__':
# 应用全局字体
FontManager.applyToApplication(app)
# 创建测试窗口
window = QtWidgets.QWidget()
window.setWindowTitle("统一样式管理器测试")
layout = QtWidgets.QVBoxLayout()
# 测试字体管理器
label1 = QtWidgets.QLabel("默认字体 - Microsoft YaHei 12pt Normal")
applyDefaultFont(label1)
layout.addWidget(label1)
......@@ -978,14 +976,12 @@ if __name__ == '__main__':
applyTitleFont(label2)
layout.addWidget(label2)
# 测试按钮样式管理器
button1 = createTextButton("确认", parent=window)
layout.addWidget(button1)
button2 = createTextButton("这是一个很长的按钮文本", parent=window)
layout.addWidget(button2)
# 测试标题文本框样式管理器
title_group, title_text = createTitleTextBox(
window,
"模型描述",
......@@ -994,7 +990,6 @@ if __name__ == '__main__':
)
layout.addWidget(title_group)
# 测试对话框管理器
def test_dialog():
result = show_question(window, "确认", "确定要执行此操作吗?")
if result:
......
......@@ -443,12 +443,9 @@ class ChannelPanel(QtWidgets.QWidget):
# 只显示文件夹名称
text = task_folder_name.strip()
self.taskLabel.setText(text)
# 有任务时启用面板
self._setDisabled(False)
else:
# 没有任务信息时显示未分配任务
self.taskLabel.setText("未分配任务")
# 无任务时禁用面板
self._setDisabled(True)
self.taskLabel.adjustSize()
......@@ -458,7 +455,6 @@ class ChannelPanel(QtWidgets.QWidget):
def clearTaskInfo(self):
"""清空任务信息显示"""
self.taskLabel.setText("未分配任务")
# 清空任务时禁用面板
self._setDisabled(True)
self.taskLabel.adjustSize()
self._positionTaskLabel()
......@@ -560,27 +556,15 @@ class ChannelPanel(QtWidgets.QWidget):
current_mission: 当前任务路径或名称
"""
if self._debug_mode and hasattr(self, 'debugLabel'):
# 🔥 添加调试信息
# print(f"[DEBUG] setCurrentMission 被调用: current_mission = {current_mission}")
if current_mission:
mission_str = str(current_mission).strip()
# print(f"[DEBUG] mission_str = '{mission_str}'")
# 检查是否是 "None" 字符串或空字符串
if mission_str.lower() == 'none' or not mission_str:
# print(f"[DEBUG] 设置为未分配任务(mission_str为空或'none')")
self.debugLabel.setText("未分配任务")
else:
# 🔥 修改:显示完整的 current_mission 值
# 统一路径分隔符为 /
normalized_path = mission_str.replace('\\', '/')
# print(f"[DEBUG] 设置debug标签为: {normalized_path}")
# 直接显示完整路径
self.debugLabel.setText(normalized_path)
else:
# print(f"[DEBUG] current_mission 为 None,设置debug标签为'未分配任务'")
self.debugLabel.setText("未分配任务")
self.debugLabel.adjustSize()
......@@ -752,13 +736,10 @@ if __name__ == "__main__":
# 创建通道面板(现在是QWidget,可以直接作为中央部件)
channel_panel = ChannelPanel("通道1", main_window)
# 连接信号用于测试
def on_channel_connected(cam_id):
# 模拟连接成功后切换状态
channel_panel.setConnected(True)
def on_channel_disconnected(cam_id):
# 模拟断开成功后切换状态
channel_panel.setConnected(False)
def on_curve_clicked():
......@@ -775,8 +756,6 @@ if __name__ == "__main__":
channel_panel.curveClicked.connect(on_curve_clicked)
channel_panel.amplifyClicked.connect(on_amplify_clicked)
channel_panel.channelEdited.connect(on_edit_clicked)
# 添加测试数据
test_channels = [
{
'id': str(uuid.uuid4()),
......
......@@ -784,8 +784,6 @@ class CurvePanel(QtWidgets.QWidget):
if self.chk_auto_follow.isChecked():
min_time = max_time - view_width
self.plot_widget.setXRange(min_time, max_time, padding=0)
# 调试:打印X轴范围
# print(f"X轴范围: {datetime.datetime.fromtimestamp(min_time).strftime('%H:%M:%S')} ~ {datetime.datetime.fromtimestamp(max_time).strftime('%H:%M:%S')}")
def setYRangeAuto(self, max_value):
"""
......@@ -930,7 +928,6 @@ if __name__ == "__main__":
curve_panel = CurvePanel()
window.setCentralWidget(curve_panel)
# 添加测试通道(通道名_检测窗口名)
curve_panel.addChannel("channel1", "通道1", "1", "#1f77b4")
curve_panel.addChannel("channel2", "通道2", "2", "#ff7f0e")
curve_panel.addChannel("channel3", "通道3", "3", "#2ca02c")
......@@ -945,7 +942,6 @@ if __name__ == "__main__":
# 计算经过的秒数(用于生成波形)
elapsed = (current_time - start_time).total_seconds()
# 为每个通道生成不同的测试数据(整数值,范围在0-23之间)
value1 = int(16 + 3 * np.sin(elapsed * 0.1) + np.random.randn() * 1.5)
value2 = int(15 + 2 * np.cos(elapsed * 0.15) + np.random.randn() * 1.2)
value3 = int(14 + 3 * np.sin(elapsed * 0.08 + 1) + np.random.randn() * 1.0)
......@@ -965,51 +961,19 @@ if __name__ == "__main__":
timer.timeout.connect(update_test_data)
timer.start(100) # 100ms更新一次,10Hz刷新率
# 连接信号测试
def on_curve_updated(channel_id, t, v):
pass # 静默处理,避免打印过多
def on_channel_switched(channel_id):
print(f" 切换到通道: {channel_id}")
pass
def on_data_exported(channel_id, file_path):
print(f" 数据已导出: {channel_id} -> {file_path}")
pass
curve_panel.curveDataUpdated.connect(on_curve_updated)
curve_panel.channelSwitched.connect(on_channel_switched)
curve_panel.dataExported.connect(on_data_exported)
print("\n" + "="*70)
print(" CurvePanel 高性能曲线面板测试程序")
print("="*70)
print(" 已添加3个测试通道(通道1_1、通道2_2、通道3_3)")
print(" 正在模拟实时数据更新 (10Hz)")
print("\n 数据格式:")
print(" - CSV格式:时间戳 数值")
print(" - 时间格式:2025-09-28-17:37:39.220")
print(" - 文件名:通道名_检测窗口名称_时间戳.csv")
print(" - 图例显示:通道名_检测窗口名")
print("\n 可以尝试以下操作:")
print(" 通道管理:")
print(" - 下拉框切换不同通道")
print(" - 点击'添加通道'动态添加新通道")
print(" - 点击'清空数据'清除当前通道数据")
print("\n 视图控制:")
print(" - 鼠标左键拖动:左右平移(X轴)或上下平移(Y轴)")
print(" - 鼠标滚轮:放大/缩小时间轴或数值轴")
print(" - 点击'查看全部':显示所有历史数据(自动调整XY轴)")
print(" - 点击'重置视图':显示最新10秒数据(Y:0-23)")
print(" - '自动跟随'开关:启用后自动滚动显示最新时间数据")
print(" - '自动Y轴'开关:启用后自动扩展Y轴以适应数据")
print("\n 数据操作:")
print(" - 点击'导出数据':保存为CSV文件")
print(" - 点击'保存图片':导出为PNG图片")
print(" - '自动保存'开关:定时自动保存数据(每2分钟)")
print("\n 坐标轴:")
print(" - X轴(横坐标):时间戳,显示实际时间(HH:MM:SS),支持左右拖动和缩放")
print(" - Y轴(纵坐标):数值,范围0-23,支持上下拖动和缩放")
print(" - Y轴限制:最小值不能小于0(只显示非负值)")
print("="*70 + "\n")
window.show()
sys.exit(app.exec_())
......
......@@ -742,8 +742,7 @@ class GeneralSetPanel(QtWidgets.QWidget):
self.model_setting_widget.readModelDescriptionRequested.emit(absolute_path)
except Exception as e:
import traceback
traceback.print_exc()
pass
def _autoSaveModelPath(self, model_path):
"""
......@@ -1846,7 +1845,7 @@ class AnnotationWidget(QtWidgets.QWidget):
elif self.annotation_engine.step == 1:
# 标注底部点模式:删除最后一个检测框(回退到画框模式)
if len(self.annotation_engine.boxes) > 0:
removed_box = self.annotation_engine.boxes.pop()
self.annotation_engine.boxes.pop()
# 同时删除对应的区域名称和高度
if len(self.area_names) > 0:
......@@ -1863,7 +1862,7 @@ class AnnotationWidget(QtWidgets.QWidget):
elif self.annotation_engine.step == 2:
# 标注顶部点模式:删除最后一个底部点(回退到标注底部点模式)
if len(self.annotation_engine.bottom_points) > 0:
removed_point = self.annotation_engine.bottom_points.pop()
self.annotation_engine.bottom_points.pop()
# 回到标注底部点模式
self.annotation_engine.step = 1
......@@ -1882,7 +1881,7 @@ class AnnotationWidget(QtWidgets.QWidget):
# 发送标注数据请求信号给handler
self.annotationDataRequested.emit()
else:
pass
return
def keyPressEvent(self, event):
"""键盘事件处理 - 增强版快捷键绑定"""
......@@ -1955,27 +1954,27 @@ class AnnotationWidget(QtWidgets.QWidget):
return
# 删除检测框
removed_box = self.annotation_engine.boxes.pop(area_index)
self.annotation_engine.boxes.pop(area_index)
# 删除对应的底部点
if area_index < len(self.annotation_engine.bottom_points):
removed_bottom = self.annotation_engine.bottom_points.pop(area_index)
self.annotation_engine.bottom_points.pop(area_index)
# 删除对应的顶部点
if area_index < len(self.annotation_engine.top_points):
removed_top = self.annotation_engine.top_points.pop(area_index)
self.annotation_engine.top_points.pop(area_index)
# 删除对应的区域名称
if area_index < len(self.area_names):
removed_name = self.area_names.pop(area_index)
self.area_names.pop(area_index)
# 删除对应的区域高度
if area_index < len(self.area_heights):
removed_height = self.area_heights.pop(area_index)
self.area_heights.pop(area_index)
# 删除对应的区域状态
if area_index < len(self.area_states):
removed_state = self.area_states.pop(area_index)
self.area_states.pop(area_index)
# 更新显示
self._updateDisplay()
......@@ -1996,8 +1995,6 @@ class AnnotationWidget(QtWidgets.QWidget):
self._help_visible = not self._help_visible
pass
# 更新显示
self._updateDisplay()
......@@ -2010,7 +2007,6 @@ class AnnotationWidget(QtWidgets.QWidget):
self._ensureAreaConfig(area_index)
# 设置高度
old_height = self.area_heights[area_index]
self.area_heights[area_index] = height_value
# 更新显示
......@@ -2047,4 +2043,4 @@ class AnnotationWidget(QtWidgets.QWidget):
def showAnnotationError(self, message):
"""显示标注错误(由handler调用)"""
pass
return
......@@ -134,7 +134,7 @@ class HistoryVideoPanel(QtWidgets.QWidget):
self.nameLabel.setText("")
self.nameLabel.hide() # 隐藏标签
# 创建debug模式静态文本控件(叠加在视频区域顶部中间,仅在debug模式下显示)
# 创建调试模式静态文本控件(叠加在视频区域顶部中间,仅在调试模式下显示)
self.debugLabel = QtWidgets.QLabel(self.videoLabel)
self.debugLabel.setText("") # 初始为空,等待设置current_mission
self.debugLabel.setStyleSheet("""
......@@ -150,7 +150,7 @@ class HistoryVideoPanel(QtWidgets.QWidget):
self.debugLabel.adjustSize()
# 定位到顶部中间
self._positionDebugLabel()
# 根据debug_mode控制显示/隐藏
# 根据调试模式控制显示/隐藏
self.debugLabel.setVisible(self._debug_mode)
# 🔥 历史视频面板不显示任务信息标签
......@@ -780,7 +780,7 @@ if __name__ == "__main__":
# 连接信号用于测试
def on_channel_name_changed(channel_id, new_name):
print(f"📝 通道名称改变: {channel_id} -> {new_name}")
pass
history_panel.channelNameChanged.connect(on_channel_name_changed)
......
......@@ -116,7 +116,6 @@ if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
# 测试逻辑配置数据
test_config = {
'detection_mode': 'continuous',
'high_threshold': 80.0,
......@@ -134,9 +133,6 @@ if __name__ == "__main__":
dialog = LogicSettingDialog(logic_config=test_config, channel_id='channel1')
if dialog.exec_() == QtWidgets.QDialog.Accepted:
config = dialog.getLogicConfig()
print("=== 获取的逻辑配置 ===")
for key, value in config.items():
print(f"{key}: {value}")
sys.exit(0)
......@@ -95,9 +95,6 @@ class MissionPanel(QtWidgets.QWidget):
self._initUI()
self._connectSignals()
# 🔥 暂时禁用全局字体管理器,测试是否解决重复显示问题
# FontManager.applyToWidgetRecursive(self)
# 自动应用默认配置
self._applyDefaultConfig()
......@@ -757,7 +754,8 @@ class MissionPanel(QtWidgets.QWidget):
def _testFontException(self):
"""
测试字体例外机制是否正常工作
"""
"""
pass
def updateRow(self, row_index, row_data):
......@@ -1421,8 +1419,6 @@ class MissionPanel(QtWidgets.QWidget):
if not addr.startswith('rtsp://'):
addr = 'rtsp://' + addr
print(f"🔧 [MissionPanel] 请求测试通道{channel_id}连接: {addr}")
# 发送调试信号给handler处理
self.channelDebugRequested.emit(channel_id, addr)
......@@ -1529,35 +1525,26 @@ class MissionPanel(QtWidgets.QWidget):
'' # 曲线列(按钮会自动添加)
]
self.addRow(row_data, task_info)
print(f" 任务已添加到表格: {task_info['task_name']}")
def removeTaskRow(self, row_index):
"""删除任务行(由handler调用)"""
if self.removeRow(row_index):
print(f" 已删除任务行: {row_index}")
self.removeRow(row_index)
def _showWarningDialog(self, title, message):
"""显示警告对话框(内部使用)"""
print(f"\n[对话框调试] 创建警告对话框: {title}")
msg_box = QtWidgets.QMessageBox(self)
msg_box.setWindowTitle(title)
msg_box.setText(message)
msg_box.setIcon(QtWidgets.QMessageBox.NoIcon) # 不显示内容区域图标
print(f" - 设置内容区域图标: NoIcon")
# 设置左上角图标为系统警告图标
warning_icon = msg_box.style().standardIcon(QtWidgets.QStyle.SP_MessageBoxWarning)
msg_box.setWindowIcon(warning_icon)
print(f" - 设置窗口图标: SP_MessageBoxWarning")
print(f" - 窗口图标是否为空: {warning_icon.isNull()}")
# 移除帮助按钮
original_flags = msg_box.windowFlags()
msg_box.setWindowFlags(
msg_box.windowFlags() & ~QtCore.Qt.WindowContextHelpButtonHint
)
print(f" - 原始窗口标志: {original_flags}")
print(f" - 最终窗口标志: {msg_box.windowFlags()}")
# 设置文字水平和垂直居中
msg_box.setStyleSheet("""
......@@ -1569,9 +1556,7 @@ class MissionPanel(QtWidgets.QWidget):
qproperty-alignment: 'AlignCenter';
}
""")
print(f" - 已设置样式表")
print(f" - 显示对话框...")
msg_box.exec_()
def showConfirmDialog(self, title, message):
......@@ -1679,17 +1664,15 @@ class MissionPanel(QtWidgets.QWidget):
def _onFirstPage(self):
"""跳转到首页"""
if self._current_page != 1:
if self._current_page > 1:
self._current_page = 1
self._updatePagination()
print(f" 跳转到首页")
def _onPrevPage(self):
"""上一页"""
if self._current_page > 1:
self._current_page -= 1
self._updatePagination()
print(f" 上一页 -> 第 {self._current_page} 页")
def _onNextPage(self):
"""下一页"""
......@@ -1697,7 +1680,6 @@ class MissionPanel(QtWidgets.QWidget):
if self._current_page < total_pages:
self._current_page += 1
self._updatePagination()
print(f" 下一页 -> 第 {self._current_page} 页")
def _onLastPage(self):
"""跳转到末页"""
......@@ -1705,7 +1687,6 @@ class MissionPanel(QtWidgets.QWidget):
if self._current_page != total_pages:
self._current_page = total_pages
self._updatePagination()
print(f" 跳转到末页")
def _onGoToPage(self):
"""跳转到指定页"""
......@@ -1716,7 +1697,6 @@ class MissionPanel(QtWidgets.QWidget):
if 1 <= page_num <= total_pages:
self._current_page = page_num
self._updatePagination()
print(f" 跳转到第 {page_num} 页")
self.page_input.clear()
else:
self._showWarningDialog(
......@@ -1754,7 +1734,7 @@ if __name__ == "__main__":
# 曲线按钮点击处理
def on_curve_button_clicked(row, col):
row_data = table.getRowData(row)
print(f" 点击曲线按钮: 行 {row}, 数据: {row_data}")
pass
# 添加数据(按钮会自动添加)
for row_data, user_data in test_data:
......@@ -1762,7 +1742,7 @@ if __name__ == "__main__":
# 连接按钮点击信号
def on_button_signal(row, col):
print(f" 按钮点击信号: 行 {row}, 列 {col}")
pass
table.buttonClicked.connect(on_button_signal)
......@@ -1770,15 +1750,14 @@ if __name__ == "__main__":
def on_item_selected(row_index):
row_data = table.getRowData(row_index)
user_data = table.getUserData(row_index)
print(f" 选中行 {row_index}: {row_data}")
print(f" 用户数据: {user_data}")
pass
def on_item_double_clicked(row_index):
row_data = table.getRowData(row_index)
print(f" 双击行 {row_index}: {row_data}")
pass
def on_item_changed(row, col, new_value):
print(f" 单元格改变 ({row}, {col}): {new_value}")
pass
table.itemSelected.connect(on_item_selected)
table.itemDoubleClicked.connect(on_item_double_clicked)
......
......@@ -281,8 +281,6 @@ class ModelSettingDialog(QtWidgets.QDialog):
# 发送读取模型描述请求信号给handler
if model_path:
self.readModelDescriptionRequested.emit(model_path)
print(f"[对话框] 用户双击选择模型: {model_path}")
def _updateCurrentModelInfo(self, model_path):
"""
......@@ -312,7 +310,6 @@ class ModelSettingDialog(QtWidgets.QDialog):
# 选中当前模型项
self.modelListWidget.setCurrentItem(item)
item.setSelected(True)
print(f"[模型设置对话框] 已在列表中高亮当前模型: {model_path}")
break
def _readModelDescription(self, model_path):
......@@ -395,7 +392,6 @@ if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
# 测试模型配置数据
test_config = {
'model_path': '',
}
......
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