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
Administrator
Oil_Level_Recognition_System
Commits
c94177be
Commit
c94177be
authored
Nov 30, 2025
by
Yuhaibo
Browse files
Options
Browse Files
Download
Plain Diff
merge: 保留Liuqingjun分支版本解决冲突
parents
c3228c2c
25bb16b1
Hide whitespace changes
Inline
Side-by-side
Showing
16 changed files
with
552 additions
and
139 deletions
+552
-139
.gitignore
.gitignore
+2
-0
crop_preview_handler.py
handlers/datasetpage/crop_preview_handler.py
+37
-4
datacollection_channel_handler.py
handlers/datasetpage/datacollection_channel_handler.py
+3
-3
datapreprocess_handler.py
handlers/datasetpage/datapreprocess_handler.py
+86
-14
app.py
labelme/app.py
+125
-46
brightness_contrast_dialog.py
labelme/widgets/brightness_contrast_dialog.py
+17
-0
canvas.py
labelme/widgets/canvas.py
+12
-4
color_dialog.py
labelme/widgets/color_dialog.py
+17
-0
file_dialog_preview.py
labelme/widgets/file_dialog_preview.py
+17
-0
label_dialog.py
labelme/widgets/label_dialog.py
+40
-15
annotationtool.py
widgets/datasetpage/annotationtool.py
+46
-4
crop_config_dialog.py
widgets/datasetpage/crop_config_dialog.py
+30
-9
crop_preview_panel.py
widgets/datasetpage/crop_preview_panel.py
+16
-5
datacollection_panel.py
widgets/datasetpage/datacollection_panel.py
+47
-13
datapreprocess_panel.py
widgets/datasetpage/datapreprocess_panel.py
+10
-22
style_manager.py
widgets/style_manager.py
+47
-0
No files found.
.gitignore
View file @
c94177be
...
@@ -16,6 +16,8 @@
...
@@ -16,6 +16,8 @@
!widgets/**
!widgets/**
!handlers/
!handlers/
!handlers/**
!handlers/**
!labelme/
!labelme/**
!app.py
!app.py
!__main__.py
!__main__.py
!__init__.py
!__init__.py
...
...
handlers/datasetpage/crop_preview_handler.py
View file @
c94177be
...
@@ -47,7 +47,7 @@ class CropPreviewHandler(QtCore.QObject):
...
@@ -47,7 +47,7 @@ class CropPreviewHandler(QtCore.QObject):
# 定时器(用于轮询检测,作为文件监控的补充)
# 定时器(用于轮询检测,作为文件监控的补充)
self
.
_poll_timer
=
None
self
.
_poll_timer
=
None
self
.
_poll_interval
=
2000
# 2秒轮询一次
self
.
_poll_interval
=
500
# 500毫秒轮询一次(提高实时性)
# 是否启用自动监控
# 是否启用自动监控
self
.
_auto_monitor_enabled
=
False
self
.
_auto_monitor_enabled
=
False
...
@@ -98,9 +98,15 @@ class CropPreviewHandler(QtCore.QObject):
...
@@ -98,9 +98,15 @@ class CropPreviewHandler(QtCore.QObject):
print
(
f
"[CropPreviewHandler] 设置保存路径(不自动刷新)"
)
print
(
f
"[CropPreviewHandler] 设置保存路径(不自动刷新)"
)
self
.
_panel
.
setSavePath
(
save_liquid_data_path
,
auto_refresh
=
False
,
video_name
=
video_name
)
self
.
_panel
.
setSavePath
(
save_liquid_data_path
,
auto_refresh
=
False
,
video_name
=
video_name
)
# 初始化已知图片列表(扫描现有图片,避免重复显示)
# 初始化已知图片列表
print
(
f
"[CropPreviewHandler] 扫描现有图片..."
)
if
clear_first
:
self
.
_initKnownImages
(
save_liquid_data_path
,
video_name
)
# 如果清空显示,则不扫描现有图片,所有图片都视为新增
print
(
f
"[CropPreviewHandler] 清空模式:不扫描现有图片,所有图片将被视为新增"
)
self
.
_known_images
.
clear
()
else
:
# 否则扫描现有图片,避免重复显示
print
(
f
"[CropPreviewHandler] 扫描现有图片..."
)
self
.
_initKnownImages
(
save_liquid_data_path
,
video_name
)
# 设置文件系统监控
# 设置文件系统监控
self
.
_setupFileWatcher
(
save_liquid_data_path
,
video_name
)
self
.
_setupFileWatcher
(
save_liquid_data_path
,
video_name
)
...
@@ -113,6 +119,13 @@ class CropPreviewHandler(QtCore.QObject):
...
@@ -113,6 +119,13 @@ class CropPreviewHandler(QtCore.QObject):
# 清除重置标志
# 清除重置标志
self
.
_is_resetting
=
False
self
.
_is_resetting
=
False
# 如果是清空模式,立即执行一次检查,显示所有现有图片
if
clear_first
:
print
(
f
"[CropPreviewHandler] 清空模式:立即检查并显示所有现有图片"
)
for
i
,
region_path
in
enumerate
(
self
.
_monitored_paths
):
if
region_path
and
osp
.
exists
(
region_path
):
self
.
_checkRegionForNewImages
(
i
,
region_path
)
print
(
f
"[CropPreviewHandler] === 监控已启动 ==="
)
print
(
f
"[CropPreviewHandler] === 监控已启动 ==="
)
def
stopMonitoring
(
self
):
def
stopMonitoring
(
self
):
...
@@ -409,6 +422,12 @@ class CropPreviewHandler(QtCore.QObject):
...
@@ -409,6 +422,12 @@ class CropPreviewHandler(QtCore.QObject):
# 更新已知图片列表
# 更新已知图片列表
self
.
_known_images
[
region_index
]
=
current_images
self
.
_known_images
[
region_index
]
=
current_images
# 检测到新图片后,立即触发一次额外检查(提高响应速度)
if
self
.
_poll_timer
and
self
.
_poll_timer
.
isActive
():
# 重启定时器,立即进行下一次检查
self
.
_poll_timer
.
stop
()
self
.
_poll_timer
.
start
(
self
.
_poll_interval
)
except
Exception
as
e
:
except
Exception
as
e
:
print
(
f
"[CropPreviewHandler] 检查区域{region_index+1}新图片失败: {e}"
)
print
(
f
"[CropPreviewHandler] 检查区域{region_index+1}新图片失败: {e}"
)
...
@@ -417,6 +436,20 @@ class CropPreviewHandler(QtCore.QObject):
...
@@ -417,6 +436,20 @@ class CropPreviewHandler(QtCore.QObject):
if
self
.
_panel
:
if
self
.
_panel
:
self
.
_panel
.
refreshImages
()
self
.
_panel
.
refreshImages
()
def
forceRefresh
(
self
):
"""
强制立即检查所有区域的新图片
用于在裁剪过程中手动触发检查,确保实时更新
"""
if
not
self
.
_auto_monitor_enabled
or
self
.
_is_resetting
:
return
print
(
f
"[CropPreviewHandler] 强制刷新检查"
)
for
i
,
region_path
in
enumerate
(
self
.
_monitored_paths
):
if
region_path
and
osp
.
exists
(
region_path
):
self
.
_checkRegionForNewImages
(
i
,
region_path
)
def
clearPanel
(
self
):
def
clearPanel
(
self
):
"""清空面板显示"""
"""清空面板显示"""
if
self
.
_panel
:
if
self
.
_panel
:
...
...
handlers/datasetpage/datacollection_channel_handler.py
View file @
c94177be
...
@@ -184,17 +184,17 @@ class DataCollectionChannelHandler:
...
@@ -184,17 +184,17 @@ class DataCollectionChannelHandler:
if
channel_config
:
if
channel_config
:
success
=
self
.
_connectRealChannel
(
channel_config
)
success
=
self
.
_connectRealChannel
(
channel_config
)
if
not
success
:
if
not
success
:
error_detail
=
f
"无法连接到配置的通道 {channel_source}
\n
请检查:
\n
相机是否已开机并连接到网络
\n
2.
IP地址和端口是否正确"
error_detail
=
f
"无法连接到配置的通道 {channel_source}
\n
请检查:
\n
相机是否已开机并连接到网络
\n
IP地址和端口是否正确"
else
:
else
:
# 如果没有配置,尝试直接连接USB通道
# 如果没有配置,尝试直接连接USB通道
success
=
self
.
_connectUSBChannel
(
channel_source
)
success
=
self
.
_connectUSBChannel
(
channel_source
)
if
not
success
:
if
not
success
:
error_detail
=
f
"无法连接到USB通道 {channel_source}
\n
请检查:
\n
USB相机是否已连接
\n
2. 相机驱动是否已安装
\n
3.
相机是否被其他程序占用"
error_detail
=
f
"无法连接到USB通道 {channel_source}
\n
请检查:
\n
USB相机是否已连接
\n
相机驱动是否已安装
\n
相机是否被其他程序占用"
else
:
else
:
# 如果是RTSP地址,直接连接
# 如果是RTSP地址,直接连接
success
=
self
.
_connectRTSPChannel
(
channel_source
)
success
=
self
.
_connectRTSPChannel
(
channel_source
)
if
not
success
:
if
not
success
:
error_detail
=
f
"无法连接到RTSP地址
\n
请检查:
\n
1. 网络连接是否正常
\n
2. RTSP地址格式是否正确
\n
3.
相机是否支持RTSP协议
\n\n
地址:{channel_source}"
error_detail
=
f
"无法连接到RTSP地址
\n
请检查:
\n
网络连接是否正常
\n
RTSP地址格式是否正确
\n
相机是否支持RTSP协议
\n\n
地址:{channel_source}"
if
not
success
:
if
not
success
:
self
.
_showDataCollectionChannelError
(
self
.
_showDataCollectionChannelError
(
...
...
handlers/datasetpage/datapreprocess_handler.py
View file @
c94177be
...
@@ -32,6 +32,10 @@ except Exception:
...
@@ -32,6 +32,10 @@ except Exception:
DEFAULT_CROP_SAVE_DIR
=
osp
.
join
(
get_project_root
(),
'database'
,
'Corp_picture'
)
DEFAULT_CROP_SAVE_DIR
=
osp
.
join
(
get_project_root
(),
'database'
,
'Corp_picture'
)
os
.
makedirs
(
DEFAULT_CROP_SAVE_DIR
,
exist_ok
=
True
)
os
.
makedirs
(
DEFAULT_CROP_SAVE_DIR
,
exist_ok
=
True
)
# 调试输出
print
(
f
"[DataPreprocessHandler] 项目根目录: {get_project_root()}"
)
print
(
f
"[DataPreprocessHandler] 默认裁剪保存目录: {DEFAULT_CROP_SAVE_DIR}"
)
class
DrawableLabel
(
QtWidgets
.
QLabel
):
class
DrawableLabel
(
QtWidgets
.
QLabel
):
"""
"""
...
@@ -2243,12 +2247,30 @@ class DataPreprocessHandler(QtCore.QObject):
...
@@ -2243,12 +2247,30 @@ class DataPreprocessHandler(QtCore.QObject):
# 转换为绝对路径
# 转换为绝对路径
save_liquid_data_path
=
osp
.
abspath
(
save_liquid_data_path
)
save_liquid_data_path
=
osp
.
abspath
(
save_liquid_data_path
)
# 打开视频
# 输出保存路径信息(帮助用户定位图片位置)
print
(
f
"[DataPreprocessHandler] ========== 裁剪配置 =========="
)
print
(
f
"[DataPreprocessHandler] 视频文件: {osp.basename(video_path)}"
)
print
(
f
"[DataPreprocessHandler] 保存根目录: {save_liquid_data_path}"
)
print
(
f
"[DataPreprocessHandler] 裁剪区域数: {len(crop_rects)}"
)
print
(
f
"[DataPreprocessHandler] 裁剪频率: 每{crop_frequency}帧"
)
print
(
f
"[DataPreprocessHandler] 图片格式: {image_format}"
)
print
(
f
"[DataPreprocessHandler] ====================================="
)
# 打开视频(设置参数以提高容错性)
cap
=
cv2
.
VideoCapture
(
video_path
)
cap
=
cv2
.
VideoCapture
(
video_path
)
# 设置视频读取参数,提高对损坏帧的容错性
# CAP_PROP_BUFFERSIZE: 减小缓冲区大小,加快错误恢复
try
:
cap
.
set
(
cv2
.
CAP_PROP_BUFFERSIZE
,
1
)
except
:
pass
if
not
cap
.
isOpened
():
if
not
cap
.
isOpened
():
raise
Exception
(
"无法打开视频文件"
)
raise
Exception
(
"无法打开视频文件"
)
print
(
f
"[DataPreprocessHandler] 视频已打开: {osp.basename(video_path)}"
)
total_frames
=
int
(
cap
.
get
(
cv2
.
CAP_PROP_FRAME_COUNT
))
total_frames
=
int
(
cap
.
get
(
cv2
.
CAP_PROP_FRAME_COUNT
))
# 检查是否启用了时间段选择
# 检查是否启用了时间段选择
...
@@ -2269,12 +2291,14 @@ class DataPreprocessHandler(QtCore.QObject):
...
@@ -2269,12 +2291,14 @@ class DataPreprocessHandler(QtCore.QObject):
# 为每个裁剪区域创建子目录(使用"视频名_区域X"的中文形式)
# 为每个裁剪区域创建子目录(使用"视频名_区域X"的中文形式)
region_paths
=
[]
region_paths
=
[]
print
(
f
"[DataPreprocessHandler] 创建区域文件夹:"
)
for
i
in
range
(
len
(
crop_rects
)):
for
i
in
range
(
len
(
crop_rects
)):
region_folder_name
=
f
"{video_name}_区域{i+1}"
region_folder_name
=
f
"{video_name}_区域{i+1}"
region_path
=
osp
.
join
(
save_liquid_data_path
,
region_folder_name
)
region_path
=
osp
.
join
(
save_liquid_data_path
,
region_folder_name
)
region_paths
.
append
(
region_path
)
region_paths
.
append
(
region_path
)
try
:
try
:
os
.
makedirs
(
region_path
,
exist_ok
=
True
)
os
.
makedirs
(
region_path
,
exist_ok
=
True
)
print
(
f
"[DataPreprocessHandler] 区域{i+1}: {region_path}"
)
except
Exception
as
mkdir_err
:
except
Exception
as
mkdir_err
:
raise
Exception
(
f
"无法创建区域{i+1}的保存目录: {mkdir_err}"
)
raise
Exception
(
f
"无法创建区域{i+1}的保存目录: {mkdir_err}"
)
...
@@ -2289,17 +2313,44 @@ class DataPreprocessHandler(QtCore.QObject):
...
@@ -2289,17 +2313,44 @@ class DataPreprocessHandler(QtCore.QObject):
# 计算实际需要处理的帧数(用于进度显示)
# 计算实际需要处理的帧数(用于进度显示)
frames_to_process
=
end_frame_limit
-
start_frame_limit
+
1
frames_to_process
=
end_frame_limit
-
start_frame_limit
+
1
# 连续读取失败计数器
consecutive_read_failures
=
0
max_consecutive_failures
=
10
# 允许最多连续失败10次
# 进度更新计数器(减少processEvents调用频率)
progress_update_counter
=
0
progress_update_interval
=
5
# 每处理5帧更新一次进度(平衡性能和响应性)
while
frame_count
<=
end_frame_limit
:
while
frame_count
<=
end_frame_limit
:
# 检查是否取消
# 检查是否取消
if
progress_dialog
.
wasCanceled
():
if
progress_dialog
.
wasCanceled
():
print
(
"[DataPreprocessHandler] 用户取消裁剪"
)
break
break
# 读取帧
# 检查连续失败次数
ret
,
frame
=
cap
.
read
()
if
consecutive_read_failures
>=
max_consecutive_failures
:
print
(
f
"[DataPreprocessHandler] 连续读取失败{consecutive_read_failures}次,跳过剩余帧"
)
if
not
ret
:
break
break
# 读取帧
try
:
ret
,
frame
=
cap
.
read
()
if
not
ret
:
consecutive_read_failures
+=
1
print
(
f
"[DataPreprocessHandler] 读取帧{frame_count}失败,跳过 (连续失败:{consecutive_read_failures})"
)
frame_count
+=
1
continue
# 读取成功,重置失败计数器
consecutive_read_failures
=
0
except
Exception
as
read_err
:
consecutive_read_failures
+=
1
print
(
f
"[DataPreprocessHandler] 读取帧{frame_count}异常: {read_err} (连续失败:{consecutive_read_failures})"
)
frame_count
+=
1
continue
# 根据频率决定是否裁剪
# 根据频率决定是否裁剪
if
(
frame_count
-
start_frame_limit
)
%
crop_frequency
==
0
:
if
(
frame_count
-
start_frame_limit
)
%
crop_frequency
==
0
:
# 对每个裁剪区域进行处理
# 对每个裁剪区域进行处理
...
@@ -2324,20 +2375,32 @@ class DataPreprocessHandler(QtCore.QObject):
...
@@ -2324,20 +2375,32 @@ class DataPreprocessHandler(QtCore.QObject):
f
.
write
(
encoded_img
.
tobytes
())
f
.
write
(
encoded_img
.
tobytes
())
saved_counts
[
i
]
+=
1
saved_counts
[
i
]
+=
1
else
:
print
(
f
"[DataPreprocessHandler] 编码失败: 区域{i+1}, 帧{frame_count}"
)
except
Exception
as
save_err
:
except
Exception
as
save_err
:
pass
print
(
f
"[DataPreprocessHandler] 保存失败: 区域{i+1}, 帧{frame_count}, 错误: {save_err}"
)
# 继续处理下一个区域,不中断整个流程
frame_count
+=
1
frame_count
+=
1
progress_update_counter
+=
1
# 更新进度(基于实际处理的帧数)
# 优化进度更新频率(减少processEvents调用)
processed_frames
=
frame_count
-
start_frame_limit
if
progress_update_counter
>=
progress_update_interval
:
progress
=
int
((
processed_frames
/
frames_to_process
)
*
100
)
progress_update_counter
=
0
progress_dialog
.
setValue
(
progress
)
self
.
cropProgress
.
emit
(
progress
)
# 更新进度(基于实际处理的帧数)
processed_frames
=
frame_count
-
start_frame_limit
# 处理事件,保持界面响应
progress
=
int
((
processed_frames
/
frames_to_process
)
*
100
)
QtWidgets
.
QApplication
.
processEvents
()
progress_dialog
.
setValue
(
progress
)
self
.
cropProgress
.
emit
(
progress
)
# 强制刷新预览面板(确保实时更新)
if
self
.
_crop_preview_handler
is
not
None
:
self
.
_crop_preview_handler
.
forceRefresh
()
# 处理事件,保持界面响应
QtWidgets
.
QApplication
.
processEvents
()
# 释放资源
# 释放资源
cap
.
release
()
cap
.
release
()
...
@@ -2345,6 +2408,15 @@ class DataPreprocessHandler(QtCore.QObject):
...
@@ -2345,6 +2408,15 @@ class DataPreprocessHandler(QtCore.QObject):
# 完成
# 完成
progress_dialog
.
setValue
(
100
)
progress_dialog
.
setValue
(
100
)
# 输出统计信息
print
(
f
"[DataPreprocessHandler] ===== 裁剪完成 ====="
)
print
(
f
"[DataPreprocessHandler] 处理帧数: {frame_count - start_frame_limit}/{frames_to_process}"
)
for
i
in
range
(
len
(
crop_rects
)):
print
(
f
"[DataPreprocessHandler] 区域{i+1}: 保存 {saved_counts[i]} 张图片"
)
if
consecutive_read_failures
>
0
:
print
(
f
"[DataPreprocessHandler] 警告: 跳过了 {consecutive_read_failures} 个损坏/无法读取的帧"
)
print
(
f
"[DataPreprocessHandler] ========================="
)
# 保存视频与裁剪图片的映射关系
# 保存视频与裁剪图片的映射关系
import
time
import
time
self
.
_video_crop_mapping
[
video_path
]
=
{
self
.
_video_crop_mapping
[
video_path
]
=
{
...
...
labelme/app.py
View file @
c94177be
...
@@ -34,6 +34,14 @@ from .widgets import ToolBar
...
@@ -34,6 +34,14 @@ from .widgets import ToolBar
from
.widgets
import
UniqueLabelQListWidget
from
.widgets
import
UniqueLabelQListWidget
from
.widgets
import
ZoomWidget
from
.widgets
import
ZoomWidget
# Import DialogManager for styled dialogs
try
:
import
sys
sys
.
path
.
insert
(
0
,
osp
.
dirname
(
osp
.
dirname
(
osp
.
abspath
(
__file__
))))
from
widgets.style_manager
import
DialogManager
except
ImportError
:
DialogManager
=
None
# FIXME
# FIXME
# - [medium] Set max zoom value to something big enough for FitWidth/Window
# - [medium] Set max zoom value to something big enough for FitWidth/Window
...
@@ -131,11 +139,12 @@ class MainWindow(QtWidgets.QMainWindow):
...
@@ -131,11 +139,12 @@ class MainWindow(QtWidgets.QMainWindow):
self
.
shape_dock
.
setWidget
(
self
.
labelList
)
self
.
shape_dock
.
setWidget
(
self
.
labelList
)
self
.
uniqLabelList
=
UniqueLabelQListWidget
()
self
.
uniqLabelList
=
UniqueLabelQListWidget
()
self
.
uniqLabelList
.
setToolTip
(
# 隐藏提示框
self
.
tr
(
# self.uniqLabelList.setToolTip(
"点击选择标签
\n
或按 'Esc' 键取消"
# self.tr(
)
# "点击选择标签\n或按 'Esc' 键取消"
)
# )
# )
if
self
.
_config
[
"labels"
]:
if
self
.
_config
[
"labels"
]:
for
label
in
self
.
_config
[
"labels"
]:
for
label
in
self
.
_config
[
"labels"
]:
item
=
self
.
uniqLabelList
.
createItemFromLabel
(
label
)
item
=
self
.
uniqLabelList
.
createItemFromLabel
(
label
)
...
@@ -725,8 +734,8 @@ class MainWindow(QtWidgets.QMainWindow):
...
@@ -725,8 +734,8 @@ class MainWindow(QtWidgets.QMainWindow):
utils
.
addActions
(
utils
.
addActions
(
self
.
canvas
.
menus
[
1
],
self
.
canvas
.
menus
[
1
],
(
(
action
(
"
&Copy here
"
,
self
.
copyShape
),
action
(
"
复制到此处(&C)
"
,
self
.
copyShape
),
action
(
"
&Move here
"
,
self
.
moveShape
),
action
(
"
移动到此处(&M)
"
,
self
.
moveShape
),
),
),
)
)
...
@@ -1884,14 +1893,27 @@ class MainWindow(QtWidgets.QMainWindow):
...
@@ -1884,14 +1893,27 @@ class MainWindow(QtWidgets.QMainWindow):
return
label_file
return
label_file
def
deleteFile
(
self
):
def
deleteFile
(
self
):
mb
=
QtWidgets
.
QMessageBox
msg
=
self
.
tr
(
msg
=
self
.
tr
(
""
""
""
""
)
)
answer
=
mb
.
warning
(
self
,
self
.
tr
(
""
),
msg
,
mb
.
Yes
|
mb
.
No
)
if
answer
!=
mb
.
Yes
:
# Use DialogManager if available
return
if
DialogManager
:
result
=
DialogManager
.
show_question_warning
(
self
,
"警告"
,
msg
,
yes_text
=
"是"
,
no_text
=
"否"
)
if
not
result
:
return
else
:
mb
=
QtWidgets
.
QMessageBox
answer
=
mb
.
warning
(
self
,
"警告"
,
msg
,
mb
.
Yes
|
mb
.
No
)
if
answer
!=
mb
.
Yes
:
return
label_file
=
self
.
getLabelFile
()
label_file
=
self
.
getLabelFile
()
if
osp
.
exists
(
label_file
):
if
osp
.
exists
(
label_file
):
...
@@ -1923,29 +1945,65 @@ class MainWindow(QtWidgets.QMainWindow):
...
@@ -1923,29 +1945,65 @@ class MainWindow(QtWidgets.QMainWindow):
def
mayContinue
(
self
):
def
mayContinue
(
self
):
if
not
self
.
dirty
:
if
not
self
.
dirty
:
return
True
return
True
mb
=
QtWidgets
.
QMessageBox
msg
=
self
.
tr
(
'"{}"'
)
.
format
(
msg
=
self
.
tr
(
'"{}"'
)
.
format
(
self
.
filename
self
.
filename
)
)
answer
=
mb
.
question
(
self
,
# Use DialogManager if available
self
.
tr
(
""
),
if
DialogManager
:
msg
,
# Create custom dialog with Save/Discard/Cancel buttons
mb
.
Save
|
mb
.
Discard
|
mb
.
Cancel
,
msg_box
=
QtWidgets
.
QMessageBox
(
self
)
mb
.
Save
,
msg_box
.
setWindowTitle
(
"提示"
)
)
msg_box
.
setText
(
msg
)
if
answer
==
mb
.
Discard
:
msg_box
.
setIcon
(
QtWidgets
.
QMessageBox
.
Question
)
return
True
elif
answer
==
mb
.
Save
:
# Add custom buttons with Chinese text
self
.
saveFile
()
save_btn
=
msg_box
.
addButton
(
"保存"
,
QtWidgets
.
QMessageBox
.
AcceptRole
)
return
True
discard_btn
=
msg_box
.
addButton
(
"不保存"
,
QtWidgets
.
QMessageBox
.
DestructiveRole
)
else
:
# answer == mb.Cancel
cancel_btn
=
msg_box
.
addButton
(
"取消"
,
QtWidgets
.
QMessageBox
.
RejectRole
)
return
False
msg_box
.
setDefaultButton
(
save_btn
)
# Apply DialogManager styling
msg_box
.
setStyleSheet
(
DialogManager
.
DEFAULT_STYLE
)
from
widgets.style_manager
import
FontManager
FontManager
.
applyToWidgetRecursive
(
msg_box
)
msg_box
.
exec_
()
clicked
=
msg_box
.
clickedButton
()
if
clicked
==
discard_btn
:
return
True
elif
clicked
==
save_btn
:
self
.
saveFile
()
return
True
else
:
# cancel_btn
return
False
else
:
mb
=
QtWidgets
.
QMessageBox
answer
=
mb
.
question
(
self
,
"提示"
,
msg
,
mb
.
Save
|
mb
.
Discard
|
mb
.
Cancel
,
mb
.
Save
,
)
if
answer
==
mb
.
Discard
:
return
True
elif
answer
==
mb
.
Save
:
self
.
saveFile
()
return
True
else
:
# answer == mb.Cancel
return
False
def
errorMessage
(
self
,
title
,
message
):
def
errorMessage
(
self
,
title
,
message
):
return
QtWidgets
.
QMessageBox
.
critical
(
formatted_message
=
"<p><b>
%
s</b></p>
%
s"
%
(
title
,
message
)
self
,
title
,
"<p><b>
%
s</b></p>
%
s"
%
(
title
,
message
)
)
# Use DialogManager if available
if
DialogManager
:
DialogManager
.
show_critical
(
self
,
title
,
formatted_message
)
else
:
return
QtWidgets
.
QMessageBox
.
critical
(
self
,
title
,
formatted_message
)
def
currentPath
(
self
):
def
currentPath
(
self
):
return
osp
.
dirname
(
str
(
self
.
filename
))
if
self
.
filename
else
"."
return
osp
.
dirname
(
str
(
self
.
filename
))
if
self
.
filename
else
"."
...
@@ -1965,19 +2023,34 @@ class MainWindow(QtWidgets.QMainWindow):
...
@@ -1965,19 +2023,34 @@ class MainWindow(QtWidgets.QMainWindow):
self
.
setDirty
()
self
.
setDirty
()
def
deleteSelectedShape
(
self
):
def
deleteSelectedShape
(
self
):
yes
,
no
=
QtWidgets
.
QMessageBox
.
Yes
,
QtWidgets
.
QMessageBox
.
No
msg
=
"确定删除该标签?"
msg
=
self
.
tr
(
" {} "
# Use DialogManager if available, otherwise fallback to QMessageBox
""
if
DialogManager
:
)
.
format
(
len
(
self
.
canvas
.
selectedShapes
))
result
=
DialogManager
.
show_question_warning
(
if
yes
==
QtWidgets
.
QMessageBox
.
warning
(
self
,
self
,
self
.
tr
(
""
),
msg
,
yes
|
no
,
yes
"警告"
,
):
msg
,
self
.
remLabels
(
self
.
canvas
.
deleteSelected
())
yes_text
=
"是"
,
self
.
setDirty
()
no_text
=
"否"
,
if
self
.
noShapes
():
text_alignment
=
DialogManager
.
ALIGN_CENTER
for
action
in
self
.
actions
.
onShapesPresent
:
)
action
.
setEnabled
(
False
)
if
result
:
self
.
remLabels
(
self
.
canvas
.
deleteSelected
())
self
.
setDirty
()
if
self
.
noShapes
():
for
action
in
self
.
actions
.
onShapesPresent
:
action
.
setEnabled
(
False
)
else
:
yes
,
no
=
QtWidgets
.
QMessageBox
.
Yes
,
QtWidgets
.
QMessageBox
.
No
if
yes
==
QtWidgets
.
QMessageBox
.
warning
(
self
,
"警告"
,
msg
,
yes
|
no
,
yes
):
self
.
remLabels
(
self
.
canvas
.
deleteSelected
())
self
.
setDirty
()
if
self
.
noShapes
():
for
action
in
self
.
actions
.
onShapesPresent
:
action
.
setEnabled
(
False
)
def
copyShape
(
self
):
def
copyShape
(
self
):
self
.
canvas
.
endMove
(
copy
=
True
)
self
.
canvas
.
endMove
(
copy
=
True
)
...
@@ -1994,13 +2067,19 @@ class MainWindow(QtWidgets.QMainWindow):
...
@@ -1994,13 +2067,19 @@ class MainWindow(QtWidgets.QMainWindow):
if
not
self
.
mayContinue
():
if
not
self
.
mayContinue
():
return
return
defaultOpenDirPath
=
dirpath
if
dirpath
else
"."
# 默认打开 database\Corp_picture 目录
default_dir
=
osp
.
join
(
osp
.
dirname
(
osp
.
dirname
(
osp
.
abspath
(
__file__
))),
"database"
,
"Corp_picture"
)
defaultOpenDirPath
=
dirpath
if
dirpath
else
default_dir
if
self
.
lastOpenDir
and
osp
.
exists
(
self
.
lastOpenDir
):
if
self
.
lastOpenDir
and
osp
.
exists
(
self
.
lastOpenDir
):
defaultOpenDirPath
=
self
.
lastOpenDir
defaultOpenDirPath
=
self
.
lastOpenDir
else
:
else
:
defaultOpenDirPath
=
(
# 优先使用 database\Corp_picture,如果不存在则使用当前文件目录或当前工作目录
osp
.
dirname
(
self
.
filename
)
if
self
.
filename
else
"."
if
osp
.
exists
(
default_dir
):
)
defaultOpenDirPath
=
default_dir
else
:
defaultOpenDirPath
=
(
osp
.
dirname
(
self
.
filename
)
if
self
.
filename
else
"."
)
targetDirPath
=
str
(
targetDirPath
=
str
(
QtWidgets
.
QFileDialog
.
getExistingDirectory
(
QtWidgets
.
QFileDialog
.
getExistingDirectory
(
...
...
labelme/widgets/brightness_contrast_dialog.py
View file @
c94177be
import
PIL.Image
import
PIL.Image
import
PIL.ImageEnhance
import
PIL.ImageEnhance
import
sys
import
os
from
qtpy.QtCore
import
Qt
from
qtpy.QtCore
import
Qt
from
qtpy
import
QtGui
from
qtpy
import
QtGui
from
qtpy
import
QtWidgets
from
qtpy
import
QtWidgets
from
..
import
utils
from
..
import
utils
# 导入全局样式管理器
try
:
current_dir
=
os
.
path
.
dirname
(
os
.
path
.
abspath
(
__file__
))
project_root
=
os
.
path
.
dirname
(
os
.
path
.
dirname
(
os
.
path
.
dirname
(
current_dir
)))
if
project_root
not
in
sys
.
path
:
sys
.
path
.
insert
(
0
,
project_root
)
from
widgets.style_manager
import
FontManager
STYLE_MANAGER_AVAILABLE
=
True
except
ImportError
:
STYLE_MANAGER_AVAILABLE
=
False
class
BrightnessContrastDialog
(
QtWidgets
.
QDialog
):
class
BrightnessContrastDialog
(
QtWidgets
.
QDialog
):
def
__init__
(
self
,
img
,
callback
,
parent
=
None
):
def
__init__
(
self
,
img
,
callback
,
parent
=
None
):
...
@@ -20,6 +33,10 @@ class BrightnessContrastDialog(QtWidgets.QDialog):
...
@@ -20,6 +33,10 @@ class BrightnessContrastDialog(QtWidgets.QDialog):
formLayout
.
addRow
(
self
.
tr
(
""
),
self
.
slider_brightness
)
formLayout
.
addRow
(
self
.
tr
(
""
),
self
.
slider_brightness
)
formLayout
.
addRow
(
self
.
tr
(
""
),
self
.
slider_contrast
)
formLayout
.
addRow
(
self
.
tr
(
""
),
self
.
slider_contrast
)
self
.
setLayout
(
formLayout
)
self
.
setLayout
(
formLayout
)
# 应用全局字体样式
if
STYLE_MANAGER_AVAILABLE
:
FontManager
.
applyToWidgetRecursive
(
self
)
assert
isinstance
(
img
,
PIL
.
Image
.
Image
)
assert
isinstance
(
img
,
PIL
.
Image
.
Image
)
self
.
img
=
img
self
.
img
=
img
...
...
labelme/widgets/canvas.py
View file @
c94177be
...
@@ -6,6 +6,12 @@ from labelme import QT5
...
@@ -6,6 +6,12 @@ from labelme import QT5
from
labelme.shape
import
Shape
from
labelme.shape
import
Shape
import
labelme.utils
import
labelme.utils
# 导入标签显示名称映射
try
:
from
labelme.widgets.label_dialog
import
LABEL_DISPLAY_NAMES
except
ImportError
:
LABEL_DISPLAY_NAMES
=
{}
# TODO(unknown):
# TODO(unknown):
# - [maybe] Find optimal epsilon value.
# - [maybe] Find optimal epsilon value.
...
@@ -286,7 +292,7 @@ class Canvas(QtWidgets.QWidget):
...
@@ -286,7 +292,7 @@ class Canvas(QtWidgets.QWidget):
# - Highlight shapes
# - Highlight shapes
# - Highlight vertex
# - Highlight vertex
# Update shape/vertex fill and tooltip value accordingly.
# Update shape/vertex fill and tooltip value accordingly.
self
.
setToolTip
(
self
.
tr
(
"
Image
"
))
self
.
setToolTip
(
self
.
tr
(
"
图像
"
))
for
shape
in
reversed
([
s
for
s
in
self
.
shapes
if
self
.
isVisible
(
s
)]):
for
shape
in
reversed
([
s
for
s
in
self
.
shapes
if
self
.
isVisible
(
s
)]):
# Look for a nearby vertex to highlight. If that fails,
# Look for a nearby vertex to highlight. If that fails,
# check if we happen to be inside a shape.
# check if we happen to be inside a shape.
...
@@ -301,7 +307,7 @@ class Canvas(QtWidgets.QWidget):
...
@@ -301,7 +307,7 @@ class Canvas(QtWidgets.QWidget):
self
.
hEdge
=
None
self
.
hEdge
=
None
shape
.
highlightVertex
(
index
,
shape
.
MOVE_VERTEX
)
shape
.
highlightVertex
(
index
,
shape
.
MOVE_VERTEX
)
self
.
overrideCursor
(
CURSOR_POINT
)
self
.
overrideCursor
(
CURSOR_POINT
)
self
.
setToolTip
(
self
.
tr
(
"
Click & drag to move point
"
))
self
.
setToolTip
(
self
.
tr
(
"
点击并拖动以移动点
"
))
self
.
setStatusTip
(
self
.
toolTip
())
self
.
setStatusTip
(
self
.
toolTip
())
self
.
update
()
self
.
update
()
break
break
...
@@ -313,7 +319,7 @@ class Canvas(QtWidgets.QWidget):
...
@@ -313,7 +319,7 @@ class Canvas(QtWidgets.QWidget):
self
.
prevhShape
=
self
.
hShape
=
shape
self
.
prevhShape
=
self
.
hShape
=
shape
self
.
prevhEdge
=
self
.
hEdge
=
index_edge
self
.
prevhEdge
=
self
.
hEdge
=
index_edge
self
.
overrideCursor
(
CURSOR_POINT
)
self
.
overrideCursor
(
CURSOR_POINT
)
self
.
setToolTip
(
self
.
tr
(
"
Click to create point
"
))
self
.
setToolTip
(
self
.
tr
(
"
点击以创建点
"
))
self
.
setStatusTip
(
self
.
toolTip
())
self
.
setStatusTip
(
self
.
toolTip
())
self
.
update
()
self
.
update
()
break
break
...
@@ -325,8 +331,10 @@ class Canvas(QtWidgets.QWidget):
...
@@ -325,8 +331,10 @@ class Canvas(QtWidgets.QWidget):
self
.
prevhShape
=
self
.
hShape
=
shape
self
.
prevhShape
=
self
.
hShape
=
shape
self
.
prevhEdge
=
self
.
hEdge
self
.
prevhEdge
=
self
.
hEdge
self
.
hEdge
=
None
self
.
hEdge
=
None
# 将英文标签转换为中文显示名称
display_label
=
LABEL_DISPLAY_NAMES
.
get
(
shape
.
label
,
shape
.
label
)
self
.
setToolTip
(
self
.
setToolTip
(
self
.
tr
(
"
Click & drag to move shape '
%
s'"
)
%
shape
.
label
self
.
tr
(
"
点击并拖动以移动形状 '
%
s'"
)
%
display_
label
)
)
self
.
setStatusTip
(
self
.
toolTip
())
self
.
setStatusTip
(
self
.
toolTip
())
self
.
overrideCursor
(
CURSOR_GRAB
)
self
.
overrideCursor
(
CURSOR_GRAB
)
...
...
labelme/widgets/color_dialog.py
View file @
c94177be
import
sys
import
os
from
qtpy
import
QtWidgets
from
qtpy
import
QtWidgets
# 导入全局样式管理器
try
:
current_dir
=
os
.
path
.
dirname
(
os
.
path
.
abspath
(
__file__
))
project_root
=
os
.
path
.
dirname
(
os
.
path
.
dirname
(
os
.
path
.
dirname
(
current_dir
)))
if
project_root
not
in
sys
.
path
:
sys
.
path
.
insert
(
0
,
project_root
)
from
widgets.style_manager
import
FontManager
STYLE_MANAGER_AVAILABLE
=
True
except
ImportError
:
STYLE_MANAGER_AVAILABLE
=
False
class
ColorDialog
(
QtWidgets
.
QColorDialog
):
class
ColorDialog
(
QtWidgets
.
QColorDialog
):
def
__init__
(
self
,
parent
=
None
):
def
__init__
(
self
,
parent
=
None
):
...
@@ -14,6 +27,10 @@ class ColorDialog(QtWidgets.QColorDialog):
...
@@ -14,6 +27,10 @@ class ColorDialog(QtWidgets.QColorDialog):
self
.
bb
=
self
.
layout
()
.
itemAt
(
1
)
.
widget
()
self
.
bb
=
self
.
layout
()
.
itemAt
(
1
)
.
widget
()
self
.
bb
.
addButton
(
QtWidgets
.
QDialogButtonBox
.
RestoreDefaults
)
self
.
bb
.
addButton
(
QtWidgets
.
QDialogButtonBox
.
RestoreDefaults
)
self
.
bb
.
clicked
.
connect
(
self
.
checkRestore
)
self
.
bb
.
clicked
.
connect
(
self
.
checkRestore
)
# 应用全局字体样式
if
STYLE_MANAGER_AVAILABLE
:
FontManager
.
applyToWidgetRecursive
(
self
)
def
getColor
(
self
,
value
=
None
,
title
=
None
,
default
=
None
):
def
getColor
(
self
,
value
=
None
,
title
=
None
,
default
=
None
):
self
.
default
=
default
self
.
default
=
default
...
...
labelme/widgets/file_dialog_preview.py
View file @
c94177be
...
@@ -3,6 +3,19 @@ from qtpy import QtGui
...
@@ -3,6 +3,19 @@ from qtpy import QtGui
from
qtpy
import
QtWidgets
from
qtpy
import
QtWidgets
import
json
import
json
import
sys
import
os
# 导入全局样式管理器
try
:
current_dir
=
os
.
path
.
dirname
(
os
.
path
.
abspath
(
__file__
))
project_root
=
os
.
path
.
dirname
(
os
.
path
.
dirname
(
os
.
path
.
dirname
(
current_dir
)))
if
project_root
not
in
sys
.
path
:
sys
.
path
.
insert
(
0
,
project_root
)
from
widgets.style_manager
import
FontManager
STYLE_MANAGER_AVAILABLE
=
True
except
ImportError
:
STYLE_MANAGER_AVAILABLE
=
False
class
ScrollAreaPreview
(
QtWidgets
.
QScrollArea
):
class
ScrollAreaPreview
(
QtWidgets
.
QScrollArea
):
...
@@ -54,6 +67,10 @@ class FileDialogPreview(QtWidgets.QFileDialog):
...
@@ -54,6 +67,10 @@ class FileDialogPreview(QtWidgets.QFileDialog):
self
.
setFixedSize
(
self
.
width
()
+
300
,
self
.
height
())
self
.
setFixedSize
(
self
.
width
()
+
300
,
self
.
height
())
self
.
layout
()
.
addLayout
(
box
,
1
,
3
,
1
,
1
)
self
.
layout
()
.
addLayout
(
box
,
1
,
3
,
1
,
1
)
self
.
currentChanged
.
connect
(
self
.
onChange
)
self
.
currentChanged
.
connect
(
self
.
onChange
)
# 应用全局字体样式
if
STYLE_MANAGER_AVAILABLE
:
FontManager
.
applyToWidgetRecursive
(
self
)
def
onChange
(
self
,
path
):
def
onChange
(
self
,
path
):
if
path
.
lower
()
.
endswith
(
".json"
):
if
path
.
lower
()
.
endswith
(
".json"
):
...
...
labelme/widgets/label_dialog.py
View file @
c94177be
import
re
import
re
import
sys
import
os
from
qtpy
import
QT_VERSION
from
qtpy
import
QT_VERSION
from
qtpy
import
QtCore
from
qtpy
import
QtCore
...
@@ -8,17 +10,31 @@ from qtpy import QtWidgets
...
@@ -8,17 +10,31 @@ from qtpy import QtWidgets
from
labelme.logger
import
logger
from
labelme.logger
import
logger
import
labelme.utils
import
labelme.utils
# 导入全局样式管理器
try
:
# 获取项目根目录(labelme的上级目录)
current_dir
=
os
.
path
.
dirname
(
os
.
path
.
abspath
(
__file__
))
project_root
=
os
.
path
.
dirname
(
os
.
path
.
dirname
(
os
.
path
.
dirname
(
current_dir
)))
if
project_root
not
in
sys
.
path
:
sys
.
path
.
insert
(
0
,
project_root
)
from
widgets.style_manager
import
FontManager
,
DialogManager
STYLE_MANAGER_AVAILABLE
=
True
except
ImportError
as
e
:
print
(
f
"警告: 无法导入样式管理器: {e}"
)
STYLE_MANAGER_AVAILABLE
=
False
DialogManager
=
None
QT5
=
QT_VERSION
[
0
]
==
"5"
QT5
=
QT_VERSION
[
0
]
==
"5"
# -
# -
FIXED_LABELS
=
[
"liquid"
,
"air"
,
"foam"
]
FIXED_LABELS
=
[
"liquid"
,
"air"
,
"foam"
]
#
- UI
# - UI
LABEL_DISPLAY_NAMES
=
{
LABEL_DISPLAY_NAMES
=
{
"liquid"
:
""
,
"liquid"
:
"
液体
"
,
"air"
:
""
,
"air"
:
"
空气
"
,
"foam"
:
""
"foam"
:
"
泡沫
"
}
}
# -
# -
...
@@ -66,7 +82,9 @@ class LabelDialog(QtWidgets.QDialog):
...
@@ -66,7 +82,9 @@ class LabelDialog(QtWidgets.QDialog):
self
.
_fit_to_content
=
fit_to_content
self
.
_fit_to_content
=
fit_to_content
super
(
LabelDialog
,
self
)
.
__init__
(
parent
)
super
(
LabelDialog
,
self
)
.
__init__
(
parent
)
self
.
setWindowTitle
(
""
)
self
.
setWindowTitle
(
"请选择标签"
)
# 隐藏右上角的帮助按钮(?)
self
.
setWindowFlags
(
self
.
windowFlags
()
&
~
QtCore
.
Qt
.
WindowContextHelpButtonHint
)
#
#
# self.edit self.edit_group_id
# self.edit self.edit_group_id
...
@@ -100,6 +118,10 @@ class LabelDialog(QtWidgets.QDialog):
...
@@ -100,6 +118,10 @@ class LabelDialog(QtWidgets.QDialog):
self
.
labelList
.
setFixedHeight
(
150
)
self
.
labelList
.
setFixedHeight
(
150
)
# self.edit.setListWidgetself.edit
# self.edit.setListWidgetself.edit
layout
.
addWidget
(
self
.
labelList
)
layout
.
addWidget
(
self
.
labelList
)
# 应用全局字体样式到列表控件
if
STYLE_MANAGER_AVAILABLE
:
FontManager
.
applyToWidget
(
self
.
labelList
)
# label_flags
# label_flags
if
flags
is
None
:
if
flags
is
None
:
flags
=
{}
flags
=
{}
...
@@ -111,6 +133,10 @@ class LabelDialog(QtWidgets.QDialog):
...
@@ -111,6 +133,10 @@ class LabelDialog(QtWidgets.QDialog):
#
#
# self.editDescription
# self.editDescription
self
.
setLayout
(
layout
)
self
.
setLayout
(
layout
)
# 应用全局字体样式到整个对话框
if
STYLE_MANAGER_AVAILABLE
:
FontManager
.
applyToWidgetRecursive
(
self
)
# completion
# completion
completer
=
QtWidgets
.
QCompleter
()
completer
=
QtWidgets
.
QCompleter
()
if
not
QT5
and
completion
!=
"startswith"
:
if
not
QT5
and
completion
!=
"startswith"
:
...
@@ -154,11 +180,10 @@ class LabelDialog(QtWidgets.QDialog):
...
@@ -154,11 +180,10 @@ class LabelDialog(QtWidgets.QDialog):
current_item
=
self
.
labelList
.
currentItem
()
current_item
=
self
.
labelList
.
currentItem
()
if
not
current_item
:
if
not
current_item
:
logger
.
warning
(
""
)
logger
.
warning
(
""
)
QtWidgets
.
QMessageBox
.
warning
(
if
DialogManager
:
self
,
DialogManager
.
show_warning
(
self
,
""
,
""
)
""
,
else
:
""
QtWidgets
.
QMessageBox
.
warning
(
self
,
""
,
""
)
)
return
return
#
#
...
@@ -168,11 +193,11 @@ class LabelDialog(QtWidgets.QDialog):
...
@@ -168,11 +193,11 @@ class LabelDialog(QtWidgets.QDialog):
#
#
if
text
not
in
FIXED_LABELS
:
if
text
not
in
FIXED_LABELS
:
logger
.
warning
(
f
" '{text}' : {', '.join(FIXED_LABELS)}"
)
logger
.
warning
(
f
" '{text}' : {', '.join(FIXED_LABELS)}"
)
QtWidgets
.
QMessageBox
.
warning
(
msg
=
f
" '{text}'
\n\n
:
\n
"
+
"
\n
"
.
join
(
FIXED_LABELS
)
self
,
if
DialogManager
:
""
,
DialogManager
.
show_warning
(
self
,
""
,
msg
)
f
" '{text}'
\n\n
:
\n
"
+
"
\n
"
.
join
(
FIXED_LABELS
)
else
:
)
QtWidgets
.
QMessageBox
.
warning
(
self
,
""
,
msg
)
return
return
if
text
:
if
text
:
...
...
widgets/datasetpage/annotationtool.py
View file @
c94177be
...
@@ -61,6 +61,25 @@ except ImportError as e:
...
@@ -61,6 +61,25 @@ except ImportError as e:
labelme_get_config
=
None
labelme_get_config
=
None
class
ColorPreservingDelegate
(
QtWidgets
.
QStyledItemDelegate
):
"""自定义委托,保持选中状态下的文字颜色"""
def
paint
(
self
,
painter
,
option
,
index
):
# 获取item的前景色(文字颜色)
foreground
=
index
.
data
(
Qt
.
ForegroundRole
)
# 如果item被选中,修改选项的调色板以保持文字颜色
if
option
.
state
&
QtWidgets
.
QStyle
.
State_Selected
:
# 设置选中背景色为灰色
option
.
palette
.
setBrush
(
QtGui
.
QPalette
.
Highlight
,
QtGui
.
QBrush
(
QtGui
.
QColor
(
208
,
208
,
208
)))
# 如果有自定义前景色,保持它
if
foreground
:
option
.
palette
.
setBrush
(
QtGui
.
QPalette
.
HighlightedText
,
foreground
)
# 调用父类的绘制方法
super
()
.
paint
(
painter
,
option
,
index
)
class
AnnotationTool
(
QtWidgets
.
QWidget
):
class
AnnotationTool
(
QtWidgets
.
QWidget
):
"""
"""
数据标注工具组件
数据标注工具组件
...
@@ -181,6 +200,10 @@ class AnnotationTool(QtWidgets.QWidget):
...
@@ -181,6 +200,10 @@ class AnnotationTool(QtWidgets.QWidget):
self
.
annotation_list
.
setAlternatingRowColors
(
True
)
self
.
annotation_list
.
setAlternatingRowColors
(
True
)
self
.
annotation_list
.
setIconSize
(
QtCore
.
QSize
(
80
,
80
))
self
.
annotation_list
.
setIconSize
(
QtCore
.
QSize
(
80
,
80
))
self
.
annotation_list
.
setSpacing
(
5
)
self
.
annotation_list
.
setSpacing
(
5
)
# 设置自定义委托以保持选中状态下的文字颜色
self
.
annotation_list
.
setItemDelegate
(
ColorPreservingDelegate
(
self
.
annotation_list
))
self
.
annotation_list
.
setStyleSheet
(
"""
self
.
annotation_list
.
setStyleSheet
(
"""
QListWidget {
QListWidget {
border: 1px solid #c0c0c0;
border: 1px solid #c0c0c0;
...
@@ -192,8 +215,7 @@ class AnnotationTool(QtWidgets.QWidget):
...
@@ -192,8 +215,7 @@ class AnnotationTool(QtWidgets.QWidget):
border-bottom: 1px solid #e0e0e0;
border-bottom: 1px solid #e0e0e0;
}
}
QListWidget::item:selected {
QListWidget::item:selected {
background-color: #0078d7;
background-color: #d0d0d0;
color: white;
}
}
QListWidget::item:hover {
QListWidget::item:hover {
background-color: #e0e0e0;
background-color: #e0e0e0;
...
@@ -552,8 +574,11 @@ class AnnotationTool(QtWidgets.QWidget):
...
@@ -552,8 +574,11 @@ class AnnotationTool(QtWidgets.QWidget):
def
onDirectoryChanged
(
self
,
dir_path
):
def
onDirectoryChanged
(
self
,
dir_path
):
"""目录变化时的UI更新(响应handler信号)"""
"""目录变化时的UI更新(响应handler信号)"""
self
.
lbl_current_folder
.
setText
(
dir_path
)
# 只显示文件夹名称,不显示完整路径
self
.
lbl_current_folder
.
setStyleSheet
(
"color: #2ca02c; font-style: normal; font-weight: bold;"
)
import
os.path
as
osp
folder_name
=
osp
.
basename
(
dir_path
)
if
dir_path
else
""
self
.
lbl_current_folder
.
setText
(
folder_name
)
self
.
lbl_current_folder
.
setStyleSheet
(
"color: #2ca02c; font-style: normal; font-weight: bold; font-size: 9pt;"
)
def
onFileListUpdated
(
self
,
file_info_list
):
def
onFileListUpdated
(
self
,
file_info_list
):
"""文件列表更新时的UI更新(响应handler信号)"""
"""文件列表更新时的UI更新(响应handler信号)"""
...
@@ -634,11 +659,28 @@ class AnnotationTool(QtWidgets.QWidget):
...
@@ -634,11 +659,28 @@ class AnnotationTool(QtWidgets.QWidget):
"""列表项点击事件"""
"""列表项点击事件"""
data
=
item
.
data
(
Qt
.
UserRole
)
data
=
item
.
data
(
Qt
.
UserRole
)
if
data
:
if
data
:
# 强制设置选中项的颜色
if
data
.
get
(
'has_json'
):
item
.
setForeground
(
QtGui
.
QBrush
(
QtGui
.
QColor
(
44
,
160
,
44
)))
# 绿色
else
:
item
.
setForeground
(
QtGui
.
QBrush
(
QtGui
.
QColor
(
128
,
128
,
128
)))
# 灰色
image_path
=
data
[
'image_path'
]
image_path
=
data
[
'image_path'
]
self
.
loadImageForAnnotation
(
image_path
)
self
.
loadImageForAnnotation
(
image_path
)
def
onItemSelectionChanged
(
self
):
def
onItemSelectionChanged
(
self
):
"""列表项选择变化事件"""
"""列表项选择变化事件"""
# 重新设置所有项目的颜色(因为选中状态会改变颜色)
for
i
in
range
(
self
.
annotation_list
.
count
()):
item
=
self
.
annotation_list
.
item
(
i
)
data
=
item
.
data
(
Qt
.
UserRole
)
if
data
and
data
.
get
(
'has_json'
):
# 已标注 - 保持深绿色
item
.
setForeground
(
QtGui
.
QBrush
(
QtGui
.
QColor
(
44
,
160
,
44
)))
# #2ca02c
else
:
# 未标注 - 灰色
item
.
setForeground
(
QtGui
.
QBrush
(
QtGui
.
QColor
(
128
,
128
,
128
)))
# #808080
items
=
self
.
annotation_list
.
selectedItems
()
items
=
self
.
annotation_list
.
selectedItems
()
if
not
items
:
if
not
items
:
return
return
...
...
widgets/datasetpage/crop_config_dialog.py
View file @
c94177be
...
@@ -41,6 +41,10 @@ except Exception:
...
@@ -41,6 +41,10 @@ except Exception:
DEFAULT_CROP_SAVE_DIR
=
osp
.
join
(
get_project_root
(),
'database'
,
'Corp_picture'
)
DEFAULT_CROP_SAVE_DIR
=
osp
.
join
(
get_project_root
(),
'database'
,
'Corp_picture'
)
os
.
makedirs
(
DEFAULT_CROP_SAVE_DIR
,
exist_ok
=
True
)
os
.
makedirs
(
DEFAULT_CROP_SAVE_DIR
,
exist_ok
=
True
)
# 调试输出
print
(
f
"[CropConfigDialog模块] 项目根目录: {get_project_root()}"
)
print
(
f
"[CropConfigDialog模块] 默认裁剪保存目录: {DEFAULT_CROP_SAVE_DIR}"
)
class
CropConfigDialog
(
QtWidgets
.
QDialog
):
class
CropConfigDialog
(
QtWidgets
.
QDialog
):
"""
"""
...
@@ -62,8 +66,11 @@ class CropConfigDialog(QtWidgets.QDialog):
...
@@ -62,8 +66,11 @@ class CropConfigDialog(QtWidgets.QDialog):
"""
"""
super
(
CropConfigDialog
,
self
)
.
__init__
(
parent
)
super
(
CropConfigDialog
,
self
)
.
__init__
(
parent
)
# 保存路径和频率
# 【强制修改】始终使用项目默认路径,忽略传入的参数
self
.
_save_liquid_data_path
=
default_save_liquid_data_path
or
DEFAULT_CROP_SAVE_DIR
# 这样可以确保图片始终保存在项目目录下
self
.
_save_liquid_data_path
=
DEFAULT_CROP_SAVE_DIR
print
(
f
"[CropConfigDialog] 强制使用默认路径: {DEFAULT_CROP_SAVE_DIR}"
)
self
.
_crop_frequency
=
default_frequency
self
.
_crop_frequency
=
default_frequency
self
.
_file_prefix
=
"frame"
self
.
_file_prefix
=
"frame"
self
.
_image_format
=
"jpg"
self
.
_image_format
=
"jpg"
...
@@ -321,10 +328,19 @@ class CropConfigDialog(QtWidgets.QDialog):
...
@@ -321,10 +328,19 @@ class CropConfigDialog(QtWidgets.QDialog):
try
:
try
:
settings
=
QtCore
.
QSettings
(
"Detection"
,
"CropConfigDialog"
)
settings
=
QtCore
.
QSettings
(
"Detection"
,
"CropConfigDialog"
)
saved_path
=
settings
.
value
(
"save_liquid_data_path"
,
""
)
# 【强制修改】清除旧的保存路径设置,不再记住保存路径
if
saved_path
and
osp
.
exists
(
saved_path
):
# 检查是否有旧设置
self
.
_save_liquid_data_path
=
saved_path
old_path
=
settings
.
value
(
"save_liquid_data_path"
,
""
)
self
.
path_edit
.
setText
(
saved_path
)
if
old_path
:
print
(
f
"[CropConfigDialog] 检测到旧的保存路径: {old_path}"
)
settings
.
remove
(
"save_liquid_data_path"
)
print
(
f
"[CropConfigDialog] 已清除旧的保存路径设置"
)
# 强制使用项目默认路径
# 这样可以确保图片始终保存在项目目录下,避免用户找不到图片
self
.
path_edit
.
setText
(
self
.
_save_liquid_data_path
)
print
(
f
"[CropConfigDialog] 对话框路径已设置为: {self._save_liquid_data_path}"
)
print
(
f
"[CropConfigDialog] 文本框内容: {self.path_edit.text()}"
)
saved_freq
=
settings
.
value
(
"crop_frequency"
,
1
)
saved_freq
=
settings
.
value
(
"crop_frequency"
,
1
)
try
:
try
:
...
@@ -347,7 +363,8 @@ class CropConfigDialog(QtWidgets.QDialog):
...
@@ -347,7 +363,8 @@ class CropConfigDialog(QtWidgets.QDialog):
"""保存当前设置"""
"""保存当前设置"""
try
:
try
:
settings
=
QtCore
.
QSettings
(
"Detection"
,
"CropConfigDialog"
)
settings
=
QtCore
.
QSettings
(
"Detection"
,
"CropConfigDialog"
)
settings
.
setValue
(
"save_liquid_data_path"
,
self
.
path_edit
.
text
())
# 【修改】不再保存路径,每次都使用默认路径
# settings.setValue("save_liquid_data_path", self.path_edit.text())
settings
.
setValue
(
"crop_frequency"
,
self
.
frequency_spinbox
.
value
())
settings
.
setValue
(
"crop_frequency"
,
self
.
frequency_spinbox
.
value
())
settings
.
setValue
(
"file_prefix"
,
self
.
prefix_edit
.
text
())
settings
.
setValue
(
"file_prefix"
,
self
.
prefix_edit
.
text
())
settings
.
setValue
(
"image_format"
,
self
.
format_combo
.
currentText
())
settings
.
setValue
(
"image_format"
,
self
.
format_combo
.
currentText
())
...
@@ -414,12 +431,16 @@ class CropConfigDialog(QtWidgets.QDialog):
...
@@ -414,12 +431,16 @@ class CropConfigDialog(QtWidgets.QDialog):
- file_prefix: 文件名前缀
- file_prefix: 文件名前缀
- image_format: 图片格式
- image_format: 图片格式
"""
"""
return
{
# 【强制修改】始终返回默认路径,忽略文本框内容
'save_liquid_data_path'
:
self
.
path_edit
.
text
()
.
strip
(),
# 确保图片保存在项目目录下
config
=
{
'save_liquid_data_path'
:
DEFAULT_CROP_SAVE_DIR
,
# 强制使用默认路径
'crop_frequency'
:
self
.
frequency_spinbox
.
value
(),
'crop_frequency'
:
self
.
frequency_spinbox
.
value
(),
'file_prefix'
:
self
.
prefix_edit
.
text
()
.
strip
(),
'file_prefix'
:
self
.
prefix_edit
.
text
()
.
strip
(),
'image_format'
:
self
.
format_combo
.
currentText
()
'image_format'
:
self
.
format_combo
.
currentText
()
}
}
print
(
f
"[CropConfigDialog] getConfig返回的保存路径: {config['save_liquid_data_path']}"
)
return
config
def
setConfig
(
self
,
config
):
def
setConfig
(
self
,
config
):
"""
"""
...
...
widgets/datasetpage/crop_preview_panel.py
View file @
c94177be
...
@@ -281,7 +281,7 @@ class CropPreviewPanel(QtWidgets.QWidget):
...
@@ -281,7 +281,7 @@ class CropPreviewPanel(QtWidgets.QWidget):
self
.
refreshImages
()
self
.
refreshImages
()
def
_findRegionPaths
(
self
):
def
_findRegionPaths
(
self
):
"""查找所有区域文件夹(支持新旧命名格式,可根据视频名称过滤)"""
"""查找所有区域文件夹(支持新旧命名格式,可根据视频名
称过滤)"""
self
.
_region_paths
=
[]
self
.
_region_paths
=
[]
if
not
self
.
_save_liquid_data_path
or
not
osp
.
exists
(
self
.
_save_liquid_data_path
):
if
not
self
.
_save_liquid_data_path
or
not
osp
.
exists
(
self
.
_save_liquid_data_path
):
...
@@ -342,9 +342,19 @@ class CropPreviewPanel(QtWidgets.QWidget):
...
@@ -342,9 +342,19 @@ class CropPreviewPanel(QtWidgets.QWidget):
if
not
self
.
_save_liquid_data_path
:
if
not
self
.
_save_liquid_data_path
:
return
return
# 保存当前的视频名称(clearImages会清空它)
current_video_name
=
self
.
_video_name
# 如果没有视频名称上下文,说明当前没有选中有效的裁剪视频,不应该刷新
if
not
current_video_name
:
return
# 先清空所有图片
# 先清空所有图片
self
.
clearImages
()
self
.
clearImages
()
# 恢复视频名称
self
.
_video_name
=
current_video_name
# 重新查找区域文件夹
# 重新查找区域文件夹
self
.
_findRegionPaths
()
self
.
_findRegionPaths
()
...
@@ -485,6 +495,7 @@ class CropPreviewPanel(QtWidgets.QWidget):
...
@@ -485,6 +495,7 @@ class CropPreviewPanel(QtWidgets.QWidget):
# 清空缓存数据
# 清空缓存数据
self
.
_region_images
.
clear
()
self
.
_region_images
.
clear
()
self
.
_region_paths
=
[]
self
.
_region_paths
=
[]
self
.
_video_name
=
None
# 清空视频名称,防止刷新时显示其他视频的图片
# 更新统计
# 更新统计
self
.
_updateStats
()
self
.
_updateStats
()
...
@@ -600,16 +611,16 @@ class CropPreviewPanel(QtWidgets.QWidget):
...
@@ -600,16 +611,16 @@ class CropPreviewPanel(QtWidgets.QWidget):
reply
=
DialogManager
.
show_question_warning
(
reply
=
DialogManager
.
show_question_warning
(
self
,
self
,
"确认删除"
,
"确认删除"
,
f
"确定要删除区域 {current_region + 1} 的所有文件吗?
\n
\n
"
f
"确定要删除区域 {current_region + 1} 的所有文件吗?
\n
"
f
"当前区域共有 {image_count} 张图片
\n\n
"
f
"当前区域共有 {image_count} 张图片"
f
"文件夹将被移动到回收站"
f
"文件夹将被移动到回收站"
)
)
else
:
else
:
reply
=
QtWidgets
.
QMessageBox
.
question
(
reply
=
QtWidgets
.
QMessageBox
.
question
(
self
,
self
,
"确认删除"
,
"确认删除"
,
f
"确定要删除区域 {current_region + 1} 的所有文件吗?
\n
\n
"
f
"确定要删除区域 {current_region + 1} 的所有文件吗?
\n
"
f
"当前区域共有 {image_count} 张图片
\n\n
"
f
"当前区域共有 {image_count} 张图片"
f
"文件夹将被移动到回收站"
,
f
"文件夹将被移动到回收站"
,
QtWidgets
.
QMessageBox
.
Yes
|
QtWidgets
.
QMessageBox
.
No
,
QtWidgets
.
QMessageBox
.
Yes
|
QtWidgets
.
QMessageBox
.
No
,
QtWidgets
.
QMessageBox
.
No
QtWidgets
.
QMessageBox
.
No
...
...
widgets/datasetpage/datacollection_panel.py
View file @
c94177be
...
@@ -167,7 +167,7 @@ class DataCollectionPanel(QtWidgets.QWidget):
...
@@ -167,7 +167,7 @@ class DataCollectionPanel(QtWidgets.QWidget):
# 标题栏
# 标题栏
title_layout
=
QtWidgets
.
QHBoxLayout
()
title_layout
=
QtWidgets
.
QHBoxLayout
()
title_label
=
QtWidgets
.
QLabel
(
"
目录
"
)
title_label
=
QtWidgets
.
QLabel
(
"
数据采集
"
)
title_label
.
setStyleSheet
(
"font-size: 12pt; font-weight: bold;"
)
title_label
.
setStyleSheet
(
"font-size: 12pt; font-weight: bold;"
)
title_layout
.
addWidget
(
title_label
)
title_layout
.
addWidget
(
title_label
)
...
@@ -833,12 +833,25 @@ class DataCollectionPanel(QtWidgets.QWidget):
...
@@ -833,12 +833,25 @@ class DataCollectionPanel(QtWidgets.QWidget):
file_name_no_ext
,
file_ext
=
osp
.
splitext
(
file_name
)
file_name_no_ext
,
file_ext
=
osp
.
splitext
(
file_name
)
# 弹出输入对话框
# 弹出输入对话框
new_name
,
ok
=
QtWidgets
.
QInputDialog
.
getText
(
dialog
=
QtWidgets
.
QInputDialog
(
self
)
self
,
"重命名文件"
,
dialog
.
setWindowTitle
(
"重命名文件"
)
"请输入新的文件名(不含扩展名):"
,
dialog
.
setLabelText
(
"请输入新的文件名(不含扩展名):"
)
QtWidgets
.
QLineEdit
.
Normal
,
dialog
.
setTextValue
(
file_name_no_ext
)
file_name_no_ext
dialog
.
setInputMode
(
QtWidgets
.
QInputDialog
.
TextInput
)
)
# 隐藏问号按钮
dialog
.
setWindowFlags
(
dialog
.
windowFlags
()
&
~
Qt
.
WindowContextHelpButtonHint
)
# 设置中文按钮文本
dialog
.
setOkButtonText
(
"确定"
)
dialog
.
setCancelButtonText
(
"取消"
)
# 应用全局字体管理
if
DialogManager
:
from
..style_manager
import
FontManager
FontManager
.
applyToWidgetRecursive
(
dialog
)
# 应用统一按钮样式
DialogManager
.
applyButtonStylesToDialog
(
dialog
)
ok
=
dialog
.
exec_
()
new_name
=
dialog
.
textValue
()
if
not
ok
or
not
new_name
.
strip
():
if
not
ok
or
not
new_name
.
strip
():
return
return
...
@@ -990,6 +1003,14 @@ class DataCollectionPanel(QtWidgets.QWidget):
...
@@ -990,6 +1003,14 @@ class DataCollectionPanel(QtWidgets.QWidget):
dialog
.
setLayout
(
layout
)
dialog
.
setLayout
(
layout
)
# 应用全局字体管理和按钮样式
if
DialogManager
:
from
..style_manager
import
FontManager
,
TextButtonStyleManager
FontManager
.
applyToWidgetRecursive
(
dialog
)
# 应用统一按钮样式
TextButtonStyleManager
.
applyToButton
(
ok_btn
)
TextButtonStyleManager
.
applyToButton
(
cancel_btn
)
# 连接按钮信号
# 连接按钮信号
ok_btn
.
clicked
.
connect
(
dialog
.
accept
)
ok_btn
.
clicked
.
connect
(
dialog
.
accept
)
cancel_btn
.
clicked
.
connect
(
dialog
.
reject
)
cancel_btn
.
clicked
.
connect
(
dialog
.
reject
)
...
@@ -1058,9 +1079,9 @@ class DataCollectionPanel(QtWidgets.QWidget):
...
@@ -1058,9 +1079,9 @@ class DataCollectionPanel(QtWidgets.QWidget):
if
file_info
:
if
file_info
:
file_info_text
=
"、"
.
join
(
file_info
)
file_info_text
=
"、"
.
join
(
file_info
)
message
=
f
"确定要删除文件夹
'{folder_name}' 吗?
\n\n
文件夹内含有{file_info_text}
\n\n
所有内容将被移动到回收站
"
message
=
f
"确定要删除文件夹
“{folder_name}”吗?文件夹内含有{file_info_text},所有内容将被移动到回收站。
"
else
:
else
:
message
=
f
"确定要删除文件夹
'{folder_name}' 吗?
\n\n
文件夹为空
\n\n
将被移动到回收站
"
message
=
f
"确定要删除文件夹
“{folder_name}”吗?文件夹为空,将被移动到回收站。
"
# 确认删除
# 确认删除
if
self
.
_showQuestionWarning
(
"确认删除"
,
message
):
if
self
.
_showQuestionWarning
(
"确认删除"
,
message
):
...
@@ -1383,10 +1404,23 @@ def _getSelectedChannel(self):
...
@@ -1383,10 +1404,23 @@ def _getSelectedChannel(self):
if
current_data
==
"custom"
:
if
current_data
==
"custom"
:
# 自定义RTSP地址
# 自定义RTSP地址
rtsp_url
,
ok
=
QtWidgets
.
QInputDialog
.
getText
(
dialog
=
QtWidgets
.
QInputDialog
(
self
)
self
,
"自定义RTSP地址"
,
dialog
.
setWindowTitle
(
"自定义RTSP地址"
)
"请输入RTSP地址:
\n
(格式: rtsp://username:password@ip:port/path)"
dialog
.
setLabelText
(
"请输入RTSP地址:
\n
(格式: rtsp://username:password@ip:port/path)"
)
)
dialog
.
setInputMode
(
QtWidgets
.
QInputDialog
.
TextInput
)
dialog
.
setWindowFlags
(
dialog
.
windowFlags
()
&
~
Qt
.
WindowContextHelpButtonHint
)
dialog
.
setOkButtonText
(
"确定"
)
dialog
.
setCancelButtonText
(
"取消"
)
# 应用全局字体管理
if
DialogManager
:
from
style_manager
import
FontManager
FontManager
.
applyToWidgetRecursive
(
dialog
)
# 应用统一按钮样式
DialogManager
.
applyButtonStylesToDialog
(
dialog
)
ok
=
dialog
.
exec_
()
rtsp_url
=
dialog
.
textValue
()
if
ok
and
rtsp_url
.
strip
():
if
ok
and
rtsp_url
.
strip
():
return
rtsp_url
.
strip
()
return
rtsp_url
.
strip
()
else
:
else
:
...
...
widgets/datasetpage/datapreprocess_panel.py
View file @
c94177be
...
@@ -172,7 +172,7 @@ class DataPreprocessPanel(QtWidgets.QWidget):
...
@@ -172,7 +172,7 @@ class DataPreprocessPanel(QtWidgets.QWidget):
# 标题栏
# 标题栏
title_layout
=
QtWidgets
.
QHBoxLayout
()
title_layout
=
QtWidgets
.
QHBoxLayout
()
title_label
=
QtWidgets
.
QLabel
(
"
目录
"
)
title_label
=
QtWidgets
.
QLabel
(
"
数据预处理
"
)
title_label
.
setStyleSheet
(
"font-size: 12pt; font-weight: bold;"
)
title_label
.
setStyleSheet
(
"font-size: 12pt; font-weight: bold;"
)
title_layout
.
addWidget
(
title_label
)
title_layout
.
addWidget
(
title_label
)
...
@@ -615,12 +615,17 @@ class DataPreprocessPanel(QtWidgets.QWidget):
...
@@ -615,12 +615,17 @@ class DataPreprocessPanel(QtWidgets.QWidget):
def
getCropConfig
(
self
):
def
getCropConfig
(
self
):
"""获取裁剪配置"""
"""获取裁剪配置"""
return
{
# 【强制修改】始终使用项目默认路径,忽略文本框内容
'save_liquid_data_path'
:
self
.
crop_path_edit
.
text
()
.
strip
(),
# 确保图片保存在项目目录下
default_path
=
self
.
_getDefaultCropFolder
()
config
=
{
'save_liquid_data_path'
:
default_path
,
# 强制使用默认路径
'crop_frequency'
:
self
.
crop_frequency_spinbox
.
value
(),
'crop_frequency'
:
self
.
crop_frequency_spinbox
.
value
(),
'file_prefix'
:
self
.
crop_prefix_edit
.
text
()
.
strip
(),
'file_prefix'
:
self
.
crop_prefix_edit
.
text
()
.
strip
(),
'image_format'
:
self
.
crop_format_combo
.
currentText
()
'image_format'
:
self
.
crop_format_combo
.
currentText
()
}
}
print
(
f
"[DataPreprocessPanel] getCropConfig返回的保存路径: {config['save_liquid_data_path']}"
)
return
config
def
_createCropPreviewPanel
(
self
):
def
_createCropPreviewPanel
(
self
):
"""创建右侧裁剪图片预览面板"""
"""创建右侧裁剪图片预览面板"""
...
@@ -1124,6 +1129,7 @@ class DataPreprocessPanel(QtWidgets.QWidget):
...
@@ -1124,6 +1129,7 @@ class DataPreprocessPanel(QtWidgets.QWidget):
# 创建右键菜单
# 创建右键菜单
menu
=
QtWidgets
.
QMenu
(
self
)
menu
=
QtWidgets
.
QMenu
(
self
)
# 只在空白处点击时显示刷新菜单
if
not
item
:
if
not
item
:
# 在空白处点击,显示刷新菜单
# 在空白处点击,显示刷新菜单
action_refresh
=
menu
.
addAction
(
newIcon
(
"刷新"
),
"刷新"
)
action_refresh
=
menu
.
addAction
(
newIcon
(
"刷新"
),
"刷新"
)
...
@@ -1134,26 +1140,8 @@ class DataPreprocessPanel(QtWidgets.QWidget):
...
@@ -1134,26 +1140,8 @@ class DataPreprocessPanel(QtWidgets.QWidget):
# 处理刷新动作
# 处理刷新动作
if
action
==
action_refresh
:
if
action
==
action_refresh
:
self
.
_onRefreshVideos
()
self
.
_onRefreshVideos
()
return
# 获取视频路径
video_path
=
item
.
data
(
Qt
.
UserRole
)
if
not
video_path
:
return
# 添加菜单项
action_rename
=
menu
.
addAction
(
newIcon
(
"设置"
),
"重命名"
)
action_delete
=
menu
.
addAction
(
newIcon
(
"关闭"
),
"删除"
)
# 显示菜单并获取选择的动作
action
=
menu
.
exec_
(
self
.
video_grid
.
mapToGlobal
(
position
))
# 处理选择的动作
# 在视频项上右键时不显示任何菜单(已删除重命名和删除功能)
if
action
==
action_rename
:
self
.
_onRenameVideo
(
item
)
elif
action
==
action_delete
:
self
.
_onDeleteVideo
(
item
)
def
_onRenameVideo
(
self
,
item
):
def
_onRenameVideo
(
self
,
item
):
"""重命名视频文件"""
"""重命名视频文件"""
...
...
widgets/style_manager.py
View file @
c94177be
...
@@ -275,6 +275,10 @@ class DialogManager:
...
@@ -275,6 +275,10 @@ class DialogManager:
QMessageBox {
QMessageBox {
min-width: 400px;
min-width: 400px;
}
}
QMessageBox QLabel {
border: none;
background: transparent;
}
"""
"""
# 文本对齐方式常量
# 文本对齐方式常量
...
@@ -323,6 +327,9 @@ class DialogManager:
...
@@ -323,6 +327,9 @@ class DialogManager:
# 🔥 根据文本内容自动调整对话框大小
# 🔥 根据文本内容自动调整对话框大小
DialogManager
.
_adjust_dialog_size
(
msg_box
,
message
)
DialogManager
.
_adjust_dialog_size
(
msg_box
,
message
)
# 🔥 应用统一按钮样式到对话框的所有按钮
DialogManager
.
_apply_button_styles
(
msg_box
)
return
msg_box
return
msg_box
@staticmethod
@staticmethod
...
@@ -339,6 +346,9 @@ class DialogManager:
...
@@ -339,6 +346,9 @@ class DialogManager:
# 只设置消息文本标签的对齐方式,不影响其他标签
# 只设置消息文本标签的对齐方式,不影响其他标签
if
label
.
text
()
and
not
label
.
pixmap
():
if
label
.
text
()
and
not
label
.
pixmap
():
label
.
setAlignment
(
alignment
)
label
.
setAlignment
(
alignment
)
# 移除任何边框样式
label
.
setFrameStyle
(
QtWidgets
.
QFrame
.
NoFrame
)
label
.
setStyleSheet
(
"border: none; background: transparent;"
)
except
Exception
as
e
:
except
Exception
as
e
:
pass
pass
...
@@ -382,6 +392,24 @@ class DialogManager:
...
@@ -382,6 +392,24 @@ class DialogManager:
pass
pass
@staticmethod
@staticmethod
def
_apply_button_styles
(
msg_box
):
"""应用统一按钮样式到对话框的所有按钮
Args:
msg_box: QMessageBox对象
"""
try
:
# 查找对话框中的所有QPushButton
buttons
=
msg_box
.
findChildren
(
QtWidgets
.
QPushButton
)
for
button
in
buttons
:
# 应用TextButtonStyleManager样式
TextButtonStyleManager
.
applyToButton
(
button
)
except
Exception
as
e
:
pass
@staticmethod
def
_set_chinese_button_texts
(
msg_box
,
button_texts
=
None
):
def
_set_chinese_button_texts
(
msg_box
,
button_texts
=
None
):
"""设置按钮为中文文本"""
"""设置按钮为中文文本"""
# 默认中文按钮文本映射
# 默认中文按钮文本映射
...
@@ -702,6 +730,25 @@ class DialogManager:
...
@@ -702,6 +730,25 @@ class DialogManager:
def
set_default_style
(
style_sheet
):
def
set_default_style
(
style_sheet
):
"""设置默认样式"""
"""设置默认样式"""
DialogManager
.
DEFAULT_STYLE
=
style_sheet
DialogManager
.
DEFAULT_STYLE
=
style_sheet
@staticmethod
def
applyButtonStylesToDialog
(
dialog
):
"""应用统一按钮样式到对话框的所有按钮
Args:
dialog: QDialog或QInputDialog对象
"""
try
:
# 查找对话框中的所有QPushButton
buttons
=
dialog
.
findChildren
(
QtWidgets
.
QPushButton
)
for
button
in
buttons
:
# 应用TextButtonStyleManager样式
from
widgets.style_manager
import
TextButtonStyleManager
TextButtonStyleManager
.
applyToButton
(
button
)
except
Exception
as
e
:
pass
# 对话框管理器便捷函数
# 对话框管理器便捷函数
...
...
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