Skip to content

Instantly share code, notes, and snippets.

@honix
Created January 3, 2016 20:59
Show Gist options
  • Save honix/6433bcd40131f42f9502 to your computer and use it in GitHub Desktop.
Save honix/6433bcd40131f42f9502 to your computer and use it in GitHub Desktop.
Tkinter canvas round rect
from tkinter import *
from math import sin, cos
master = Tk()
c = Canvas(master, width=230, height=230)
c.pack()
def create_good_rectangle(c, x1, y1, x2, y2, feather, res=5, color='black'):
points = []
# top side
points += [x1 + feather, y1,
x2 - feather, y1]
# top right corner
for i in range(res):
points += [x2 - feather + sin(i/res*2) * feather,
y1 + feather - cos(i/res*2) * feather]
# right side
points += [x2, y1 + feather,
x2, y2 - feather]
# bottom right corner
for i in range(res):
points += [x2 - feather + cos(i/res*2) * feather,
y2 - feather + sin(i/res*2) * feather]
# bottom side
points += [x2 - feather, y2,
x1 + feather, y2]
# bottom left corner
for i in range(res):
points += [x1 + feather - sin(i/res*2) * feather,
y2 - feather + cos(i/res*2) * feather]
# left side
points += [x1, y2 - feather,
x1, y1 + feather]
# top left corner
for i in range(res):
points += [x1 + feather - cos(i/res*2) * feather,
y1 + feather - sin(i/res*2) * feather]
return c.create_polygon(points, fill=color) #?
create_good_rectangle(c, 10, 10, 200, 100, 20)
create_good_rectangle(c, 100, 40, 220, 200, 40, 8, 'blue')
mainloop()
@sebdelsol
Copy link

sebdelsol commented Jan 15, 2024

Your math is wrong: for each corner you want your $angle$ to travel a quarter of a circle i.e. $angle \in[0, \frac{\pi}{2}]$
And you don't want redundant points on the outline.

import tkinter as tk
from math import cos, pi, sin
from typing import Iterator


class RoundedCanvas(tk.Canvas):
    minimum_steps = 10  # lower values give pixelated corners

    @staticmethod
    def get_cos_sin(radius: int) -> Iterator[tuple[float, float]]:
        steps = max(radius, RoundedCanvas.minimum_steps)
        for i in range(steps + 1):
            angle = pi * (i / steps) * 0.5
            yield (cos(angle) - 1) * radius, (sin(angle) - 1) * radius

    def create_rounded_box(self, x0: int, y0: int, x1: int, y1: int, radius: int, color: str) -> int:
        points = []
        cos_sin_r = tuple(self.get_cos_sin(radius))
        for cos_r, sin_r in cos_sin_r:
            points.append((x1 + sin_r, y0 - cos_r))
        for cos_r, sin_r in cos_sin_r:
            points.append((x1 + cos_r, y1 + sin_r))
        for cos_r, sin_r in cos_sin_r:
            points.append((x0 - sin_r, y1 + cos_r))
        for cos_r, sin_r in cos_sin_r:
            points.append((x0 - cos_r, y0 - sin_r))
        return self.create_polygon(points, fill=color)


root = tk.Tk()
canvas = RoundedCanvas(root, width=230, height=230)
canvas.create_rounded_box(100, 40, 220, 200, 40, "blue")
canvas.pack()
root.mainloop()

@honix
Copy link
Author

honix commented Jan 31, 2024

Oh, I see. Thanks for code @sebdelsol !

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment