Commit a965fd26 by yhb

1

parent 9f93245a
---
alwaysApply: true
---
...@@ -18,13 +18,13 @@ channels: ...@@ -18,13 +18,13 @@ channels:
channel2: channel2:
general: general:
task_id: '1' task_id: '1'
task_name: 海信现场测试 task_name: test
save_liquid_data_path: D:\restructure\liquid_level_line_detection_system\database\mission_result\1_海信现场测试 save_liquid_data_path: d:\restructure\liquid_level_line_detection_system\database\mission_result\1_test
channel3: channel3:
general: general:
task_id: '1' task_id: '1'
task_name: 海信现场测试 task_name: test
save_liquid_data_path: D:\restructure\liquid_level_line_detection_system\database\mission_result\1_海信现场测试 save_liquid_data_path: d:\restructure\liquid_level_line_detection_system\database\mission_result\1_test
channel4: channel4:
general: general:
task_id: '1' task_id: '1'
...@@ -33,7 +33,7 @@ channel4: ...@@ -33,7 +33,7 @@ channel4:
channel1: channel1:
general: general:
task_id: '1' task_id: '1'
task_name: 历史回放功能展示 task_name: 曲线
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\1_历史回放功能展示 save_liquid_data_path: D:\restructure\liquid_level_line_detection_system\database\mission_result\1_曲线
areas: areas:
area_1: 通道1_区域1 area_1: 通道1_区域1
area_heights: area_heights:
......
task_id: '1'
task_id: '1'
task_name: 曲线
status: 未启动
selected_channels:
- 通道1
created_time: '2025-12-02 11:19:50'
mission_result_folder_path: D:\restructure\liquid_level_line_detection_system\database\mission_result\1_曲线
task_id: '1'
task_id: '1'
task_name: 曲线
status: 未启动
selected_channels:
- 通道1
created_time: '2025-12-02 11:19:50'
mission_result_folder_path: D:\restructure\liquid_level_line_detection_system\database\mission_result\1_曲线
...@@ -364,37 +364,17 @@ class CurvePanelHandler: ...@@ -364,37 +364,17 @@ class CurvePanelHandler:
print(f" - 实际添加数据点数: {added_count}") print(f" - 实际添加数据点数: {added_count}")
print(f" - 更新后数据点数: {after_add_count}") print(f" - 更新后数据点数: {after_add_count}")
# 🔥 同步布局模式:根据时间轴范围过滤数据 # 🔥 数据点数量限制(已禁用)
is_sync_layout = hasattr(self, '_video_layout_mode') and self._video_layout_mode == 1 # - 原逻辑:'realtime' 同步布局模式限制为3000个点(滚动窗口)
if is_sync_layout and self.curve_panel: # - 已禁用:不再限制数据点数量,所有模式都显示完整数据
start_time, end_time = self.curve_panel.getTimeAxisRange()
if start_time is not None and end_time is not None: # if self.curve_load_mode == 'realtime':
print(f" - 同步布局模式,过滤时间范围: {start_time:.3f} 到 {end_time:.3f}") # # 同步布局模式:保留最新3000个点
# 过滤数据点,只保留在时间范围内的点 # if len(channel['time']) > self.max_points:
filtered_time = [] # before_limit = len(channel['time'])
filtered_value = [] # channel['time'] = channel['time'][-self.max_points:]
for t, v in zip(channel['time'], channel['value']): # channel['value'] = channel['value'][-self.max_points:]
if start_time <= t <= end_time: # print(f" - 限制数据点: {before_limit} -> {len(channel['time'])}")
filtered_time.append(t)
filtered_value.append(v)
before_filter = len(channel['time'])
channel['time'] = filtered_time
channel['value'] = filtered_value
after_filter = len(channel['time'])
print(f" - 时间过滤: {before_filter} -> {after_filter} 个数据点")
# 🔥 限制数据点数量
# - 'realtime' 同步布局模式:限制为3000个点(滚动窗口)
# - 'history' 历史回放模式:显示完整数据(不采样)
if self.curve_load_mode == 'realtime':
# 同步布局模式:保留最新3000个点
if len(channel['time']) > self.max_points:
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断开连接 # 🔥 处理时间间隔断点:超过2分钟的数据点之间插入NaN断开连接
processed_time, processed_value = self._processTimeGaps( processed_time, processed_value = self._processTimeGaps(
...@@ -404,9 +384,43 @@ class CurvePanelHandler: ...@@ -404,9 +384,43 @@ class CurvePanelHandler:
) )
print(f" - 处理后数据点数: {len(processed_time)}") print(f" - 处理后数据点数: {len(processed_time)}")
# 🔥 数据验证:确保有数据才更新UI
if not processed_time or not processed_value:
print(f" - ⚠️ 处理后数据为空,跳过UI更新")
return
if len(processed_time) != len(processed_value):
print(f" - ⚠️ 处理后数据长度不匹配: time={len(processed_time)}, value={len(processed_value)}")
return
# 🔥 检查是否有有效数据点(至少有一个非NaN值)
has_valid_data = False
for i in range(len(processed_value)):
if np.isfinite(processed_value[i]) and np.isfinite(processed_time[i]):
has_valid_data = True
break
if not has_valid_data:
print(f" - ⚠️ 没有有效数据点(全是NaN/Inf),跳过UI更新")
return
# 更新UI显示(只更新一次) # 更新UI显示(只更新一次)
if self.curve_panel: if self.curve_panel:
print(f" - 开始更新UI显示...") print(f" - 开始更新UI显示...")
# 🔥 安全地计算数据范围(避免NaN值导致错误)
try:
valid_values = [v for v in processed_value if np.isfinite(v)]
valid_times = [t for t in processed_time if np.isfinite(t)]
if valid_values and valid_times:
value_range_str = f"[{min(valid_values):.2f}, {max(valid_values):.2f}]"
time_range_str = f"[{min(valid_times):.2f}, {max(valid_times):.2f}]"
else:
value_range_str = "无有效值"
time_range_str = "无有效值"
print(f" - 数据详情: time范围={time_range_str}, value范围={value_range_str}")
except Exception as e:
print(f" - 数据详情: 计算范围时出错: {e}")
self.curve_panel.updateCurveDisplay( self.curve_panel.updateCurveDisplay(
channel_id, channel_id,
processed_time, processed_time,
...@@ -414,14 +428,27 @@ class CurvePanelHandler: ...@@ -414,14 +428,27 @@ class CurvePanelHandler:
) )
print(f" - UI显示更新完成") print(f" - UI显示更新完成")
# 更新X轴范围 # 🔥 根据加载模式决定X轴范围设置
if channel['time']: if channel['time']:
min_time = min(channel['time'])
max_time = max(channel['time']) max_time = max(channel['time'])
self.curve_panel.setXRangeAuto(max_time)
# 更新Y轴范围 if self.curve_load_mode == 'history':
# 历史模式:显示全部数据范围
if channel['value']: if channel['value']:
max_value = max(channel['value']) max_value = max(channel['value'])
else:
max_value = 23
self.curve_panel.setViewAll(min_time, max_time, max_value)
print(f" - 历史模式:显示全部数据范围 [{min_time:.2f}, {max_time:.2f}]")
else:
# 实时模式:只显示最后几分钟(自动跟随)
self.curve_panel.setXRangeAuto(max_time)
print(f" - 实时模式:显示最后几分钟数据")
# 更新Y轴范围(仅在实时模式下)
if self.curve_load_mode == 'realtime' and channel['value']:
max_value = max(channel['value'])
self.curve_panel.setYRangeAuto(max_value) self.curve_panel.setYRangeAuto(max_value)
else: else:
print(f" - ⚠️ curve_panel不存在,无法更新UI!") print(f" - ⚠️ curve_panel不存在,无法更新UI!")
...@@ -439,6 +466,14 @@ class CurvePanelHandler: ...@@ -439,6 +466,14 @@ class CurvePanelHandler:
Returns: Returns:
tuple: (处理后的时间列表, 处理后的数值列表) tuple: (处理后的时间列表, 处理后的数值列表)
""" """
# 🔥 处理空数据情况
if not time_data or not value_data:
return [], []
if len(time_data) != len(value_data):
print(f"⚠️ [时间间隔处理] 数据长度不匹配: time={len(time_data)}, value={len(value_data)}")
return time_data, value_data
if len(time_data) <= 1: if len(time_data) <= 1:
return time_data, value_data return time_data, value_data
...@@ -707,7 +742,15 @@ class CurvePanelHandler: ...@@ -707,7 +742,15 @@ class CurvePanelHandler:
tuple: (starttime, endtime) - 时间轴起始时刻和结束时刻(Unix时间戳) tuple: (starttime, endtime) - 时间轴起始时刻和结束时刻(Unix时间戳)
""" """
if self.curve_panel: if self.curve_panel:
return self.curve_panel.getTimeAxisRange() result = self.curve_panel.getTimeAxisRange()
# 🔥 调试信息:输出handler获取的结果
import datetime
start_time, end_time = result
start_str = datetime.datetime.fromtimestamp(start_time).strftime('%Y-%m-%d %H:%M:%S') if start_time else 'None'
end_str = datetime.datetime.fromtimestamp(end_time).strftime('%Y-%m-%d %H:%M:%S') if end_time else 'None'
print(f"🎯 [Handler获取时间轴范围] starttime={start_time} ({start_str}) -> endtime={end_time} ({end_str})")
return result
print(f"⚠️ [Handler获取时间轴范围] curve_panel不存在,返回(None, None)")
return (None, None) return (None, None)
# ========== 数据管理方法 ========== # ========== 数据管理方法 ==========
...@@ -1027,6 +1070,33 @@ class CurvePanelHandler: ...@@ -1027,6 +1070,33 @@ class CurvePanelHandler:
print(f"✅ [曲线数据加载] 成功加载 {count} 个文件") print(f"✅ [曲线数据加载] 成功加载 {count} 个文件")
# 🔥 设置历史数据已加载标志 # 🔥 设置历史数据已加载标志
self._history_data_loaded = True self._history_data_loaded = True
# 🔥 历史数据加载完成后,确保显示全部数据范围
if self.curve_load_mode == 'history' and self.curve_panel:
# 找出所有通道的最小和最大时间值、最大数值
min_time = None
max_time = None
max_value = 0
for channel_id, channel in self.channel_data.items():
if len(channel['time']) > 0:
channel_min_time = min(channel['time'])
channel_max_time = max(channel['time'])
if min_time is None or channel_min_time < min_time:
min_time = channel_min_time
if max_time is None or channel_max_time > max_time:
max_time = channel_max_time
if len(channel['value']) > 0:
channel_max_value = max(channel['value'])
max_value = max(max_value, channel_max_value)
# 如果有数据,设置视图显示全部范围
if min_time is not None and max_time is not None:
print(f"🔄 [历史数据加载完成] 设置视图显示全部数据范围")
print(f" - 时间范围: [{min_time:.2f}, {max_time:.2f}]")
print(f" - 数值范围: [0, {max_value:.2f}]")
self.curve_panel.setViewAll(min_time, max_time, max_value)
else: else:
print(f"⚠️ [曲线数据加载] 加载失败") print(f"⚠️ [曲线数据加载] 加载失败")
......
...@@ -11,6 +11,13 @@ from pathlib import Path ...@@ -11,6 +11,13 @@ from pathlib import Path
# 导入动态路径获取函数 # 导入动态路径获取函数
from database.config import get_temp_models_dir from database.config import get_temp_models_dir
# 导入检测逻辑模块
from handlers.videopage.detection_logic import (
parse_yolo_result,
calculate_liquid_height,
process_yolo_result_to_liquid_height
)
# ==================== 辅助函数 ==================== # ==================== 辅助函数 ====================
...@@ -25,84 +32,6 @@ def get_class_color(class_name): ...@@ -25,84 +32,6 @@ def get_class_color(class_name):
return color_map.get(class_name, (128, 128, 128)) # 默认灰色 return color_map.get(class_name, (128, 128, 128)) # 默认灰色
def calculate_foam_boundary_lines(mask):
"""计算foam mask的顶部和底部边界线"""
if np.sum(mask) == 0:
return None, None
y_coords = np.where(mask)[0]
if len(y_coords) == 0:
return None, None
top_y = np.min(y_coords)
bottom_y = np.max(y_coords)
# 计算顶部边界线的平均位置
top_region_height = max(1, int((bottom_y - top_y) * 0.1))
top_region_mask = mask[top_y:top_y + top_region_height, :]
if np.sum(top_region_mask) > 0:
top_y_coords = np.where(top_region_mask)[0] + top_y
top_line_y = np.mean(top_y_coords)
else:
top_line_y = top_y
# 计算底部边界线的平均位置
bottom_region_height = max(1, int((bottom_y - top_y) * 0.1))
bottom_region_mask = mask[bottom_y - bottom_region_height:bottom_y + 1, :]
if np.sum(bottom_region_mask) > 0:
bottom_y_coords = np.where(bottom_region_mask)[0] + (bottom_y - bottom_region_height)
bottom_line_y = np.mean(bottom_y_coords)
else:
bottom_line_y = bottom_y
return top_line_y, bottom_line_y
def analyze_multiple_foams(foam_masks, container_pixel_height):
"""分析多个foam,找到可能的液位边界"""
if len(foam_masks) < 2:
return None
foam_boundaries = []
# 计算每个foam的边界信息
for i, mask in enumerate(foam_masks):
top_y, bottom_y = calculate_foam_boundary_lines(mask)
if top_y is not None and bottom_y is not None:
center_y = (top_y + bottom_y) / 2
foam_boundaries.append({
'index': i,
'top_y': top_y,
'bottom_y': bottom_y,
'center_y': center_y
})
if len(foam_boundaries) < 2:
return None
# 按垂直位置排序
foam_boundaries.sort(key=lambda x: x['center_y'])
error_threshold_px = container_pixel_height * 0.1
# 检查相邻foam之间的边界
for i in range(len(foam_boundaries) - 1):
upper_foam = foam_boundaries[i]
lower_foam = foam_boundaries[i + 1]
upper_bottom = upper_foam['bottom_y']
lower_top = lower_foam['top_y']
boundary_distance = abs(upper_bottom - lower_top)
if boundary_distance <= error_threshold_px:
liquid_level_y = (upper_bottom + lower_top) / 2
return liquid_level_y
return None
def stable_median(data, max_std=1.0): def stable_median(data, max_std=1.0):
"""稳健地计算中位数""" """稳健地计算中位数"""
if len(data) == 0: if len(data) == 0:
...@@ -606,26 +535,13 @@ class LiquidDetectionEngine: ...@@ -606,26 +535,13 @@ class LiquidDetectionEngine:
liquid_height = None liquid_height = None
# 处理检测结果 # 解析YOLO推理结果
if mission_result.masks is not None: all_masks_info = parse_yolo_result(
masks = mission_result.masks.data.cpu().numpy() > 0.5 mission_result=mission_result,
classes = mission_result.boxes.cls.cpu().numpy().astype(int) model_names=self.model.names,
confidences = mission_result.boxes.conf.cpu().numpy() image_shape=cropped.shape[:2],
else: confidence_threshold=0.5
return None )
# 收集所有mask信息
all_masks_info = []
for i in range(len(masks)):
class_name = self.model.names[classes[i]]
conf = confidences[i]
if confidences[i] >= 0.5:
resized_mask = cv2.resize(
masks[i].astype(np.uint8),
(cropped.shape[1], cropped.shape[0])
) > 0.5
all_masks_info.append((resized_mask, class_name, confidences[i]))
if len(all_masks_info) == 0: if len(all_masks_info) == 0:
return None return None
...@@ -642,12 +558,12 @@ class LiquidDetectionEngine: ...@@ -642,12 +558,12 @@ class LiquidDetectionEngine:
# print(f" - 裁剪图像中容器高度: {container_bottom_in_crop - container_top_in_crop}px(应等于{container_pixel_height}px)") # print(f" - 裁剪图像中容器高度: {container_bottom_in_crop - container_top_in_crop}px(应等于{container_pixel_height}px)")
# 分析mask获取液位高度(使用裁剪图像坐标) # 分析mask获取液位高度(使用裁剪图像坐标)
liquid_height = self._enhanced_liquid_detection( liquid_height = calculate_liquid_height(
all_masks_info, all_masks_info=all_masks_info,
container_bottom_in_crop, # 使用裁剪图像坐标 container_bottom=container_bottom_in_crop, # 使用裁剪图像坐标
container_pixel_height, container_pixel_height=container_pixel_height,
container_height_mm, container_height_mm=container_height_mm,
idx no_liquid_count=self.no_liquid_count[idx] if idx < len(self.no_liquid_count) else 0
) )
return liquid_height return liquid_height
...@@ -656,85 +572,6 @@ class LiquidDetectionEngine: ...@@ -656,85 +572,6 @@ class LiquidDetectionEngine:
print(f"[检测-目标{idx}] ❌ 检测异常: {e}") print(f"[检测-目标{idx}] ❌ 检测异常: {e}")
return None return None
def _enhanced_liquid_detection(self, all_masks_info, container_bottom,
container_pixel_height, container_height_mm, idx):
"""
增强的液位检测,结合连续帧逻辑和foam分析
Args:
all_masks_info: mask信息列表 [(mask, class_name, confidence), ...]
container_bottom: 容器底部y坐标
container_pixel_height: 容器像素高度
container_height_mm: 容器实际高度(毫米)
idx: 目标索引
Returns:
float: 液位高度(毫米),失败返回 None
"""
pixel_per_mm = container_pixel_height / container_height_mm
# 分离不同类别的mask
liquid_masks = []
foam_masks = []
air_masks = []
for mask, class_name, confidence in all_masks_info:
if class_name == 'liquid':
liquid_masks.append(mask)
elif class_name == 'foam':
foam_masks.append(mask)
elif class_name == 'air':
air_masks.append(mask)
# 方法1:直接liquid检测(优先)
if liquid_masks:
# 找到最上层的液体mask
topmost_y = float('inf')
for i, mask in enumerate(liquid_masks):
y_indices = np.where(mask)[0]
if len(y_indices) > 0:
mask_top_y = np.min(y_indices)
# print(f" - liquid mask {i+1}: 顶部y={mask_top_y}px")
if mask_top_y < topmost_y:
topmost_y = mask_top_y
if topmost_y != float('inf'):
liquid_height_px = container_bottom - topmost_y
liquid_height_mm = liquid_height_px / pixel_per_mm
return max(0, min(liquid_height_mm, container_height_mm))
# 方法2:foam边界分析(备选)- 连续3帧未检测到liquid时启用
if self.no_liquid_count[idx] >= 3:
if len(foam_masks) >= 2:
# 多个foam,寻找液位边界
liquid_y = analyze_multiple_foams(foam_masks, container_pixel_height)
if liquid_y is not None:
liquid_height_px = container_bottom - liquid_y
liquid_height_mm = liquid_height_px / pixel_per_mm
return max(0, min(liquid_height_mm, container_height_mm))
elif len(foam_masks) == 1:
# 单个foam,使用下边界
foam_mask = foam_masks[0]
top_y, bottom_y = calculate_foam_boundary_lines(foam_mask)
if bottom_y is not None:
liquid_height_px = container_bottom - bottom_y
liquid_height_mm = liquid_height_px / pixel_per_mm
return max(0, min(liquid_height_mm, container_height_mm))
elif len(air_masks) == 1:
# 单个air,使用下边界
air_mask = air_masks[0]
y_coords = np.where(air_mask)[0]
if len(y_coords) > 0:
bottom_y = np.max(y_coords)
liquid_height_px = container_bottom - bottom_y
liquid_height_mm = liquid_height_px / pixel_per_mm
return max(0, min(liquid_height_mm, container_height_mm))
return None
def _apply_kalman_filter(self, observation, idx, container_height_mm): def _apply_kalman_filter(self, observation, idx, container_height_mm):
""" """
......
时间轴变量starttime,endtime 时间轴变量starttime,endtime
...@@ -2,4 +2,4 @@ ...@@ -2,4 +2,4 @@
采样逻辑,根据数据点个数设置不同采样率 采样逻辑,根据数据点个数设置不同采样率
@curvepanel_handler.py#L371-378 修改self._video_layout_mode = 1同步布局,绘制曲线的数据范围 @curvepanel_handler.py#L371-378 修改self._video_layout_mode = 1同步布局,绘制曲线的数据范围
实验最长时间三天,strattime到endtime,缩放限制 实验最长时间三天,strattime到endtime,缩放限制
曲线面板所有都历史模式下, 曲线面板历史模式下,,
\ No newline at end of file \ No newline at end of file
...@@ -624,11 +624,45 @@ class CurvePanel(QtWidgets.QWidget): ...@@ -624,11 +624,45 @@ class CurvePanel(QtWidgets.QWidget):
注意:此方法只负责UI更新,数据处理由handler完成 注意:此方法只负责UI更新,数据处理由handler完成
""" """
if channel_id not in self.curve_items: if channel_id not in self.curve_items:
print(f"⚠️ [UI更新] 通道不存在: {channel_id}")
return return
# 🔥 数据验证和调试
if not time_data or not value_data:
print(f"⚠️ [UI更新] 数据为空: channel_id={channel_id}, time_data长度={len(time_data) if time_data else 0}, value_data长度={len(value_data) if value_data else 0}")
# 即使数据为空,也调用setData清空显示
curve = self.curve_items[channel_id]
curve.setData([], [])
return
if len(time_data) != len(value_data):
print(f"⚠️ [UI更新] 数据长度不匹配: channel_id={channel_id}, time_data长度={len(time_data)}, value_data长度={len(value_data)}")
return
# 🔥 转换为numpy数组(PyQtGraph需要numpy数组)
try:
time_array = np.array(time_data, dtype=np.float64)
value_array = np.array(value_data, dtype=np.float64)
except Exception as e:
print(f"⚠️ [UI更新] 数据转换失败: channel_id={channel_id}, 错误={e}")
return
# 🔥 检查有效数据点数量(排除NaN和Inf)
valid_mask = np.isfinite(time_array) & np.isfinite(value_array)
valid_count = np.sum(valid_mask)
if valid_count == 0:
print(f"⚠️ [UI更新] 没有有效数据点: channel_id={channel_id}, 总数据点={len(time_array)}, 有效数据点=0")
# 即使没有有效数据,也调用setData清空显示
curve = self.curve_items[channel_id]
curve.setData([], [])
return
print(f"✅ [UI更新] 更新曲线: channel_id={channel_id}, 总数据点={len(time_array)}, 有效数据点={valid_count}")
# 只更新UI显示 # 只更新UI显示
curve = self.curve_items[channel_id] curve = self.curve_items[channel_id]
curve.setData(time_data, value_data) curve.setData(time_array, value_array)
# 动态调整X轴范围 # 动态调整X轴范围
self._updateXRange() self._updateXRange()
...@@ -729,6 +763,12 @@ class CurvePanel(QtWidgets.QWidget): ...@@ -729,6 +763,12 @@ class CurvePanel(QtWidgets.QWidget):
Returns: Returns:
tuple: (starttime, endtime) - 时间轴起始时刻和结束时刻(Unix时间戳) tuple: (starttime, endtime) - 时间轴起始时刻和结束时刻(Unix时间戳)
""" """
# 🔥 调试信息:输出获取的时间轴范围
import datetime
start_str = datetime.datetime.fromtimestamp(self.starttime).strftime('%Y-%m-%d %H:%M:%S') if self.starttime else 'None'
end_str = datetime.datetime.fromtimestamp(self.endtime).strftime('%Y-%m-%d %H:%M:%S') if self.endtime else 'None'
print(f"📊 [获取时间轴范围] starttime={self.starttime} ({start_str}) -> endtime={self.endtime} ({end_str})")
return (self.starttime, self.endtime) return (self.starttime, self.endtime)
...@@ -793,12 +833,35 @@ class CurvePanel(QtWidgets.QWidget): ...@@ -793,12 +833,35 @@ class CurvePanel(QtWidgets.QWidget):
Args: Args:
max_time: 最大时间戳 max_time: 最大时间戳
view_width: 显示宽度(秒),默认60秒 view_width: 显示宽度(秒),默认120秒(2分钟)
""" """
if self.chk_auto_follow.isChecked(): if self.chk_auto_follow.isChecked():
min_time = max_time - view_width min_time = max_time - view_width
self.plot_widget.setXRange(min_time, max_time, padding=0) self.plot_widget.setXRange(min_time, max_time, padding=0)
def setViewAll(self, min_time, max_time, max_value):
"""
显示全部数据范围(用于历史数据加载)
Args:
min_time: 最小时间戳
max_time: 最大时间戳
max_value: 最大数值
"""
# 设置X轴显示全部时间范围(留出5%的边距)
time_span = max_time - min_time
if time_span > 0:
padding = time_span * 0.05 # 5%边距
self.plot_widget.setXRange(min_time - padding, max_time + padding, padding=0)
# 设置Y轴范围(留出10%的边距)
if max_value > 0:
y_max = min(23, max_value * 1.1) # 不超过23
self.plot_widget.setYRange(0, y_max, padding=0)
# 固定X轴位置
self._fixXAxisPosition()
def setYRangeAuto(self, max_value): def setYRangeAuto(self, max_value):
""" """
自动设置Y轴范围(由handler调用) 自动设置Y轴范围(由handler调用)
...@@ -829,6 +892,26 @@ class CurvePanel(QtWidgets.QWidget): ...@@ -829,6 +892,26 @@ class CurvePanel(QtWidgets.QWidget):
self.starttime = x_min # 时间轴起始时刻(Unix时间戳) self.starttime = x_min # 时间轴起始时刻(Unix时间戳)
self.endtime = x_max # 时间轴结束时刻(Unix时间戳) self.endtime = x_max # 时间轴结束时刻(Unix时间戳)
# 🔥 调试信息:输出时间轴范围变化(安全处理时间戳转换)
import datetime
try:
if x_min and np.isfinite(x_min):
start_str = datetime.datetime.fromtimestamp(x_min).strftime('%Y-%m-%d %H:%M:%S')
else:
start_str = 'None'
except (OSError, ValueError, OverflowError):
start_str = f'Invalid({x_min})'
try:
if x_max and np.isfinite(x_max):
end_str = datetime.datetime.fromtimestamp(x_max).strftime('%Y-%m-%d %H:%M:%S')
else:
end_str = 'None'
except (OSError, ValueError, OverflowError):
end_str = f'Invalid({x_max})'
print(f"🔄 [时间轴范围] starttime={x_min:.3f} ({start_str}) -> endtime={x_max:.3f} ({end_str})")
# 确保Y轴范围在0-23之间 # 确保Y轴范围在0-23之间
if y_min < 0 or y_max > 23: if y_min < 0 or y_max > 23:
# 调整到合法范围 # 调整到合法范围
......
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