tkinter绘制组件13——开关

引言

开关组件实际上类似于复选框组件,但是两者的用途是有区别的。复选框用于在大部分的选项中选定,而开关组件则是用在开启或关闭功能上,因此有必要在TinUI增加开关组件(OnOffButton)。

关于开关组件的样式,本来打算仿造如今UWP中的开关组件,但是遇到了一个问题:在Windows平台下,tkinter绘制曲线会产生影响视觉效果的锯齿形状,而且无法消除。然而开关组件的两侧就是半圆形,显然在TinUI中无法使用这个样式。那么我们直接使用新的开关组件样式算了。

如果你是在Mac或Linux平台上运行,你可以自行修改TinUI在github上绘制开关组件的代码。


布局

函数结构

def add_onoff(self,pos:tuple,fg='#333333',bg='#FFFFFF',onfg='#FFFFFF',onbg='#4258CC',font=('微软雅黑',12),command=None):#绘制开关控件
    '''
    pos::位置
    fg::关闭状态下的文本和边框颜色
    bg::关闭状态下的背景颜色
    onfg::开启状态下的文本颜色
    onbg::开启状态下的边框和背景颜色
    font::字体
    command::响应开关时调用函数,必须接受一个参数:True(开启)或False(关闭)
    '''

样式

TinUI绘制的开关组件大概是这个样子的:

  _____________________
 /                     \
/         on            \
\         off           /
 \                     /
  —————————————————————

因为是文本符号标识,所以有一点粗糙。

文本绘制

这个命名很简单,但是为什么要着重强调呢?

因为文本的绘制,关系到背景样式的绘制。背景样式的绘制需要依靠文本的覆盖尺寸,通过这些数据进行计算,然后才能够得出背景的绘制范围。

state=self.create_text(pos,anchor='nw',text=state,fill=fg,font=font)
bbox=self.bbox(state)
d=int(bbox[3]-bbox[1])#获得绘制半径
width=bbox[2]-bbox[0]#获取绘制宽度
#将文本移到最大高度位置
self.move(state,d,0)

背景绘制

通过文本的绘制,我们得到并执行了以下数据操作:

  1. 获取背景两侧的各三个关键折点
  2. 将文本移动到符合的位置

因为背景是一个对称的六边形,因此我们不再使用画布的标准图形绘制的了,我们将直接使用create_polygon函数绘制这一个多边形。

通过计算得到六个关键点的坐标:

(pos[0]+d,pos[1],#左上折点
 pos[0],pos[1]+d/2,#左折点
 pos[0]+d,pos[1]+d,#左下折点
 pos[0]+d+width,pos[1]+d,#右下折点
 pos[0]+d*2+width,pos[1]+d/2,#右折点
 pos[0]+d+width,pos[1],#右上这点
 pos[0]+d,pos[1])#重新回到左上折点

接着就是绘制多边形的代码:

back=self.create_polygon((pos[0]+d,pos[1],pos[0],pos[1]+d/2,pos[0]+d,pos[1]+d,pos[0]+d+width,pos[1]+d,pos[0]+d*2+width,pos[1]+d/2,pos[0]+d+width,pos[1],pos[0]+d,pos[1]),fill=bg,outline=fg,width=2,joinstyle='miter')
self.tkraise(state)

响应开关

在开关组件的响应事件中,我们通过三个函数实现开关的响应:

def __on():
    #开启时响应函数调用
def __off():
    #关闭时响应函数调用
def __on_click(event):
    #判断开关状态,并重绘开关样式

主要问题是判断开关的状态,根据之前的经验,我们可以通过画布对象样式判断开关的状态,并且重绘样式和执行相应的操作。

def __on():
    if command!=None:
        command(True)
def __off():
    if command!=None:
        command(False)
