如何创建一个带有圆角的tkinter画布矩形?

15

我想在使用 tkinter 的 canvas 中创建一个带有圆角的矩形。

3个回答

37

提供一种替代Tobias方法的方法是确实只使用一个多边形进行操作。

如果您担心优化问题,或者不想为引用单个对象而担心标签系统,这将具有单个画布对象的优点。

代码略微冗长,但非常基本,因为它只利用了这样一个想法:在平滑多边形时,可以将相同的坐标给两次以“停止”平滑效果的发生。

以下是一个示例:

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()

使用此函数,您只需提供传递给矩形的普通坐标,然后指定在拐角处圆角的“半径”。使用**kwargs表示您可以传递关键字参数,例如fill="blue",就像您通常可以使用create_方法一样。

尽管坐标看起来很复杂,但实际上只是按照方法一步一步地绕着“矩形”中的每个点,给每个非角点重复两次。

如果您不介意代码很长,您可以将所有坐标都放在一行上,使函数只有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)

1
我很惊讶这看起来如此出色。当我尝试相同的操作时,直边稍微向外弯曲,就像旧电视一样。但我只使用了12个点,而不是20个。在我看来,+1最好使用明确的“x1,y1,x2,y2”或“x,y,w,h”参数。 - tobias_k
1
@tobias_k 我认为这种方法的技巧在于,在每一边上给出点(它们距离角落的'半径'),使它们不会平滑。此外,我同意x1,y1,x2,y2的建议,已经编辑过了。 - SneakyTurtle
1
@CoolCloud,请查看我对答案的最新编辑 - 我们可以使用类似创建矩形的函数来更新其位置。 - SneakyTurtle
@CoolCloud 我支持你的提议! - WinEunuuchs2Unix
@SneakyTurtle 做得好。现在只需要一个小三角形来指向鼠标悬停在哪个小部件上,当多边形圆角矩形被绘制时。 - WinEunuuchs2Unix
显示剩余2条评论

14

我知道这篇文章已经有一个关于矩形的被接受的答案了。但是对于那些寻找任何带有圆角的多边形(包括矩形)的人,我根据@SneakyTutle的答案编写了这段代码。

roundPolygon(x_array, y_array, sharpness, **kwargs)

结果

Image

这样做的逻辑是为了使得多边形在平滑的同时可以将子点放在顶点旁边。这样,只有角落会被圆角化,而多边形的其余部分保持平坦。

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之间的任何数值都适用于任何情况。随意更改代码。

仅供可视化参考,对于一个具有锐度=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]
    ]

8

似乎没有内置的方法可以做到这一点。最接近的方法是使用带有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参数来设置单个部分的线宽、颜色等,但是这样做的问题在于例如线和弧所使用的线条颜色(filloutline)的参数不同。此外,如果您想要一个填充的圆角矩形,您将不得不指定第二种方法,使用多个矩形。

输入图像描述


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接