Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to make a tkinter canvas rectangle with rounded corners?

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

like image 673
Cthulhu Avatar asked May 21 '17 17:05

Cthulhu


People also ask

How do you make a rounded rectangle in Tkinter?

Using this function, you can just provide the normal coordinates that you would to a rectangle, and then specify the 'radius' which is rounded in the corners. The use of **kwargs denotes that you can pass keyword arguments such as fill="blue" , just as you usually could with a create_ method.

How do I make rounded corners on canvas?

Corners may be rounded by four quadraticCurve functions Code: function roundedRectangle(x, y, w, h) { var canvas = document. getElementById("canvas4"); var context = canvas. getContext("2d"); var mx = x + w / 2; var my = y + h / 2; context.


2 Answers

Offering an alternate approach to tobias's method would be to indeed do it with one polygon.

This would have the advantage of being one canvas object if you are worried about optimization, or not having to worry about a tag system for referring to a single object.

The code is a bit longer, but very basic, as it is just utilizing the idea that when smoothing a polygon, you can give the same coordinate twice to 'stop' the smooth from occuring.

This is an example of what can be done:

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

Using this function, you can just provide the normal coordinates that you would to a rectangle, and then specify the 'radius' which is rounded in the corners. The use of **kwargs denotes that you can pass keyword arguments such as fill="blue", just as you usually could with a create_ method.

Although the coords look complex, it is just going around methodically to each point in the 'rectangle', giving each non-corner point twice.

If you didn't mind a rather long line of code, you could put all the coordinates on one line, making the function just 2 lines(!). This looks like:

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)

This produces the following (Note in mind this is ONE canvas object):

Rounded rectangle produced by function


If you want to update the position of the rectangle after it has been created, you could use a function like this (if in the same scope as the original canvas object):

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)

So, to update my_rectangle's position (from the first code example), we could say:

update_rectangle_coords(my_rectangle, 20, 20, 100, 100)
like image 89
SneakyTurtle Avatar answered Oct 26 '22 15:10

SneakyTurtle


There seems not to be a built-in method for this. The closest thing would be a polyline with smooth=1, but that still looks more like an old TV screen, with the sides also slightly curved.

Instead, you could define a helper function, combining the rounded rectangle from lines and arcs:

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)

Example:

import tkinter
root = tkinter.Tk()
canvas = tkinter.Canvas(root)
canvas.pack()
rounded_rect(canvas, 20, 20, 60, 40, 10)
root.mainloop()

enter image description here

You could also provide another **options parameter to set line width, color etc. for the individual parts, but the problem with this is that e.g. lines and arcs are using different parameters for the line color (fill and outline respectively). Also, if you want to have a filled rounded rectangle, you will have to specify that as a second method, using multiple rectangles.

like image 20
tobias_k Avatar answered Oct 26 '22 16:10

tobias_k