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
Show whitespace changes
Inline
Side-by-side
Showing
16 changed files
with
512 additions
and
99 deletions
+512
-99
.gitignore
.gitignore
+2
-0
crop_preview_handler.py
handlers/datasetpage/crop_preview_handler.py
+35
-2
datacollection_channel_handler.py
handlers/datasetpage/datacollection_channel_handler.py
+3
-3
datapreprocess_handler.py
handlers/datasetpage/datapreprocess_handler.py
+75
-3
app.py
labelme/app.py
+99
-20
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
+39
-14
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 @@
!widgets/**
!handlers/
!handlers/**
!labelme/
!labelme/**
!app.py
!__main__.py
!__init__.py
...
...
handlers/datasetpage/crop_preview_handler.py
View file @
c94177be
...
...
@@ -47,7 +47,7 @@ class CropPreviewHandler(QtCore.QObject):
# 定时器(用于轮询检测,作为文件监控的补充)
self
.
_poll_timer
=
None
self
.
_poll_interval
=
2000
# 2秒轮询一次
self
.
_poll_interval
=
500
# 500毫秒轮询一次(提高实时性)
# 是否启用自动监控
self
.
_auto_monitor_enabled
=
False
...
...
@@ -98,7 +98,13 @@ class CropPreviewHandler(QtCore.QObject):
print
(
f
"[CropPreviewHandler] 设置保存路径(不自动刷新)"
)
self
.
_panel
.
setSavePath
(
save_liquid_data_path
,
auto_refresh
=
False
,
video_name
=
video_name
)
# 初始化已知图片列表(扫描现有图片,避免重复显示)
# 初始化已知图片列表
if
clear_first
:
# 如果清空显示,则不扫描现有图片,所有图片都视为新增
print
(
f
"[CropPreviewHandler] 清空模式:不扫描现有图片,所有图片将被视为新增"
)
self
.
_known_images
.
clear
()
else
:
# 否则扫描现有图片,避免重复显示
print
(
f
"[CropPreviewHandler] 扫描现有图片..."
)
self
.
_initKnownImages
(
save_liquid_data_path
,
video_name
)
...
...
@@ -113,6 +119,13 @@ class CropPreviewHandler(QtCore.QObject):
# 清除重置标志
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] === 监控已启动 ==="
)
def
stopMonitoring
(
self
):
...
...
@@ -409,6 +422,12 @@ class CropPreviewHandler(QtCore.QObject):
# 更新已知图片列表
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
:
print
(
f
"[CropPreviewHandler] 检查区域{region_index+1}新图片失败: {e}"
)
...
...
@@ -417,6 +436,20 @@ class CropPreviewHandler(QtCore.QObject):
if
self
.
_panel
:
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
):
"""清空面板显示"""
if
self
.
_panel
:
...
...
handlers/datasetpage/datacollection_channel_handler.py
View file @
c94177be
...
...
@@ -184,17 +184,17 @@ class DataCollectionChannelHandler:
if
channel_config
:
success
=
self
.
_connectRealChannel
(
channel_config
)
if
not
success
:
error_detail
=
f
"无法连接到配置的通道 {channel_source}
\n
请检查:
\n
相机是否已开机并连接到网络
\n
2.
IP地址和端口是否正确"
error_detail
=
f
"无法连接到配置的通道 {channel_source}
\n
请检查:
\n
相机是否已开机并连接到网络
\n
IP地址和端口是否正确"
else
:
# 如果没有配置,尝试直接连接USB通道
success
=
self
.
_connectUSBChannel
(
channel_source
)
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
:
# 如果是RTSP地址,直接连接
success
=
self
.
_connectRTSPChannel
(
channel_source
)
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
:
self
.
_showDataCollectionChannelError
(
...
...
handlers/datasetpage/datapreprocess_handler.py
View file @
c94177be
...
...
@@ -32,6 +32,10 @@ except Exception:
DEFAULT_CROP_SAVE_DIR
=
osp
.
join
(
get_project_root
(),
'database'
,
'Corp_picture'
)
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
):
"""
...
...
@@ -2243,12 +2247,30 @@ class DataPreprocessHandler(QtCore.QObject):
# 转换为绝对路径
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_PROP_BUFFERSIZE: 减小缓冲区大小,加快错误恢复
try
:
cap
.
set
(
cv2
.
CAP_PROP_BUFFERSIZE
,
1
)
except
:
pass
if
not
cap
.
isOpened
():
raise
Exception
(
"无法打开视频文件"
)
print
(
f
"[DataPreprocessHandler] 视频已打开: {osp.basename(video_path)}"
)
total_frames
=
int
(
cap
.
get
(
cv2
.
CAP_PROP_FRAME_COUNT
))
# 检查是否启用了时间段选择
...
...
@@ -2269,12 +2291,14 @@ class DataPreprocessHandler(QtCore.QObject):
# 为每个裁剪区域创建子目录(使用"视频名_区域X"的中文形式)
region_paths
=
[]
print
(
f
"[DataPreprocessHandler] 创建区域文件夹:"
)
for
i
in
range
(
len
(
crop_rects
)):
region_folder_name
=
f
"{video_name}_区域{i+1}"
region_path
=
osp
.
join
(
save_liquid_data_path
,
region_folder_name
)
region_paths
.
append
(
region_path
)
try
:
os
.
makedirs
(
region_path
,
exist_ok
=
True
)
print
(
f
"[DataPreprocessHandler] 区域{i+1}: {region_path}"
)
except
Exception
as
mkdir_err
:
raise
Exception
(
f
"无法创建区域{i+1}的保存目录: {mkdir_err}"
)
...
...
@@ -2289,16 +2313,43 @@ class DataPreprocessHandler(QtCore.QObject):
# 计算实际需要处理的帧数(用于进度显示)
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
:
# 检查是否取消
if
progress_dialog
.
wasCanceled
():
print
(
"[DataPreprocessHandler] 用户取消裁剪"
)
break
# 检查连续失败次数
if
consecutive_read_failures
>=
max_consecutive_failures
:
print
(
f
"[DataPreprocessHandler] 连续读取失败{consecutive_read_failures}次,跳过剩余帧"
)
break
# 读取帧
try
:
ret
,
frame
=
cap
.
read
()
if
not
ret
:
break
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
:
...
...
@@ -2324,11 +2375,19 @@ class DataPreprocessHandler(QtCore.QObject):
f
.
write
(
encoded_img
.
tobytes
())
saved_counts
[
i
]
+=
1
else
:
print
(
f
"[DataPreprocessHandler] 编码失败: 区域{i+1}, 帧{frame_count}"
)
except
Exception
as
save_err
:
pass
print
(
f
"[DataPreprocessHandler] 保存失败: 区域{i+1}, 帧{frame_count}, 错误: {save_err}"
)
# 继续处理下一个区域,不中断整个流程
frame_count
+=
1
progress_update_counter
+=
1
# 优化进度更新频率(减少processEvents调用)
if
progress_update_counter
>=
progress_update_interval
:
progress_update_counter
=
0
# 更新进度(基于实际处理的帧数)
processed_frames
=
frame_count
-
start_frame_limit
...
...
@@ -2336,6 +2395,10 @@ class DataPreprocessHandler(QtCore.QObject):
progress_dialog
.
setValue
(
progress
)
self
.
cropProgress
.
emit
(
progress
)
# 强制刷新预览面板(确保实时更新)
if
self
.
_crop_preview_handler
is
not
None
:
self
.
_crop_preview_handler
.
forceRefresh
()
# 处理事件,保持界面响应
QtWidgets
.
QApplication
.
processEvents
()
...
...
@@ -2345,6 +2408,15 @@ class DataPreprocessHandler(QtCore.QObject):
# 完成
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
self
.
_video_crop_mapping
[
video_path
]
=
{
...
...
labelme/app.py
View file @
c94177be
...
...
@@ -34,6 +34,14 @@ from .widgets import ToolBar
from
.widgets
import
UniqueLabelQListWidget
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
# - [medium] Set max zoom value to something big enough for FitWidth/Window
...
...
@@ -131,11 +139,12 @@ class MainWindow(QtWidgets.QMainWindow):
self
.
shape_dock
.
setWidget
(
self
.
labelList
)
self
.
uniqLabelList
=
UniqueLabelQListWidget
()
self
.
uniqLabelList
.
setToolTip
(
self
.
tr
(
"点击选择标签
\n
或按 'Esc' 键取消"
)
)
# 隐藏提示框
# self.uniqLabelList.setToolTip(
# self.tr(
# "点击选择标签\n或按 'Esc' 键取消"
# )
# )
if
self
.
_config
[
"labels"
]:
for
label
in
self
.
_config
[
"labels"
]:
item
=
self
.
uniqLabelList
.
createItemFromLabel
(
label
)
...
...
@@ -725,8 +734,8 @@ class MainWindow(QtWidgets.QMainWindow):
utils
.
addActions
(
self
.
canvas
.
menus
[
1
],
(
action
(
"
&Copy here
"
,
self
.
copyShape
),
action
(
"
&Move here
"
,
self
.
moveShape
),
action
(
"
复制到此处(&C)
"
,
self
.
copyShape
),
action
(
"
移动到此处(&M)
"
,
self
.
moveShape
),
),
)
...
...
@@ -1884,12 +1893,25 @@ class MainWindow(QtWidgets.QMainWindow):
return
label_file
def
deleteFile
(
self
):
mb
=
QtWidgets
.
QMessageBox
msg
=
self
.
tr
(
""
""
)
answer
=
mb
.
warning
(
self
,
self
.
tr
(
""
),
msg
,
mb
.
Yes
|
mb
.
No
)
# Use DialogManager if available
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
...
...
@@ -1923,13 +1945,45 @@ class MainWindow(QtWidgets.QMainWindow):
def
mayContinue
(
self
):
if
not
self
.
dirty
:
return
True
mb
=
QtWidgets
.
QMessageBox
msg
=
self
.
tr
(
'"{}"'
)
.
format
(
self
.
filename
)
# Use DialogManager if available
if
DialogManager
:
# Create custom dialog with Save/Discard/Cancel buttons
msg_box
=
QtWidgets
.
QMessageBox
(
self
)
msg_box
.
setWindowTitle
(
"提示"
)
msg_box
.
setText
(
msg
)
msg_box
.
setIcon
(
QtWidgets
.
QMessageBox
.
Question
)
# Add custom buttons with Chinese text
save_btn
=
msg_box
.
addButton
(
"保存"
,
QtWidgets
.
QMessageBox
.
AcceptRole
)
discard_btn
=
msg_box
.
addButton
(
"不保存"
,
QtWidgets
.
QMessageBox
.
DestructiveRole
)
cancel_btn
=
msg_box
.
addButton
(
"取消"
,
QtWidgets
.
QMessageBox
.
RejectRole
)
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
,
self
.
tr
(
""
)
,
"提示"
,
msg
,
mb
.
Save
|
mb
.
Discard
|
mb
.
Cancel
,
mb
.
Save
,
...
...
@@ -1943,9 +1997,13 @@ class MainWindow(QtWidgets.QMainWindow):
return
False
def
errorMessage
(
self
,
title
,
message
):
return
QtWidgets
.
QMessageBox
.
critical
(
self
,
title
,
"<p><b>
%
s</b></p>
%
s"
%
(
title
,
message
)
)
formatted_message
=
"<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
):
return
osp
.
dirname
(
str
(
self
.
filename
))
if
self
.
filename
else
"."
...
...
@@ -1965,13 +2023,28 @@ class MainWindow(QtWidgets.QMainWindow):
self
.
setDirty
()
def
deleteSelectedShape
(
self
):
msg
=
"确定删除该标签?"
# Use DialogManager if available, otherwise fallback to QMessageBox
if
DialogManager
:
result
=
DialogManager
.
show_question_warning
(
self
,
"警告"
,
msg
,
yes_text
=
"是"
,
no_text
=
"否"
,
text_alignment
=
DialogManager
.
ALIGN_CENTER
)
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
msg
=
self
.
tr
(
" {} "
""
)
.
format
(
len
(
self
.
canvas
.
selectedShapes
))
if
yes
==
QtWidgets
.
QMessageBox
.
warning
(
self
,
self
.
tr
(
""
)
,
msg
,
yes
|
no
,
yes
self
,
"警告"
,
msg
,
yes
|
no
,
yes
):
self
.
remLabels
(
self
.
canvas
.
deleteSelected
())
self
.
setDirty
()
...
...
@@ -1994,10 +2067,16 @@ class MainWindow(QtWidgets.QMainWindow):
if
not
self
.
mayContinue
():
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
):
defaultOpenDirPath
=
self
.
lastOpenDir
else
:
# 优先使用 database\Corp_picture,如果不存在则使用当前文件目录或当前工作目录
if
osp
.
exists
(
default_dir
):
defaultOpenDirPath
=
default_dir
else
:
defaultOpenDirPath
=
(
osp
.
dirname
(
self
.
filename
)
if
self
.
filename
else
"."
)
...
...
labelme/widgets/brightness_contrast_dialog.py
View file @
c94177be
import
PIL.Image
import
PIL.ImageEnhance
import
sys
import
os
from
qtpy.QtCore
import
Qt
from
qtpy
import
QtGui
from
qtpy
import
QtWidgets
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
):
def
__init__
(
self
,
img
,
callback
,
parent
=
None
):
...
...
@@ -21,6 +34,10 @@ class BrightnessContrastDialog(QtWidgets.QDialog):
formLayout
.
addRow
(
self
.
tr
(
""
),
self
.
slider_contrast
)
self
.
setLayout
(
formLayout
)
# 应用全局字体样式
if
STYLE_MANAGER_AVAILABLE
:
FontManager
.
applyToWidgetRecursive
(
self
)
assert
isinstance
(
img
,
PIL
.
Image
.
Image
)
self
.
img
=
img
self
.
callback
=
callback
...
...
labelme/widgets/canvas.py
View file @
c94177be
...
...
@@ -6,6 +6,12 @@ from labelme import QT5
from
labelme.shape
import
Shape
import
labelme.utils
# 导入标签显示名称映射
try
:
from
labelme.widgets.label_dialog
import
LABEL_DISPLAY_NAMES
except
ImportError
:
LABEL_DISPLAY_NAMES
=
{}
# TODO(unknown):
# - [maybe] Find optimal epsilon value.
...
...
@@ -286,7 +292,7 @@ class Canvas(QtWidgets.QWidget):
# - Highlight shapes
# - Highlight vertex
# 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
)]):
# Look for a nearby vertex to highlight. If that fails,
# check if we happen to be inside a shape.
...
...
@@ -301,7 +307,7 @@ class Canvas(QtWidgets.QWidget):
self
.
hEdge
=
None
shape
.
highlightVertex
(
index
,
shape
.
MOVE_VERTEX
)
self
.
overrideCursor
(
CURSOR_POINT
)
self
.
setToolTip
(
self
.
tr
(
"
Click & drag to move point
"
))
self
.
setToolTip
(
self
.
tr
(
"
点击并拖动以移动点
"
))
self
.
setStatusTip
(
self
.
toolTip
())
self
.
update
()
break
...
...
@@ -313,7 +319,7 @@ class Canvas(QtWidgets.QWidget):
self
.
prevhShape
=
self
.
hShape
=
shape
self
.
prevhEdge
=
self
.
hEdge
=
index_edge
self
.
overrideCursor
(
CURSOR_POINT
)
self
.
setToolTip
(
self
.
tr
(
"
Click to create point
"
))
self
.
setToolTip
(
self
.
tr
(
"
点击以创建点
"
))
self
.
setStatusTip
(
self
.
toolTip
())
self
.
update
()
break
...
...
@@ -325,8 +331,10 @@ class Canvas(QtWidgets.QWidget):
self
.
prevhShape
=
self
.
hShape
=
shape
self
.
prevhEdge
=
self
.
hEdge
self
.
hEdge
=
None
# 将英文标签转换为中文显示名称
display_label
=
LABEL_DISPLAY_NAMES
.
get
(
shape
.
label
,
shape
.
label
)
self
.
setToolTip
(
self
.
tr
(
"
Click & drag to move shape '
%
s'"
)
%
shape
.
label
self
.
tr
(
"
点击并拖动以移动形状 '
%
s'"
)
%
display_
label
)
self
.
setStatusTip
(
self
.
toolTip
())
self
.
overrideCursor
(
CURSOR_GRAB
)
...
...
labelme/widgets/color_dialog.py
View file @
c94177be
import
sys
import
os
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
):
def
__init__
(
self
,
parent
=
None
):
...
...
@@ -15,6 +28,10 @@ class ColorDialog(QtWidgets.QColorDialog):
self
.
bb
.
addButton
(
QtWidgets
.
QDialogButtonBox
.
RestoreDefaults
)
self
.
bb
.
clicked
.
connect
(
self
.
checkRestore
)
# 应用全局字体样式
if
STYLE_MANAGER_AVAILABLE
:
FontManager
.
applyToWidgetRecursive
(
self
)
def
getColor
(
self
,
value
=
None
,
title
=
None
,
default
=
None
):
self
.
default
=
default
if
title
:
...
...
labelme/widgets/file_dialog_preview.py
View file @
c94177be
...
...
@@ -3,6 +3,19 @@ from qtpy import QtGui
from
qtpy
import
QtWidgets
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
):
...
...
@@ -55,6 +68,10 @@ class FileDialogPreview(QtWidgets.QFileDialog):
self
.
layout
()
.
addLayout
(
box
,
1
,
3
,
1
,
1
)
self
.
currentChanged
.
connect
(
self
.
onChange
)
# 应用全局字体样式
if
STYLE_MANAGER_AVAILABLE
:
FontManager
.
applyToWidgetRecursive
(
self
)
def
onChange
(
self
,
path
):
if
path
.
lower
()
.
endswith
(
".json"
):
with
open
(
path
,
"r"
)
as
f
:
...
...
labelme/widgets/label_dialog.py
View file @
c94177be
import
re
import
sys
import
os
from
qtpy
import
QT_VERSION
from
qtpy
import
QtCore
...
...
@@ -8,6 +10,20 @@ from qtpy import QtWidgets
from
labelme.logger
import
logger
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"
...
...
@@ -16,9 +32,9 @@ FIXED_LABELS = ["liquid", "air", "foam"]
# - UI
LABEL_DISPLAY_NAMES
=
{
"liquid"
:
""
,
"air"
:
""
,
"foam"
:
""
"liquid"
:
"
液体
"
,
"air"
:
"
空气
"
,
"foam"
:
"
泡沫
"
}
# -
...
...
@@ -66,7 +82,9 @@ class LabelDialog(QtWidgets.QDialog):
self
.
_fit_to_content
=
fit_to_content
super
(
LabelDialog
,
self
)
.
__init__
(
parent
)
self
.
setWindowTitle
(
""
)
self
.
setWindowTitle
(
"请选择标签"
)
# 隐藏右上角的帮助按钮(?)
self
.
setWindowFlags
(
self
.
windowFlags
()
&
~
QtCore
.
Qt
.
WindowContextHelpButtonHint
)
#
# self.edit self.edit_group_id
...
...
@@ -100,6 +118,10 @@ class LabelDialog(QtWidgets.QDialog):
self
.
labelList
.
setFixedHeight
(
150
)
# self.edit.setListWidgetself.edit
layout
.
addWidget
(
self
.
labelList
)
# 应用全局字体样式到列表控件
if
STYLE_MANAGER_AVAILABLE
:
FontManager
.
applyToWidget
(
self
.
labelList
)
# label_flags
if
flags
is
None
:
flags
=
{}
...
...
@@ -111,6 +133,10 @@ class LabelDialog(QtWidgets.QDialog):
#
# self.editDescription
self
.
setLayout
(
layout
)
# 应用全局字体样式到整个对话框
if
STYLE_MANAGER_AVAILABLE
:
FontManager
.
applyToWidgetRecursive
(
self
)
# completion
completer
=
QtWidgets
.
QCompleter
()
if
not
QT5
and
completion
!=
"startswith"
:
...
...
@@ -154,11 +180,10 @@ class LabelDialog(QtWidgets.QDialog):
current_item
=
self
.
labelList
.
currentItem
()
if
not
current_item
:
logger
.
warning
(
""
)
QtWidgets
.
QMessageBox
.
warning
(
self
,
""
,
""
)
if
DialogManager
:
DialogManager
.
show_warning
(
self
,
""
,
""
)
else
:
QtWidgets
.
QMessageBox
.
warning
(
self
,
""
,
""
)
return
#
...
...
@@ -168,11 +193,11 @@ class LabelDialog(QtWidgets.QDialog):
#
if
text
not
in
FIXED_LABELS
:
logger
.
warning
(
f
" '{text}' : {', '.join(FIXED_LABELS)}"
)
QtWidgets
.
QMessageBox
.
warning
(
self
,
""
,
f
" '{text}'
\n\n
:
\n
"
+
"
\n
"
.
join
(
FIXED_LABELS
)
)
msg
=
f
" '{text}'
\n\n
:
\n
"
+
"
\n
"
.
join
(
FIXED_LABELS
)
if
DialogManager
:
DialogManager
.
show_warning
(
self
,
""
,
msg
)
else
:
QtWidgets
.
QMessageBox
.
warning
(
self
,
""
,
msg
)
return
if
text
:
...
...
widgets/datasetpage/annotationtool.py
View file @
c94177be
...
...
@@ -61,6 +61,25 @@ except ImportError as e:
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
):
"""
数据标注工具组件
...
...
@@ -181,6 +200,10 @@ class AnnotationTool(QtWidgets.QWidget):
self
.
annotation_list
.
setAlternatingRowColors
(
True
)
self
.
annotation_list
.
setIconSize
(
QtCore
.
QSize
(
80
,
80
))
self
.
annotation_list
.
setSpacing
(
5
)
# 设置自定义委托以保持选中状态下的文字颜色
self
.
annotation_list
.
setItemDelegate
(
ColorPreservingDelegate
(
self
.
annotation_list
))
self
.
annotation_list
.
setStyleSheet
(
"""
QListWidget {
border: 1px solid #c0c0c0;
...
...
@@ -192,8 +215,7 @@ class AnnotationTool(QtWidgets.QWidget):
border-bottom: 1px solid #e0e0e0;
}
QListWidget::item:selected {
background-color: #0078d7;
color: white;
background-color: #d0d0d0;
}
QListWidget::item:hover {
background-color: #e0e0e0;
...
...
@@ -552,8 +574,11 @@ class AnnotationTool(QtWidgets.QWidget):
def
onDirectoryChanged
(
self
,
dir_path
):
"""目录变化时的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
):
"""文件列表更新时的UI更新(响应handler信号)"""
...
...
@@ -634,11 +659,28 @@ class AnnotationTool(QtWidgets.QWidget):
"""列表项点击事件"""
data
=
item
.
data
(
Qt
.
UserRole
)
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'
]
self
.
loadImageForAnnotation
(
image_path
)
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
()
if
not
items
:
return
...
...
widgets/datasetpage/crop_config_dialog.py
View file @
c94177be
...
...
@@ -41,6 +41,10 @@ except Exception:
DEFAULT_CROP_SAVE_DIR
=
osp
.
join
(
get_project_root
(),
'database'
,
'Corp_picture'
)
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
):
"""
...
...
@@ -62,8 +66,11 @@ class CropConfigDialog(QtWidgets.QDialog):
"""
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
.
_file_prefix
=
"frame"
self
.
_image_format
=
"jpg"
...
...
@@ -321,10 +328,19 @@ class CropConfigDialog(QtWidgets.QDialog):
try
:
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
self
.
path_edit
.
setText
(
saved_path
)
# 【强制修改】清除旧的保存路径设置,不再记住保存路径
# 检查是否有旧设置
old_path
=
settings
.
value
(
"save_liquid_data_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
)
try
:
...
...
@@ -347,7 +363,8 @@ class CropConfigDialog(QtWidgets.QDialog):
"""保存当前设置"""
try
:
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
(
"file_prefix"
,
self
.
prefix_edit
.
text
())
settings
.
setValue
(
"image_format"
,
self
.
format_combo
.
currentText
())
...
...
@@ -414,12 +431,16 @@ class CropConfigDialog(QtWidgets.QDialog):
- file_prefix: 文件名前缀
- 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
(),
'file_prefix'
:
self
.
prefix_edit
.
text
()
.
strip
(),
'image_format'
:
self
.
format_combo
.
currentText
()
}
print
(
f
"[CropConfigDialog] getConfig返回的保存路径: {config['save_liquid_data_path']}"
)
return
config
def
setConfig
(
self
,
config
):
"""
...
...
widgets/datasetpage/crop_preview_panel.py
View file @
c94177be
...
...
@@ -281,7 +281,7 @@ class CropPreviewPanel(QtWidgets.QWidget):
self
.
refreshImages
()
def
_findRegionPaths
(
self
):
"""查找所有区域文件夹(支持新旧命名格式,可根据视频名称过滤)"""
"""查找所有区域文件夹(支持新旧命名格式,可根据视频名
称过滤)"""
self
.
_region_paths
=
[]
if
not
self
.
_save_liquid_data_path
or
not
osp
.
exists
(
self
.
_save_liquid_data_path
):
...
...
@@ -342,9 +342,19 @@ class CropPreviewPanel(QtWidgets.QWidget):
if
not
self
.
_save_liquid_data_path
:
return
# 保存当前的视频名称(clearImages会清空它)
current_video_name
=
self
.
_video_name
# 如果没有视频名称上下文,说明当前没有选中有效的裁剪视频,不应该刷新
if
not
current_video_name
:
return
# 先清空所有图片
self
.
clearImages
()
# 恢复视频名称
self
.
_video_name
=
current_video_name
# 重新查找区域文件夹
self
.
_findRegionPaths
()
...
...
@@ -485,6 +495,7 @@ class CropPreviewPanel(QtWidgets.QWidget):
# 清空缓存数据
self
.
_region_images
.
clear
()
self
.
_region_paths
=
[]
self
.
_video_name
=
None
# 清空视频名称,防止刷新时显示其他视频的图片
# 更新统计
self
.
_updateStats
()
...
...
@@ -600,16 +611,16 @@ class CropPreviewPanel(QtWidgets.QWidget):
reply
=
DialogManager
.
show_question_warning
(
self
,
"确认删除"
,
f
"确定要删除区域 {current_region + 1} 的所有文件吗?
\n
\n
"
f
"当前区域共有 {image_count} 张图片
\n\n
"
f
"确定要删除区域 {current_region + 1} 的所有文件吗?
\n
"
f
"当前区域共有 {image_count} 张图片"
f
"文件夹将被移动到回收站"
)
else
:
reply
=
QtWidgets
.
QMessageBox
.
question
(
self
,
"确认删除"
,
f
"确定要删除区域 {current_region + 1} 的所有文件吗?
\n
\n
"
f
"当前区域共有 {image_count} 张图片
\n\n
"
f
"确定要删除区域 {current_region + 1} 的所有文件吗?
\n
"
f
"当前区域共有 {image_count} 张图片"
f
"文件夹将被移动到回收站"
,
QtWidgets
.
QMessageBox
.
Yes
|
QtWidgets
.
QMessageBox
.
No
,
QtWidgets
.
QMessageBox
.
No
...
...
widgets/datasetpage/datacollection_panel.py
View file @
c94177be
...
...
@@ -167,7 +167,7 @@ class DataCollectionPanel(QtWidgets.QWidget):
# 标题栏
title_layout
=
QtWidgets
.
QHBoxLayout
()
title_label
=
QtWidgets
.
QLabel
(
"
目录
"
)
title_label
=
QtWidgets
.
QLabel
(
"
数据采集
"
)
title_label
.
setStyleSheet
(
"font-size: 12pt; font-weight: bold;"
)
title_layout
.
addWidget
(
title_label
)
...
...
@@ -833,12 +833,25 @@ class DataCollectionPanel(QtWidgets.QWidget):
file_name_no_ext
,
file_ext
=
osp
.
splitext
(
file_name
)
# 弹出输入对话框
new_name
,
ok
=
QtWidgets
.
QInputDialog
.
getText
(
self
,
"重命名文件"
,
"请输入新的文件名(不含扩展名):"
,
QtWidgets
.
QLineEdit
.
Normal
,
file_name_no_ext
)
dialog
=
QtWidgets
.
QInputDialog
(
self
)
dialog
.
setWindowTitle
(
"重命名文件"
)
dialog
.
setLabelText
(
"请输入新的文件名(不含扩展名):"
)
dialog
.
setTextValue
(
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
():
return
...
...
@@ -990,6 +1003,14 @@ class DataCollectionPanel(QtWidgets.QWidget):
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
)
cancel_btn
.
clicked
.
connect
(
dialog
.
reject
)
...
...
@@ -1058,9 +1079,9 @@ class DataCollectionPanel(QtWidgets.QWidget):
if
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
:
message
=
f
"确定要删除文件夹
'{folder_name}' 吗?
\n\n
文件夹为空
\n\n
将被移动到回收站
"
message
=
f
"确定要删除文件夹
“{folder_name}”吗?文件夹为空,将被移动到回收站。
"
# 确认删除
if
self
.
_showQuestionWarning
(
"确认删除"
,
message
):
...
...
@@ -1383,10 +1404,23 @@ def _getSelectedChannel(self):
if
current_data
==
"custom"
:
# 自定义RTSP地址
rtsp_url
,
ok
=
QtWidgets
.
QInputDialog
.
getText
(
self
,
"自定义RTSP地址"
,
"请输入RTSP地址:
\n
(格式: rtsp://username:password@ip:port/path)"
)
dialog
=
QtWidgets
.
QInputDialog
(
self
)
dialog
.
setWindowTitle
(
"自定义RTSP地址"
)
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
():
return
rtsp_url
.
strip
()
else
:
...
...
widgets/datasetpage/datapreprocess_panel.py
View file @
c94177be
...
...
@@ -172,7 +172,7 @@ class DataPreprocessPanel(QtWidgets.QWidget):
# 标题栏
title_layout
=
QtWidgets
.
QHBoxLayout
()
title_label
=
QtWidgets
.
QLabel
(
"
目录
"
)
title_label
=
QtWidgets
.
QLabel
(
"
数据预处理
"
)
title_label
.
setStyleSheet
(
"font-size: 12pt; font-weight: bold;"
)
title_layout
.
addWidget
(
title_label
)
...
...
@@ -615,12 +615,17 @@ class DataPreprocessPanel(QtWidgets.QWidget):
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
(),
'file_prefix'
:
self
.
crop_prefix_edit
.
text
()
.
strip
(),
'image_format'
:
self
.
crop_format_combo
.
currentText
()
}
print
(
f
"[DataPreprocessPanel] getCropConfig返回的保存路径: {config['save_liquid_data_path']}"
)
return
config
def
_createCropPreviewPanel
(
self
):
"""创建右侧裁剪图片预览面板"""
...
...
@@ -1124,6 +1129,7 @@ class DataPreprocessPanel(QtWidgets.QWidget):
# 创建右键菜单
menu
=
QtWidgets
.
QMenu
(
self
)
# 只在空白处点击时显示刷新菜单
if
not
item
:
# 在空白处点击,显示刷新菜单
action_refresh
=
menu
.
addAction
(
newIcon
(
"刷新"
),
"刷新"
)
...
...
@@ -1134,26 +1140,8 @@ class DataPreprocessPanel(QtWidgets.QWidget):
# 处理刷新动作
if
action
==
action_refresh
:
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
):
"""重命名视频文件"""
...
...
widgets/style_manager.py
View file @
c94177be
...
...
@@ -275,6 +275,10 @@ class DialogManager:
QMessageBox {
min-width: 400px;
}
QMessageBox QLabel {
border: none;
background: transparent;
}
"""
# 文本对齐方式常量
...
...
@@ -323,6 +327,9 @@ class DialogManager:
# 🔥 根据文本内容自动调整对话框大小
DialogManager
.
_adjust_dialog_size
(
msg_box
,
message
)
# 🔥 应用统一按钮样式到对话框的所有按钮
DialogManager
.
_apply_button_styles
(
msg_box
)
return
msg_box
@staticmethod
...
...
@@ -339,6 +346,9 @@ class DialogManager:
# 只设置消息文本标签的对齐方式,不影响其他标签
if
label
.
text
()
and
not
label
.
pixmap
():
label
.
setAlignment
(
alignment
)
# 移除任何边框样式
label
.
setFrameStyle
(
QtWidgets
.
QFrame
.
NoFrame
)
label
.
setStyleSheet
(
"border: none; background: transparent;"
)
except
Exception
as
e
:
pass
...
...
@@ -382,6 +392,24 @@ class DialogManager:
pass
@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
):
"""设置按钮为中文文本"""
# 默认中文按钮文本映射
...
...
@@ -703,6 +731,25 @@ class DialogManager:
"""设置默认样式"""
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
# 对话框管理器便捷函数
def
show_warning
(
parent
,
title
,
message
):
...
...
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