python - 在 Python 中为多边形创建圆边

标签 python python-3.x tkinter canvas tkinter-canvas

我遇到了这个有趣的问题 ( 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])

当我调整此代码以与我的图形库一起使用时,它运行良好!但是当我创建一个“拉伸(stretch)正方形”(一个非正方形矩形)时,圆度也会被拉伸(stretch):

enter image description here

那么我该如何更改此代码以消除拉伸(stretch)的圆度并使其保持恒定半径?

最佳答案

这是一种方法,它使用内置的 tcl tk 基元 canvas.create_linecanvas.create_arc 来构建各种尺寸的矩形, 以及带圆角(圆弧)的比例。

角半径表示为矩形最短边的比例(0.0 --> 0.5),并且可以参数化。

函数 make_round_corners_rect 返回一个元组,其中包含所有 canvas item ids 作为矩形实体的片段。所有片段都标有其伙伴的 ID,因此仅使用一个片段 ID 就可以访问整个对象。


enter image description here


#! 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 基元的聚合。在此编辑中,圆角矩形由两个重叠的矩形和四个圆盘填充;它允许创建一个填充/未填充的形状,但在创建后不能更改该属性——尽管也不需要太多的工作就可以做到这一点。 (收集 Canvas 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')
    ...

enter image description here


关于python - 在 Python 中为多边形创建圆边,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62715279/

相关文章:

python - Tkinter 回调中的奇怪异常

python - GAE + NDB + Blobstore + Google 高性能图像服务

python - 修改函数中的变量没有结果

python - 计算二维数组中出现的次数

python - pytest 模拟类的 __init__ 中的函数调用

python - Python 函数列表

python-3.x - 查找两列 df 之间存在的任何元素

python - 从 tkinter gui 停止 python 线程

python - 重新缩放给定最大值和最小值之间的值,其中最大值为正,最小值为负

python - 在琶音中实现节点变压器