You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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状态,确保下次点击可正常切换操作模式

修复后交互逻辑

  1. 点击红色端点:触发缩放功能,拖动可调整斐波那契工具的范围
  2. 点击绿色中心点:触发拖动功能,拖动可移动整个斐波那契工具
  3. 松开鼠标后,所有交互状态重置,可自由切换操作模式

内容的提问来源于stack exchange,提问作者cilindro

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 22:22:04