在 tkinter canvas 上的两点之间绘制弧线

Draw an arc between two points on a tkinter canvas

我的问题很简单:我有两个椭圆,中心分别是(x0, y0) 和(x1, y1)。 如果我想在它们之间画一条线,我会简单地做 create_line(x0, y0, x1, y1).

但我想在它们之间画一条弧线。我正在为这里的数学而苦苦挣扎。情况是这样的:

我需要:

我也在想也许画弧是错误的方法?我可以用一条线做一些等效的事情,为此我在该弧上指定了很多点(即使它不是实际的弧)

编辑回答盲人:

使用 tkinter,您可以像这样定义弧线: http://infohost.nmt.edu/tcc/help/pubs/tkinter/web/create_oval.html 我们不定义椭圆本身,只定义包含椭圆的矩形的 top-left 和 lower-right 角坐标。

两个椭圆如何相互关联:它们没有。你只是在 canvas 上的随机位置有两个椭圆,你希望它们之间有一条弧线。 我想要连接这些椭圆的两个中心的圆弧。

最后,这是我想要的想法,以防两个椭圆具有相同的 y 坐标:

ellipse

我想要的正是这个,适用于任何位置。

以及 tkinter 代码:

import tkinter as tk

class SampleApp(tk.Tk):

    def __init__(self, *args, **kwargs):
        tk.Tk.__init__(self, *args, **kwargs)

        self.canvas = tk.Canvas(width=400, height=400)
        self.canvas.pack(fill="both", expand=True)

        self._create_token((100, 100), "white")
        self._create_token((200, 100), "pink")

        self.canvas.create_arc(100, 100 + 10, 200, 100 - 10, extent=180, style=tk.ARC)

    def _create_token(self, coord, color):
        '''Create a token at the given coordinate in the given color'''
        (x,y) = coord
        self.canvas.create_oval(x-5, y-5, x+5, y+5, 
                                outline=color, fill=color, tags="token")

if __name__ == "__main__":
    app = SampleApp()
    app.mainloop()

有一个用于创建圆弧的 start 选项,它定义了在给出角度时从哪里开始绘图。使用这个和一些数学你可以使用 create_arc-method 为任何位置绘制弧线:

import Tkinter as tk


class SampleApp(tk.Tk):

    def __init__(self, *args, **kwargs):
        tk.Tk.__init__(self, *args, **kwargs)

        self.canvas = tk.Canvas(width=400, height=400)
        self.canvas.pack(fill="both", expand=True)

        self._create_token((100, 100), "white")
        self._create_token((200, 300), "pink")
        self._create_arc((100,100), (200, 300))

    def _create_token(self, coord, color):
        '''Create a token at the given coordinate in the given color'''
        (x,y) = coord
        self.canvas.create_oval(x-5, y-5, x+5, y+5, 
                                outline=color, fill=color, tags="token")

    def _create_arc(self, p0, p1):
        extend_x = (self._distance(p0,p1) -(p1[0]-p0[0]))/2 # extend x boundary 
        extend_y = (self._distance(p0,p1) -(p1[1]-p0[1]))/2 # extend y boundary
        startAngle = math.atan2(p0[0] - p1[0], p0[1] - p1[1]) *180 / math.pi # calculate starting angle  
        self.canvas.create_arc(p0[0]-extend_x, p0[1]-extend_y , 
                               p1[0]+extend_x, p1[1]+extend_y, 
                               extent=180, start=90+startAngle, style=tk.ARC)

        '''use this rectangle for visualisation'''
        #self.canvas.create_rectangle(p0[0]-extend_x, p0[1]-extend_y, 
        #                                p1[0]+extend_x, p1[1]+extend_y)       

    def _distance(self, p0, p1):
        '''calculate distance between 2 points'''
        return sqrt((p0[0] - p1[0])**2 + (p0[1] - p1[1])**2)   

if __name__ == "__main__":
    app = SampleApp()
    app.mainloop()

实际上,我会回答我自己的问题,因为我找到了更好的方法。 VRage 的答案很好,但如果您希望弧线更接近两点之间的线,则很难适应。 正确的方法是使用线段的垂直平分线上带有第三个点的线,并结合 "smooth" 选项。

下面是一个完全符合我要求的示例:

import tkinter as tk
import math

class SampleApp(tk.Tk):

    def __init__(self, *args, **kwargs):
        tk.Tk.__init__(self, *args, **kwargs)

        self.canvas = tk.Canvas(width=400, height=400)
        self.canvas.pack(fill="both", expand=True)

    def _create_token(self, coord, color):
        '''Create a token at the given coordinate in the given color'''
        (x,y) = coord
        self.canvas.create_oval(x-5, y-5, x+5, y+5, 
                                outline=color, fill=color, tags="token")

    def create(self, xA, yA, xB, yB, d=10):
        self._create_token((xA, yA), "white")
        self._create_token((xB, yB), "pink")

        t = math.atan2(yB - yA, xB - xA)
        xC = (xA + xB)/2 + d * math.sin(t)
        yC = (yA + yB)/2 - d * math.cos(t)
        xD = (xA + xB)/2 - d * math.sin(t)
        yD = (yA + yB)/2 + d * math.cos(t)

        self.canvas.create_line((xA, yA), (xC, yC), (xB, yB), smooth=True)
        self.canvas.create_line((xA, yA), (xD, yD), (xB, yB), smooth=True, fill="red")

   if __name__ == "__main__":
        app = SampleApp()
        app.create(100, 100, 300, 250)
        app.mainloop()