Metatrader斐波那契回撤复刻:抓取与缩放功能异常修复
修复斐波那契回撤工具的抓取与缩放交互异常
问题描述
我正在复刻Metatrader的斐波那契回撤工具,已实现斐波那契水平绘制及缩放功能,但存在交互异常:一旦点击中心抓取点后,工具始终处于抓取状态,无法再执行缩放操作。
原代码如下:
import tkinter as tk class FibonacciRetracementApp: def __init__(self, master): self.master = master self.master.title("Fibonacci Retracement") self.canvas = tk.Canvas(self.master, width=600, height=400, bg="white") self.canvas.pack(expand=tk.YES, fill=tk.BOTH) self.points = [] self.lines = [] self.center_point = None self.second_set_drawn = False self.resizing = False self.resizing_point = None self.canvas.bind("<Button-1>", self.create_point) self.canvas.bind("<B1-Motion>", self.on_motion) self.canvas.bind("<ButtonRelease-1>", self.release_button) def create_point(self, event): x, y = event.x, event.y if self.second_set_drawn: self.check_for_resizing(x, y) return self.points.append((x, y)) if len(self.points) == 2: if not self.second_set_drawn: self.draw_fibonacci_retracement() self.second_set_drawn = True self.canvas.delete("point") def check_for_resizing(self, x, y): if len(self.points) == 2: if self.center_point is None: self.add_center_point() x1, y1 = self.points[0] x2, y2 = self.points[1] # Check if the cursor is near the first resize corner if self.is_near_resize_corner(x, y, x1, y1): self.resizing = True self.resizing_point = 0 # Check if the cursor is near the second resize corner elif self.is_near_resize_corner(x, y, x2, y2): self.resizing = True self.resizing_point = 1 def is_near_resize_corner(self, x, y, corner_x, corner_y): return (corner_x - 5 <= x <= corner_x + 5) and (corner_y - 5 <= y <= corner_y + 5) def add_center_point(self): x1, y1 = self.points[0] x2, y2 = self.points[1] center_x = (x1 + x2) // 2 center_y = (y1 + y2) // 2 self.center_point = (center_x, center_y) def move_center_point(self, event): if self.center_point is not None: x, y = event.x, event.y dx = x - self.center_point[0] dy = y - self.center_point[1] self.canvas.move("blue_line", dx, dy) self.center_point = (x, y) def move_existing_point(self, x, y): if self.resizing_point is not None: self.points[self.resizing_point] = (x, y) self.update_fibonacci_retracement() def draw_fibonacci_retracement(self): x1, y1 = self.points[0] x2, y2 = self.points[1] retracement_levels = [0, 0.236, 0.382, 0.5, 0.618, 0.786, 1.0] for level in retracement_levels: y_level = y1 + level * (y2 - y1) line = self.canvas.create_line(x1, y_level, x2, y_level, fill="blue", dash=(4, 2), tags="blue_line") self.lines.append(line) def update_fibonacci_retracement(self): if len(self.points) == 2: x1, y1 = self.points[0] x2, y2 = self.points[1] self.canvas.delete("blue_line") retracement_levels = [0, 0.236, 0.382, 0.5, 0.618, 0.786, 1.0] for level in retracement_levels: y_level = y1 + level * (y2 - y1) line = self.canvas.create_line(x1, y_level, x2, y_level, fill="blue", dash=(4, 2), tags="blue_line") self.lines.append(line) def on_motion(self, event): if self.resizing: x, y = event.x, event.y self.move_existing_point(x, y) elif self.center_point is not None: self.move_center_point(event) def release_button(self, event): self.resizing = False self.resizing_point = None if __name__ == "__main__": root = tk.Tk() app = FibonacciRetracementApp(root) root.mainloop()
修复方案
修改后的完整代码
import tkinter as tk class FibonacciRetracementApp: def __init__(self, master): self.master = master self.master.title("Fibonacci Retracement") self.canvas = tk.Canvas(self.master, width=600, height=400, bg="white") self.canvas.pack(expand=tk.YES, fill=tk.BOTH) self.points = [] self.lines = [] self.center_point = None self.second_set_drawn = False self.resizing = False self.resizing_point = None self.dragging = False # 标记是否正在拖动整个工具 self.canvas.bind("<Button-1>", self.create_point) self.canvas.bind("<B1-Motion>", self.on_motion) self.canvas.bind("<ButtonRelease-1>", self.release_button) def create_point(self, event): x, y = event.x, event.y if self.second_set_drawn: self.check_for_interaction(x, y) return self.points.append((x, y)) if len(self.points) == 2: if not self.second_set_drawn: self.draw_fibonacci_retracement() self.second_set_drawn = True def check_for_interaction(self, x, y): if len(self.points) == 2: if self.center_point is None: self.add_center_point() x1, y1 = self.points[0] x2, y2 = self.points[1] # 优先检测中心抓取点 if self.is_near_center_point(x, y): self.dragging = True self.resizing = False self.resizing_point = None return # 检测缩放端点 if self.is_near_resize_corner(x, y, x1, y1): self.resizing = True self.resizing_point = 0 self.dragging = False elif self.is_near_resize_corner(x, y, x2, y2): self.resizing = True self.resizing_point = 1 self.dragging = False def is_near_resize_corner(self, x, y, corner_x, corner_y): return (corner_x - 5 <= x <= corner_x + 5) and (corner_y - 5 <= y <= corner_y + 5) def is_near_center_point(self, x, y): if self.center_point: cx, cy = self.center_point return (cx - 5 <= x <= cx + 5) and (cy - 5 <= y <= cy + 5) return False def add_center_point(self): x1, y1 = self.points[0] x2, y2 = self.points[1] center_x = (x1 + x2) // 2 center_y = (y1 + y2) // 2 self.center_point = (center_x, center_y) def move_center_point(self, event): if self.center_point is not None: x, y = event.x, event.y dx = x - self.center_point[0] dy = y - self.center_point[1] self.canvas.move("blue_line", dx, dy) self.canvas.move("handle", dx, dy) # 更新端点和中心点位置 self.center_point = (x, y) self.points = [(p[0]+dx, p[1]+dy) for p in self.points] def move_existing_point(self, x, y): if self.resizing_point is not None: self.points[self.resizing_point] = (x, y) self.update_fibonacci_retracement() # 更新中心点 self.add_center_point() def draw_fibonacci_retracement(self): x1, y1 = self.points[0] x2, y2 = self.points[1] retracement_levels = [0, 0.236, 0.382, 0.5, 0.618, 0.786, 1.0] for level in retracement_levels: y_level = y1 + level * (y2 - y1) line = self.canvas.create_line(x1, y_level, x2, y_level, fill="blue", dash=(4, 2), tags="blue_line") self.lines.append(line) # 绘制交互手柄:红色端点(缩放)、绿色中心点(拖动) self.canvas.create_oval(x1-3, y1-3, x1+3, y1+3, fill="red", tags="handle") self.canvas.create_oval(x2-3, y2-3, x2+3, y2+3, fill="red", tags="handle") self.canvas.create_oval(self.center_point[0]-3, self.center_point[1]-3, self.center_point[0]+3, self.center_point[1]+3, fill="green", tags="handle") def update_fibonacci_retracement(self): if len(self.points) == 2: x1, y1 = self.points[0] x2, y2 = self.points[1] self.canvas.delete("blue_line") self.canvas.delete("handle") retracement_levels = [0, 0.236, 0.382, 0.5, 0.618, 0.786, 1.0] for level in retracement_levels: y_level = y1 + level * (y2 - y1) line = self.canvas.create_line(x1, y_level, x2, y_level, fill="blue", dash=(4, 2), tags="blue_line") self.lines.append(line) # 重新绘制交互手柄 self.canvas.create_oval(x1-3, y1-3, x1+3, y1+3, fill="red", tags="handle") self.canvas.create_oval(x2-3, y2-3, x2+3, y2+3, fill="red", tags="handle") self.canvas.create_oval(self.center_point[0]-3, self.center_point[1]-3, self.center_point[0]+3, self.center_point[1]+3, fill="green", tags="handle") def on_motion(self, event): if self.resizing: self.move_existing_point(event.x, event.y) elif self.dragging: self.move_center_point(event) def release_button(self, event): self.resizing = False self.resizing_point = None self.dragging = False # 松开鼠标后重置拖动状态 if __name__ == "__main__": root = tk.Tk() app = FibonacciRetracementApp(root) root.mainloop()
关键改动说明
- 新增
dragging状态变量:明确区分拖动和缩放两种交互状态,避免逻辑冲突 - 重命名交互检测方法:将
check_for_resizing改为check_for_interaction,覆盖拖动和缩放两种检测逻辑 - 添加中心点点击检测:新增
is_near_center_point方法,点击绿色中心点时触发拖动状态,同时重置缩放状态 - 优化拖动逻辑:拖动中心点时同步更新端点位置和手柄,保持工具整体结构一致
- 添加可视化手柄:绘制红色端点(缩放)和绿色中心点(拖动),让交互区域更直观
- 重置所有状态:松开鼠标时同时重置
resizing和dragging状态,确保下次点击可正常切换操作模式
修复后交互逻辑
- 点击红色端点:触发缩放功能,拖动可调整斐波那契工具的范围
- 点击绿色中心点:触发拖动功能,拖动可移动整个斐波那契工具
- 松开鼠标后,所有交互状态重置,可自由切换操作模式
内容的提问来源于stack exchange,提问作者cilindro
相关产品推荐
相关产品推荐

