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