[Rod Stephens Books]
Index Books Python Examples About Rod Contact
[Mastodon] [Bluesky]
[Build Your Own Ray Tracer With Python]

[Beginning Database Design Solutions, Second Edition]

[Beginning Software Engineering, Second Edition]

[Essential Algorithms, Second Edition]

[The Modern C# Challenge]

[WPF 3d, Three-Dimensional Graphics with WPF and C#]

[The C# Helper Top 100]

[Interview Puzzles Dissected]

Title: Draw rounded rectangles in tkinter with Python

[Four rounded rectangles drawn in tkinter with Python]

My previous post, Find corner arcs for two points in Python, explained how to find corner arcs connecting two points. This post uses that technique to make rounded rectangles. This isn't too hard; it's mostly just a lot of simple but tedious work.

The following section describes the find_rounded_rect function, which finds the corner arcs for a rounded rectangle. The section after that describes the draw_rounded_rect method that draws the rounded rectangle.

find_rounded_rect

The following method finds the corner arcs needed to draw a rounded rectangle.

def find_rounded_rect(xmin, ymin, xmax, ymax, x_radius, y_radius): '''Return arc and line definitions to draw a rounded rectangle.''' # Make lists to hold results. arc_rects = [] start_angles = [] # UL corner. ul_x1 = xmin ul_y1 = ymin + y_radius ul_x2 = xmin + x_radius ul_y2 = ymin rect1, start_angle11, sweep11, rect2, start_angle12, sweep12 = \ find_corner_arcs((ul_x1, ul_y1), (ul_x2, ul_y2)) arc_rects.append(rect2) start_angles.append(start_angle12) # UR corner. ur_x1 = xmax - x_radius ur_y1 = ymin ur_x2 = xmax ur_y2 = ymin + y_radius rect1, start_angle1, sweep1, rect2, start_angle2, sweep2 = \ find_corner_arcs((ur_x1, ur_y1), (ur_x2, ur_y2)) arc_rects.append(rect1) start_angles.append(start_angle1) # LL corner. ll_x1 = xmin + x_radius ll_y1 = ymax ll_x2 = xmin ll_y2 = ymax - y_radius rect1, start_angle1, sweep1, rect2, start_angle2, sweep2 = \ find_corner_arcs((ll_x1, ll_y1), (ll_x2, ll_y2)) arc_rects.append(rect2) start_angles.append(start_angle2) # lr corner. lr_x1 = xmax lr_y1 = ymax - y_radius lr_x2 = xmax - x_radius lr_y2 = ymax rect1, start_angle1, sweep1, rect2, start_angle2, sweep2 = \ find_corner_arcs((lr_x1, lr_y1), (lr_x2, lr_y2)) arc_rects.append(rect1) start_angles.append(start_angle1) # Define the segments. segments = [ ((ul_x2, ul_y2), (ur_x1, ur_y1)), ((ur_x2, ur_y2), (lr_x1, lr_y1)), ((lr_x2, lr_y2), (ll_x1, ll_y1)), ((ll_x2, ll_y2), (ul_x1, ul_y1)), ] # Define the interior rectangle. interior_rect = (ul_x2, ul_y1, lr_x2, lr_y1) # Define the side rectangles. side_rectangles = [ (ul_x2, ul_y2, ur_x1, ur_y2), (ur_x1, ur_y2, lr_x1, lr_y1), (ll_x1, ll_y2, lr_x2, lr_y2), (ul_x1, ul_y1, ll_x1, ll_y2), ] return arc_rects, start_angles, segments, interior_rect, side_rectangles

The function first makes lists to hold the resulting arcs and their start angles. (They all have 90° sweep angles.)

[Finding the points that define a rounded corner] For each corner, it calculates the locations of the two points that define the corner arc as shown in the picture on the right. It then calls the find_corner_arcs function described in the earlier post to find the 90° arcs that connect the two points. That function returns two arcs: one above the line connecting the two points and one below that line. The code appends the appropriate arc data to the arc_rects and start_angles lists. (Figuring out which of the arcs is the one we want isn't magic. I just tried them until I found the right ones.)

The code repeats this process four times to find the rectangle's four corner arcs. It then creates a segments list that defines the line segments that connect the four corner arcs.

The function then defines the rectangle inside all of the corner arcs, and the rectangles that lie along the rounded rectangle's sides.

Finally, the function returns the arc rectangles, start angles, side segments, interior rectangle, and side rectangles.

draw_rounded_rect

The following draw_rounded_rect function draws a rounded rectangle.

def draw_rounded_rect(canvas, point1, point2, x_radius, y_radius, outline='', fill='', thickness=1): '''Draw a rounded rectangle.''' # Find the rounded rectangle parameters. xmin = min(point1[0], point2[0]) ymin = min(point1[1], point2[1]) xmax = max(point1[0], point2[0]) ymax = max(point1[1], point2[1]) # Find the corner arcs and side segments. arc_rects, start_angles, segments, interior_rect, side_rectangles = \ find_rounded_rect(xmin, ymin, xmax, ymax, x_radius, y_radius) # Fill. if fill: for i in range(4): canvas.create_arc(arc_rects[i], start=start_angles[i], extent=90, style=tk.PIESLICE, fill=fill, outline='') canvas.create_rectangle(interior_rect, fill=fill, outline='') for rect in side_rectangles: canvas.create_rectangle(rect, fill=fill, outline='') # Outline. if outline: for i in range(4): canvas.create_arc(arc_rects[i], start=start_angles[i], extent=90, style=tk.ARC, outline=outline, width=thickness) canvas.create_line(segments[i][0], segments[i][1], fill=outline, width=thickness)

This function first calls the previous find_rounded_rect function to get the parameters it needs to draw the rounded rectangle.

Next, if the fill parameter is not an empty string, the function fills the rounded rectangle. It loops through the arcs and fills them. It fills the interior rectangle and then loops through the side rectangles to fill them. (For an interesting experiment, try filling the various parts of the rounded rectangle with different colors. For example, you can fill the arcs and the interior rectangle with one color and the side rectangles with another color to make a checkerboard pattern.)

After it has filled the rounded rectangle, the code checks whether the outline parameter is a blank string and, if it is not, the function outlines the rounded rectangle. To do that, it loops through the arcs and line segments and draws them with the desired color and line width.

Conclusion

The result isn't perfect but it's pretty good. The fit between the arcs and segments sometimes isn't quite as smooth as I would like. It doesn't allow you to use different radii for different corners and it can't handle weird special cases like when the radii are larger than half of the rectangle's width/height. (Try it and see what happens.)

Overall, however, it does a reasonable job. Download the example to experiment with it and to see additional details.

© 2025 Rocky Mountain Computer Consulting, Inc. All rights reserved.