How to make a tkinter canvas rectangle with rounded corners?

Viewed 26392

I would like to create a rectangle with rounded corners. I'm using canvas from tkinter.

3 Answers

I know this post already has an accepted answer for a rectangle. But for those looking for any polygon with rounded corners (rectangle included obviously), I made this code based on @SneakyTutle's answer.

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

Result

Image

The logic behind this is to enable smooth and place subpoints next to the vertex. This way, only the corners are going to be rounded and the rest of the polygon is kept flat.

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

I couldn't figure a nice way to normalize the sharpness. Anyway, something between 2 and 10 will be good for any case. Feel free to change the code as you wish.

Just for visualization, for a triangle with sharpness=8 the result code for the for loop is as follows. As you might note, if sharpness is 2, the sub-points are going to be placed in the middle of the vertex.

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]
    ]
Related