Saltar al contenido

¿Cómo hacer un rectángulo de lienzo tkinter con esquinas redondeadas?

Solución:

Ofrecer un enfoque alternativo al método de tobias sería hacerlo con un polígono.

Esto tendría la ventaja de ser un objeto de lienzo si le preocupa la optimización o no tener que preocuparse por un sistema de etiquetas para hacer referencia a un solo objeto.

El código es un poco más largo, pero muy básico, ya que solo utiliza la idea de que al suavizar un polígono, puede dar la misma coordenada dos veces para ‘evitar’ que ocurra el suavizado.

Este es un ejemplo de lo que se puede hacer:

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

Con esta función, puede simplemente proporcionar las coordenadas normales que le daría a un rectángulo y luego especificar el ‘radio’ que se redondea en las esquinas. El uso de **kwargs denota que puede pasar argumentos de palabras clave como fill="blue", tal como lo haría normalmente con un create_ método.

Aunque las coordenadas parecen complejas, simplemente se desplaza metódicamente a cada punto del ‘rectángulo’, dando dos puntos a cada punto que no es de esquina.

Si no le importaba una línea de código bastante larga, podría poner todas las coordenadas en una línea, haciendo que la función solo tenga 2 líneas (!). Esto se parece a:

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)

Esto produce lo siguiente (tenga en cuenta que este es UN objeto de lienzo):

Rectángulo redondeado producido por función

Parece que no hay un método incorporado para esto. Lo más parecido sería una polilínea con smooth=1, pero que todavía se parece más a una vieja pantalla de televisión, con los lados también ligeramente curvados.

En su lugar, podría definir una función auxiliar, combinando el rectángulo redondeado de líneas y arcos:

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)

Ejemplo:

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

ingrese la descripción de la imagen aquí

También puede proporcionar otro **options parámetro para establecer el ancho de línea, el color, etc. para las partes individuales, pero el problema con esto es que, por ejemplo, las líneas y los arcos están usando diferentes parámetros para el color de la línea (fill y outline respectivamente). Además, si desea tener un rectángulo redondeado relleno, deberá especificarlo como un segundo método, utilizando múltiples rectángulos.

Sé que esta publicación ya tiene una respuesta aceptada para un rectángulo. Pero para aquellos que buscan cualquier polígono con esquinas redondeadas (el rectángulo incluido obviamente), hice este código basado en la respuesta de @ SneakyTutle.

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

Resultado

Imagen

La lógica detrás de esto es permitir suavizar y colocar subpuntos junto al vértice. De esta manera, solo se redondearán las esquinas y el resto del polígono se mantendrá plano.

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

No pude encontrar una buena manera de normalizar la nitidez. De todos modos, algo entre 2 y 10 será bueno para cualquier caso. No dude en cambiar el código como desee.

Solo para visualización, para un triángulo con nitidez = 8 el código de resultado para el por bucle es el siguiente. Como puede observar, si la nitidez es 2, los subpuntos se colocarán en el medio del vértice.

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]
    ]
¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *