Commit e8ce54c3 by Yuhaibo

1

parent 34bd34cc
......@@ -777,7 +777,7 @@ class MainWindow(
# 保存第一个面板的引用(兼容现有代码)
self.channelPanel = self.channelPanels[0] if self.channelPanels else None
# 🔥 创建4个历史视频面板(用于曲线模式的历史回放布局)
# 🔥 创建4个历史视频面板(用于曲线模式布局的历史回放子布局)
try:
from .widgets.videopage import HistoryVideoPanel
except ImportError:
......@@ -804,8 +804,8 @@ class MainWindow(
"""创建曲线模式布局:左侧垂直排列通道 + 右侧曲线面板
包含两个子布局:
- 子布局0:实时检测模式(带任务选择和底部按钮)
- 子布局1:历史回放模式(无任务选择和底部按钮)
- 子布局0:同步布局(带任务选择和底部按钮)
- 子布局1:历史回放布局(无任务选择和底部按钮)
🔥 两个子布局共用同一个CurvePanel(右侧)
"""
......@@ -836,12 +836,12 @@ class MainWindow(
# 🔥 创建左侧子布局栈(实时检测 vs 历史回放)
self.curveLayoutStack = QtWidgets.QStackedWidget()
self.curveLayoutStack.setFixedWidth(660)
self._curve_sub_layout_mode = 0 # 0=实时检测, 1=历史回放
self._curve_sub_layout_mode = 0 # 0=同步布局, 1=历史回放布局
# === 子布局0:实时检测模式(左侧通道列表)===
# === 子布局0:同步布局(左侧通道列表)===
self._createRealtimeCurveSubLayout()
# === 子布局1:历史回放模式(左侧历史视频面板容器)===
# === 子布局1:历史回放布局(左侧历史视频面板容器)===
self._createHistoryCurveSubLayout()
# 布局结构:左侧子布局栈 + 右侧共用CurvePanel
......@@ -873,7 +873,7 @@ class MainWindow(
self.curve_channel_layout.setContentsMargins(5, 5, 5, 5)
self.curve_channel_layout.setSpacing(10)
# 初始化通道包裹容器列表(实时检测模式
# 初始化通道包裹容器列表(同步布局
self.channel_widgets_for_curve = []
# 创建4个通道容器(初始隐藏,等待CSV文件加载)
......@@ -916,7 +916,7 @@ class MainWindow(
self.history_channel_layout.setContentsMargins(5, 5, 5, 5)
self.history_channel_layout.setSpacing(10)
# 初始化历史视频包裹容器列表(历史回放模式
# 初始化历史视频包裹容器列表(历史回放布局
self.history_channel_widgets_for_curve = []
# 创建4个历史视频容器(初始隐藏,等待CSV文件加载)
......@@ -965,10 +965,10 @@ class MainWindow(
# 🔥 根据检测状态决定显示逻辑(而不是依赖 getCurveLoadMode)
if detection_running:
# 🔥 实时检测模式:只显示任务配置中使用的通道
# 🔥 同步布局:只显示任务配置中使用的通道
selected_channels = self._getTaskChannels(mission_name)
else:
# 🔥 历史回放模式:显示所有通道容器
# 🔥 历史回放布局:显示所有通道容器
selected_channels = ['通道1', '通道2', '通道3', '通道4']
self._updateCurveChannelDisplay(selected_channels)
......@@ -1087,11 +1087,11 @@ class MainWindow(
# 🔥 根据当前曲线子布局模式选择要操作的容器
if hasattr(self, '_curve_sub_layout_mode'):
if self._curve_sub_layout_mode == 0:
# 实时检测模式:操作channel_widgets_for_curve
# 同步布局:操作channel_widgets_for_curve
target_widgets = self.channel_widgets_for_curve if hasattr(self, 'channel_widgets_for_curve') else []
target_container = self.curve_channel_container if hasattr(self, 'curve_channel_container') else None
else:
# 历史回放模式:操作history_channel_widgets_for_curve
# 历史回放布局:操作history_channel_widgets_for_curve
target_widgets = self.history_channel_widgets_for_curve if hasattr(self, 'history_channel_widgets_for_curve') else []
target_container = self.history_channel_container if hasattr(self, 'history_channel_container') else None
else:
......@@ -1131,7 +1131,6 @@ class MainWindow(
if target_container:
target_container.setFixedSize(640, total_height)
print(f"[曲线布局] 已更新通道显示,显示 {visible_count} 个通道")
def _createModelPage(self):
"""创建模型管理页面"""
......
......@@ -5,14 +5,14 @@ channel1:
height: 20mm
name: 通道1_区域1
boxes:
- - 881
- 799
- - 855
- 737
- 192
fixed_bottoms:
- 875
- 813
fixed_tops:
- 722
last_updated: '2025-11-29 16:02:16'
- 660
last_updated: '2025-11-29 17:01:13'
channel2:
annotation_count: 1
areas:
......
......@@ -17,23 +17,23 @@ channels:
name: '4'
channel2:
general:
task_id: '21'
task_name: '321'
save_liquid_data_path: d:\restructure\liquid_level_line_detection_system\database\mission_result\21_321
task_id: '123'
task_name: '21'
save_liquid_data_path: d:\restructure\liquid_level_line_detection_system\database\mission_result\123_21
channel3:
general:
task_id: '21'
task_name: '321'
save_liquid_data_path: d:\restructure\liquid_level_line_detection_system\database\mission_result\21_321
task_id: '123'
task_name: '21'
save_liquid_data_path: d:\restructure\liquid_level_line_detection_system\database\mission_result\123_21
channel4:
general:
task_id: '21'
task_name: '321'
save_liquid_data_path: d:\restructure\liquid_level_line_detection_system\database\mission_result\21_321
task_id: '1'
task_name: '222'
save_liquid_data_path: d:\restructure\liquid_level_line_detection_system\database\mission_result\1_222
channel1:
general:
task_id: '21'
task_name: '321'
task_id: '1'
task_name: '222'
area_count: 0
safe_low: 2.0mm
safe_high: 10.0mm
......@@ -41,7 +41,7 @@ channel1:
video_format: AVI
push_address: ''
video_path: ''
save_liquid_data_path: d:\restructure\liquid_level_line_detection_system\database\mission_result\21_321
save_liquid_data_path: d:\restructure\liquid_level_line_detection_system\database\mission_result\1_222
areas:
area_1: 通道1_区域1
area_heights:
......
2025-11-29-13:26:33.445 0.0
2025-11-29-13:26:33.445 0.0
......@@ -440,3 +440,39 @@
2025-11-29-13:28:03.293 16.6
2025-11-29-13:28:03.550 17.1
2025-11-29-13:28:03.718 16.3
2025-11-29-16:41:31.498 0.0
2025-11-29-16:41:31.526 0.0
2025-11-29-16:41:31.858 0.0
2025-11-29-16:41:31.933 0.0
2025-11-29-16:41:32.136 0.0
2025-11-29-16:41:32.334 0.0
2025-11-29-16:41:32.548 0.0
2025-11-29-16:41:32.746 0.0
2025-11-29-16:41:32.943 0.0
2025-11-29-16:41:33.147 0.0
2025-11-29-16:41:33.348 0.0
2025-11-29-16:41:33.540 0.0
2025-11-29-16:41:33.750 0.0
2025-11-29-16:41:33.953 0.0
2025-11-29-16:41:34.146 0.0
2025-11-29-16:41:34.361 0.0
2025-11-29-16:41:34.554 0.0
2025-11-29-17:26:08.241 0.0
2025-11-29-17:26:08.278 0.0
2025-11-29-17:26:08.481 0.0
2025-11-29-17:26:08.684 0.0
2025-11-29-17:26:08.883 0.0
2025-11-29-17:26:09.085 0.0
2025-11-29-17:26:09.287 0.0
2025-11-29-17:26:09.484 0.0
2025-11-29-17:26:09.679 0.0
2025-11-29-17:26:09.998 0.0
2025-11-29-17:26:10.233 0.0
2025-11-29-17:26:10.298 0.0
2025-11-29-17:26:10.504 0.0
2025-11-29-17:26:10.708 0.0
2025-11-29-17:26:10.890 0.0
2025-11-29-17:26:11.101 0.0
2025-11-29-17:26:11.295 0.0
2025-11-29-17:26:11.496 0.0
2025-11-29-17:26:11.704 0.0
2025-11-29-11:16:22.428 0.0
2025-11-29-11:16:22.428 0.0
......@@ -1710,3 +1710,165 @@
2025-11-29-13:51:30.788 0.0
2025-11-29-13:51:30.985 0.0
2025-11-29-13:51:31.195 0.0
2025-11-29-16:14:35.854 0.0
2025-11-29-16:14:35.891 0.0
2025-11-29-16:14:36.123 0.0
2025-11-29-16:14:36.313 0.0
2025-11-29-16:14:36.518 0.0
2025-11-29-16:14:36.735 0.0
2025-11-29-16:14:36.956 0.0
2025-11-29-16:14:37.137 0.0
2025-11-29-16:14:37.337 0.0
2025-11-29-16:14:37.552 0.0
2025-11-29-16:14:37.765 0.0
2025-11-29-16:14:37.957 0.0
2025-11-29-16:14:38.163 0.0
2025-11-29-16:14:38.369 0.0
2025-11-29-16:14:38.577 0.0
2025-11-29-16:14:38.790 0.0
2025-11-29-16:14:38.979 0.0
2025-11-29-16:14:39.181 0.0
2025-11-29-16:14:39.378 0.0
2025-11-29-16:14:39.609 0.0
2025-11-29-16:14:39.808 0.0
2025-11-29-16:14:40.003 0.0
2025-11-29-16:14:40.221 0.0
2025-11-29-16:14:40.496 18.2
2025-11-29-16:14:40.626 0.0
2025-11-29-16:14:40.828 0.0
2025-11-29-16:14:41.026 0.0
2025-11-29-16:14:41.228 0.0
2025-11-29-16:14:41.427 0.0
2025-11-29-16:14:41.628 0.0
2025-11-29-16:14:41.837 0.0
2025-11-29-16:14:42.036 0.0
2025-11-29-16:14:42.241 0.0
2025-11-29-16:14:42.443 0.0
2025-11-29-16:14:42.661 0.0
2025-11-29-16:14:42.874 0.0
2025-11-29-16:14:43.076 0.0
2025-11-29-16:14:43.290 0.0
2025-11-29-16:14:43.491 0.0
2025-11-29-16:14:43.717 0.0
2025-11-29-16:14:43.908 0.0
2025-11-29-16:14:44.111 0.0
2025-11-29-16:14:44.336 0.0
2025-11-29-16:14:44.505 0.0
2025-11-29-16:14:44.719 0.0
2025-11-29-16:14:44.919 0.0
2025-11-29-16:14:45.180 0.0
2025-11-29-16:14:45.309 0.0
2025-11-29-16:14:45.519 0.0
2025-11-29-16:14:45.715 0.0
2025-11-29-16:14:45.929 0.0
2025-11-29-16:14:46.148 0.0
2025-11-29-16:14:46.335 0.0
2025-11-29-16:14:46.525 0.0
2025-11-29-16:14:46.724 0.0
2025-11-29-16:14:46.925 0.0
2025-11-29-16:14:47.116 0.0
2025-11-29-16:14:47.330 0.0
2025-11-29-16:14:47.540 0.0
2025-11-29-16:14:47.724 0.0
2025-11-29-16:14:47.929 0.0
2025-11-29-16:14:48.143 0.0
2025-11-29-16:14:48.346 0.0
2025-11-29-16:14:48.533 0.0
2025-11-29-16:14:48.744 0.0
2025-11-29-16:14:48.950 0.0
2025-11-29-16:14:49.159 0.0
2025-11-29-16:14:49.352 0.0
2025-11-29-16:14:49.552 0.0
2025-11-29-16:14:49.746 0.0
2025-11-29-16:14:49.933 0.0
2025-11-29-16:14:50.105 0.0
2025-11-29-16:14:50.316 0.0
2025-11-29-16:14:50.519 0.0
2025-11-29-16:14:50.722 0.0
2025-11-29-16:14:50.924 0.0
2025-11-29-16:14:51.137 0.0
2025-11-29-16:14:51.346 0.0
2025-11-29-16:14:51.538 0.0
2025-11-29-16:14:51.738 0.0
2025-11-29-16:14:51.943 0.0
2025-11-29-16:14:52.147 0.0
2025-11-29-16:14:52.364 0.0
2025-11-29-16:14:52.573 0.0
2025-11-29-16:14:52.771 0.0
2025-11-29-16:14:52.980 0.0
2025-11-29-16:14:53.182 0.0
2025-11-29-16:14:53.395 0.0
2025-11-29-16:14:53.595 0.0
2025-11-29-16:14:53.791 0.0
2025-11-29-16:14:53.999 0.0
2025-11-29-16:14:54.207 0.0
2025-11-29-16:14:54.415 0.0
2025-11-29-16:14:54.628 0.0
2025-11-29-16:14:54.850 0.0
2025-11-29-16:14:55.027 0.0
2025-11-29-16:14:55.234 0.0
2025-11-29-16:14:55.427 0.0
2025-11-29-16:14:55.641 0.0
2025-11-29-16:14:55.851 0.0
2025-11-29-16:14:56.033 0.0
2025-11-29-16:14:56.244 0.0
2025-11-29-16:14:56.445 0.0
2025-11-29-16:14:56.647 0.0
2025-11-29-16:14:56.851 0.0
2025-11-29-16:14:57.049 0.0
2025-11-29-16:14:57.254 0.0
2025-11-29-16:14:57.453 0.0
2025-11-29-16:14:57.655 0.0
2025-11-29-16:14:57.855 0.0
2025-11-29-16:14:58.054 0.0
2025-11-29-16:14:58.262 0.0
2025-11-29-16:14:58.458 0.0
2025-11-29-16:14:58.661 0.0
2025-11-29-16:14:58.864 0.0
2025-11-29-16:14:59.066 0.0
2025-11-29-16:14:59.269 0.0
2025-11-29-16:14:59.469 0.0
2025-11-29-16:14:59.675 0.0
2025-11-29-16:14:59.876 0.0
2025-11-29-16:15:00.082 0.0
2025-11-29-16:15:00.288 19.1
2025-11-29-16:15:00.508 0.0
2025-11-29-16:15:00.681 0.0
2025-11-29-16:15:00.901 0.0
2025-11-29-16:15:01.095 0.0
2025-11-29-16:15:01.302 19.2
2025-11-29-16:15:01.526 18.7
2025-11-29-16:15:01.695 0.0
2025-11-29-16:15:01.926 18.7
2025-11-29-16:15:02.125 18.3
2025-11-29-16:15:02.309 18.4
2025-11-29-16:15:02.509 0.0
2025-11-29-16:15:02.715 18.4
2025-11-29-16:15:02.940 18.7
2025-11-29-16:15:03.121 0.0
2025-11-29-16:15:03.318 0.0
2025-11-29-16:15:03.534 18.3
2025-11-29-16:15:03.719 0.0
2025-11-29-16:15:03.952 0.0
2025-11-29-16:15:04.117 0.0
2025-11-29-16:15:04.375 18.4
2025-11-29-16:15:04.554 18.3
2025-11-29-16:15:04.740 18.3
2025-11-29-16:15:04.944 0.0
2025-11-29-16:15:05.148 0.0
2025-11-29-16:15:05.356 0.0
2025-11-29-16:15:05.543 0.0
2025-11-29-16:15:05.826 0.0
2025-11-29-16:15:05.955 0.0
2025-11-29-16:15:06.113 0.0
2025-11-29-16:15:06.316 0.0
2025-11-29-16:15:06.521 0.0
2025-11-29-16:15:06.726 0.0
2025-11-29-16:15:06.914 0.0
2025-11-29-16:15:07.123 18.3
2025-11-29-16:15:07.318 0.0
2025-11-29-16:15:07.520 0.0
2025-11-29-16:15:07.725 0.0
2025-11-29-16:15:07.984 0.0
2025-11-29-16:15:08.186 0.0
2025-11-29-16:15:08.389 18.3
2025-11-29-16:14:45.309 0.0
2025-11-29-16:14:45.309 0.0
2025-11-29-16:14:45.519 0.0
2025-11-29-16:14:45.715 0.0
2025-11-29-16:14:45.929 0.0
2025-11-29-16:14:46.148 0.0
2025-11-29-16:14:46.335 0.0
2025-11-29-16:14:46.525 0.0
2025-11-29-16:14:46.724 0.0
2025-11-29-16:14:46.925 0.0
2025-11-29-16:14:47.116 0.0
2025-11-29-16:14:47.330 0.0
2025-11-29-16:14:47.540 0.0
2025-11-29-16:14:47.724 0.0
2025-11-29-16:14:47.929 0.0
2025-11-29-16:14:48.143 0.0
2025-11-29-16:14:48.346 0.0
2025-11-29-16:14:48.533 0.0
2025-11-29-16:14:48.744 0.0
2025-11-29-16:14:48.950 0.0
2025-11-29-16:14:49.159 0.0
2025-11-29-16:14:49.352 0.0
2025-11-29-16:14:49.552 0.0
This source diff could not be displayed because it is too large. You can view the blob instead.
2025-11-29-12:49:02.930 0.0
2025-11-29-12:49:02.930 0.0
......@@ -317,3 +317,40 @@
2025-11-29-15:49:07.323 0.0
2025-11-29-15:49:07.844 0.0
2025-11-29-15:49:08.416 0.0
2025-11-29-16:16:55.866 0.0
2025-11-29-16:16:56.435 0.0
2025-11-29-16:16:57.084 0.0
2025-11-29-16:16:57.981 0.0
2025-11-29-16:16:58.641 0.0
2025-11-29-16:16:59.614 0.0
2025-11-29-16:17:00.740 0.0
2025-11-29-16:17:01.434 0.0
2025-11-29-16:17:01.711 0.0
2025-11-29-16:17:01.974 0.0
2025-11-29-16:17:02.249 0.0
2025-11-29-16:17:02.487 0.0
2025-11-29-16:17:02.683 0.0
2025-11-29-16:17:33.877 0.0
2025-11-29-16:17:34.285 0.0
2025-11-29-16:17:34.707 0.0
2025-11-29-16:17:34.989 0.0
2025-11-29-16:17:35.474 0.0
2025-11-29-16:17:35.846 0.0
2025-11-29-16:17:36.138 0.0
2025-11-29-16:17:36.596 0.0
2025-11-29-16:17:37.087 0.0
2025-11-29-16:17:37.601 0.0
2025-11-29-16:17:38.152 0.0
2025-11-29-16:17:38.675 0.0
2025-11-29-16:17:39.166 0.0
2025-11-29-16:17:39.598 0.0
2025-11-29-16:17:39.935 0.0
2025-11-29-16:17:40.111 0.0
2025-11-29-16:17:40.326 0.0
2025-11-29-16:17:40.516 0.0
2025-11-29-16:17:40.732 0.0
2025-11-29-16:17:40.815 0.0
2025-11-29-16:17:41.026 0.0
2025-11-29-16:17:41.215 0.0
2025-11-29-16:17:41.428 0.0
2025-11-29-16:17:41.634 0.0
2025-11-29-15:48:49.971 0.0
2025-11-29-15:48:49.971 0.0
......@@ -52,3 +52,53 @@
2025-11-29-15:49:07.323 0.0
2025-11-29-15:49:07.850 0.0
2025-11-29-15:49:08.419 0.0
2025-11-29-16:16:55.866 0.0
2025-11-29-16:16:56.435 0.0
2025-11-29-16:16:57.090 0.0
2025-11-29-16:16:57.981 0.0
2025-11-29-16:16:58.648 0.0
2025-11-29-16:16:59.614 0.0
2025-11-29-16:17:00.741 0.0
2025-11-29-16:17:01.434 0.0
2025-11-29-16:17:01.711 0.0
2025-11-29-16:17:01.974 0.0
2025-11-29-16:17:02.249 0.0
2025-11-29-16:17:02.487 0.0
2025-11-29-16:17:02.683 0.0
2025-11-29-16:17:02.871 0.0
2025-11-29-16:17:03.038 0.0
2025-11-29-16:17:03.197 0.0
2025-11-29-16:17:03.453 0.0
2025-11-29-16:17:03.642 0.0
2025-11-29-16:17:03.803 0.0
2025-11-29-16:17:04.008 0.0
2025-11-29-16:17:04.216 0.0
2025-11-29-16:17:30.725 0.0
2025-11-29-16:17:30.929 0.0
2025-11-29-16:17:31.273 0.0
2025-11-29-16:17:31.550 0.0
2025-11-29-16:17:31.856 0.0
2025-11-29-16:17:32.096 0.0
2025-11-29-16:17:32.411 0.0
2025-11-29-16:17:32.695 0.0
2025-11-29-16:17:32.941 0.0
2025-11-29-16:17:33.181 0.0
2025-11-29-16:17:33.477 0.0
2025-11-29-16:17:33.878 0.0
2025-11-29-16:17:34.285 0.0
2025-11-29-16:17:34.708 0.0
2025-11-29-16:17:34.989 0.0
2025-11-29-16:17:35.474 0.0
2025-11-29-16:17:35.838 20.0
2025-11-29-16:17:36.138 0.0
2025-11-29-16:17:36.596 0.0
2025-11-29-16:17:37.087 0.0
2025-11-29-16:17:37.601 0.0
2025-11-29-16:17:38.152 0.0
2025-11-29-16:17:38.675 0.0
2025-11-29-16:17:39.166 0.0
2025-11-29-16:17:39.599 0.0
2025-11-29-16:17:39.935 0.0
2025-11-29-16:17:40.111 0.0
2025-11-29-16:17:40.326 0.0
2025-11-29-16:17:40.516 0.0
2025-11-29-15:49:00.891 0.0
2025-11-29-15:49:00.891 0.0
......@@ -11,3 +11,71 @@
2025-11-29-15:49:07.323 0.0
2025-11-29-15:49:07.844 0.0
2025-11-29-15:49:08.416 0.0
2025-11-29-16:16:59.614 0.0
2025-11-29-16:17:00.741 20.0
2025-11-29-16:17:01.434 0.0
2025-11-29-16:17:01.711 0.0
2025-11-29-16:17:01.974 0.0
2025-11-29-16:17:02.249 0.0
2025-11-29-16:17:02.487 0.0
2025-11-29-16:17:02.683 0.0
2025-11-29-16:17:02.871 0.0
2025-11-29-16:17:03.038 20.0
2025-11-29-16:17:03.197 0.0
2025-11-29-16:17:03.453 0.0
2025-11-29-16:17:03.642 0.0
2025-11-29-16:17:03.803 0.0
2025-11-29-16:17:04.008 0.0
2025-11-29-16:17:04.216 0.0
2025-11-29-16:17:04.361 0.0
2025-11-29-16:17:04.556 0.0
2025-11-29-16:17:04.756 0.0
2025-11-29-16:17:04.963 0.0
2025-11-29-16:17:05.169 0.0
2025-11-29-16:17:05.368 0.0
2025-11-29-16:17:26.350 0.0
2025-11-29-16:17:26.478 0.0
2025-11-29-16:17:26.664 0.0
2025-11-29-16:17:26.852 0.0
2025-11-29-16:17:27.169 0.0
2025-11-29-16:17:27.260 0.0
2025-11-29-16:17:27.453 0.0
2025-11-29-16:17:27.729 0.0
2025-11-29-16:17:27.921 20.0
2025-11-29-16:17:28.099 0.0
2025-11-29-16:17:28.323 0.0
2025-11-29-16:17:28.482 0.0
2025-11-29-16:17:28.678 0.0
2025-11-29-16:17:28.888 0.0
2025-11-29-16:17:29.116 0.0
2025-11-29-16:17:29.340 0.0
2025-11-29-16:17:29.494 0.0
2025-11-29-16:17:29.831 0.0
2025-11-29-16:17:29.935 0.0
2025-11-29-16:17:30.166 0.0
2025-11-29-16:17:30.408 0.0
2025-11-29-16:17:30.725 0.0
2025-11-29-16:17:30.929 0.0
2025-11-29-16:17:31.273 0.0
2025-11-29-16:17:31.549 0.0
2025-11-29-16:17:31.855 0.0
2025-11-29-16:17:32.096 0.0
2025-11-29-16:17:32.411 0.0
2025-11-29-16:17:32.691 0.0
2025-11-29-16:17:32.941 0.0
2025-11-29-16:17:33.180 0.0
2025-11-29-16:17:33.477 0.0
2025-11-29-16:17:33.877 0.0
2025-11-29-16:17:34.285 0.0
2025-11-29-16:17:34.707 0.0
2025-11-29-16:17:34.989 0.0
2025-11-29-16:17:35.474 20.0
2025-11-29-16:17:35.846 0.0
2025-11-29-16:17:36.138 0.0
2025-11-29-16:17:36.596 0.0
2025-11-29-16:17:37.087 0.0
2025-11-29-16:17:37.601 0.0
2025-11-29-16:17:38.152 0.0
2025-11-29-16:17:38.675 0.0
2025-11-29-16:17:39.166 0.0
2025-11-29-16:17:39.598 0.0
......@@ -877,19 +877,11 @@ class ChannelPanelHandler:
if panel:
panel.updateChannelStatus(channel_id, 'connected')
panel.setConnected(True)
pass
else:
pass
# 兼容单通道场景
if hasattr(self, 'channelPanel'):
self.channelPanel.updateChannelStatus(channel_id, 'connected')
self.channelPanel.setConnected(True)
self.statusBar().showMessage(
self.tr(" 通道已连接: {} - 视频流已启动").format(channel_id)
)
except Exception as e:
pass
import traceback
traceback.print_exc()
......@@ -941,12 +933,6 @@ class ChannelPanelHandler:
# 🔥 保留映射(不删除),以便任务同步时能找到面板
# del self._channel_panels_map[channel_id] # 注释掉,保留映射
# 兼容单通道场景
if hasattr(self, 'channelPanel'):
self.channelPanel.updateChannelStatus(channel_id, 'disconnected')
self.channelPanel.setConnected(False)
self.channelPanel.clearDisplay()
self.statusBar().showMessage(self.tr("⏹ 通道已断开: {}").format(channel_id))
def onChannelManage(self):
......
......@@ -280,15 +280,18 @@ class CurvePanelHandler:
def addChannelData(self, channel_id, channel_name=None, window_name=None, color=None):
"""
添加通道数据管理(业务逻辑)
添加通道数据(业务逻辑)
Args:
channel_id: 通道唯一ID
channel_name: 通道名称(可选)
window_name: 检测窗口名称(可选)
color: 曲线颜色(可选)
channel_id: 通道ID
channel_name: 通道名称
window_name: 窗口名称
color: 曲线颜色
"""
print(f"➕ [添加通道] channel_id={channel_id}, channel_name={channel_name}, window_name={window_name}")
if channel_id in self.channel_data:
print(f" - 通道已存在,跳过添加")
return
# 默认名称
......@@ -311,7 +314,11 @@ class CurvePanelHandler:
# 通知UI创建通道
if self.curve_panel:
print(f" - 通知UI创建通道...")
self.curve_panel.addChannel(channel_id, channel_name, window_name, color)
print(f" - UI通道创建完成")
else:
print(f" - ⚠️ curve_panel不存在!")
def updateCurveData(self, channel_id, data_points):
"""
......@@ -322,17 +329,21 @@ class CurvePanelHandler:
data_points: 数据点列表 [{'timestamp': float, 'height_mm': float}, ...]
height_mm精度为0.1mm(保留1位小数)
"""
print(f"📊 [更新曲线] channel_id={channel_id}, 数据点数量={len(data_points)}")
if not data_points:
# print(f"⚠️ [曲线数据更新] 数据点为空,channel_id={channel_id}")
print(f" - ⚠️ 数据点为空,跳过更新")
return
if channel_id not in self.channel_data:
print(f" - 通道不存在,先添加通道")
self.addChannelData(channel_id)
channel = self.channel_data[channel_id]
# 🔥 调试:记录更新前的数据点数
before_count = len(channel['time'])
print(f" - 更新前数据点数: {before_count}")
# 批量添加数据
added_count = 0
......@@ -350,6 +361,8 @@ class CurvePanelHandler:
# 🔥 调试:记录添加后的数据点数
after_add_count = len(channel['time'])
print(f" - 实际添加数据点数: {added_count}")
print(f" - 更新后数据点数: {after_add_count}")
# 🔥 限制数据点数量
# - 'realtime' 模式:限制为3000个点(滚动窗口)
......@@ -361,6 +374,7 @@ class CurvePanelHandler:
before_limit = len(channel['time'])
channel['time'] = channel['time'][-self.max_points:]
channel['value'] = channel['value'][-self.max_points:]
print(f" - 限制数据点: {before_limit} -> {len(channel['time'])}")
# 🔥 处理时间间隔断点:超过2分钟的数据点之间插入NaN断开连接
processed_time, processed_value = self._processTimeGaps(
......@@ -368,14 +382,17 @@ class CurvePanelHandler:
channel['value'],
max_gap_seconds=120 # 2分钟 = 120秒
)
print(f" - 处理后数据点数: {len(processed_time)}")
# 更新UI显示(只更新一次)
if self.curve_panel:
print(f" - 开始更新UI显示...")
self.curve_panel.updateCurveDisplay(
channel_id,
processed_time,
processed_value
)
print(f" - UI显示更新完成")
# 更新X轴范围
if channel['time']:
......@@ -386,6 +403,8 @@ class CurvePanelHandler:
if channel['value']:
max_value = max(channel['value'])
self.curve_panel.setYRangeAuto(max_value)
else:
print(f" - ⚠️ curve_panel不存在,无法更新UI!")
def _processTimeGaps(self, time_data, value_data, max_gap_seconds=120):
......@@ -694,23 +713,23 @@ class CurvePanelHandler:
try:
import sys
# 动态获取项目根目录
# 🔥 动态获取数据目录(与storage_thread保持一致)
if getattr(sys, 'frozen', False):
# 打包后的exe
project_root = os.path.dirname(sys.executable)
# 打包后:使用 sys._MEIPASS 指向 _internal 目录
data_root = sys._MEIPASS
else:
# 开发环境:基于配置模块获取
try:
from database.config import get_project_root
project_root = get_project_root()
data_root = get_project_root()
except ImportError:
# 后备方案:当前工作目录
project_root = os.getcwd()
data_root = os.getcwd()
# 构建完整路径
mission_folder_path = os.path.join(project_root, 'database', 'mission_result', mission_name)
mission_folder_path = os.path.join(data_root, 'database', 'mission_result', mission_name)
print(f"🔍 [路径构建] 任务名称: {mission_name}")
print(f"🔍 [路径构建] 项目根目录: {project_root}")
print(f"🔍 [路径构建] 数据根目录: {data_root}")
print(f"🔍 [路径构建] 完整路径: {mission_folder_path}")
print(f"🔍 [路径构建] 路径是否存在: {os.path.exists(mission_folder_path)}")
......@@ -769,22 +788,22 @@ class CurvePanelHandler:
import sys
try:
# 动态获取项目根目录
# 🔥 动态获取数据目录(与storage_thread保持一致)
if getattr(sys, 'frozen', False):
# 打包后的exe
project_root = os.path.dirname(sys.executable)
# 打包后:使用 sys._MEIPASS 指向 _internal 目录
data_root = sys._MEIPASS
else:
# 开发环境:基于配置模块获取
try:
from database.config import get_project_root
project_root = get_project_root()
data_root = get_project_root()
except ImportError:
# 后备方案:当前工作目录
project_root = os.getcwd()
data_root = os.getcwd()
# 构建 mission_result 目录路径
mission_result_dir = os.path.join(project_root, 'database', 'mission_result')
print(f"🔍 [任务列表] 项目根目录: {project_root}")
mission_result_dir = os.path.join(data_root, 'database', 'mission_result')
print(f"🔍 [任务列表] 数据根目录: {data_root}")
print(f"🔍 [任务列表] 任务目录: {mission_result_dir}")
print(f"🔍 [任务列表] 目录是否存在: {os.path.exists(mission_result_dir)}")
......@@ -842,6 +861,7 @@ class CurvePanelHandler:
self.setCurveLoadMode('history')
# 查找所有CSV文件
print(f"\n🔍 [曲线加载] ==================== 开始加载 ====================")
print(f"🔍 [曲线加载] 扫描目录: {data_directory}")
print(f"🔍 [曲线加载] 目录是否存在: {os.path.exists(data_directory)}")
......@@ -849,7 +869,11 @@ class CurvePanelHandler:
print(f"❌ [曲线加载] 目录不存在: {data_directory}")
return False
csv_files = [f for f in os.listdir(data_directory) if f.endswith('.csv')]
# 列出目录中的所有文件
all_files = os.listdir(data_directory)
print(f"🔍 [曲线加载] 目录中所有文件: {all_files}")
csv_files = [f for f in all_files if f.endswith('.csv')]
print(f"🔍 [曲线加载] 找到 {len(csv_files)} 个CSV文件: {csv_files}")
if not csv_files:
......@@ -889,11 +913,13 @@ class CurvePanelHandler:
print(f"✅ [进度条] 已显示进度对话框")
# 创建并启动后台加载线程
print(f"🧵 [曲线加载] 创建后台加载线程...")
self._load_thread = CurveDataLoadThread(
data_directory=data_directory,
csv_files=csv_files,
handler=self
)
print(f"🧵 [曲线加载] 后台线程已创建")
# 连接信号(使用Qt.QueuedConnection确保跨线程安全)
self._load_thread.progress_updated.connect(
......@@ -911,7 +937,10 @@ class CurvePanelHandler:
)
# 启动线程
print(f"🚀 [曲线加载] 启动后台线程...")
self._load_thread.start()
print(f"✅ [曲线数据加载] 后台加载线程已启动")
print(f"🔍 [曲线加载] ==================== 加载流程启动完成 ====================\n")
return True
......@@ -928,17 +957,28 @@ class CurvePanelHandler:
def _onFileLoaded(self, channel_id, channel_name, window_name, color, data_points):
"""处理单个文件加载完成"""
print(f"📥 [文件加载] 收到文件数据:")
print(f" - channel_id: {channel_id}")
print(f" - channel_name: {channel_name}")
print(f" - window_name: {window_name}")
print(f" - 数据点数量: {len(data_points)}")
# 添加通道(如果不存在)
if channel_id not in self.channel_data:
print(f" - 添加新通道: {channel_id}")
self.addChannelData(
channel_id=channel_id,
channel_name=channel_name,
window_name=window_name,
color=color
)
else:
print(f" - 通道已存在: {channel_id}")
# 批量更新曲线数据
print(f" - 开始更新曲线数据...")
self.updateCurveData(channel_id, data_points)
print(f" - 曲线数据更新完成")
def _onLoadFinished(self, progress_dialog, success, count):
"""处理加载完成"""
......
......@@ -174,7 +174,6 @@ class MissionPanelHandler:
self.mission_panel.btn_debug.setEnabled(False)
except Exception as e:
pass
import traceback
traceback.print_exc()
# 出错时默认隐藏调试按钮,确保安全性
......@@ -467,8 +466,6 @@ class MissionPanelHandler:
# 🔥 第一步:同步到 channel_config.yaml(独立业务,无论通道是否打开都执行)
sync_success = self._syncTaskToConfigFile(channel_id, task_id, task_name, save_liquid_data_path)
pass
# 🔥 第二步:更新通道面板UI(可选操作,仅在通道已打开时执行)
# 检查是否有通道面板映射(从ChannelPanelHandler获取)
if not hasattr(self, '_channel_panels_map'):
......@@ -1377,45 +1374,31 @@ class MissionPanelHandler:
channel_id: 通道ID(如 'channel1')
task_folder_name: 任务文件夹名称(如 '1_1')
"""
try:
# 从 channel_id 提取通道编号
if not channel_id.startswith('channel'):
print(f"❌ [_updateChannelMissionLabel] 无效的通道ID: {channel_id}")
return
channel_num = int(channel_id.replace('channel', ''))
if not channel_id.startswith('channel'):
return
channel_num = int(channel_id.replace('channel', ''))
# 使用 updateMissionLabelByVar 方法更新标签
if hasattr(self, 'updateMissionLabelByVar'):
self.updateMissionLabelByVar(channel_num, task_folder_name)
print(f"✅ [多任务] 已更新 {channel_id} 的任务标签: {task_folder_name}")
# 使用 updateMissionLabelByVar 方法更新标签
if hasattr(self, 'updateMissionLabelByVar'):
self.updateMissionLabelByVar(channel_num, task_folder_name)
print(f"✅ [多任务] 已更新 {channel_id} 的任务标签: {task_folder_name}")
# 删除状态更新逻辑,双击不改变任务状态
else:
# 备用方案:直接通过变量名更新
mission_var_name = f'channel{channel_num}mission'
if hasattr(self, mission_var_name):
mission_label = getattr(self, mission_var_name)
mission_label.setText(str(task_folder_name))
mission_label.adjustSize()
# 删除状态更新逻辑,双击不改变任务状态
else:
# 备用方案:直接通过变量名更新
mission_var_name = f'channel{channel_num}mission'
if hasattr(self, mission_var_name):
mission_label = getattr(self, mission_var_name)
mission_label.setText(str(task_folder_name))
mission_label.adjustSize()
# 重新定位标签
panel = self._channel_panels_map.get(channel_id)
if panel and hasattr(panel, '_positionTaskLabel'):
panel._positionTaskLabel()
print(f"✅ [多任务] 已更新 {channel_id} 的任务标签: {task_folder_name}")
# 删除状态更新逻辑,双击不改变任务状态
else:
print(f"❌ [_updateChannelMissionLabel] 未找到变量: {mission_var_name}")
# 注意:不在这里调用 _setTaskRowChannelColor,而是在 _handleTaskSelected 中更新完所有通道后再调用
except Exception as e:
print(f"❌ [_updateChannelMissionLabel] 更新通道任务标签失败 ({channel_id}): {e}")
import traceback
traceback.print_exc()
# 重新定位标签
panel = self._channel_panels_map.get(channel_id)
if panel and hasattr(panel, '_positionTaskLabel'):
panel._positionTaskLabel()
def _isTaskInUse(self, task_folder_name):
"""
......@@ -1427,32 +1410,27 @@ class MissionPanelHandler:
Returns:
bool: 如果任务被使用返回True,否则返回False
"""
try:
# 检查所有通道(channel1-channel4)
for channel_num in range(1, 5):
channel_id = f'channel{channel_num}'
# 方法1:检查通道任务标签
mission_var_name = f'channel{channel_num}mission'
if hasattr(self, mission_var_name):
mission_label = getattr(self, mission_var_name)
current_task = mission_label.text()
if current_task == task_folder_name:
return True
# 方法2:检查通道面板内存(备用)
if hasattr(self, '_channel_panels_map'):
panel = self._channel_panels_map.get(channel_id)
if panel and hasattr(panel, 'getTaskInfo'):
panel_task = panel.getTaskInfo()
if panel_task == task_folder_name:
return True
# 检查所有通道(channel1-channel4)
for channel_num in range(1, 5):
channel_id = f'channel{channel_num}'
return False
# 方法1:检查通道任务标签
mission_var_name = f'channel{channel_num}mission'
if hasattr(self, mission_var_name):
mission_label = getattr(self, mission_var_name)
current_task = mission_label.text()
if current_task == task_folder_name:
return True
except Exception as e:
print(f"❌ [状态检查] 检查任务使用状态失败: {e}")
return False
# 方法2:检查通道面板内存(备用)
if hasattr(self, '_channel_panels_map'):
panel = self._channel_panels_map.get(channel_id)
if panel and hasattr(panel, 'getTaskInfo'):
panel_task = panel.getTaskInfo()
if panel_task == task_folder_name:
return True
return False
def _refreshAllTaskStatus(self):
"""
......@@ -1460,13 +1438,8 @@ class MissionPanelHandler:
委托给 MissionTextStatus 类处理
"""
try:
if self.mission_text_status:
self.mission_text_status.refreshAllTaskStatus(self)
except Exception as e:
print(f"❌ [状态刷新] 刷新任务状态失败: {e}")
import traceback
traceback.print_exc()
if self.mission_text_status:
self.mission_text_status.refreshAllTaskStatus(self)
def _updateTaskStatus(self, task_folder_name, new_status):
"""
......@@ -1476,39 +1449,30 @@ class MissionPanelHandler:
task_folder_name: 任务文件夹名称(如 "21321_312312")
new_status: 新状态(如 "已启动")
"""
try:
if not hasattr(self, 'mission_panel'):
return
if not hasattr(self, 'mission_panel'):
return
# 遍历任务面板的所有行,查找匹配的任务
table = self.mission_panel.table
for row in range(table.rowCount()):
# 获取任务编号和任务名称
task_id_item = table.item(row, 0) # 任务编号列
task_name_item = table.item(row, 1) # 任务名称列
# 遍历任务面板的所有行,查找匹配的任务
table = self.mission_panel.table
for row in range(table.rowCount()):
# 获取任务编号和任务名称
task_id_item = table.item(row, 0) # 任务编号列
task_name_item = table.item(row, 1) # 任务名称列
if task_id_item and task_name_item:
# 构建任务文件夹名称进行匹配
current_task_folder = f"{task_id_item.text()}_{task_name_item.text()}"
if task_id_item and task_name_item:
# 构建任务文件夹名称进行匹配
current_task_folder = f"{task_id_item.text()}_{task_name_item.text()}"
if current_task_folder == task_folder_name:
# 找到匹配的任务,更新状态列(第2列)
status_item = table.item(row, 2)
if status_item:
old_status = status_item.text()
status_item.setText(new_status)
print(f"✅ [_updateTaskStatus] 任务 {task_folder_name} 状态已更新: {old_status} → {new_status}")
# 同时更新配置文件中的状态
self._updateTaskConfigStatus(task_id_item.text(), new_status)
return
print(f"⚠️ [_updateTaskStatus] 未找到任务: {task_folder_name}")
except Exception as e:
print(f"❌ [_updateTaskStatus] 更新任务状态失败: {e}")
import traceback
traceback.print_exc()
if current_task_folder == task_folder_name:
# 找到匹配的任务,更新状态列(第2列)
status_item = table.item(row, 2)
if status_item:
old_status = status_item.text()
status_item.setText(new_status)
# 同时更新配置文件中的状态
self._updateTaskConfigStatus(task_id_item.text(), new_status)
return
# 🔥 已删除 _updateRowColor 和 _updateRowColorForQTableWidgetItem 方法
# 所有文本颜色管理现在由 MissionTextStatus 类统一处理
......@@ -1519,17 +1483,8 @@ class MissionPanelHandler:
委托给 MissionTextStatus 类处理所有文本颜色更新
"""
print(f"🎯 [_updateChannelColumnColor] 方法被调用")
try:
if self.mission_text_status:
print(f" ✅ mission_text_status 存在,开始更新")
self.mission_text_status.updateAllChannelColumnColors(self)
else:
print(f" ❌ mission_text_status 不存在!")
except Exception as e:
print(f"❌ [状态列更新] 更新状态列失败: {e}")
import traceback
traceback.print_exc()
if self.mission_text_status:
self.mission_text_status.updateAllChannelColumnColors(self)
def _updateTaskConfigStatus(self, task_id, new_status):
"""
......@@ -1539,38 +1494,29 @@ class MissionPanelHandler:
task_id: 任务编号
new_status: 新状态
"""
try:
from database.config import get_project_root
import yaml
import os
config_dir = os.path.join(get_project_root(), 'database', 'config', 'mission')
# 查找对应的任务配置文件
for filename in os.listdir(config_dir):
if filename.endswith('.yaml') and filename.startswith(f"{task_id}_"):
config_path = os.path.join(config_dir, filename)
# 读取配置文件
with open(config_path, 'r', encoding='utf-8') as f:
config_data = yaml.safe_load(f) or {}
# 更新状态
config_data['status'] = new_status
# 写回配置文件
with open(config_path, 'w', encoding='utf-8') as f:
yaml.safe_dump(config_data, f, allow_unicode=True, default_flow_style=False)
print(f"✅ [_updateTaskConfigStatus] 已更新配置文件 {filename} 状态为: {new_status}")
return
print(f"⚠️ [_updateTaskConfigStatus] 未找到任务 {task_id} 的配置文件")
except Exception as e:
print(f"❌ [_updateTaskConfigStatus] 更新任务配置状态失败: {e}")
import traceback
traceback.print_exc()
from database.config import get_project_root
import yaml
import os
config_dir = os.path.join(get_project_root(), 'database', 'config', 'mission')
# 查找对应的任务配置文件
for filename in os.listdir(config_dir):
if filename.endswith('.yaml') and filename.startswith(f"{task_id}_"):
config_path = os.path.join(config_dir, filename)
# 读取配置文件
with open(config_path, 'r', encoding='utf-8') as f:
config_data = yaml.safe_load(f) or {}
# 更新状态
config_data['status'] = new_status
# 写回配置文件
with open(config_path, 'w', encoding='utf-8') as f:
yaml.safe_dump(config_data, f, allow_unicode=True, default_flow_style=False)
return
def _refreshCurveMissionList(self):
"""
......@@ -1578,36 +1524,31 @@ class MissionPanelHandler:
从 mission_result 目录重新扫描任务文件夹并更新下拉框
"""
try:
# 如果有 curvePanelHandler,调用其 loadMissionFolders 方法
if hasattr(self, 'curvePanelHandler') and self.curvePanelHandler:
self.curvePanelHandler.loadMissionFolders()
# 否则尝试直接调用 curvePanel 的方法
elif hasattr(self, 'curvePanel') and self.curvePanel:
# 手动扫描任务文件夹
import sys
project_root = get_project_root()
mission_result_dir = os.path.join(project_root, 'database', 'mission_result')
# 如果有 curvePanelHandler,调用其 loadMissionFolders 方法
if hasattr(self, 'curvePanelHandler') and self.curvePanelHandler:
self.curvePanelHandler.loadMissionFolders()
# 否则尝试直接调用 curvePanel 的方法
elif hasattr(self, 'curvePanel') and self.curvePanel:
# 手动扫描任务文件夹
import sys
project_root = get_project_root()
mission_result_dir = os.path.join(project_root, 'database', 'mission_result')
if os.path.exists(mission_result_dir):
mission_folders = []
for item in os.listdir(mission_result_dir):
item_path = os.path.join(mission_result_dir, item)
if os.path.isdir(item_path):
mission_folders.append({
'name': item,
'path': item_path
})
if os.path.exists(mission_result_dir):
mission_folders = []
for item in os.listdir(mission_result_dir):
item_path = os.path.join(mission_result_dir, item)
if os.path.isdir(item_path):
mission_folders.append({
'name': item,
'path': item_path
})
# 按文件夹名称排序
mission_folders.sort(key=lambda x: x['name'])
# 更新下拉框
self.curvePanel.updateMissionFolderList(mission_folders)
except Exception as e:
print(f"⚠️ [刷新曲线任务列表] 失败: {e}")
import traceback
traceback.print_exc()
# 按文件夹名称排序
mission_folders.sort(key=lambda x: x['name'])
# 更新下拉框
self.curvePanel.updateMissionFolderList(mission_folders)
class MissionTextStatus:
......@@ -1647,13 +1588,8 @@ class MissionTextStatus:
"""
1. 初始化所有任务行文本为灰色
"""
try:
for row in range(self.table.rowCount()):
self._setRowColor(row, self.COLOR_GRAY, exclude_columns=[])
except Exception as e:
print(f"❌ [文本状态] 初始化失败: {e}")
import traceback
traceback.print_exc()
for row in range(self.table.rowCount()):
self._setRowColor(row, self.COLOR_GRAY, exclude_columns=[])
def setRowBlackOnSelect(self, row_index):
"""
......@@ -1664,14 +1600,10 @@ class MissionTextStatus:
Args:
row_index: 选中的行索引
"""
try:
# 🔥 不再恢复之前选中行的颜色,所有"已启动"的任务点击后都保持黑色
# 将新选中的行置为黑色
self._setRowColor(row_index, self.COLOR_BLACK, exclude_columns=[2]) # 排除状态列
self.selected_row = row_index
except Exception as e:
import traceback
traceback.print_exc()
self._setRowColor(row_index, self.COLOR_BLACK, exclude_columns=[2]) # 排除状态列
self.selected_row = row_index
def setStatusColumnGreenOnDetection(self, task_folder_name):
"""
......@@ -1680,20 +1612,15 @@ class MissionTextStatus:
Args:
task_folder_name: 任务文件夹名称(如 "1_1")
"""
try:
# 查找对应的任务行
row_index = self._findTaskRow(task_folder_name)
if row_index >= 0:
status_item = self.table.item(row_index, 2) # 状态列索引为2
if status_item:
status_item.setText("检测中")
status_item.setForeground(self.COLOR_GREEN)
else:
print(f"⚠️ [文本状态] 未找到任务 {task_folder_name}")
except Exception as e:
print(f"❌ [文本状态] 设置检测状态颜色失败: {e}")
import traceback
traceback.print_exc()
# 查找对应的任务行
row_index = self._findTaskRow(task_folder_name)
if row_index >= 0:
status_item = self.table.item(row_index, 2) # 状态列索引为2
if status_item:
status_item.setText("检测中")
status_item.setForeground(self.COLOR_GREEN)
def resetStatusColumnOnStopDetection(self, task_folder_name):
"""
......@@ -1702,20 +1629,16 @@ class MissionTextStatus:
Args:
task_folder_name: 任务文件夹名称(如 "1_1")
"""
try:
row_index = self._findTaskRow(task_folder_name)
if row_index >= 0:
status_item = self.table.item(row_index, 2)
if status_item:
status_item.setText("已启动")
# 如果是选中行,保持黑色;否则恢复为灰色
if row_index == self.selected_row:
status_item.setForeground(self.COLOR_BLACK)
else:
status_item.setForeground(self.COLOR_GRAY)
except Exception as e:
import traceback
traceback.print_exc()
row_index = self._findTaskRow(task_folder_name)
if row_index >= 0:
status_item = self.table.item(row_index, 2)
if status_item:
status_item.setText("已启动")
# 如果是选中行,保持黑色;否则恢复为灰色
if row_index == self.selected_row:
status_item.setForeground(self.COLOR_BLACK)
else:
status_item.setForeground(self.COLOR_GRAY)
def setStatusColumnBlackOnStarted(self, task_folder_name):
"""
......@@ -1726,18 +1649,12 @@ class MissionTextStatus:
Args:
task_folder_name: 任务文件夹名称(如 "1_1")
"""
try:
row_index = self._findTaskRow(task_folder_name)
if row_index >= 0:
status_item = self.table.item(row_index, 2)
if status_item:
status_item.setText("已启动")
status_item.setForeground(self.COLOR_BLACK)
print(f"✅ [文本状态] 任务 {task_folder_name} 状态列已切换为黑色(已启动)")
except Exception as e:
print(f"❌ [文本状态] 设置状态列为已启动失败: {e}")
import traceback
traceback.print_exc()
row_index = self._findTaskRow(task_folder_name)
if row_index >= 0:
status_item = self.table.item(row_index, 2)
if status_item:
status_item.setText("已启动")
status_item.setForeground(self.COLOR_BLACK)
def setChannelColumnGreenOnDetection(self, task_folder_name, channel_num):
"""
......@@ -1747,23 +1664,15 @@ class MissionTextStatus:
task_folder_name: 任务文件夹名称(如 "1_1")
channel_num: 通道编号(1-4)
"""
try:
# 查找对应的任务行
row_index = self._findTaskRow(task_folder_name)
if row_index >= 0:
# 通道列从第3列开始(0:任务编号, 1:任务名称, 2:状态, 3-6:通道1-4)
col_index = 3 + (channel_num - 1)
channel_item = self.table.item(row_index, col_index)
if channel_item:
channel_item.setForeground(self.COLOR_GREEN)
print(f"✅ [文本状态] 任务 {task_folder_name} 通道{channel_num}列已置为绿色(检测中)")
else:
print(f"⚠️ [文本状态] 未找到任务 {task_folder_name}")
except Exception as e:
print(f"❌ [文本状态] 设置通道列检测状态颜色失败: {e}")
import traceback
traceback.print_exc()
# 查找对应的任务行
row_index = self._findTaskRow(task_folder_name)
if row_index >= 0:
# 通道列从第3列开始(0:任务编号, 1:任务名称, 2:状态, 3-6:通道1-4)
col_index = 3 + (channel_num - 1)
channel_item = self.table.item(row_index, col_index)
if channel_item:
channel_item.setForeground(self.COLOR_GREEN)
def resetChannelColumnOnStopDetection(self, task_folder_name, channel_num):
"""
......@@ -1773,22 +1682,16 @@ class MissionTextStatus:
task_folder_name: 任务文件夹名称(如 "1_1")
channel_num: 通道编号(1-4)
"""
try:
row_index = self._findTaskRow(task_folder_name)
if row_index >= 0:
col_index = 3 + (channel_num - 1)
channel_item = self.table.item(row_index, col_index)
if channel_item:
# 如果是选中行,恢复为黑色;否则恢复为灰色
if row_index == self.selected_row:
channel_item.setForeground(self.COLOR_BLACK)
else:
channel_item.setForeground(self.COLOR_GRAY)
print(f"✅ [文本状态] 任务 {task_folder_name} 通道{channel_num}列已恢复")
except Exception as e:
print(f"❌ [文本状态] 恢复通道列颜色失败: {e}")
import traceback
traceback.print_exc()
row_index = self._findTaskRow(task_folder_name)
if row_index >= 0:
col_index = 3 + (channel_num - 1)
channel_item = self.table.item(row_index, col_index)
if channel_item:
# 如果是选中行,恢复为黑色;否则恢复为灰色
if row_index == self.selected_row:
channel_item.setForeground(self.COLOR_BLACK)
else:
channel_item.setForeground(self.COLOR_GRAY)
def updateAllChannelColumnColors(self, main_window):
"""
......@@ -1802,107 +1705,95 @@ class MissionTextStatus:
Args:
main_window: 主窗口实例,用于访问通道任务标签和检测状态
"""
try:
print(f"🔍 [文本状态] 开始更新通道列颜色")
# 🔥 第一步:收集所有通道当前正在执行的任务
active_tasks = set() # 存储正在执行的任务名称
channel_task_map = {} # 通道 -> 任务映射
for channel_num in range(1, 5):
channel_id = f'channel{channel_num}'
mission_var_name = f'{channel_id}mission'
if hasattr(main_window, mission_var_name):
mission_label = getattr(main_window, mission_var_name)
current_task = mission_label.text()
if current_task and current_task != "未分配任务":
active_tasks.add(current_task)
channel_task_map[channel_id] = current_task
print(f" 📌 {channel_id}: 任务={current_task}")
# 🔥 第一步:收集所有通道当前正在执行的任务
active_tasks = set() # 存储正在执行的任务名称
channel_task_map = {} # 通道 -> 任务映射
for channel_num in range(1, 5):
channel_id = f'channel{channel_num}'
mission_var_name = f'{channel_id}mission'
# 🔥 第二步:遍历所有任务行,更新通道列颜色
for row in range(self.table.rowCount()):
task_id_item = self.table.item(row, 0)
task_name_item = self.table.item(row, 1)
status_item = self.table.item(row, 2)
if not (task_id_item and task_name_item and status_item):
continue
if hasattr(main_window, mission_var_name):
mission_label = getattr(main_window, mission_var_name)
current_task = mission_label.text()
# 获取任务文件夹名称
task_folder_name = f"{task_id_item.text()}_{task_name_item.text()}"
if current_task and current_task != "未分配任务":
active_tasks.add(current_task)
channel_task_map[channel_id] = current_task
# 🔥 第二步:遍历所有任务行,更新通道列颜色
for row in range(self.table.rowCount()):
task_id_item = self.table.item(row, 0)
task_name_item = self.table.item(row, 1)
status_item = self.table.item(row, 2)
if not (task_id_item and task_name_item and status_item):
continue
# 获取任务文件夹名称
task_folder_name = f"{task_id_item.text()}_{task_name_item.text()}"
# 🔥 处理所有任务的通道列(包括正在执行和未执行的)
# 获取该任务使用的通道列表(从表格中读取)
task_channels = []
for ch_idx in range(1, 5):
col_idx = 3 + (ch_idx - 1)
ch_item = self.table.item(row, col_idx)
if ch_item and ch_item.text():
task_channels.append(ch_idx)
# 检查每个通道的检测状态
for channel_num in task_channels:
channel_id = f'channel{channel_num}'
# 🔥 处理所有任务的通道列(包括正在执行和未执行的)
# 获取该任务使用的通道列表(从表格中读取)
task_channels = []
for ch_idx in range(1, 5):
col_idx = 3 + (ch_idx - 1)
ch_item = self.table.item(row, col_idx)
if ch_item and ch_item.text():
task_channels.append(ch_idx)
# 检查该通道是否正在执行这个任务
if channel_task_map.get(channel_id) == task_folder_name:
# 检查该通道的检测状态
detect_var_name = f'{channel_id}detect'
if hasattr(main_window, detect_var_name):
is_detecting = getattr(main_window, detect_var_name)
if is_detecting:
# 🔥 设置对应通道列为绿色
self.setChannelColumnGreenOnDetection(task_folder_name, channel_num)
else:
# 🔥 恢复通道列颜色
self.resetChannelColumnOnStopDetection(task_folder_name, channel_num)
else:
# 通道未分配此任务,恢复颜色
self.resetChannelColumnOnStopDetection(task_folder_name, channel_num)
# 🔥 只处理正在执行的任务(更新状态列)
if task_folder_name in active_tasks:
# 检查该任务使用的所有通道是否都在检测
# 只统计分配给该任务的通道
assigned_channels_count = 0 # 分配给该任务的通道数
detecting_channels_count = 0 # 正在检测的通道数
# 检查每个通道的检测状态
for channel_num in task_channels:
channel_id = f'channel{channel_num}'
# 检查该通道是否正在执行这个任务
if channel_task_map.get(channel_id) == task_folder_name:
assigned_channels_count += 1
# 检查该通道的检测状态
detect_var_name = f'{channel_id}detect'
if hasattr(main_window, detect_var_name):
is_detecting = getattr(main_window, detect_var_name)
print(f" 🔸 任务{task_folder_name} 通道{channel_num}: 检测状态={is_detecting}")
if is_detecting:
# 🔥 设置对应通道列为绿色
self.setChannelColumnGreenOnDetection(task_folder_name, channel_num)
else:
# 🔥 恢复通道列颜色
self.resetChannelColumnOnStopDetection(task_folder_name, channel_num)
else:
# 通道未分配此任务,恢复颜色
self.resetChannelColumnOnStopDetection(task_folder_name, channel_num)
detecting_channels_count += 1
# 🔥 只处理正在执行的任务(更新状态列)
if task_folder_name in active_tasks:
# 检查该任务使用的所有通道是否都在检测
# 只统计分配给该任务的通道
assigned_channels_count = 0 # 分配给该任务的通道数
detecting_channels_count = 0 # 正在检测的通道数
for channel_num in task_channels:
channel_id = f'channel{channel_num}'
# 检查该通道是否正在执行这个任务
if channel_task_map.get(channel_id) == task_folder_name:
assigned_channels_count += 1
# 检查该通道的检测状态
detect_var_name = f'{channel_id}detect'
if hasattr(main_window, detect_var_name):
is_detecting = getattr(main_window, detect_var_name)
if is_detecting:
detecting_channels_count += 1
# 🔥 规则:根据检测状态设置状态列颜色
# 只有当分配给该任务的所有通道都在检测时,才设置为绿色"检测中"
if assigned_channels_count > 0 and detecting_channels_count == assigned_channels_count:
# 所有分配的通道都在检测中 -> 绿色"检测中"
self.setStatusColumnGreenOnDetection(task_folder_name)
print(f" ✅ 任务{task_folder_name}: {detecting_channels_count}/{assigned_channels_count}通道检测中 -> 绿色")
elif detecting_channels_count == 0:
# 所有通道都未检测,但任务已分配 -> 黑色"已启动"
self.setStatusColumnBlackOnStarted(task_folder_name)
print(f" ✅ 任务{task_folder_name}: 0/{assigned_channels_count}通道检测中 -> 黑色已启动")
else:
# 部分通道在检测 -> 黑色"已启动"(不是所有通道都在检测)
self.setStatusColumnBlackOnStarted(task_folder_name)
print(f" ⚠️ 任务{task_folder_name}: {detecting_channels_count}/{assigned_channels_count}通道检测中 -> 黑色已启动")
except Exception as e:
print(f"❌ [文本状态] 更新通道列和状态列失败: {e}")
import traceback
traceback.print_exc()
# 🔥 规则:根据检测状态设置状态列颜色
# 只有当分配给该任务的所有通道都在检测时,才设置为绿色"检测中"
if assigned_channels_count > 0 and detecting_channels_count == assigned_channels_count:
# 所有分配的通道都在检测中 -> 绿色"检测中"
self.setStatusColumnGreenOnDetection(task_folder_name)
elif detecting_channels_count == 0:
# 所有通道都未检测,但任务已分配 -> 黑色"已启动"
self.setStatusColumnBlackOnStarted(task_folder_name)
else:
# 部分通道在检测 -> 黑色"已启动"(不是所有通道都在检测)
self.setStatusColumnBlackOnStarted(task_folder_name)
def initializeNewTaskRowGray(self, row_index):
"""
......@@ -1913,9 +1804,7 @@ class MissionTextStatus:
"""
try:
self._setRowColor(row_index, self.COLOR_GRAY, exclude_columns=[])
print(f"✅ [文本状态] 新建任务行 {row_index} 已初始化为灰色")
except Exception as e:
print(f"❌ [文本状态] 初始化新任务行颜色失败: {e}")
import traceback
traceback.print_exc()
......@@ -1952,7 +1841,6 @@ class MissionTextStatus:
current_status = status_item.text()
if current_status != new_status:
status_item.setText(new_status)
print(f"🔄 [状态刷新] 任务 {task_folder_name}: {current_status} → {new_status}")
# 🔥 更新文本颜色
if new_status == "未启动":
......@@ -1967,7 +1855,6 @@ class MissionTextStatus:
pass
except Exception as e:
print(f"❌ [文本状态] 刷新任务状态失败: {e}")
import traceback
traceback.print_exc()
......@@ -1995,7 +1882,8 @@ class MissionTextStatus:
return False
except Exception as e:
print(f"❌ [文本状态] 检查任务使用状态失败: {e}")
import traceback
traceback.print_exc()
return False
def _setRowColor(self, row_index, color, exclude_columns=None):
......@@ -2025,7 +1913,6 @@ class MissionTextStatus:
if item:
item.setForeground(color)
except Exception as e:
print(f"❌ [文本状态] 设置行颜色失败: {e}")
import traceback
traceback.print_exc()
......@@ -2050,5 +1937,6 @@ class MissionTextStatus:
return row
return -1
except Exception as e:
print(f"❌ [文本状态] 查找任务行失败: {e}")
import traceback
traceback.print_exc()
return -1
\ No newline at end of file
......@@ -34,11 +34,18 @@ class StorageThread:
Returns:
str: 项目根目录的绝对路径
"""
# 当前文件是 handlers/videopage/thread_manager/threads/storage_thread.py
# 需要向上5级到达项目根目录
current_file = os.path.abspath(__file__)
project_root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(current_file)))))
return project_root
import sys
# 🔥 打包后:使用 sys._MEIPASS 指向 _internal 目录
if getattr(sys, 'frozen', False):
# 打包环境:返回 _internal 目录
return sys._MEIPASS
else:
# 开发环境:当前文件是 handlers/videopage/thread_manager/threads/storage_thread.py
# 需要向上5级到达项目根目录
current_file = os.path.abspath(__file__)
project_root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(current_file)))))
return project_root
@staticmethod
def run(context, frame_rate: float, main_window=None):
......
......@@ -30,7 +30,7 @@ class ViewHandler:
def __init__(self, *args, **kwargs):
"""初始化视图处理器"""
super().__init__(*args, **kwargs)
# 曲线分析模式状态(False=实时检测模式, True=曲线分析模式
# 曲线分析模式状态(False=默认布局, True=曲线模式布局
self._is_curve_mode_active = False
@property
......@@ -60,17 +60,24 @@ class ViewHandler:
import os
import sys
# 动态获取项目根目录
# 🔥 动态获取数据目录(与storage_thread和curvepanel_handler保持一致)
if getattr(sys, 'frozen', False):
project_root = os.path.dirname(sys.executable)
# 打包后:使用 sys._MEIPASS 指向 _internal 目录
data_root = sys._MEIPASS
else:
try:
from database.config import get_project_root
project_root = get_project_root()
data_root = get_project_root()
except ImportError:
project_root = os.getcwd()
data_root = os.getcwd()
mission_path = os.path.join(data_root, 'database', 'mission_result', mission_name)
print(f"🔍 [_getCurveMissionPath] 任务名称: {mission_name}")
print(f"🔍 [_getCurveMissionPath] 数据根目录: {data_root}")
print(f"🔍 [_getCurveMissionPath] 完整路径: {mission_path}")
print(f"🔍 [_getCurveMissionPath] 路径是否存在: {os.path.exists(mission_path)}")
mission_path = os.path.join(project_root, 'database', 'mission_result', mission_name)
return mission_path if os.path.exists(mission_path) else None
def toggleToolBar(self):
......@@ -208,6 +215,9 @@ class ViewHandler:
def _switchToCurveLayout(self):
"""切换到曲线模式:根据检测线程状态选择合适的子布局"""
# 🔥 先设置模式为曲线模式(_video_layout_mode = 1),确保后续逻辑能正确判断
self._video_layout_mode = 1
# 🔥 检查检测线程状态,决定使用哪种子布局
detection_running = self._getCurrentDetectionState()
......@@ -216,20 +226,20 @@ class ViewHandler:
# 🔥 根据检测线程状态选择通道容器
if detection_running:
# 实时检测模式:使用通道面板容器(ChannelPanel)
# 同步布局:使用通道面板容器(ChannelPanel)
target_channel_widgets = self.channel_widgets_for_curve
layout_description = "实时检测模式"
layout_description = "同步布局"
else:
# 历史回放模式:使用历史视频面板容器(HistoryVideoPanel)
# 历史回放布局:使用历史视频面板容器(HistoryVideoPanel)
target_channel_widgets = self.history_channel_widgets_for_curve
layout_description = "历史回放模式"
layout_description = "历史回放布局"
# 🔥 根据检测线程状态选择要显示的面板类型
if detection_running:
# 实时检测模式:使用通道面板(ChannelPanel)
# 同步布局:使用通道面板(ChannelPanel)
panels_to_use = self.channelPanels
else:
# 历史回放模式:使用历史视频面板(HistoryVideoPanel)
# 历史回放布局:使用历史视频面板(HistoryVideoPanel)
if hasattr(self, 'historyVideoPanels'):
panels_to_use = self.historyVideoPanels
else:
......@@ -271,10 +281,7 @@ class ViewHandler:
wrapper.updateGeometry()
panel.updateGeometry()
# 先设置模式为曲线模式(_video_layout_mode = 1)
self._video_layout_mode = 1
# 切换到曲线模式主布局
# 切换到曲线模式主布局(_video_layout_mode已在方法开头设置为1)
self.videoLayoutStack.setCurrentIndex(1)
# 更新状态栏信息
......@@ -293,7 +300,7 @@ class ViewHandler:
# 🔥 根据检测线程状态更新通道显示
if detection_running:
# 实时检测模式:根据当前选择的任务更新通道显示
# 同步布局:根据当前选择的任务更新通道显示
if hasattr(self, 'curvemission'):
current_mission = self.curvemission.currentText()
if current_mission and current_mission != "请选择任务":
......@@ -306,7 +313,7 @@ class ViewHandler:
if hasattr(self, '_updateCurveChannelDisplay'):
self._updateCurveChannelDisplay([])
else:
# 🔥 历史回放模式:显示所有历史视频面板
# 🔥 历史回放布局:显示所有历史视频面板
if hasattr(self, '_updateCurveChannelDisplay'):
all_channels = ['通道1', '通道2', '通道3', '通道4']
self._updateCurveChannelDisplay(all_channels) # 显示所有4个历史视频面板
......@@ -322,7 +329,7 @@ class ViewHandler:
QtCore.QTimer.singleShot(100, self._loadCurveDataOrStartThreads)
def _switchToDefaultLayout(self):
"""切换到默认布局(实时检测模式)"""
"""切换到默认布局(任务表格 + 2x2通道面板)"""
# 🔥 停止所有曲线线程(切换回默认布局时)
self._stopAllCurveThreads()
......@@ -389,16 +396,16 @@ class ViewHandler:
return
if detection_running:
# 切换到实时检测布局(索引0)
# 切换到同步布局(曲线模式布局的索引0)
target_index = 0
layout_name = "实时检测布局"
mode_text = "实时检测"
layout_name = "同步布局"
mode_text = "同步"
mode_style = "font-weight: bold; padding: 2px 8px;"
curve_mode = 'realtime'
print(f"✅ [布局切换] 选择索引0 - 实时检测布局")
print(f"✅ [布局切换] 选择索引0 - 同步布局")
else:
# 切换到历史回放布局(索引1)
# 切换到历史回放布局(曲线模式布局的索引1)
target_index = 1
layout_name = "历史回放布局"
mode_text = "历史回放"
......@@ -425,18 +432,31 @@ class ViewHandler:
pass
# 🔥 禁用/启用通道面板的查看曲线按钮
# 索引0(实时检测模式)时禁用,索引1(历史回放模式)时启用
if hasattr(self, 'channelPanels'):
for panel in self.channelPanels:
if hasattr(panel, 'btnCurve'):
if target_index == 0:
# 实时检测模式:禁用查看曲线按钮
panel.btnCurve.setEnabled(False)
panel.btnCurve.setToolTip("实时检测模式下无法查看曲线")
else:
# 历史回放模式:启用查看曲线按钮
panel.btnCurve.setEnabled(True)
panel.btnCurve.setToolTip("查看曲线")
# 只有在曲线模式(_video_layout_mode==1)的子布局切换时才需要处理
# 子布局索引0(实时检测模式)时禁用,索引1(历史回放模式)时根据任务状态决定
if hasattr(self, '_video_layout_mode') and self._video_layout_mode == 1:
if hasattr(self, 'channelPanels'):
for panel in self.channelPanels:
if hasattr(panel, 'btnCurve'):
if target_index == 0:
# 曲线模式的同步布局:禁用查看曲线按钮
panel.btnCurve.setEnabled(False)
panel.btnCurve.setToolTip("同步布局下无法查看曲线")
else:
# 曲线模式的历史回放布局:检查通道是否有任务
has_task = False
if hasattr(panel, 'getTaskInfo'):
task_info = panel.getTaskInfo()
has_task = (task_info is not None and task_info != "未分配任务")
if has_task:
# 有任务:启用查看曲线按钮
panel.btnCurve.setEnabled(True)
panel.btnCurve.setToolTip("查看曲线")
else:
# 无任务:保持禁用
panel.btnCurve.setEnabled(False)
panel.btnCurve.setToolTip("请先分配任务")
# 执行布局切换
current_index = self.curveLayoutStack.currentIndex()
......
1曲线模式索引0布局,只显示根据curvemission筛选使用的通道面板失效了
1曲线模式索引0布局,只显示根据curvemission筛选使用的通道面板失效了
......@@ -66,4 +66,12 @@ class ModelLoadingProgressDialog(QDialog):
7: foam_air
- **容器底部**: foam掩码的最低点
- **容器顶部**: air掩码的最高点
- **适用场景**: 同时检测到泡沫和空气,未检测到液体
\ No newline at end of file
- **适用场景**: 同时检测到泡沫和空气,未检测到液体
通道面板的查看曲线按钮禁用逻辑,任务面板的查看曲线按钮一直启用
1channelmission为未分配任务时,包括曲线按钮在内所有通道面板按钮禁用。
2切换到曲线模式布局的索引0实时检测模式时,只禁用通道面板的查看曲线按钮
PAGE_VIDEO 实时检测界面名称管理
1.PAGE_VIDEO 实时检测管理页面页面名称,self._video_layout_mode = 0 任务表格 + 2x2通道面板(PAGE_VIDEO 的索引0)称为默认布局,self._video_layout_mode = 1垂直通道面板 + 曲线面板(PAGE_VIDEO 的索引1)为曲线模式布局
2.曲线模式布局子布局(self._video_layout_mode = 1的索引0)称为同步布局,曲线模式布局子布局(self._video_layout_mode = 1的索引1)为历史回放布局
\ No newline at end of file
......@@ -482,11 +482,12 @@ class ChannelPanel(QtWidgets.QWidget):
self._is_disabled = disabled
if disabled:
# 禁用所有按钮
# 禁用所有按钮(包括查看曲线按钮)
if hasattr(self, 'btnToggleConnect'):
self.btnToggleConnect.setEnabled(False)
if hasattr(self, 'btnCurve'):
self.btnCurve.setEnabled(False)
self.btnCurve.setToolTip("请先分配任务")
if hasattr(self, 'btnAmplify'):
self.btnAmplify.setEnabled(False)
if hasattr(self, 'btnEdit'):
......@@ -499,11 +500,34 @@ class ChannelPanel(QtWidgets.QWidget):
}
""")
else:
# 启用所有按钮
# 启用按钮
if hasattr(self, 'btnToggleConnect'):
self.btnToggleConnect.setEnabled(True)
# 🔥 查看曲线按钮:有任务时启用,但需要检查是否在曲线模式的实时检测子布局
# 只有在曲线模式(_video_layout_mode==1)且子布局索引0(实时检测)时才禁用
if hasattr(self, 'btnCurve'):
self.btnCurve.setEnabled(True)
should_enable_curve = True # 默认启用(因为已经有任务了)
try:
# 通过parent链向上查找主窗口
main_window = self.window()
# 检查是否在曲线模式主布局
if hasattr(main_window, '_video_layout_mode') and main_window._video_layout_mode == 1:
# 在曲线模式下,检查子布局索引
if hasattr(main_window, 'curveLayoutStack'):
current_index = main_window.curveLayoutStack.currentIndex()
# 子布局索引0是同步布局,禁用查看曲线按钮
if current_index == 0:
should_enable_curve = False
except:
pass
self.btnCurve.setEnabled(should_enable_curve)
if not should_enable_curve:
self.btnCurve.setToolTip("同步布局下无法查看曲线")
else:
self.btnCurve.setToolTip("查看曲线")
if hasattr(self, 'btnAmplify'):
self.btnAmplify.setEnabled(True)
if hasattr(self, 'btnEdit'):
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment