python - 在 Python 中为多边形创建圆边
问题描述
我遇到了这个有趣的问题(How to make a tkinter canvas rectangle with rounded corners?),与在 Tkinter 中创建圆角矩形有关,特别是 Francisco Gomes 的这个答案(稍作修改):
def roundPolygon(x, y, sharpness):
# 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])
当我调整此代码以与我的图形库一起使用时,它运行得很好!但是当我创建一个“拉伸正方形”(一个非方形矩形)时,圆度也会被拉伸:
那么如何更改此代码以消除拉伸的圆度并使其保持恒定半径?
解决方案
这是一种使用内置tcl tk
原语的方法canvas.create_line
,并canvas.create_arc
构建各种大小的矩形,以及圆角(圆弧)的比例。
角半径表示为矩形最短边的比例(0.0 --> 0.5)
,并且可以参数化。
该函数make_round_corners_rect
返回一个元组,其中包含所有canvas item ids
矩形实体的片段。所有片段都使用其同伴的 ID 进行标记,因此只需一个片段 ID 即可访问整个对象。
#! python3
import math
import tkinter as tk
from tkinter import TclError
def make_round_corners_rect(canvas, x0, y0, x1, y1, ratio=0.2, npts=12):
if x0 > x1:
x0, x1 = x1, x0
if y0 > y1:
y0, y1 = y1, y0
r = min(x1 - x0, y1 - y0) * ratio
items = []
topleft = x0, y0
tld = x0, y0 + r
tlr = x0 + r, y0
item = canvas.create_arc(x0, y0, x0+2*r, y0+2*r, start=90, extent=90, fill='', outline='black', style=tk.ARC)
items.append(item)
top_right = x1, y0
trl = x1 - r, y0
trd = x1, y0 + r
item = canvas.create_line(*tlr, *trl, fill='black')
items.append(item)
item = canvas.create_arc(x1-2*r, y0, x1, y0+2*r, start=0, extent=90, fill='', outline='black', style=tk.ARC)
items.append(item)
bot_right = x1, y1
bru = x1, y1 - r
brl = x1 - r, y1
item = canvas.create_line(*trd, *bru, fill='black')
items.append(item)
item = canvas.create_arc(x1-2*r, y1-2*r, x1, y1, start=270, extent=90, fill='', outline='black', style=tk.ARC)
items.append(item)
bot_left = x0, y1
blr = x0 + r, y1
blu = x0, y1 - r
item = canvas.create_line(*brl, *blr, fill='black')
items.append(item)
item = canvas.create_arc(x0, y1-2*r, x0+2*r, y1, start=180, extent=90, fill='', outline='black', style=tk.ARC)
items.append(item)
item = canvas.create_line(*blu, *tld, fill='black')
items.append(item)
items = tuple(items)
print(items)
for item_ in items:
for _item in items:
canvas.addtag_withtag(item_, _item)
return items
if __name__ == '__main__':
root = tk.Tk()
canvas = tk.Canvas(root, width=500, height=500)
canvas.pack(expand=True, fill=tk.BOTH)
TL = 100, 100
BR = 400, 200
make_round_corners_rect(canvas, *TL, *BR)
TL = 100, 300
BR = 400, 400
make_round_corners_rect(canvas, *TL, *BR, ratio = .3)
TL = 300, 50
BR = 350, 450
that_rect = make_round_corners_rect(canvas, *TL, *BR, ratio=.4)
for fragment in that_rect:
canvas.itemconfig(fragment, width=4)
try:
canvas.itemconfig(fragment, outline='blue')
except TclError:
canvas.itemconfig(fragment, fill='blue')
TL = 150, 50
BR = 200, 450
make_round_corners_rect(canvas, *TL, *BR, ratio=.07)
TL = 30, 30
BR = 470, 470
that_rect = make_round_corners_rect(canvas, *TL, *BR, ratio=.3)
for fragment in that_rect:
canvas.itemconfig(fragment, dash=(3, 3))
TL = 20, 20
BR = 480, 480
make_round_corners_rect(canvas, *TL, *BR, ratio=.1)
root.mainloop()
下一步(留给读者作为练习)是将圆角矩形封装在一个类中。
编辑:如何填充圆角矩形:
它有点复杂,从长远来看,可能需要一种方法,其中所有点都被明确定义,形状形成为多边形,而不是tkinter
图元的聚合。在这个编辑中,圆角矩形被两个重叠的矩形和四个圆盘填充;它允许创建填充/未填充的形状,但不能在创建后更改该属性——尽管它也不需要太多的工作就可以做到这一点。(收集画布 ID,并与属性一起按需打开/关闭它们outline
);但是,如前所述,将所有这些行为封装在一个模仿tk.canvas.items
.
def make_round_corners_rect(canvas, x0, y0, x1, y1, ratio=0.2, npts=12, filled=False, fillcolor=''):
...
if filled:
canvas.create_rectangle(x0+r, y0, x1-r, y1, fill=fillcolor, outline='')
canvas.create_rectangle(x0, y0+r, x1, y1-r, fill=fillcolor, outline='')
canvas.create_oval(x0, y0, x0+2*r, y0+2*r, fill=fillcolor, outline='')
canvas.create_oval(x1-2*r, y0, x1, y0+2*r, fill=fillcolor, outline='')
canvas.create_oval(x1-2*r, y1-2*r, x1, y1, fill=fillcolor, outline='')
canvas.create_oval(x0, y1-2*r, x0+2*r, y1, fill=fillcolor, outline='')
...
if __name__ == '__main__':
...
TL = 100, 300
BR = 400, 400
make_round_corners_rect(canvas, *TL, *BR, ratio=.3, filled=True, fillcolor='cyan')
...
推荐阅读
- c# - 将一个对象分配给另一个对象时是否设置了 DBSet<> 对象的 ID?
- spring-boot - 当我使用@ComponentScan 时没有显示弹簧启动执行器
- vb.net - 有没有办法在我的应用程序中添加“实时更新”通知?
- xcode - clang:错误:XCode 12 更新后链接器命令失败,退出代码为 1
- android - 从firebase实时数据库中单个子项下的多个推送ID中检索数据
- c++ - 为什么它不起作用?简单的多线程示例
- java - 有什么办法可以阻止这个页面直接跳转到另一个页面?
- android-studio - Flutter:找不到提供程序安装程序的本地模块描述符类
- r - 如何每 30 天计算和绘制相关系数
- reactjs - 如何仅在第二次访问页面时显示DIV