python-3.xtkintertkinter-canvasfillcornerradius

Rounded Rectangle in tkinter that I can fill inside and change the color from until border


Ok, I will try to explain better. I need to use Canvas from tkinter to create a rounded rectangle. I need too a way that I can change the color from tha forth borders with a tuple, fill inside this rounded rectangle and change the corner radius from until corner. I see this post and I create a rounded rectangle with lines and arc that have almost the requeriments, but I don't think a way to fill this rounded rectangle.

My code:


def rounded_rect(canvas: tkinter.Canvas,
                 x: Optional[int] = 1,
                 y: Optional[int] = 1,
                 width: Optional[int] = 200,
                 height: Optional[int] = 200,
                 radius: Optional[Union[Tuple[int, int, int, int], int]] = 10,
                 border_color: Optional[Union[Tuple[str, str, str, str], str]] = 'black',
                 border_width: Optional[Union[Tuple[int, int, int, int], int]] = 1):
    def check_tuple(value):
        if isinstance(value, tuple):
            return value
        else:
            return value, value, value, value
    _radius = check_tuple(radius)
    _border_color = check_tuple(border_color)
    _border_width = check_tuple(border_width)

    # Top-Left Arc
    canvas.create_arc(x, y, x + 2 * _radius[0], y + 2 * _radius[0],
                      start=90, extent=46, style="arc", outline=_border_color[0], width=_border_width[0])
    # Top-Right Arc
    canvas.create_arc(x + width - 2 * _radius[1], y, x + width, y + 2 * _radius[1],
                      start=45, extent=46, style="arc", outline=_border_color[0], width=_border_width[0])
    # Right-Top Arc
    canvas.create_arc(x + width - 2 * _radius[1], y, x + width, y + 2 * _radius[1],
                      start=0, extent=46, style="arc", outline=_border_color[1], width=_border_width[1])
    # Right-Bottom Arc
    canvas.create_arc(x + width - 2 * _radius[2], y + height - 2 * _radius[2], x + width, y + height,
                      start=315, extent=46, style="arc", outline=_border_color[1], width=_border_width[1])
    # Bottom-Right Arc
    canvas.create_arc(x + width - 2 * _radius[2], y + height - 2 * _radius[2], x + width, y + height,
                      start=270, extent=46, style="arc", outline=_border_color[2], width=_border_width[2])
    # Bottom-Left Arc
    canvas.create_arc(x, y + height - 2 * _radius[3], x + 2 * _radius[3], y + height,
                      start=225, extent=46, style="arc", outline=_border_color[2], width=_border_width[2])
    # Left-Bottom Arc
    canvas.create_arc(x, y + height - 2 * _radius[3], x + 2 * _radius[3], y + height,
                      start=180, extent=46, style="arc", outline=_border_color[3], width=_border_width[3])
    # Left-Top Arc
    canvas.create_arc(x, y, x + 2 * _radius[0], y + 2 * _radius[0],
                      start=135, extent=46, style="arc", outline=_border_color[3], width=_border_width[3])

    # Top Line
    canvas.create_line(x + _radius[0], y, x + width - _radius[1], y,
                       fill=_border_color[0], width=_border_width[0])

    # Right Line
    canvas.create_line(x + width, y + _radius[1], x + width, y + height - _radius[2],
                       fill=_border_color[1], width=_border_width[1])

    # Bottom Line
    canvas.create_line(x + _radius[3], y + height, x + width - _radius[2], y + height,
                       fill=_border_color[2], width=_border_width[2])

    # Left Line
    canvas.create_line(x, y + _radius[0], x, y + height - _radius[3],
                       fill=_border_color[3], width=_border_width[3])

How I use:


root = tkinter.Tk()
root.geometry('500x500')
_canvas = tkinter.Canvas(root)
root.grid_rowconfigure(0, weight=1)
root.grid_columnconfigure(0, weight=1)
_canvas.grid(sticky='nswe')
rounded_rect(_canvas, 5, 5, 300, 300, (0, 10, 20, 30), ('black', 'red', 'green', 'blue'))
root.mainloop()

How it seens:

The Rectangle

It basically the same code that tobias_k answer, I only add the tuples from the colors.

I can't use the create polygon because it don't give me a way to change the border color from each borders, I tried to fill with a polygon, but it don't work, the rounded seens different.


Solution

  • One of the way is to divide the internal area of the rounded rectangle into parts: 4 corners, 4 side bars and the internal rectangle. Then you can fill those parts using canvas function:

    def rounded_rect(canvas: tkinter.Canvas,
                     x: Optional[int] = 1,
                     y: Optional[int] = 1,
                     width: Optional[int] = 200,
                     height: Optional[int] = 200,
                     radius: Optional[Union[Tuple[int, int, int, int], int]] = 10,
                     border_color: Optional[Union[Tuple[str, str, str, str], str]] = 'black',
                     border_width: Optional[Union[Tuple[int, int, int, int], int]] = 1,
                     fill_color: Optional[str] = 'yellow'):  # added fill_color
         ...
        # fill top-left corner
        canvas.create_arc(x, y, x + 2 * _radius[0], y + 2 * _radius[0],
                          start=90, extent=46, fill=fill_color, outline='')
        # fill top-right corner
        canvas.create_arc(x + width - 2 * _radius[1], y, x + width, y + 2 * _radius[1],
                          start=0, extent=90, fill=fill_color, outline='')
        # fill bottom-right corner
        canvas.create_arc(x + width - 2 * _radius[2], y + height - 2 * _radius[2], x + width, y + height,
                          start=270, extent=90, fill=fill_color, outline='')
        # fill bottom-left corner
        canvas.create_arc(x, y + height - 2 * _radius[3], x + 2 * _radius[3], y + height,
                          start=180, extent=90, fill=fill_color, outline='')
        # fill top bar
        h1 = max(_radius[0], _radius[1])
        canvas.create_rectangle(x+_radius[0], y, x+width-_radius[1], y+h1, fill=fill_color, outline='')
        # fill bottom bar
        h2 = max(_radius[2], _radius[3])
        canvas.create_rectangle(x+_radius[3], y+height-h2, x+width-_radius[2], y+height, fill=fill_color, outline='')
        # fill left bar
        w1 = max(_radius[0], _radius[3])
        canvas.create_rectangle(x, y+_radius[0], x+w1, y+height-_radius[3], fill=fill_color, outline='')
        # fill right bar
        w2 = max(_radius[1], _radius[2])
        canvas.create_rectangle(x+width-w2, y+_radius[1], x+width, y+height-_radius[2], fill=fill_color, outline='')
        # fill internal rectangle
        canvas.create_rectangle(x+w1, y+h1, x+width-w2, y+height-h2, fill=fill_color, outline='')
    
        # then draw the outline
        ...
    

    Result:

    enter image description here