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