From 42ff71796cfc153b8983df87324b65e5576f0378 Mon Sep 17 00:00:00 2001 From: zcwBit Date: Fri, 8 Aug 2025 19:38:54 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=8E=86=E5=8F=B2=E8=B6=8B?= =?UTF-8?q?=E5=8A=BF=20=E5=85=89=E6=A0=87=E5=90=B8=E9=99=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- UI/TrendManage/TrendWidget.py | 128 +++++++++++++++++++++++++--------- 1 file changed, 96 insertions(+), 32 deletions(-) diff --git a/UI/TrendManage/TrendWidget.py b/UI/TrendManage/TrendWidget.py index 2f79e64..e481c83 100644 --- a/UI/TrendManage/TrendWidget.py +++ b/UI/TrendManage/TrendWidget.py @@ -262,6 +262,8 @@ class TrendWidgets(QWidget): self.crosshair_visible = False self.crosshair_point = None # 交叉点圆点 self.crosshair_text = None # 坐标文本标签 + self._snapped_var = None # 当前吸附的变量名 + self.snap_enabled = True # 是否启用智能吸附功能 # 连接鼠标事件 self.canvas.mpl_connect('motion_notify_event', self.onMouseMove) @@ -877,10 +879,17 @@ class TrendWidgets(QWidget): if not (xlim[0] <= x <= xlim[1] and ylim[0] <= y <= ylim[1]): return - # 可选:吸附到最近的数据点 - snap_x, snap_y = self.snapToNearestDataPoint(x, y) - if snap_x is not None and snap_y is not None: - x, y = snap_x, snap_y + # 智能吸附到最近的数据点(如果启用) + if self.snap_enabled: + snap_x, snap_y, snap_var_name = self.snapToNearestDataPoint(x, y) + if snap_x is not None and snap_y is not None: + x, y = snap_x, snap_y + # 存储吸附信息,用于显示 + self._snapped_var = snap_var_name + else: + self._snapped_var = None + else: + self._snapped_var = None # 更新垂直线位置 self.crosshair_v.set_data([x, x], [ylim[0], ylim[1]]) @@ -890,8 +899,21 @@ class TrendWidgets(QWidget): self.crosshair_h.set_data([xlim[0], xlim[1]], [y, y]) self.crosshair_h.set_visible(True) - # 更新交叉点圆点位置 + # 更新交叉点圆点位置和样式 self.crosshair_point.set_offsets([[x, y]]) + + # 根据是否吸附到数据点改变圆点颜色 + if hasattr(self, '_snapped_var') and self._snapped_var: + # 吸附状态:使用绿色表示精确定位 + self.crosshair_point.set_color('#00AA00') + self.crosshair_point.set_edgecolors('white') + self.crosshair_point.set_sizes([64]) # 稍大一些 + else: + # 普通状态:使用红色 + self.crosshair_point.set_color('#FF4444') + self.crosshair_point.set_edgecolors('white') + self.crosshair_point.set_sizes([36]) # 正常大小 + self.crosshair_point.set_visible(True) # 更新坐标文本 @@ -912,40 +934,70 @@ class TrendWidgets(QWidget): pass def snapToNearestDataPoint(self, x, y): - """吸附到最近的数据点(可选功能)""" - if not self.selectedVars: - return None, None + """智能吸附到最近的数据点 - 多变量增强版""" + if not self.selectedVars or not self.multiVarData: + return None, None, None min_distance = float('inf') snap_x, snap_y = None, None + snap_var_name = None + + # 获取当前坐标轴范围,用于归一化距离计算 + xlim = self.axMain.get_xlim() + ylim = self.axMain.get_ylim() + x_range = xlim[1] - xlim[0] + y_range = ylim[1] - ylim[0] # 遍历所有选中的变量,找到最近的数据点 for varName in self.selectedVars: if varName in self.multiVarData: data = self.multiVarData[varName] if data['x'] and data['y']: - # 找到最接近的X坐标点 - closest_idx = self.findClosestPoint(data['x'], x) - if closest_idx is not None and closest_idx < len(data['y']): - data_x = data['x'][closest_idx] - data_y = data['y'][closest_idx] - - # 将datetime转换为matplotlib数值 - if isinstance(data_x, datetime.datetime): - data_x_num = mdates.date2num(data_x) - else: - data_x_num = data_x - - # 计算距离(在屏幕坐标系中) - distance = abs(data_x_num - x) - - # 只有距离足够近才吸附(避免过度吸附) - if distance < min_distance and distance < 0.01: # 可调整吸附阈值 - min_distance = distance - snap_x = data_x_num - snap_y = data_y - - return snap_x, snap_y + # 遍历该变量的所有数据点 + for i, (data_x, data_y) in enumerate(zip(data['x'], data['y'])): + try: + # 将datetime转换为matplotlib数值 + if isinstance(data_x, datetime.datetime): + data_x_num = mdates.date2num(data_x) + else: + data_x_num = data_x + + # 计算归一化的欧几里得距离 + # 归一化是为了让X轴(时间)和Y轴(数值)的距离具有可比性 + dx_norm = (data_x_num - x) / x_range if x_range != 0 else 0 + dy_norm = (data_y - y) / y_range if y_range != 0 else 0 + distance = (dx_norm ** 2 + dy_norm ** 2) ** 0.5 + + # 使用可配置的吸附阈值 + snap_threshold = getattr(self, 'snap_threshold', 0.05) # 默认5%的屏幕距离 + + if distance < min_distance and distance < snap_threshold: + min_distance = distance + snap_x = data_x_num + snap_y = data_y + snap_var_name = varName + + except Exception as e: + # 跳过有问题的数据点 + continue + + return snap_x, snap_y, snap_var_name + + def toggleSnapToDataPoints(self, enabled=None): + """切换智能吸附功能""" + if enabled is None: + self.snap_enabled = not self.snap_enabled + else: + self.snap_enabled = enabled + + print(f"智能吸附功能: {'启用' if self.snap_enabled else '禁用'}") + return self.snap_enabled + + def setSnapThreshold(self, threshold): + """设置吸附阈值(0.01-0.2之间)""" + self.snap_threshold = max(0.01, min(0.2, threshold)) + print(f"吸附阈值设置为: {self.snap_threshold:.3f}") + return self.snap_threshold def _delayed_crosshair_draw(self): """延迟绘制十字标线,避免频繁更新""" @@ -958,15 +1010,17 @@ class TrendWidgets(QWidget): pass def formatCoordinateText(self, x, y): - """格式化坐标文本显示""" + """格式化坐标文本显示 - 增强版,显示吸附信息""" try: # 格式化时间(X轴) if isinstance(x, (int, float)): # 将matplotlib数值转换为datetime time_obj = mdates.num2date(x) time_str = time_obj.strftime('%H:%M:%S') + date_str = time_obj.strftime('%m-%d') else: time_str = str(x) + date_str = "" # 格式化数值(Y轴) if isinstance(y, (int, float)): @@ -979,7 +1033,17 @@ class TrendWidgets(QWidget): else: value_str = str(y) - return f"时间: {time_str}\n数值: {value_str}" + # 构建基础文本 + if date_str: + coord_text = f"时间: {date_str} {time_str}\n数值: {value_str}" + else: + coord_text = f"时间: {time_str}\n数值: {value_str}" + + # 如果吸附到了数据点,显示变量信息 + if hasattr(self, '_snapped_var') and self._snapped_var: + coord_text += f"\n📍 {self._snapped_var}" + + return coord_text except Exception as e: return f"X: {x:.3f}\nY: {y:.3f}"