如何制作带圆角的 tkinter canvas 矩形?
How to make a tkinter canvas rectangle with rounded corners?
我想创建一个圆角矩形。我正在使用来自 tkinter 的 canvas。
似乎没有内置方法。最接近的是带有 smooth=1
的多段线,但它看起来更像是一个旧电视屏幕,侧面也略微弯曲。
相反,您可以定义一个辅助函数,将直线和圆弧组合成圆角矩形:
def rounded_rect(canvas, x, y, w, h, c):
canvas.create_arc(x, y, x+2*c, y+2*c, start= 90, extent=90, style="arc")
canvas.create_arc(x+w-2*c, y+h-2*c, x+w, y+h, start=270, extent=90, style="arc")
canvas.create_arc(x+w-2*c, y, x+w, y+2*c, start= 0, extent=90, style="arc")
canvas.create_arc(x, y+h-2*c, x+2*c, y+h, start=180, extent=90, style="arc")
canvas.create_line(x+c, y, x+w-c, y )
canvas.create_line(x+c, y+h, x+w-c, y+h )
canvas.create_line(x, y+c, x, y+h-c)
canvas.create_line(x+w, y+c, x+w, y+h-c)
示例:
import tkinter
root = tkinter.Tk()
canvas = tkinter.Canvas(root)
canvas.pack()
rounded_rect(canvas, 20, 20, 60, 40, 10)
root.mainloop()
您还可以提供另一个 **options
参数来为各个部分设置线宽、颜色等,但问题在于,例如线条和弧线使用不同的线条颜色参数(分别为 fill
和 outline
)。另外,如果你想要一个填充的圆角矩形,你必须将其指定为第二种方法,使用多个矩形。
提供 tobias 方法的另一种方法确实是用一个多边形来完成。
如果您担心优化,或者不必担心用于引用单个对象的标签系统,这将具有作为一个 canvas 对象的优势。
代码有点长,但非常基础,因为它只是利用了这样的想法,即在对多边形进行平滑处理时,您可以将相同的坐标指定两次以 'stop' 平滑发生。
这是一个可以做什么的例子:
from tkinter import *
root = Tk()
canvas = Canvas(root)
canvas.pack()
def round_rectangle(x1, y1, x2, y2, radius=25, **kwargs):
points = [x1+radius, y1,
x1+radius, y1,
x2-radius, y1,
x2-radius, y1,
x2, y1,
x2, y1+radius,
x2, y1+radius,
x2, y2-radius,
x2, y2-radius,
x2, y2,
x2-radius, y2,
x2-radius, y2,
x1+radius, y2,
x1+radius, y2,
x1, y2,
x1, y2-radius,
x1, y2-radius,
x1, y1+radius,
x1, y1+radius,
x1, y1]
return canvas.create_polygon(points, **kwargs, smooth=True)
my_rectangle = round_rectangle(50, 50, 150, 100, radius=20, fill="blue")
root.mainloop()
使用此函数,您可以只提供矩形的法线坐标,然后指定 'radius' 圆角。使用 **kwargs
表示您可以传递关键字参数,例如 fill="blue"
,就像通常使用 create_
方法一样。
虽然坐标看起来很复杂,但它只是有条不紊地绕过'rectangle'中的每个点,给每个非角点两次。
如果您不介意相当长的一行代码,您可以将所有坐标放在一行上,使函数只有 2 行(!)。这看起来像:
def round_rectangle(x1, y1, x2, y2, r=25, **kwargs):
points = (x1+r, y1, x1+r, y1, x2-r, y1, x2-r, y1, x2, y1, x2, y1+r, x2, y1+r, x2, y2-r, x2, y2-r, x2, y2, x2-r, y2, x2-r, y2, x1+r, y2, x1+r, y2, x1, y2, x1, y2-r, x1, y2-r, x1, y1+r, x1, y1+r, x1, y1)
return canvas.create_polygon(points, **kwargs, smooth=True)
这会产生以下结果(请注意这是一个 canvas 对象):
如果你想在矩形创建后更新它的位置,你可以使用这样的函数(如果在与原始 canvas
对象相同的范围内):
def update_rectangle_coords(round_rect, x1, y1, x2, y2, r=25):
points = (x1+r, y1, x1+r, y1, x2-r, y1, x2-r, y1, x2, y1, x2, y1+r, x2, y1+r, x2, y2-r, x2, y2-r, x2, y2, x2-r, y2, x2-r, y2, x1+r, y2, x1+r, y2, x1, y2, x1, y2-r, x1, y2-r, x1, y1+r, x1, y1+r, x1, y1)
canvas.coords(round_rect, *points)
因此,要更新 my_rectangle
的位置(来自第一个代码示例),我们可以说:
update_rectangle_coords(my_rectangle, 20, 20, 100, 100)
我知道这个 post 已经有一个可接受的矩形答案。但是对于那些寻找任何带圆角的多边形(显然包括矩形)的人来说,我根据@SneakyTutle 的回答制作了这段代码。
roundPolygon(x_array, y_array, sharpness, **kwargs)
结果
这背后的逻辑是启用平滑并将子点放置在顶点旁边。这样,只有角会变圆,多边形的其余部分保持平坦。
from tkinter import *
root = Tk()
canvas = Canvas(root, width = 1000, height = 1000)
canvas.pack()
def roundPolygon(x, y, sharpness, **kwargs):
# The sharpness here is just how close the sub-points
# are going to be to the vertex. The more the sharpness,
# the more the sub-points will be closer to the vertex.
# (This is not normalized)
if sharpness < 2:
sharpness = 2
ratioMultiplier = sharpness - 1
ratioDividend = sharpness
# Array to store the points
points = []
# Iterate over the x points
for i in range(len(x)):
# Set vertex
points.append(x[i])
points.append(y[i])
# If it's not the last point
if i != (len(x) - 1):
# Insert submultiples points. The more the sharpness, the more these points will be
# closer to the vertex.
points.append((ratioMultiplier*x[i] + x[i + 1])/ratioDividend)
points.append((ratioMultiplier*y[i] + y[i + 1])/ratioDividend)
points.append((ratioMultiplier*x[i + 1] + x[i])/ratioDividend)
points.append((ratioMultiplier*y[i + 1] + y[i])/ratioDividend)
else:
# Insert submultiples points.
points.append((ratioMultiplier*x[i] + x[0])/ratioDividend)
points.append((ratioMultiplier*y[i] + y[0])/ratioDividend)
points.append((ratioMultiplier*x[0] + x[i])/ratioDividend)
points.append((ratioMultiplier*y[0] + y[i])/ratioDividend)
# Close the polygon
points.append(x[0])
points.append(y[0])
return canvas.create_polygon(points, **kwargs, smooth=TRUE)
my_rectangle = roundPolygon([50, 350, 350, 50], [50, 50, 350, 350], 10 , width=5, outline="#82B366", fill="#D5E8D4")
my_triangle = roundPolygon([50, 650, 50], [400, 700, 1000], 8 , width=5, outline="#82B366", fill="#D5E8D4")
root.mainloop()
我想不出一个好的方法来标准化清晰度。无论如何,2 到 10 之间的值对任何情况都适用。随意更改代码。
只是为了可视化,对于sharpness=8的三角形,for循环的结果代码如下。正如您可能注意到的,如果锐度为 2,则子点将放置在顶点的中间。
points = [
# Begin vertex
x[0], y[0],
# Between vertices
(7*x[0] + x[1])/8, (7*y[0] + y[1])/8,
(7*x[1] + x[0])/8, (7*y[1] + y[0])/8,
# Vertex
x[1], y[1],
# Between vertices
(7*x[1] + x[2])/8, (7*y[1] + y[2])/8,
(7*x[2] + x[1])/8, (7*y[2] + y[1])/8,
# Vertex
x[2], y[2],
# Between vertices
(7*x[2] + x[0])/8, (7*y[2] + y[0])/8,
(7*x[0] + x[2])/8, (7*y[0] + y[2])/8,
# End/Begin vertex
x[0], y[0]
]
我想创建一个圆角矩形。我正在使用来自 tkinter 的 canvas。
似乎没有内置方法。最接近的是带有 smooth=1
的多段线,但它看起来更像是一个旧电视屏幕,侧面也略微弯曲。
相反,您可以定义一个辅助函数,将直线和圆弧组合成圆角矩形:
def rounded_rect(canvas, x, y, w, h, c):
canvas.create_arc(x, y, x+2*c, y+2*c, start= 90, extent=90, style="arc")
canvas.create_arc(x+w-2*c, y+h-2*c, x+w, y+h, start=270, extent=90, style="arc")
canvas.create_arc(x+w-2*c, y, x+w, y+2*c, start= 0, extent=90, style="arc")
canvas.create_arc(x, y+h-2*c, x+2*c, y+h, start=180, extent=90, style="arc")
canvas.create_line(x+c, y, x+w-c, y )
canvas.create_line(x+c, y+h, x+w-c, y+h )
canvas.create_line(x, y+c, x, y+h-c)
canvas.create_line(x+w, y+c, x+w, y+h-c)
示例:
import tkinter
root = tkinter.Tk()
canvas = tkinter.Canvas(root)
canvas.pack()
rounded_rect(canvas, 20, 20, 60, 40, 10)
root.mainloop()
您还可以提供另一个 **options
参数来为各个部分设置线宽、颜色等,但问题在于,例如线条和弧线使用不同的线条颜色参数(分别为 fill
和 outline
)。另外,如果你想要一个填充的圆角矩形,你必须将其指定为第二种方法,使用多个矩形。
提供 tobias 方法的另一种方法确实是用一个多边形来完成。
如果您担心优化,或者不必担心用于引用单个对象的标签系统,这将具有作为一个 canvas 对象的优势。
代码有点长,但非常基础,因为它只是利用了这样的想法,即在对多边形进行平滑处理时,您可以将相同的坐标指定两次以 'stop' 平滑发生。
这是一个可以做什么的例子:
from tkinter import *
root = Tk()
canvas = Canvas(root)
canvas.pack()
def round_rectangle(x1, y1, x2, y2, radius=25, **kwargs):
points = [x1+radius, y1,
x1+radius, y1,
x2-radius, y1,
x2-radius, y1,
x2, y1,
x2, y1+radius,
x2, y1+radius,
x2, y2-radius,
x2, y2-radius,
x2, y2,
x2-radius, y2,
x2-radius, y2,
x1+radius, y2,
x1+radius, y2,
x1, y2,
x1, y2-radius,
x1, y2-radius,
x1, y1+radius,
x1, y1+radius,
x1, y1]
return canvas.create_polygon(points, **kwargs, smooth=True)
my_rectangle = round_rectangle(50, 50, 150, 100, radius=20, fill="blue")
root.mainloop()
使用此函数,您可以只提供矩形的法线坐标,然后指定 'radius' 圆角。使用 **kwargs
表示您可以传递关键字参数,例如 fill="blue"
,就像通常使用 create_
方法一样。
虽然坐标看起来很复杂,但它只是有条不紊地绕过'rectangle'中的每个点,给每个非角点两次。
如果您不介意相当长的一行代码,您可以将所有坐标放在一行上,使函数只有 2 行(!)。这看起来像:
def round_rectangle(x1, y1, x2, y2, r=25, **kwargs):
points = (x1+r, y1, x1+r, y1, x2-r, y1, x2-r, y1, x2, y1, x2, y1+r, x2, y1+r, x2, y2-r, x2, y2-r, x2, y2, x2-r, y2, x2-r, y2, x1+r, y2, x1+r, y2, x1, y2, x1, y2-r, x1, y2-r, x1, y1+r, x1, y1+r, x1, y1)
return canvas.create_polygon(points, **kwargs, smooth=True)
这会产生以下结果(请注意这是一个 canvas 对象):
如果你想在矩形创建后更新它的位置,你可以使用这样的函数(如果在与原始 canvas
对象相同的范围内):
def update_rectangle_coords(round_rect, x1, y1, x2, y2, r=25):
points = (x1+r, y1, x1+r, y1, x2-r, y1, x2-r, y1, x2, y1, x2, y1+r, x2, y1+r, x2, y2-r, x2, y2-r, x2, y2, x2-r, y2, x2-r, y2, x1+r, y2, x1+r, y2, x1, y2, x1, y2-r, x1, y2-r, x1, y1+r, x1, y1+r, x1, y1)
canvas.coords(round_rect, *points)
因此,要更新 my_rectangle
的位置(来自第一个代码示例),我们可以说:
update_rectangle_coords(my_rectangle, 20, 20, 100, 100)
我知道这个 post 已经有一个可接受的矩形答案。但是对于那些寻找任何带圆角的多边形(显然包括矩形)的人来说,我根据@SneakyTutle 的回答制作了这段代码。
roundPolygon(x_array, y_array, sharpness, **kwargs)
结果
这背后的逻辑是启用平滑并将子点放置在顶点旁边。这样,只有角会变圆,多边形的其余部分保持平坦。
from tkinter import *
root = Tk()
canvas = Canvas(root, width = 1000, height = 1000)
canvas.pack()
def roundPolygon(x, y, sharpness, **kwargs):
# The sharpness here is just how close the sub-points
# are going to be to the vertex. The more the sharpness,
# the more the sub-points will be closer to the vertex.
# (This is not normalized)
if sharpness < 2:
sharpness = 2
ratioMultiplier = sharpness - 1
ratioDividend = sharpness
# Array to store the points
points = []
# Iterate over the x points
for i in range(len(x)):
# Set vertex
points.append(x[i])
points.append(y[i])
# If it's not the last point
if i != (len(x) - 1):
# Insert submultiples points. The more the sharpness, the more these points will be
# closer to the vertex.
points.append((ratioMultiplier*x[i] + x[i + 1])/ratioDividend)
points.append((ratioMultiplier*y[i] + y[i + 1])/ratioDividend)
points.append((ratioMultiplier*x[i + 1] + x[i])/ratioDividend)
points.append((ratioMultiplier*y[i + 1] + y[i])/ratioDividend)
else:
# Insert submultiples points.
points.append((ratioMultiplier*x[i] + x[0])/ratioDividend)
points.append((ratioMultiplier*y[i] + y[0])/ratioDividend)
points.append((ratioMultiplier*x[0] + x[i])/ratioDividend)
points.append((ratioMultiplier*y[0] + y[i])/ratioDividend)
# Close the polygon
points.append(x[0])
points.append(y[0])
return canvas.create_polygon(points, **kwargs, smooth=TRUE)
my_rectangle = roundPolygon([50, 350, 350, 50], [50, 50, 350, 350], 10 , width=5, outline="#82B366", fill="#D5E8D4")
my_triangle = roundPolygon([50, 650, 50], [400, 700, 1000], 8 , width=5, outline="#82B366", fill="#D5E8D4")
root.mainloop()
我想不出一个好的方法来标准化清晰度。无论如何,2 到 10 之间的值对任何情况都适用。随意更改代码。
只是为了可视化,对于sharpness=8的三角形,for循环的结果代码如下。正如您可能注意到的,如果锐度为 2,则子点将放置在顶点的中间。
points = [
# Begin vertex
x[0], y[0],
# Between vertices
(7*x[0] + x[1])/8, (7*y[0] + y[1])/8,
(7*x[1] + x[0])/8, (7*y[1] + y[0])/8,
# Vertex
x[1], y[1],
# Between vertices
(7*x[1] + x[2])/8, (7*y[1] + y[2])/8,
(7*x[2] + x[1])/8, (7*y[2] + y[1])/8,
# Vertex
x[2], y[2],
# Between vertices
(7*x[2] + x[0])/8, (7*y[2] + y[0])/8,
(7*x[0] + x[2])/8, (7*y[0] + y[2])/8,
# End/Begin vertex
x[0], y[0]
]