def __on_click(event):
    if self.itemcget(state,'fill')==fg:#开关关闭,开启
        self.itemconfig(state,fill=onfg,text='on')
        self.move(state,width//10,0)
        self.itemconfig(back,fill=onbg,outline=onbg)
        __on()
    else:#开关开启,关闭
        self.itemconfig(state,fill=fg,text='off')
        self.move(state,0-width//10,0)
        self.itemconfig(back,fill=bg,outline=fg)
        __off()

然后再为两个画布对象绑定响应事件:

self.tag_bind(state,'<Button-1>',__on_click)
self.tag_bind(state,'<Button-1>',__on_click)

完事。

完整代码函数

def add_onoff(self,pos:tuple,fg='#333333',bg='#FFFFFF',onfg='#FFFFFF',onbg='#4258CC',font=('微软雅黑',12),command=None):#绘制开关控件
    def __on():
        if command!=None:
            command(True)
    def __off():
        if command!=None:
            command(False)
    def __on_click(event):
        if self.itemcget(state,'fill')==fg:
            self.itemconfig(state,fill=onfg,text='on')
            self.move(state,width//10,0)
            self.itemconfig(back,fill=onbg,outline=onbg)
            __on()
        else:
            self.itemconfig(state,fill=fg,text='off')
            self.move(state,0-width//10,0)
            self.itemconfig(back,fill=bg,outline=fg)
            __off()
    state=self.create_text(pos,anchor='nw',text=state,fill=fg,font=font)
    bbox=self.bbox(state)
    d=int(bbox[3]-bbox[1])#获得绘制半径
    width=bbox[2]-bbox[0]#获取绘制宽度
    self.move(state,d,0)
    back=self.create_polygon((pos[0]+d,pos[1],pos[0],pos[1]+d/2,pos[0]+d,pos[1]+d,pos[0]+d+width,pos[1]+d,pos[0]+d*2+width,pos[1]+d/2,pos[0]+d+width,pos[1],pos[0]+d,pos[1]),fill=bg,outline=fg,width=2,joinstyle='miter')
    self.tkraise(state)
    self.tag_bind(state,'<Button-1>',__on_click)
    self.tag_bind(state,'<Button-1>',__on_click)
    return state,back

效果

测试代码

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()
def test3(event):
    ok2()
def test4(event):
    from time import sleep
    for i in range(1,101):
        sleep(0.02)
        progressgoto(i)

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='#CCCCCC')
    b.add_button((500,270),'停止等待动画',activefg='cyan',activebg='black',command=test2)
    bu1=b.add_button((700,200),'停止点状滚动条',activefg='white',activebg='black',command=test3)[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')
    _,_,ok2=b.add_waitbar2((600,400))
    b.add_combobox((600,550),text='你有多大可能去珠穆朗玛峰',content=('20%','40%','60%','80%','100%','1000%'))
    b.add_button((600,480),text='测试进度条(无事件版本)',command=test4)
    _,_,_,progressgoto=b.add_progressbar((600,510))
    b.add_table((180,630),data=(('a','space fans over the world','c'),('you\ncan','2','3'),('I','II','have a dream, then try your best to get it!')))
    b.add_paragraph((300,810),text='上面是一个表格')
    b.add_onoff((600,100))

    a.mainloop()

最终效果

在这里插入图片描述

2022-8-24新样式

在这里插入图片描述


这哪是新样式,这分明是重写了onoff元素控件:

  • 使用winui样式以及圆角
  • 添加相关方法,也就是funcs返回值

github项目

TinUI的github项目地址

pip下载

pip install tinui

结语

TinUI能够实现目前这些组件的绘制已经很不错了,并且TinUI是能够运用到tkinter窗口组件绘制上的。

下面是TinGroup应用中TinUpgrader使用TinUI绘制前后的差别。

之前的TinUpgrader

之前使用tkinter原生组件的TinUpgrader


使用TinUI的TinUpgrader

使用TinUI的TinUpgrader


从对比上也可以看出TinUI对tkinter界面视觉效果有很大改善。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


学习编程是顺着互联网的发展潮流,是一件好事。新手如何学习编程?其实不难,不过在学习编程之前你得先了解你的目的是什么?这个很重要,因为目的决定你的发展方向、决定你的发展速度。
IT行业是什么工作做什么?IT行业的工作有:产品策划类、页面设计类、前端与移动、开发与测试、营销推广类、数据运营类、运营维护类、游戏相关类等,根据不同的分类下面有细分了不同的岗位。
女生学Java好就业吗?女生适合学Java编程吗?目前有不少女生学习Java开发,但要结合自身的情况,先了解自己适不适合去学习Java,不要盲目的选择不适合自己的Java培训班进行学习。只要肯下功夫钻研,多看、多想、多练
Can’t connect to local MySQL server through socket \'/var/lib/mysql/mysql.sock问题 1.进入mysql路径
oracle基本命令 一、登录操作 1.管理员登录 # 管理员登录 sqlplus / as sysdba 2.普通用户登录
一、背景 因为项目中需要通北京网络,所以需要连vpn,但是服务器有时候会断掉,所以写个shell脚本每五分钟去判断是否连接,于是就有下面的shell脚本。
BETWEEN 操作符选取介于两个值之间的数据范围内的值。这些值可以是数值、文本或者日期。
假如你已经使用过苹果开发者中心上架app,你肯定知道在苹果开发者中心的web界面,无法直接提交ipa文件,而是需要使用第三方工具,将ipa文件上传到构建版本,开...
下面的 SQL 语句指定了两个别名,一个是 name 列的别名,一个是 country 列的别名。**提示:**如果列名称包含空格,要求使用双引号或方括号:
在使用H5混合开发的app打包后,需要将ipa文件上传到appstore进行发布,就需要去苹果开发者中心进行发布。​
+----+--------------+---------------------------+-------+---------+
数组的声明并不是声明一个个单独的变量,比如 number0、number1、...、number99,而是声明一个数组变量,比如 numbers,然后使用 nu...
第一步:到appuploader官网下载辅助工具和iCloud驱动,使用前面创建的AppID登录。
如需删除表中的列,请使用下面的语法(请注意,某些数据库系统不允许这种在数据库表中删除列的方式):
前不久在制作win11pe,制作了一版,1.26GB,太大了,不满意,想再裁剪下,发现这次dism mount正常,commit或discard巨慢,以前都很快...
赛门铁克各个版本概览:https://knowledge.broadcom.com/external/article?legacyId=tech163829
实测Python 3.6.6用pip 21.3.1,再高就报错了,Python 3.10.7用pip 22.3.1是可以的
Broadcom Corporation (博通公司,股票代号AVGO)是全球领先的有线和无线通信半导体公司。其产品实现向家庭、 办公室和移动环境以及在这些环境...
发现个问题,server2016上安装了c4d这些版本,低版本的正常显示窗格,但红色圈出的高版本c4d打开后不显示窗格,
TAT:https://cloud.tencent.com/document/product/1340