tkinter绘制组件(8)——标题框

引言

在Win32程序中,通常会用到标题框来划定一系列功能组件的界限,或者是区分每一部分的组件对应的主题。在tkinter中,LabelFrame就只这样的一个组件,让人对界面的感觉很整齐、明了,而且还便于操作。

那么,在TinUI中,我们也要实现这样的一个绘制组件,来使TinUI的部分界面整齐划一。


思路

不同于其它GUI框架中的LabelFrame,TinUI中的LabelFrame并不是作为一个父组件来使用,这是TinUI框架自身的性质决定的——无法进行pack/gird排版。因此,TinUI创建LabelFrame的思路跟其它的框架有不一样,是先等需要划入的组件全部绘制完后,再指定LabelFrame需要包含的组件,TinUI根据这些组件的范围,自动绘制LabelFrame。

因此,TinUI中的LabelFrame,是TinUI目前第一个需要通过指定其它绘制组件进行绘制的控件。


布局

函数结构

TinUI中,LabelFrame采取与其它组件略微不同的函数参数构造。

def add_labelframe(self,widgets:tuple=(),title='',fg='#A8A8A8',bg=''):#绘制标题框
    '''
    widgets::所涵盖的组件对应的画布对象的列表
    title::标题文字
    fg::边框及标题颜色
    bg::标题背景和组件背景填充色
    '''

边框

这个很好处理,通过获取所需要包含的组件在画布中的位置,再位置计算得出边框正确的绘制范围。

先任意获取其中一个组件的范围,作为初始值。再依次获取组件画布对象的范围,通过比较各个范围的最大值或最小值,来刷新需要绘制边框的范围:

sx,sy,ex,ey=self.bbox(widgets[0])#获取直接的起始位置
for i in widgets:
    nsx,nsy,nex,ney=self.bbox(i)
    sx=nsx if nsx<sx else sx
    sy=nsy if nsy<sy else sy
    ex=nex if nex>ex else ex
    ey=ney if ney>ey else ey

然后绘制边框:

bg=self['background'] if bg=='' else bg
frame=self.create_rectangle((sx-5,sy-20,ex+5,ey+5),fill=bg,outline=fg)

这里有一个细节,就是背景不建议在画布中透明,否则,当界面复杂时,很影响视觉效果。

标题

在TinUI中,并没有给LabelFrame的标题设置提供很多参数,至少目前是。

标题会被绘制在边框上方的正中间,同时绘制有一个矩形作为标题的背景。这个技巧在之前的几篇文章中都出现过很多次,这里直接上代码:

