在 tkinter canvas 上的两点之间绘制弧线
Draw an arc between two points on a tkinter canvas
我的问题很简单:我有两个椭圆,中心分别是(x0, y0) 和(x1, y1)。
如果我想在它们之间画一条线,我会简单地做
create_line(x0, y0, x1, y1).
但我想在它们之间画一条弧线。我正在为这里的数学而苦苦挣扎。情况是这样的:
- 我有这两个中心:它们一定是椭圆的一部分
- 经过这两点的椭圆有无穷多个,但是用tkinter,我们只能画水平椭圆。 (对吗?)
我需要:
- 包含椭圆的矩形的top-left和lower-right坐标
- 弧的起始角度和范围
我也在想也许画弧是错误的方法?我可以用一条线做一些等效的事情,为此我在该弧上指定了很多点(即使它不是实际的弧)
编辑回答盲人:
使用 tkinter,您可以像这样定义弧线:
http://infohost.nmt.edu/tcc/help/pubs/tkinter/web/create_oval.html
我们不定义椭圆本身,只定义包含椭圆的矩形的 top-left 和 lower-right 角坐标。
两个椭圆如何相互关联:它们没有。你只是在 canvas 上的随机位置有两个椭圆,你希望它们之间有一条弧线。
我想要连接这些椭圆的两个中心的圆弧。
最后,这是我想要的想法,以防两个椭圆具有相同的 y 坐标:

我想要的正是这个,适用于任何位置。
以及 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()
我的问题很简单:我有两个椭圆,中心分别是(x0, y0) 和(x1, y1)。 如果我想在它们之间画一条线,我会简单地做 create_line(x0, y0, x1, y1).
但我想在它们之间画一条弧线。我正在为这里的数学而苦苦挣扎。情况是这样的:
- 我有这两个中心:它们一定是椭圆的一部分
- 经过这两点的椭圆有无穷多个,但是用tkinter,我们只能画水平椭圆。 (对吗?)
我需要:
- 包含椭圆的矩形的top-left和lower-right坐标
- 弧的起始角度和范围
我也在想也许画弧是错误的方法?我可以用一条线做一些等效的事情,为此我在该弧上指定了很多点(即使它不是实际的弧)
编辑回答盲人:
使用 tkinter,您可以像这样定义弧线: http://infohost.nmt.edu/tcc/help/pubs/tkinter/web/create_oval.html 我们不定义椭圆本身,只定义包含椭圆的矩形的 top-left 和 lower-right 角坐标。
两个椭圆如何相互关联:它们没有。你只是在 canvas 上的随机位置有两个椭圆,你希望它们之间有一条弧线。 我想要连接这些椭圆的两个中心的圆弧。
最后,这是我想要的想法,以防两个椭圆具有相同的 y 坐标:
我想要的正是这个,适用于任何位置。
以及 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()