Commit e8ce54c3 by Yuhaibo

1

parent 34bd34cc
...@@ -777,7 +777,7 @@ class MainWindow( ...@@ -777,7 +777,7 @@ class MainWindow(
# 保存第一个面板的引用(兼容现有代码) # 保存第一个面板的引用(兼容现有代码)
self.channelPanel = self.channelPanels[0] if self.channelPanels else None self.channelPanel = self.channelPanels[0] if self.channelPanels else None
# 🔥 创建4个历史视频面板(用于曲线模式的历史回放布局) # 🔥 创建4个历史视频面板(用于曲线模式布局的历史回放子布局)
try: try:
from .widgets.videopage import HistoryVideoPanel from .widgets.videopage import HistoryVideoPanel
except ImportError: except ImportError:
...@@ -804,8 +804,8 @@ class MainWindow( ...@@ -804,8 +804,8 @@ class MainWindow(
"""创建曲线模式布局:左侧垂直排列通道 + 右侧曲线面板 """创建曲线模式布局:左侧垂直排列通道 + 右侧曲线面板
包含两个子布局: 包含两个子布局:
- 子布局0:实时检测模式(带任务选择和底部按钮) - 子布局0:同步布局(带任务选择和底部按钮)
- 子布局1:历史回放模式(无任务选择和底部按钮) - 子布局1:历史回放布局(无任务选择和底部按钮)
🔥 两个子布局共用同一个CurvePanel(右侧) 🔥 两个子布局共用同一个CurvePanel(右侧)
""" """
...@@ -836,12 +836,12 @@ class MainWindow( ...@@ -836,12 +836,12 @@ class MainWindow(
# 🔥 创建左侧子布局栈(实时检测 vs 历史回放) # 🔥 创建左侧子布局栈(实时检测 vs 历史回放)
self.curveLayoutStack = QtWidgets.QStackedWidget() self.curveLayoutStack = QtWidgets.QStackedWidget()
self.curveLayoutStack.setFixedWidth(660) self.curveLayoutStack.setFixedWidth(660)
self._curve_sub_layout_mode = 0 # 0=实时检测, 1=历史回放 self._curve_sub_layout_mode = 0 # 0=同步布局, 1=历史回放布局
# === 子布局0:实时检测模式(左侧通道列表)=== # === 子布局0:同步布局(左侧通道列表)===
self._createRealtimeCurveSubLayout() self._createRealtimeCurveSubLayout()
# === 子布局1:历史回放模式(左侧历史视频面板容器)=== # === 子布局1:历史回放布局(左侧历史视频面板容器)===
self._createHistoryCurveSubLayout() self._createHistoryCurveSubLayout()
# 布局结构:左侧子布局栈 + 右侧共用CurvePanel # 布局结构:左侧子布局栈 + 右侧共用CurvePanel
...@@ -873,7 +873,7 @@ class MainWindow( ...@@ -873,7 +873,7 @@ class MainWindow(
self.curve_channel_layout.setContentsMargins(5, 5, 5, 5) self.curve_channel_layout.setContentsMargins(5, 5, 5, 5)
self.curve_channel_layout.setSpacing(10) self.curve_channel_layout.setSpacing(10)
# 初始化通道包裹容器列表(实时检测模式 # 初始化通道包裹容器列表(同步布局
self.channel_widgets_for_curve = [] self.channel_widgets_for_curve = []
# 创建4个通道容器(初始隐藏,等待CSV文件加载) # 创建4个通道容器(初始隐藏,等待CSV文件加载)
...@@ -916,7 +916,7 @@ class MainWindow( ...@@ -916,7 +916,7 @@ class MainWindow(
self.history_channel_layout.setContentsMargins(5, 5, 5, 5) self.history_channel_layout.setContentsMargins(5, 5, 5, 5)
self.history_channel_layout.setSpacing(10) self.history_channel_layout.setSpacing(10)
# 初始化历史视频包裹容器列表(历史回放模式 # 初始化历史视频包裹容器列表(历史回放布局
self.history_channel_widgets_for_curve = [] self.history_channel_widgets_for_curve = []
# 创建4个历史视频容器(初始隐藏,等待CSV文件加载) # 创建4个历史视频容器(初始隐藏,等待CSV文件加载)
...@@ -965,10 +965,10 @@ class MainWindow( ...@@ -965,10 +965,10 @@ class MainWindow(
# 🔥 根据检测状态决定显示逻辑(而不是依赖 getCurveLoadMode) # 🔥 根据检测状态决定显示逻辑(而不是依赖 getCurveLoadMode)
if detection_running: if detection_running:
# 🔥 实时检测模式:只显示任务配置中使用的通道 # 🔥 同步布局:只显示任务配置中使用的通道
selected_channels = self._getTaskChannels(mission_name) selected_channels = self._getTaskChannels(mission_name)
else: else:
# 🔥 历史回放模式:显示所有通道容器 # 🔥 历史回放布局:显示所有通道容器
selected_channels = ['通道1', '通道2', '通道3', '通道4'] selected_channels = ['通道1', '通道2', '通道3', '通道4']
self._updateCurveChannelDisplay(selected_channels) self._updateCurveChannelDisplay(selected_channels)
...@@ -1087,11 +1087,11 @@ class MainWindow( ...@@ -1087,11 +1087,11 @@ class MainWindow(
# 🔥 根据当前曲线子布局模式选择要操作的容器 # 🔥 根据当前曲线子布局模式选择要操作的容器
if hasattr(self, '_curve_sub_layout_mode'): if hasattr(self, '_curve_sub_layout_mode'):
if self._curve_sub_layout_mode == 0: 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_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 target_container = self.curve_channel_container if hasattr(self, 'curve_channel_container') else None
else: 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_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 target_container = self.history_channel_container if hasattr(self, 'history_channel_container') else None
else: else:
...@@ -1131,7 +1131,6 @@ class MainWindow( ...@@ -1131,7 +1131,6 @@ class MainWindow(
if target_container: if target_container:
target_container.setFixedSize(640, total_height) target_container.setFixedSize(640, total_height)
print(f"[曲线布局] 已更新通道显示,显示 {visible_count} 个通道")
def _createModelPage(self): def _createModelPage(self):
"""创建模型管理页面""" """创建模型管理页面"""
......
...@@ -5,14 +5,14 @@ channel1: ...@@ -5,14 +5,14 @@ channel1:
height: 20mm height: 20mm
name: 通道1_区域1 name: 通道1_区域1
boxes: boxes:
- - 881 - - 855
- 799 - 737
- 192 - 192
fixed_bottoms: fixed_bottoms:
- 875 - 813
fixed_tops: fixed_tops:
- 722 - 660
last_updated: '2025-11-29 16:02:16' last_updated: '2025-11-29 17:01:13'
channel2: channel2:
annotation_count: 1 annotation_count: 1
areas: areas:
......
...@@ -17,23 +17,23 @@ channels: ...@@ -17,23 +17,23 @@ channels:
name: '4' name: '4'
channel2: channel2:
general: general:
task_id: '21' task_id: '123'
task_name: '321' task_name: '21'
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\123_21
channel3: channel3:
general: general:
task_id: '21' task_id: '123'
task_name: '321' task_name: '21'
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\123_21
channel4: channel4:
general: general:
task_id: '21' task_id: '1'
task_name: '321' task_name: '222'
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
channel1: channel1:
general: general:
task_id: '21' task_id: '1'
task_name: '321' task_name: '222'
area_count: 0 area_count: 0
safe_low: 2.0mm safe_low: 2.0mm
safe_high: 10.0mm safe_high: 10.0mm
...@@ -41,7 +41,7 @@ channel1: ...@@ -41,7 +41,7 @@ channel1:
video_format: AVI video_format: AVI
push_address: '' push_address: ''
video_path: '' 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: areas:
area_1: 通道1_区域1 area_1: 通道1_区域1
area_heights: area_heights:
......
2025-11-29-13:26:33.445 0.0 2025-11-29-13:26:33.445 0.0
...@@ -440,3 +440,39 @@ ...@@ -440,3 +440,39 @@
2025-11-29-13:28:03.293 16.6 2025-11-29-13:28:03.293 16.6
2025-11-29-13:28:03.550 17.1 2025-11-29-13:28:03.550 17.1
2025-11-29-13:28:03.718 16.3 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 @@ ...@@ -1710,3 +1710,165 @@
2025-11-29-13:51:30.788 0.0 2025-11-29-13:51:30.788 0.0
2025-11-29-13:51:30.985 0.0 2025-11-29-13:51:30.985 0.0
2025-11-29-13:51:31.195 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 @@ ...@@ -317,3 +317,40 @@
2025-11-29-15:49:07.323 0.0 2025-11-29-15:49:07.323 0.0
2025-11-29-15:49:07.844 0.0 2025-11-29-15:49:07.844 0.0
2025-11-29-15:49:08.416 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 @@ ...@@ -52,3 +52,53 @@
2025-11-29-15:49:07.323 0.0 2025-11-29-15:49:07.323 0.0
2025-11-29-15:49:07.850 0.0 2025-11-29-15:49:07.850 0.0
2025-11-29-15:49:08.419 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 @@ ...@@ -11,3 +11,71 @@
2025-11-29-15:49:07.323 0.0 2025-11-29-15:49:07.323 0.0
2025-11-29-15:49:07.844 0.0 2025-11-29-15:49:07.844 0.0
2025-11-29-15:49:08.416 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: ...@@ -877,19 +877,11 @@ class ChannelPanelHandler:
if panel: if panel:
panel.updateChannelStatus(channel_id, 'connected') panel.updateChannelStatus(channel_id, 'connected')
panel.setConnected(True) panel.setConnected(True)
pass
else:
pass
# 兼容单通道场景
if hasattr(self, 'channelPanel'):
self.channelPanel.updateChannelStatus(channel_id, 'connected')
self.channelPanel.setConnected(True)
self.statusBar().showMessage( self.statusBar().showMessage(
self.tr(" 通道已连接: {} - 视频流已启动").format(channel_id) self.tr(" 通道已连接: {} - 视频流已启动").format(channel_id)
) )
except Exception as e: except Exception as e:
pass
import traceback import traceback
traceback.print_exc() traceback.print_exc()
...@@ -941,12 +933,6 @@ class ChannelPanelHandler: ...@@ -941,12 +933,6 @@ class ChannelPanelHandler:
# 🔥 保留映射(不删除),以便任务同步时能找到面板 # 🔥 保留映射(不删除),以便任务同步时能找到面板
# del self._channel_panels_map[channel_id] # 注释掉,保留映射 # 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)) self.statusBar().showMessage(self.tr("⏹ 通道已断开: {}").format(channel_id))
def onChannelManage(self): def onChannelManage(self):
......
...@@ -280,15 +280,18 @@ class CurvePanelHandler: ...@@ -280,15 +280,18 @@ class CurvePanelHandler:
def addChannelData(self, channel_id, channel_name=None, window_name=None, color=None): def addChannelData(self, channel_id, channel_name=None, window_name=None, color=None):
""" """
添加通道数据管理(业务逻辑) 添加通道数据(业务逻辑)
Args: Args:
channel_id: 通道唯一ID channel_id: 通道ID
channel_name: 通道名称(可选) channel_name: 通道名称
window_name: 检测窗口名称(可选) window_name: 窗口名称
color: 曲线颜色(可选) color: 曲线颜色
""" """
print(f"➕ [添加通道] channel_id={channel_id}, channel_name={channel_name}, window_name={window_name}")
if channel_id in self.channel_data: if channel_id in self.channel_data:
print(f" - 通道已存在,跳过添加")
return return
# 默认名称 # 默认名称
...@@ -311,7 +314,11 @@ class CurvePanelHandler: ...@@ -311,7 +314,11 @@ class CurvePanelHandler:
# 通知UI创建通道 # 通知UI创建通道
if self.curve_panel: if self.curve_panel:
print(f" - 通知UI创建通道...")
self.curve_panel.addChannel(channel_id, channel_name, window_name, color) 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): def updateCurveData(self, channel_id, data_points):
""" """
...@@ -322,17 +329,21 @@ class CurvePanelHandler: ...@@ -322,17 +329,21 @@ class CurvePanelHandler:
data_points: 数据点列表 [{'timestamp': float, 'height_mm': float}, ...] data_points: 数据点列表 [{'timestamp': float, 'height_mm': float}, ...]
height_mm精度为0.1mm(保留1位小数) height_mm精度为0.1mm(保留1位小数)
""" """
print(f"📊 [更新曲线] channel_id={channel_id}, 数据点数量={len(data_points)}")
if not data_points: if not data_points:
# print(f"⚠️ [曲线数据更新] 数据点为空,channel_id={channel_id}") print(f" - ⚠️ 数据点为空,跳过更新")
return return
if channel_id not in self.channel_data: if channel_id not in self.channel_data:
print(f" - 通道不存在,先添加通道")
self.addChannelData(channel_id) self.addChannelData(channel_id)
channel = self.channel_data[channel_id] channel = self.channel_data[channel_id]
# 🔥 调试:记录更新前的数据点数 # 🔥 调试:记录更新前的数据点数
before_count = len(channel['time']) before_count = len(channel['time'])
print(f" - 更新前数据点数: {before_count}")
# 批量添加数据 # 批量添加数据
added_count = 0 added_count = 0
...@@ -350,6 +361,8 @@ class CurvePanelHandler: ...@@ -350,6 +361,8 @@ class CurvePanelHandler:
# 🔥 调试:记录添加后的数据点数 # 🔥 调试:记录添加后的数据点数
after_add_count = len(channel['time']) after_add_count = len(channel['time'])
print(f" - 实际添加数据点数: {added_count}")
print(f" - 更新后数据点数: {after_add_count}")
# 🔥 限制数据点数量 # 🔥 限制数据点数量
# - 'realtime' 模式:限制为3000个点(滚动窗口) # - 'realtime' 模式:限制为3000个点(滚动窗口)
...@@ -361,6 +374,7 @@ class CurvePanelHandler: ...@@ -361,6 +374,7 @@ class CurvePanelHandler:
before_limit = len(channel['time']) before_limit = len(channel['time'])
channel['time'] = channel['time'][-self.max_points:] channel['time'] = channel['time'][-self.max_points:]
channel['value'] = channel['value'][-self.max_points:] channel['value'] = channel['value'][-self.max_points:]
print(f" - 限制数据点: {before_limit} -> {len(channel['time'])}")
# 🔥 处理时间间隔断点:超过2分钟的数据点之间插入NaN断开连接 # 🔥 处理时间间隔断点:超过2分钟的数据点之间插入NaN断开连接
processed_time, processed_value = self._processTimeGaps( processed_time, processed_value = self._processTimeGaps(
...@@ -368,14 +382,17 @@ class CurvePanelHandler: ...@@ -368,14 +382,17 @@ class CurvePanelHandler:
channel['value'], channel['value'],
max_gap_seconds=120 # 2分钟 = 120秒 max_gap_seconds=120 # 2分钟 = 120秒
) )
print(f" - 处理后数据点数: {len(processed_time)}")
# 更新UI显示(只更新一次) # 更新UI显示(只更新一次)
if self.curve_panel: if self.curve_panel:
print(f" - 开始更新UI显示...")
self.curve_panel.updateCurveDisplay( self.curve_panel.updateCurveDisplay(
channel_id, channel_id,
processed_time, processed_time,
processed_value processed_value
) )
print(f" - UI显示更新完成")
# 更新X轴范围 # 更新X轴范围
if channel['time']: if channel['time']:
...@@ -386,6 +403,8 @@ class CurvePanelHandler: ...@@ -386,6 +403,8 @@ class CurvePanelHandler:
if channel['value']: if channel['value']:
max_value = max(channel['value']) max_value = max(channel['value'])
self.curve_panel.setYRangeAuto(max_value) self.curve_panel.setYRangeAuto(max_value)
else:
print(f" - ⚠️ curve_panel不存在,无法更新UI!")
def _processTimeGaps(self, time_data, value_data, max_gap_seconds=120): def _processTimeGaps(self, time_data, value_data, max_gap_seconds=120):
...@@ -694,23 +713,23 @@ class CurvePanelHandler: ...@@ -694,23 +713,23 @@ class CurvePanelHandler:
try: try:
import sys import sys
# 动态获取项目根目录 # 🔥 动态获取数据目录(与storage_thread保持一致)
if getattr(sys, 'frozen', False): if getattr(sys, 'frozen', False):
# 打包后的exe # 打包后:使用 sys._MEIPASS 指向 _internal 目录
project_root = os.path.dirname(sys.executable) data_root = sys._MEIPASS
else: else:
# 开发环境:基于配置模块获取 # 开发环境:基于配置模块获取
try: try:
from database.config import get_project_root from database.config import get_project_root
project_root = get_project_root() data_root = get_project_root()
except ImportError: 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"🔍 [路径构建] 任务名称: {mission_name}")
print(f"🔍 [路径构建] 项目根目录: {project_root}") print(f"🔍 [路径构建] 数据根目录: {data_root}")
print(f"🔍 [路径构建] 完整路径: {mission_folder_path}") print(f"🔍 [路径构建] 完整路径: {mission_folder_path}")
print(f"🔍 [路径构建] 路径是否存在: {os.path.exists(mission_folder_path)}") print(f"🔍 [路径构建] 路径是否存在: {os.path.exists(mission_folder_path)}")
...@@ -769,22 +788,22 @@ class CurvePanelHandler: ...@@ -769,22 +788,22 @@ class CurvePanelHandler:
import sys import sys
try: try:
# 动态获取项目根目录 # 🔥 动态获取数据目录(与storage_thread保持一致)
if getattr(sys, 'frozen', False): if getattr(sys, 'frozen', False):
# 打包后的exe # 打包后:使用 sys._MEIPASS 指向 _internal 目录
project_root = os.path.dirname(sys.executable) data_root = sys._MEIPASS
else: else:
# 开发环境:基于配置模块获取 # 开发环境:基于配置模块获取
try: try:
from database.config import get_project_root from database.config import get_project_root
project_root = get_project_root() data_root = get_project_root()
except ImportError: except ImportError:
# 后备方案:当前工作目录 # 后备方案:当前工作目录
project_root = os.getcwd() data_root = os.getcwd()
# 构建 mission_result 目录路径 # 构建 mission_result 目录路径
mission_result_dir = os.path.join(project_root, 'database', 'mission_result') mission_result_dir = os.path.join(data_root, 'database', 'mission_result')
print(f"🔍 [任务列表] 项目根目录: {project_root}") print(f"🔍 [任务列表] 数据根目录: {data_root}")
print(f"🔍 [任务列表] 任务目录: {mission_result_dir}") print(f"🔍 [任务列表] 任务目录: {mission_result_dir}")
print(f"🔍 [任务列表] 目录是否存在: {os.path.exists(mission_result_dir)}") print(f"🔍 [任务列表] 目录是否存在: {os.path.exists(mission_result_dir)}")
...@@ -842,6 +861,7 @@ class CurvePanelHandler: ...@@ -842,6 +861,7 @@ class CurvePanelHandler:
self.setCurveLoadMode('history') self.setCurveLoadMode('history')
# 查找所有CSV文件 # 查找所有CSV文件
print(f"\n🔍 [曲线加载] ==================== 开始加载 ====================")
print(f"🔍 [曲线加载] 扫描目录: {data_directory}") print(f"🔍 [曲线加载] 扫描目录: {data_directory}")
print(f"🔍 [曲线加载] 目录是否存在: {os.path.exists(data_directory)}") print(f"🔍 [曲线加载] 目录是否存在: {os.path.exists(data_directory)}")
...@@ -849,7 +869,11 @@ class CurvePanelHandler: ...@@ -849,7 +869,11 @@ class CurvePanelHandler:
print(f"❌ [曲线加载] 目录不存在: {data_directory}") print(f"❌ [曲线加载] 目录不存在: {data_directory}")
return False 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}") print(f"🔍 [曲线加载] 找到 {len(csv_files)} 个CSV文件: {csv_files}")
if not csv_files: if not csv_files:
...@@ -889,11 +913,13 @@ class CurvePanelHandler: ...@@ -889,11 +913,13 @@ class CurvePanelHandler:
print(f"✅ [进度条] 已显示进度对话框") print(f"✅ [进度条] 已显示进度对话框")
# 创建并启动后台加载线程 # 创建并启动后台加载线程
print(f"🧵 [曲线加载] 创建后台加载线程...")
self._load_thread = CurveDataLoadThread( self._load_thread = CurveDataLoadThread(
data_directory=data_directory, data_directory=data_directory,
csv_files=csv_files, csv_files=csv_files,
handler=self handler=self
) )
print(f"🧵 [曲线加载] 后台线程已创建")
# 连接信号(使用Qt.QueuedConnection确保跨线程安全) # 连接信号(使用Qt.QueuedConnection确保跨线程安全)
self._load_thread.progress_updated.connect( self._load_thread.progress_updated.connect(
...@@ -911,7 +937,10 @@ class CurvePanelHandler: ...@@ -911,7 +937,10 @@ class CurvePanelHandler:
) )
# 启动线程 # 启动线程
print(f"🚀 [曲线加载] 启动后台线程...")
self._load_thread.start() self._load_thread.start()
print(f"✅ [曲线数据加载] 后台加载线程已启动")
print(f"🔍 [曲线加载] ==================== 加载流程启动完成 ====================\n")
return True return True
...@@ -928,17 +957,28 @@ class CurvePanelHandler: ...@@ -928,17 +957,28 @@ class CurvePanelHandler:
def _onFileLoaded(self, channel_id, channel_name, window_name, color, data_points): 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: if channel_id not in self.channel_data:
print(f" - 添加新通道: {channel_id}")
self.addChannelData( self.addChannelData(
channel_id=channel_id, channel_id=channel_id,
channel_name=channel_name, channel_name=channel_name,
window_name=window_name, window_name=window_name,
color=color color=color
) )
else:
print(f" - 通道已存在: {channel_id}")
# 批量更新曲线数据 # 批量更新曲线数据
print(f" - 开始更新曲线数据...")
self.updateCurveData(channel_id, data_points) self.updateCurveData(channel_id, data_points)
print(f" - 曲线数据更新完成")
def _onLoadFinished(self, progress_dialog, success, count): def _onLoadFinished(self, progress_dialog, success, count):
"""处理加载完成""" """处理加载完成"""
......
...@@ -174,7 +174,6 @@ class MissionPanelHandler: ...@@ -174,7 +174,6 @@ class MissionPanelHandler:
self.mission_panel.btn_debug.setEnabled(False) self.mission_panel.btn_debug.setEnabled(False)
except Exception as e: except Exception as e:
pass
import traceback import traceback
traceback.print_exc() traceback.print_exc()
# 出错时默认隐藏调试按钮,确保安全性 # 出错时默认隐藏调试按钮,确保安全性
...@@ -467,8 +466,6 @@ class MissionPanelHandler: ...@@ -467,8 +466,6 @@ class MissionPanelHandler:
# 🔥 第一步:同步到 channel_config.yaml(独立业务,无论通道是否打开都执行) # 🔥 第一步:同步到 channel_config.yaml(独立业务,无论通道是否打开都执行)
sync_success = self._syncTaskToConfigFile(channel_id, task_id, task_name, save_liquid_data_path) sync_success = self._syncTaskToConfigFile(channel_id, task_id, task_name, save_liquid_data_path)
pass
# 🔥 第二步:更新通道面板UI(可选操作,仅在通道已打开时执行) # 🔥 第二步:更新通道面板UI(可选操作,仅在通道已打开时执行)
# 检查是否有通道面板映射(从ChannelPanelHandler获取) # 检查是否有通道面板映射(从ChannelPanelHandler获取)
if not hasattr(self, '_channel_panels_map'): if not hasattr(self, '_channel_panels_map'):
...@@ -1377,45 +1374,31 @@ class MissionPanelHandler: ...@@ -1377,45 +1374,31 @@ class MissionPanelHandler:
channel_id: 通道ID(如 'channel1') channel_id: 通道ID(如 'channel1')
task_folder_name: 任务文件夹名称(如 '1_1') task_folder_name: 任务文件夹名称(如 '1_1')
""" """
try:
# 从 channel_id 提取通道编号 # 从 channel_id 提取通道编号
if not channel_id.startswith('channel'): if not channel_id.startswith('channel'):
print(f"❌ [_updateChannelMissionLabel] 无效的通道ID: {channel_id}") return
return
channel_num = int(channel_id.replace('channel', ''))
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'): else:
self.updateMissionLabelByVar(channel_num, task_folder_name) # 备用方案:直接通过变量名更新
print(f"✅ [多任务] 已更新 {channel_id} 的任务标签: {task_folder_name}") 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: panel = self._channel_panels_map.get(channel_id)
# 备用方案:直接通过变量名更新 if panel and hasattr(panel, '_positionTaskLabel'):
mission_var_name = f'channel{channel_num}mission' panel._positionTaskLabel()
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()
def _isTaskInUse(self, task_folder_name): def _isTaskInUse(self, task_folder_name):
""" """
...@@ -1427,32 +1410,27 @@ class MissionPanelHandler: ...@@ -1427,32 +1410,27 @@ class MissionPanelHandler:
Returns: Returns:
bool: 如果任务被使用返回True,否则返回False bool: 如果任务被使用返回True,否则返回False
""" """
try: # 检查所有通道(channel1-channel4)
# 检查所有通道(channel1-channel4) for channel_num in range(1, 5):
for channel_num in range(1, 5): channel_id = f'channel{channel_num}'
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
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: # 方法2:检查通道面板内存(备用)
print(f"❌ [状态检查] 检查任务使用状态失败: {e}") if hasattr(self, '_channel_panels_map'):
return False 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): def _refreshAllTaskStatus(self):
""" """
...@@ -1460,13 +1438,8 @@ class MissionPanelHandler: ...@@ -1460,13 +1438,8 @@ class MissionPanelHandler:
委托给 MissionTextStatus 类处理 委托给 MissionTextStatus 类处理
""" """
try: if self.mission_text_status:
if self.mission_text_status: self.mission_text_status.refreshAllTaskStatus(self)
self.mission_text_status.refreshAllTaskStatus(self)
except Exception as e:
print(f"❌ [状态刷新] 刷新任务状态失败: {e}")
import traceback
traceback.print_exc()
def _updateTaskStatus(self, task_folder_name, new_status): def _updateTaskStatus(self, task_folder_name, new_status):
""" """
...@@ -1476,39 +1449,30 @@ class MissionPanelHandler: ...@@ -1476,39 +1449,30 @@ class MissionPanelHandler:
task_folder_name: 任务文件夹名称(如 "21321_312312") task_folder_name: 任务文件夹名称(如 "21321_312312")
new_status: 新状态(如 "已启动") new_status: 新状态(如 "已启动")
""" """
try: if not hasattr(self, 'mission_panel'):
if not hasattr(self, 'mission_panel'): return
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) # 任务名称列
# 遍历任务面板的所有行,查找匹配的任务 if task_id_item and task_name_item:
table = self.mission_panel.table # 构建任务文件夹名称进行匹配
for row in range(table.rowCount()): current_task_folder = f"{task_id_item.text()}_{task_name_item.text()}"
# 获取任务编号和任务名称
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 current_task_folder == task_folder_name: if current_task_folder == task_folder_name:
# 找到匹配的任务,更新状态列(第2列) # 找到匹配的任务,更新状态列(第2列)
status_item = table.item(row, 2) status_item = table.item(row, 2)
if status_item: if status_item:
old_status = status_item.text() old_status = status_item.text()
status_item.setText(new_status) status_item.setText(new_status)
print(f"✅ [_updateTaskStatus] 任务 {task_folder_name} 状态已更新: {old_status} → {new_status}")
# 同时更新配置文件中的状态
# 同时更新配置文件中的状态 self._updateTaskConfigStatus(task_id_item.text(), new_status)
self._updateTaskConfigStatus(task_id_item.text(), new_status) return
return
print(f"⚠️ [_updateTaskStatus] 未找到任务: {task_folder_name}")
except Exception as e:
print(f"❌ [_updateTaskStatus] 更新任务状态失败: {e}")
import traceback
traceback.print_exc()
# 🔥 已删除 _updateRowColor 和 _updateRowColorForQTableWidgetItem 方法 # 🔥 已删除 _updateRowColor 和 _updateRowColorForQTableWidgetItem 方法
# 所有文本颜色管理现在由 MissionTextStatus 类统一处理 # 所有文本颜色管理现在由 MissionTextStatus 类统一处理
...@@ -1519,17 +1483,8 @@ class MissionPanelHandler: ...@@ -1519,17 +1483,8 @@ class MissionPanelHandler:
委托给 MissionTextStatus 类处理所有文本颜色更新 委托给 MissionTextStatus 类处理所有文本颜色更新
""" """
print(f"🎯 [_updateChannelColumnColor] 方法被调用") if self.mission_text_status:
try: self.mission_text_status.updateAllChannelColumnColors(self)
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()
def _updateTaskConfigStatus(self, task_id, new_status): def _updateTaskConfigStatus(self, task_id, new_status):
""" """
...@@ -1539,38 +1494,29 @@ class MissionPanelHandler: ...@@ -1539,38 +1494,29 @@ class MissionPanelHandler:
task_id: 任务编号 task_id: 任务编号
new_status: 新状态 new_status: 新状态
""" """
try: from database.config import get_project_root
from database.config import get_project_root import yaml
import yaml import os
import os
config_dir = os.path.join(get_project_root(), 'database', 'config', 'mission')
config_dir = os.path.join(get_project_root(), 'database', 'config', 'mission')
# 查找对应的任务配置文件
# 查找对应的任务配置文件 for filename in os.listdir(config_dir):
for filename in os.listdir(config_dir): if filename.endswith('.yaml') and filename.startswith(f"{task_id}_"):
if filename.endswith('.yaml') and filename.startswith(f"{task_id}_"): config_path = os.path.join(config_dir, filename)
config_path = os.path.join(config_dir, filename)
# 读取配置文件
# 读取配置文件 with open(config_path, 'r', encoding='utf-8') as f:
with open(config_path, 'r', encoding='utf-8') as f: config_data = yaml.safe_load(f) or {}
config_data = yaml.safe_load(f) or {}
# 更新状态
# 更新状态 config_data['status'] = new_status
config_data['status'] = new_status
# 写回配置文件
# 写回配置文件 with open(config_path, 'w', encoding='utf-8') as f:
with open(config_path, 'w', encoding='utf-8') as f: yaml.safe_dump(config_data, f, allow_unicode=True, default_flow_style=False)
yaml.safe_dump(config_data, f, allow_unicode=True, default_flow_style=False)
return
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()
def _refreshCurveMissionList(self): def _refreshCurveMissionList(self):
""" """
...@@ -1578,36 +1524,31 @@ class MissionPanelHandler: ...@@ -1578,36 +1524,31 @@ class MissionPanelHandler:
从 mission_result 目录重新扫描任务文件夹并更新下拉框 从 mission_result 目录重新扫描任务文件夹并更新下拉框
""" """
try: # 如果有 curvePanelHandler,调用其 loadMissionFolders 方法
# 如果有 curvePanelHandler,调用其 loadMissionFolders 方法 if hasattr(self, 'curvePanelHandler') and self.curvePanelHandler:
if hasattr(self, 'curvePanelHandler') and self.curvePanelHandler: self.curvePanelHandler.loadMissionFolders()
self.curvePanelHandler.loadMissionFolders() # 否则尝试直接调用 curvePanel 的方法
# 否则尝试直接调用 curvePanel 的方法 elif hasattr(self, 'curvePanel') and self.curvePanel:
elif hasattr(self, 'curvePanel') and self.curvePanel: # 手动扫描任务文件夹
# 手动扫描任务文件夹 import sys
import sys project_root = get_project_root()
project_root = get_project_root() mission_result_dir = os.path.join(project_root, 'database', 'mission_result')
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 = [] mission_folders.sort(key=lambda x: x['name'])
for item in os.listdir(mission_result_dir):
item_path = os.path.join(mission_result_dir, item) # 更新下拉框
if os.path.isdir(item_path): self.curvePanel.updateMissionFolderList(mission_folders)
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()
class MissionTextStatus: class MissionTextStatus:
...@@ -1647,13 +1588,8 @@ class MissionTextStatus: ...@@ -1647,13 +1588,8 @@ class MissionTextStatus:
""" """
1. 初始化所有任务行文本为灰色 1. 初始化所有任务行文本为灰色
""" """
try: for row in range(self.table.rowCount()):
for row in range(self.table.rowCount()): self._setRowColor(row, self.COLOR_GRAY, exclude_columns=[])
self._setRowColor(row, self.COLOR_GRAY, exclude_columns=[])
except Exception as e:
print(f"❌ [文本状态] 初始化失败: {e}")
import traceback
traceback.print_exc()
def setRowBlackOnSelect(self, row_index): def setRowBlackOnSelect(self, row_index):
""" """
...@@ -1664,14 +1600,10 @@ class MissionTextStatus: ...@@ -1664,14 +1600,10 @@ class MissionTextStatus:
Args: Args:
row_index: 选中的行索引 row_index: 选中的行索引
""" """
try:
# 🔥 不再恢复之前选中行的颜色,所有"已启动"的任务点击后都保持黑色 # 🔥 不再恢复之前选中行的颜色,所有"已启动"的任务点击后都保持黑色
# 将新选中的行置为黑色 # 将新选中的行置为黑色
self._setRowColor(row_index, self.COLOR_BLACK, exclude_columns=[2]) # 排除状态列 self._setRowColor(row_index, self.COLOR_BLACK, exclude_columns=[2]) # 排除状态列
self.selected_row = row_index self.selected_row = row_index
except Exception as e:
import traceback
traceback.print_exc()
def setStatusColumnGreenOnDetection(self, task_folder_name): def setStatusColumnGreenOnDetection(self, task_folder_name):
""" """
...@@ -1680,20 +1612,15 @@ class MissionTextStatus: ...@@ -1680,20 +1612,15 @@ class MissionTextStatus:
Args: Args:
task_folder_name: 任务文件夹名称(如 "1_1") task_folder_name: 任务文件夹名称(如 "1_1")
""" """
try:
# 查找对应的任务行 # 查找对应的任务行
row_index = self._findTaskRow(task_folder_name) row_index = self._findTaskRow(task_folder_name)
if row_index >= 0: if row_index >= 0:
status_item = self.table.item(row_index, 2) # 状态列索引为2 status_item = self.table.item(row_index, 2) # 状态列索引为2
if status_item: if status_item:
status_item.setText("检测中") status_item.setText("检测中")
status_item.setForeground(self.COLOR_GREEN) status_item.setForeground(self.COLOR_GREEN)
else:
print(f"⚠️ [文本状态] 未找到任务 {task_folder_name}")
except Exception as e:
print(f"❌ [文本状态] 设置检测状态颜色失败: {e}")
import traceback
traceback.print_exc()
def resetStatusColumnOnStopDetection(self, task_folder_name): def resetStatusColumnOnStopDetection(self, task_folder_name):
""" """
...@@ -1702,20 +1629,16 @@ class MissionTextStatus: ...@@ -1702,20 +1629,16 @@ class MissionTextStatus:
Args: Args:
task_folder_name: 任务文件夹名称(如 "1_1") task_folder_name: 任务文件夹名称(如 "1_1")
""" """
try: row_index = self._findTaskRow(task_folder_name)
row_index = self._findTaskRow(task_folder_name) if row_index >= 0:
if row_index >= 0: status_item = self.table.item(row_index, 2)
status_item = self.table.item(row_index, 2) if status_item:
if status_item: status_item.setText("已启动")
status_item.setText("已启动") # 如果是选中行,保持黑色;否则恢复为灰色
# 如果是选中行,保持黑色;否则恢复为灰色 if row_index == self.selected_row:
if row_index == self.selected_row: status_item.setForeground(self.COLOR_BLACK)
status_item.setForeground(self.COLOR_BLACK) else:
else: status_item.setForeground(self.COLOR_GRAY)
status_item.setForeground(self.COLOR_GRAY)
except Exception as e:
import traceback
traceback.print_exc()
def setStatusColumnBlackOnStarted(self, task_folder_name): def setStatusColumnBlackOnStarted(self, task_folder_name):
""" """
...@@ -1726,18 +1649,12 @@ class MissionTextStatus: ...@@ -1726,18 +1649,12 @@ class MissionTextStatus:
Args: Args:
task_folder_name: 任务文件夹名称(如 "1_1") task_folder_name: 任务文件夹名称(如 "1_1")
""" """
try: row_index = self._findTaskRow(task_folder_name)
row_index = self._findTaskRow(task_folder_name) if row_index >= 0:
if row_index >= 0: status_item = self.table.item(row_index, 2)
status_item = self.table.item(row_index, 2) if status_item:
if status_item: status_item.setText("已启动")
status_item.setText("已启动") status_item.setForeground(self.COLOR_BLACK)
status_item.setForeground(self.COLOR_BLACK)
print(f"✅ [文本状态] 任务 {task_folder_name} 状态列已切换为黑色(已启动)")
except Exception as e:
print(f"❌ [文本状态] 设置状态列为已启动失败: {e}")
import traceback
traceback.print_exc()
def setChannelColumnGreenOnDetection(self, task_folder_name, channel_num): def setChannelColumnGreenOnDetection(self, task_folder_name, channel_num):
""" """
...@@ -1747,23 +1664,15 @@ class MissionTextStatus: ...@@ -1747,23 +1664,15 @@ class MissionTextStatus:
task_folder_name: 任务文件夹名称(如 "1_1") task_folder_name: 任务文件夹名称(如 "1_1")
channel_num: 通道编号(1-4) channel_num: 通道编号(1-4)
""" """
try: # 查找对应的任务行
# 查找对应的任务行 row_index = self._findTaskRow(task_folder_name)
row_index = self._findTaskRow(task_folder_name) if row_index >= 0:
if row_index >= 0: # 通道列从第3列开始(0:任务编号, 1:任务名称, 2:状态, 3-6:通道1-4)
# 通道列从第3列开始(0:任务编号, 1:任务名称, 2:状态, 3-6:通道1-4) col_index = 3 + (channel_num - 1)
col_index = 3 + (channel_num - 1)
channel_item = self.table.item(row_index, col_index)
channel_item = self.table.item(row_index, col_index) if channel_item:
if channel_item: channel_item.setForeground(self.COLOR_GREEN)
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()
def resetChannelColumnOnStopDetection(self, task_folder_name, channel_num): def resetChannelColumnOnStopDetection(self, task_folder_name, channel_num):
""" """
...@@ -1773,22 +1682,16 @@ class MissionTextStatus: ...@@ -1773,22 +1682,16 @@ class MissionTextStatus:
task_folder_name: 任务文件夹名称(如 "1_1") task_folder_name: 任务文件夹名称(如 "1_1")
channel_num: 通道编号(1-4) channel_num: 通道编号(1-4)
""" """
try: row_index = self._findTaskRow(task_folder_name)
row_index = self._findTaskRow(task_folder_name) if row_index >= 0:
if row_index >= 0: col_index = 3 + (channel_num - 1)
col_index = 3 + (channel_num - 1) channel_item = self.table.item(row_index, col_index)
channel_item = self.table.item(row_index, col_index) if channel_item:
if channel_item: # 如果是选中行,恢复为黑色;否则恢复为灰色
# 如果是选中行,恢复为黑色;否则恢复为灰色 if row_index == self.selected_row:
if row_index == self.selected_row: channel_item.setForeground(self.COLOR_BLACK)
channel_item.setForeground(self.COLOR_BLACK) else:
else: channel_item.setForeground(self.COLOR_GRAY)
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()
def updateAllChannelColumnColors(self, main_window): def updateAllChannelColumnColors(self, main_window):
""" """
...@@ -1802,107 +1705,95 @@ class MissionTextStatus: ...@@ -1802,107 +1705,95 @@ class MissionTextStatus:
Args: Args:
main_window: 主窗口实例,用于访问通道任务标签和检测状态 main_window: 主窗口实例,用于访问通道任务标签和检测状态
""" """
try: # 🔥 第一步:收集所有通道当前正在执行的任务
print(f"🔍 [文本状态] 开始更新通道列颜色") active_tasks = set() # 存储正在执行的任务名称
# 🔥 第一步:收集所有通道当前正在执行的任务 channel_task_map = {} # 通道 -> 任务映射
active_tasks = set() # 存储正在执行的任务名称
channel_task_map = {} # 通道 -> 任务映射 for channel_num in range(1, 5):
channel_id = f'channel{channel_num}'
for channel_num in range(1, 5): mission_var_name = f'{channel_id}mission'
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}")
# 🔥 第二步:遍历所有任务行,更新通道列颜色 if hasattr(main_window, mission_var_name):
for row in range(self.table.rowCount()): mission_label = getattr(main_window, mission_var_name)
task_id_item = self.table.item(row, 0) current_task = mission_label.text()
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 current_task and current_task != "未分配任务":
task_folder_name = f"{task_id_item.text()}_{task_name_item.text()}" 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}'
# 🔥 处理所有任务的通道列(包括正在执行和未执行的) # 检查该通道是否正在执行这个任务
# 获取该任务使用的通道列表(从表格中读取) if channel_task_map.get(channel_id) == task_folder_name:
task_channels = [] # 检查该通道的检测状态
for ch_idx in range(1, 5): detect_var_name = f'{channel_id}detect'
col_idx = 3 + (ch_idx - 1) if hasattr(main_window, detect_var_name):
ch_item = self.table.item(row, col_idx) is_detecting = getattr(main_window, detect_var_name)
if ch_item and ch_item.text(): if is_detecting:
task_channels.append(ch_idx) # 🔥 设置对应通道列为绿色
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: for channel_num in task_channels:
channel_id = f'channel{channel_num}' channel_id = f'channel{channel_num}'
# 检查该通道是否正在执行这个任务 # 检查该通道是否正在执行这个任务
if channel_task_map.get(channel_id) == task_folder_name: if channel_task_map.get(channel_id) == task_folder_name:
assigned_channels_count += 1
# 检查该通道的检测状态 # 检查该通道的检测状态
detect_var_name = f'{channel_id}detect' detect_var_name = f'{channel_id}detect'
if hasattr(main_window, detect_var_name): if hasattr(main_window, detect_var_name):
is_detecting = getattr(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: if is_detecting:
# 🔥 设置对应通道列为绿色 detecting_channels_count += 1
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: # 只有当分配给该任务的所有通道都在检测时,才设置为绿色"检测中"
# 检查该任务使用的所有通道是否都在检测 if assigned_channels_count > 0 and detecting_channels_count == assigned_channels_count:
# 只统计分配给该任务的通道 # 所有分配的通道都在检测中 -> 绿色"检测中"
assigned_channels_count = 0 # 分配给该任务的通道数 self.setStatusColumnGreenOnDetection(task_folder_name)
detecting_channels_count = 0 # 正在检测的通道数 elif detecting_channels_count == 0:
# 所有通道都未检测,但任务已分配 -> 黑色"已启动"
for channel_num in task_channels: self.setStatusColumnBlackOnStarted(task_folder_name)
channel_id = f'channel{channel_num}' else:
# 部分通道在检测 -> 黑色"已启动"(不是所有通道都在检测)
# 检查该通道是否正在执行这个任务 self.setStatusColumnBlackOnStarted(task_folder_name)
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()
def initializeNewTaskRowGray(self, row_index): def initializeNewTaskRowGray(self, row_index):
""" """
...@@ -1913,9 +1804,7 @@ class MissionTextStatus: ...@@ -1913,9 +1804,7 @@ class MissionTextStatus:
""" """
try: try:
self._setRowColor(row_index, self.COLOR_GRAY, exclude_columns=[]) self._setRowColor(row_index, self.COLOR_GRAY, exclude_columns=[])
print(f"✅ [文本状态] 新建任务行 {row_index} 已初始化为灰色")
except Exception as e: except Exception as e:
print(f"❌ [文本状态] 初始化新任务行颜色失败: {e}")
import traceback import traceback
traceback.print_exc() traceback.print_exc()
...@@ -1952,7 +1841,6 @@ class MissionTextStatus: ...@@ -1952,7 +1841,6 @@ class MissionTextStatus:
current_status = status_item.text() current_status = status_item.text()
if current_status != new_status: if current_status != new_status:
status_item.setText(new_status) status_item.setText(new_status)
print(f"🔄 [状态刷新] 任务 {task_folder_name}: {current_status} → {new_status}")
# 🔥 更新文本颜色 # 🔥 更新文本颜色
if new_status == "未启动": if new_status == "未启动":
...@@ -1967,7 +1855,6 @@ class MissionTextStatus: ...@@ -1967,7 +1855,6 @@ class MissionTextStatus:
pass pass
except Exception as e: except Exception as e:
print(f"❌ [文本状态] 刷新任务状态失败: {e}")
import traceback import traceback
traceback.print_exc() traceback.print_exc()
...@@ -1995,7 +1882,8 @@ class MissionTextStatus: ...@@ -1995,7 +1882,8 @@ class MissionTextStatus:
return False return False
except Exception as e: except Exception as e:
print(f"❌ [文本状态] 检查任务使用状态失败: {e}") import traceback
traceback.print_exc()
return False return False
def _setRowColor(self, row_index, color, exclude_columns=None): def _setRowColor(self, row_index, color, exclude_columns=None):
...@@ -2025,7 +1913,6 @@ class MissionTextStatus: ...@@ -2025,7 +1913,6 @@ class MissionTextStatus:
if item: if item:
item.setForeground(color) item.setForeground(color)
except Exception as e: except Exception as e:
print(f"❌ [文本状态] 设置行颜色失败: {e}")
import traceback import traceback
traceback.print_exc() traceback.print_exc()
...@@ -2050,5 +1937,6 @@ class MissionTextStatus: ...@@ -2050,5 +1937,6 @@ class MissionTextStatus:
return row return row
return -1 return -1
except Exception as e: except Exception as e:
print(f"❌ [文本状态] 查找任务行失败: {e}") import traceback
traceback.print_exc()
return -1 return -1
\ No newline at end of file
...@@ -34,11 +34,18 @@ class StorageThread: ...@@ -34,11 +34,18 @@ class StorageThread:
Returns: Returns:
str: 项目根目录的绝对路径 str: 项目根目录的绝对路径
""" """
# 当前文件是 handlers/videopage/thread_manager/threads/storage_thread.py import sys
# 需要向上5级到达项目根目录
current_file = os.path.abspath(__file__) # 🔥 打包后:使用 sys._MEIPASS 指向 _internal 目录
project_root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(current_file))))) if getattr(sys, 'frozen', False):
return project_root # 打包环境:返回 _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 @staticmethod
def run(context, frame_rate: float, main_window=None): def run(context, frame_rate: float, main_window=None):
......
...@@ -30,7 +30,7 @@ class ViewHandler: ...@@ -30,7 +30,7 @@ class ViewHandler:
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
"""初始化视图处理器""" """初始化视图处理器"""
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
# 曲线分析模式状态(False=实时检测模式, True=曲线分析模式 # 曲线分析模式状态(False=默认布局, True=曲线模式布局
self._is_curve_mode_active = False self._is_curve_mode_active = False
@property @property
...@@ -60,17 +60,24 @@ class ViewHandler: ...@@ -60,17 +60,24 @@ class ViewHandler:
import os import os
import sys import sys
# 动态获取项目根目录 # 🔥 动态获取数据目录(与storage_thread和curvepanel_handler保持一致)
if getattr(sys, 'frozen', False): if getattr(sys, 'frozen', False):
project_root = os.path.dirname(sys.executable) # 打包后:使用 sys._MEIPASS 指向 _internal 目录
data_root = sys._MEIPASS
else: else:
try: try:
from database.config import get_project_root from database.config import get_project_root
project_root = get_project_root() data_root = get_project_root()
except ImportError: 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 return mission_path if os.path.exists(mission_path) else None
def toggleToolBar(self): def toggleToolBar(self):
...@@ -208,6 +215,9 @@ class ViewHandler: ...@@ -208,6 +215,9 @@ class ViewHandler:
def _switchToCurveLayout(self): def _switchToCurveLayout(self):
"""切换到曲线模式:根据检测线程状态选择合适的子布局""" """切换到曲线模式:根据检测线程状态选择合适的子布局"""
# 🔥 先设置模式为曲线模式(_video_layout_mode = 1),确保后续逻辑能正确判断
self._video_layout_mode = 1
# 🔥 检查检测线程状态,决定使用哪种子布局 # 🔥 检查检测线程状态,决定使用哪种子布局
detection_running = self._getCurrentDetectionState() detection_running = self._getCurrentDetectionState()
...@@ -216,20 +226,20 @@ class ViewHandler: ...@@ -216,20 +226,20 @@ class ViewHandler:
# 🔥 根据检测线程状态选择通道容器 # 🔥 根据检测线程状态选择通道容器
if detection_running: if detection_running:
# 实时检测模式:使用通道面板容器(ChannelPanel) # 同步布局:使用通道面板容器(ChannelPanel)
target_channel_widgets = self.channel_widgets_for_curve target_channel_widgets = self.channel_widgets_for_curve
layout_description = "实时检测模式" layout_description = "同步布局"
else: else:
# 历史回放模式:使用历史视频面板容器(HistoryVideoPanel) # 历史回放布局:使用历史视频面板容器(HistoryVideoPanel)
target_channel_widgets = self.history_channel_widgets_for_curve target_channel_widgets = self.history_channel_widgets_for_curve
layout_description = "历史回放模式" layout_description = "历史回放布局"
# 🔥 根据检测线程状态选择要显示的面板类型 # 🔥 根据检测线程状态选择要显示的面板类型
if detection_running: if detection_running:
# 实时检测模式:使用通道面板(ChannelPanel) # 同步布局:使用通道面板(ChannelPanel)
panels_to_use = self.channelPanels panels_to_use = self.channelPanels
else: else:
# 历史回放模式:使用历史视频面板(HistoryVideoPanel) # 历史回放布局:使用历史视频面板(HistoryVideoPanel)
if hasattr(self, 'historyVideoPanels'): if hasattr(self, 'historyVideoPanels'):
panels_to_use = self.historyVideoPanels panels_to_use = self.historyVideoPanels
else: else:
...@@ -271,10 +281,7 @@ class ViewHandler: ...@@ -271,10 +281,7 @@ class ViewHandler:
wrapper.updateGeometry() wrapper.updateGeometry()
panel.updateGeometry() panel.updateGeometry()
# 先设置模式为曲线模式(_video_layout_mode = 1) # 切换到曲线模式主布局(_video_layout_mode已在方法开头设置为1)
self._video_layout_mode = 1
# 切换到曲线模式主布局
self.videoLayoutStack.setCurrentIndex(1) self.videoLayoutStack.setCurrentIndex(1)
# 更新状态栏信息 # 更新状态栏信息
...@@ -293,7 +300,7 @@ class ViewHandler: ...@@ -293,7 +300,7 @@ class ViewHandler:
# 🔥 根据检测线程状态更新通道显示 # 🔥 根据检测线程状态更新通道显示
if detection_running: if detection_running:
# 实时检测模式:根据当前选择的任务更新通道显示 # 同步布局:根据当前选择的任务更新通道显示
if hasattr(self, 'curvemission'): if hasattr(self, 'curvemission'):
current_mission = self.curvemission.currentText() current_mission = self.curvemission.currentText()
if current_mission and current_mission != "请选择任务": if current_mission and current_mission != "请选择任务":
...@@ -306,7 +313,7 @@ class ViewHandler: ...@@ -306,7 +313,7 @@ class ViewHandler:
if hasattr(self, '_updateCurveChannelDisplay'): if hasattr(self, '_updateCurveChannelDisplay'):
self._updateCurveChannelDisplay([]) self._updateCurveChannelDisplay([])
else: else:
# 🔥 历史回放模式:显示所有历史视频面板 # 🔥 历史回放布局:显示所有历史视频面板
if hasattr(self, '_updateCurveChannelDisplay'): if hasattr(self, '_updateCurveChannelDisplay'):
all_channels = ['通道1', '通道2', '通道3', '通道4'] all_channels = ['通道1', '通道2', '通道3', '通道4']
self._updateCurveChannelDisplay(all_channels) # 显示所有4个历史视频面板 self._updateCurveChannelDisplay(all_channels) # 显示所有4个历史视频面板
...@@ -322,7 +329,7 @@ class ViewHandler: ...@@ -322,7 +329,7 @@ class ViewHandler:
QtCore.QTimer.singleShot(100, self._loadCurveDataOrStartThreads) QtCore.QTimer.singleShot(100, self._loadCurveDataOrStartThreads)
def _switchToDefaultLayout(self): def _switchToDefaultLayout(self):
"""切换到默认布局(实时检测模式)""" """切换到默认布局(任务表格 + 2x2通道面板)"""
# 🔥 停止所有曲线线程(切换回默认布局时) # 🔥 停止所有曲线线程(切换回默认布局时)
self._stopAllCurveThreads() self._stopAllCurveThreads()
...@@ -389,16 +396,16 @@ class ViewHandler: ...@@ -389,16 +396,16 @@ class ViewHandler:
return return
if detection_running: if detection_running:
# 切换到实时检测布局(索引0) # 切换到同步布局(曲线模式布局的索引0)
target_index = 0 target_index = 0
layout_name = "实时检测布局" layout_name = "同步布局"
mode_text = "实时检测" mode_text = "同步"
mode_style = "font-weight: bold; padding: 2px 8px;" mode_style = "font-weight: bold; padding: 2px 8px;"
curve_mode = 'realtime' curve_mode = 'realtime'
print(f"✅ [布局切换] 选择索引0 - 实时检测布局") print(f"✅ [布局切换] 选择索引0 - 同步布局")
else: else:
# 切换到历史回放布局(索引1) # 切换到历史回放布局(曲线模式布局的索引1)
target_index = 1 target_index = 1
layout_name = "历史回放布局" layout_name = "历史回放布局"
mode_text = "历史回放" mode_text = "历史回放"
...@@ -425,18 +432,31 @@ class ViewHandler: ...@@ -425,18 +432,31 @@ class ViewHandler:
pass pass
# 🔥 禁用/启用通道面板的查看曲线按钮 # 🔥 禁用/启用通道面板的查看曲线按钮
# 索引0(实时检测模式)时禁用,索引1(历史回放模式)时启用 # 只有在曲线模式(_video_layout_mode==1)的子布局切换时才需要处理
if hasattr(self, 'channelPanels'): # 子布局索引0(实时检测模式)时禁用,索引1(历史回放模式)时根据任务状态决定
for panel in self.channelPanels: if hasattr(self, '_video_layout_mode') and self._video_layout_mode == 1:
if hasattr(panel, 'btnCurve'): if hasattr(self, 'channelPanels'):
if target_index == 0: for panel in self.channelPanels:
# 实时检测模式:禁用查看曲线按钮 if hasattr(panel, 'btnCurve'):
panel.btnCurve.setEnabled(False) if target_index == 0:
panel.btnCurve.setToolTip("实时检测模式下无法查看曲线") # 曲线模式的同步布局:禁用查看曲线按钮
else: panel.btnCurve.setEnabled(False)
# 历史回放模式:启用查看曲线按钮 panel.btnCurve.setToolTip("同步布局下无法查看曲线")
panel.btnCurve.setEnabled(True) else:
panel.btnCurve.setToolTip("查看曲线") # 曲线模式的历史回放布局:检查通道是否有任务
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() current_index = self.curveLayoutStack.currentIndex()
......
1曲线模式索引0布局,只显示根据curvemission筛选使用的通道面板失效了 1曲线模式索引0布局,只显示根据curvemission筛选使用的通道面板失效了
...@@ -66,4 +66,12 @@ class ModelLoadingProgressDialog(QDialog): ...@@ -66,4 +66,12 @@ class ModelLoadingProgressDialog(QDialog):
7: foam_air 7: foam_air
- **容器底部**: foam掩码的最低点 - **容器底部**: foam掩码的最低点
- **容器顶部**: air掩码的最高点 - **容器顶部**: 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): ...@@ -482,11 +482,12 @@ class ChannelPanel(QtWidgets.QWidget):
self._is_disabled = disabled self._is_disabled = disabled
if disabled: if disabled:
# 禁用所有按钮 # 禁用所有按钮(包括查看曲线按钮)
if hasattr(self, 'btnToggleConnect'): if hasattr(self, 'btnToggleConnect'):
self.btnToggleConnect.setEnabled(False) self.btnToggleConnect.setEnabled(False)
if hasattr(self, 'btnCurve'): if hasattr(self, 'btnCurve'):
self.btnCurve.setEnabled(False) self.btnCurve.setEnabled(False)
self.btnCurve.setToolTip("请先分配任务")
if hasattr(self, 'btnAmplify'): if hasattr(self, 'btnAmplify'):
self.btnAmplify.setEnabled(False) self.btnAmplify.setEnabled(False)
if hasattr(self, 'btnEdit'): if hasattr(self, 'btnEdit'):
...@@ -499,11 +500,34 @@ class ChannelPanel(QtWidgets.QWidget): ...@@ -499,11 +500,34 @@ class ChannelPanel(QtWidgets.QWidget):
} }
""") """)
else: else:
# 启用所有按钮 # 启用按钮
if hasattr(self, 'btnToggleConnect'): if hasattr(self, 'btnToggleConnect'):
self.btnToggleConnect.setEnabled(True) self.btnToggleConnect.setEnabled(True)
# 🔥 查看曲线按钮:有任务时启用,但需要检查是否在曲线模式的实时检测子布局
# 只有在曲线模式(_video_layout_mode==1)且子布局索引0(实时检测)时才禁用
if hasattr(self, 'btnCurve'): 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'): if hasattr(self, 'btnAmplify'):
self.btnAmplify.setEnabled(True) self.btnAmplify.setEnabled(True)
if hasattr(self, 'btnEdit'): 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