label=self.create_text(((sx+ex)//2,sy-20),font='微软雅黑 10',text=title,fill=fg,anchor='center')
self.create_rectangle(self.bbox(label),fill=bg,outline=bg)
self.tag_raise(label)
self.tag_lower(frame)

完整代码

def add_labelframe(self,widgets:tuple=(),title='',fg='#A8A8A8',bg=''):#绘制标题框
    sx,sy,ex,ey=self.bbox(widgets[0])#获取直接的起始位置
    for i in widgets:
        nsx,nsy,nex,ney=self.bbox(i)
        sx=nsx if nsx<sx else sx
        sy=nsy if nsy<sy else sy
        ex=nex if nex>ex else ex
        ey=ney if ney>ey else ey
    bg=self['background'] if bg=='' else bg
    frame=self.create_rectangle((sx-5,sy-20,ex+5,ey+5),fill=bg,outline=fg)
    label=self.create_text(((sx+ex)//2,sy-20),font='微软雅黑 10',text=title,fill=fg,anchor='center')
    self.create_rectangle(self.bbox(label),fill=bg,outline=bg)
    self.tag_raise(label)
    self.tag_lower(frame)
    return label,frame

效果

测试代码

def test(event):
    a.title('TinUI Test')
    b.add_paragraph((50,150),'这是TinUI按钮触达的事件函数回显,此外,窗口标题也被改变、首行标题缩进减小')
    b.coords(m,100,5)
def test1(word):
    print(word)
def test2(event):
    ok1()

if __name__=='__main__':
    a=Tk()
    a.geometry('700x700+5+5')

    b=TinUI(a,bg='white')
    b.pack(fill='both',expand=True)
    m=b.add_title((600,0),'TinUI is a test project for futher tin using')
    m1=b.add_title((0,680),'test TinUI scrolled',size=2,angle=24)
    b.add_paragraph((20,290),'''     TinUI是基于tkinter画布开发的界面UI布局方案,作为tkinter拓展和TinEngine的拓展而存在。目前,TinUI尚处于开发阶段。如果想要使用完整的TinUI,敬请期待。''',
    angle=-18)
    b.add_paragraph((20,100),'下面的段落是测试画布的非平行字体显示效果,也是TinUI的简单介绍')
    b.add_button((250,450),'测试按钮',activefg='white',activebg='red',command=test,anchor='center')
    b.add_checkbutton((80,430),'允许TinUI测试',command=test1)
    b.add_label((10,220),'这是由画布TinUI绘制的Label组件')
    b.add_entry((250,300),350,30,'这里用来输入')
    b.add_separate((20,200),600)
    b.add_radiobutton((50,480),300,'sky is blue, water is blue, too. So, what is your heart',('red','blue','black'),command=test1)
    b.add_link((400,500),'TinGroup知识库','http://tinhome.baklib-free.com/')
    _,ok1=b.add_waitbar1((500,220),bg='lightgreen')
    b.add_button((500,270),'停止等待动画',activefg='cyan',activebg='black',command=test2)
    bu1=b.add_button((700,200),'nothing button 1')[1]
    bu2=b.add_button((700,250),'nothing button 2')[1]
    bu3=b.add_button((700,300),'nothing button 3')[1]
    b.add_labelframe((bu1,bu2,bu3),'box buttons')

    a.mainloop()

最终效果

在这里插入图片描述


github项目

TinUI的github项目地址

pip下载

pip install tinui

结语

下一次将添加新的等待组件。

🔆tkinter创新🔆

### 使用 Python Tkinter 实现仪表盘绘制 为了实现一个简单的仪表盘,Tkinter 提供了足够的工具来完成这一任务。下面是一个具体的例子展示如何利用 `Canvas` 组件以及基本图形函数如 `create_arc()` 来创建一个类似于速度表的仪表盘。 #### 创建基础窗口并初始化 Canvas ```python import tkinter as tk from math import pi, sin, cos class Speedometer(tk.Canvas): def __init__(self, master=None, **kwargs): super().__init__(master, bg='white', highlightthickness=0, **kwargs) width = kwargs.get('width', 400) height = kwargs.get('height', 400) self.center_x = width / 2 self.center_y = height * 0.6 # 偏下一点更像真实的速度计 self.draw_background() self.update_needle(0) # 初始化指针位置 def draw_background(self): outer_radius = min(self.winfo_reqwidth(), self.winfo_reqheight()) * 0.48 inner_radius = outer_radius * 0.9 # 外环 self.create_oval( self.center_x - outer_radius, self.center_y - outer_radius, self.center_x + outer_radius, self.center_y + outer_radius, outline="black", fill="#ebebeb" ) # 内部刻度线与文字标记 start_angle = -pi/2 # 开始角度 (顶部) end_angle = pi/2 # 结束角度 (底部) total_degrees = int((end_angle - start_angle)/pi*180)+1 step_size = 20 # 每格代表多少单位 for i in range(total_degrees//step_size+1): angle = start_angle+i*(end_angle-start_angle)/(total_degrees//step_size) x_start = self.center_x + cos(angle)*inner_radius y_start = self.center_y + sin(angle)*inner_radius tick_length_ratio = 0.1 if i % 5 != 0 else 0.2 x_end = self.center_x + cos(angle)*(inner_radius-(tick_length_ratio*outer_radius)) y_end = self.center_y + sin(angle)*(inner_radius-(tick_length_ratio*outer_radius)) self.create_line(x_start, y_start, x_end, y_end, fill='black') if i % 5 == 0: value_text = str(int(i/(total_degrees//step_size)*100))+"%" text_position_offset = (-7,-7) if i==0 else (+7,+7) self.create_text( self.center_x + cos(angle)*(inner_radius-0.3*outer_radius)+text_position_offset[0], self.center_y + sin(angle)*(inner_radius-0.3*outer_radius)+text_position_offset[1], anchor=tk.CENTER, font=("Arial", 12), text=value_text ) def update_needle(self, percentage): """ 更新指针的位置 """ max_angle = pi # 半圆范围内的最大弧度数 current_angle = ((percentage / 100) * max_angle)-pi/2 needle_length = min(self.winfo_reqwidth(), self.winfo_reqheight()) * 0.4 tip_x = self.center_x + cos(current_angle) * needle_length tip_y = self.center_y + sin(current_angle) * needle_length base_width = 5 head_width = 15 try: self.delete("needle") # 清除旧的指针 except Exception: pass self.create_polygon([ self.center_x-base_width/2, self.center_y-head_width/2, self.center_x+base_width/2, self.center_y+head_width/2, tip_x, tip_y], tags="needle", fill='#ffcc00') if __name__ == '__main__': root = tk.Tk() speedo = Speedometer(root, width=400, height=300) speedo.pack(expand=True, fill=tk.BOTH) # 测试用法:每隔一秒改变一次百分比读数 from threading import Timer import random def change_value(): new_percentage = round(random.uniform(0, 100), 2) print(f'Updating to {new_percentage}%...') speedo.update_needle(new_percentage) Timer(1.0, change_value).start() change_value() root.mainloop() ``` 这段代码展示了如何通过继承 `tk.Canvas` 类来自定义一个新的组件——`Speedometer`,该类负责管理整个仪表盘的外观及其内部元素(比如背景、刻度线和指针)。此程序还包含了定时器逻辑以便于测试目的动态调整仪表数值[^1]。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值