tkinter绘制组件(29)——单选组控件

引言

其实,光看到TinUI这个控件的名称——“单选组控件”,是否想起了之前的TinUI单选框?

事实上,单选组控件(radiobox)与单选框的功能很像,都是允许使用者确定一个唯一的选项,以此让程序决定去做些什么。但是,这两者真的重复吗?不尽然,首先看看radiobutton的一些不足,或者说是特点:

  1. 固化规定了是问题类型的单选情形

  2. 竖直排列布局

  3. 样式为TinUI原创样式

当然,以上内容并不能说是单选框的缺点,而只能说它的专一项目。因此,radiobutton更像是一个封装好的控件,而单选组控件则像是一个标准控件。当然,它们两个不能互相替代,这也就是为TinUI加上radiobox控件的原因。

最近有人提议为radiobutton添加边框参数,以及加入add_image函数。但是希望大家把要求说明白些,否则我也无从下手,因为我还有自己的改进计划。


布局

函数结构

    def add_radiobox(self,pos:tuple,fontfg='black',font='微软雅黑 12',fg='#8b8b8b',bg='#ededed',activefg='#898989',activebg='#e5e5e5',onfg='#3041d8',onbg='#ffffff',content:tuple=('1','','2'),padx=10,pady=5,command=None):#绘制单选组控件
        '''
- pos::位置
- fontfg::文本颜色
- fg::标识符边框颜色
- bg::标识符背景颜色
- activefg::鼠标进入标识符边框颜色
- activebg::鼠标进入标识符背景颜色
- onfg::选定标识符边框颜色
- onbg::选定标识符背景颜色
- content::选择文本内容。如果为空字符串则代表换行
- padx::水平间距
- pady::行间距
- command::回调函数,必须接受一个参数,所选选项的文本
        '''

确定固定元素

这里所说的固定元素,包括:

  • 标识符常态直径和边框宽度

  • 标识符激活时直径和边框宽度

  • boxes字典,用于放置每一个单选元素的标识符、文本、背景

  • 当前选择项目(初始化为-1

        #标识符内部宽度width和边框宽度line
        back_width=16
        back_line=1#16+1*2=18
        #active... = back...
        on_width=8
        on_line=4#8+(4+1)*2=18
        boxes=[]#[(sign_id,text_id,back_id),...],换行为(None,'\n',None)
        nowx,nowy=pos#x坐标为左上角插入坐标,y坐标为底部坐标
        uid='radiobox'+str(id(pos))
        select=-1#当前选定
        count=-1
        t_bbox=None

绘制单选元素

这些文本都在元组content里,但是,我们不可能只有横向布局吧,如何让TinUI知道应该在新的一行布置单选元素呢?那就是使用文本元组中的空字符。当字符串为空时,则代表新增一行。值得注意的是,当目前没有任何单选元素,也就是前面没有任何文本或全是空字符时,我们往下加行的纵坐标边行量就是pady,当本行存在单选元素时,就再加上本行高度即可。

        for i in content:
            count+=1#计数
            if i=='':
                if t_bbox==None:#没有底部坐标数据
                    nowy+=pady
                else:
                    nowy=t_bbox[3]+pady
                nowx=pos[0]
                boxes.append((None,'\n',None))
                continue
            x1=nowx+back_line
            y1=nowy+back_line
            x2=nowx+back_line+back_width
            y2=nowy+back_line+back_width
            ar=(x1,y1,x2,y2)
            sign=self.create_oval(ar,width=back_line,fill=bg,outline=fg,tags=uid)
            text=self.create_text((x2+5,nowy),text=i,font=font,fill=fontfg,anchor='nw',tags=uid)
            s_bbox=self.bbox(sign)
            t_bbox=self.bbox(text)
            back=self.create_rectangle((s_bbox[0],s_bbox[1],t_bbox[2],t_bbox[3]),width=0,fill='',tags=uid)
            boxes.append((sign,text,back))
            nowx=t_bbox[2]+padx

back元素是背景元素,其填充色为透明,但是层级在标识符元素和文本元素之上,因此虽然我们点击的是单选元素,但实际上是覆盖在其上的一层“膜”,而标识符只负责变化提示色。随着时间的推移,我对TinUI开发的逻辑正在持续优化>^<。

鼠标进入和离开

        def button_in(area,sel,sign):
            if sel==select:
                return
            self.itemconfig(sign,outline=activefg,fill=activebg)
        def button_out(area,sel,sign):
            if sel==select:
                return
            self.itemconfig(sign,outline=fg,fill=bg)

这里面也有新逻辑:记录前一个选定项目,根据这个值,也就是select的值,对前一个选定项目做出样式更改,而不是像早期一样,通过循环找到选定向。这种逻辑大大提高了运行效率。

虽然早期的逻辑代码仍然存在,有issue再改。

项目选定

        def sel_it(area,sel,sign):
            nonlocal select
            #...

项目的选定分为三个逻辑。

判断是否已经为选定项:

        def sel_it(area,sel,sign):
            nonlocal select
            if sel==select:
                return

如果有先前的选定项,则将其更换为普通样式:

            #...
            old_select=select#原先选定项目序号
            select=sel
            if old_select>=0:#恢复原先的单选组
                old_sign=boxes[old_select][0]
                self.itemconfig(old_sign,width=1)
                button_out(None,None,old_sign)
                self.update()    
            #...

将当前更改为选定标识样式:

            self.itemconfig(sign,outline=onfg,fill=onbg,width=on_line)
            if command!=None:
                textid=boxes[sel][1]
                text=self.itemcget(textid,'text')
                command(text)

绑定样式:

            self.tag_bind(back,'<Enter>',lambda event,ar=ar,sel=count,sign=sign:button_in(ar,sel,sign))
            self.tag_bind(back,'<Leave>',lambda event,ar=ar,sel=count,sign=sign:button_out(ar,sel,sign))
            self.tag_bind(back,'<Button-1>',lambda event,ar=ar,sel=count,sign=sign:sel_it(ar,sel,sign))

完整代码函数

    def add_radiobox(self,pos:tuple,fontfg='black',font='微软雅黑 12',fg='#8b8b8b',bg='#ededed',activefg='#898989',activebg='#e5e5e5',onfg='#3041d8',onbg='#ffffff',content:tuple=('1','','2'),padx=10,pady=5,command=None):#绘制单选组控件
        def button_in(area,sel,sign):
            if sel==select:
                return
            self.itemconfig(sign,outline=activefg,fill=activebg)
        def button_out(area,sel,sign):
            if sel==select:
                return
            self.itemconfig(sign,outline=fg,fill=bg)
        def sel_it(area,sel,sign):
            nonlocal select
            if sel==select:
                return
            old_select=select#原先选定项目序号
            select=sel
            if old_select>=0:#恢复原先的单选组
                old_sign=boxes[old_select][0]
                self.itemconfig(old_sign,width=1)
                button_out(None,None,old_sign)
                self.update()
            self.itemconfig(sign,outline=onfg,fill=onbg,width=on_line)
            if command!=None:
                textid=boxes[sel][1]
                text=self.itemcget(textid,'text')
                command(text)
        #标识符内部宽度width和边框宽度line
        back_width=16
        back_line=1#16+1*2=18
        #active... = back...
        on_width=8
        on_line=4#8+(4+1)*2=18
        boxes=[]#[(sign_id,text_id,back_id),...],换行为(None,'\n',None)
        nowx,nowy=pos#x坐标为左上角插入坐标,y坐标为底部坐标
        uid='radiobox'+str(id(pos))
        select=-1#当前选定
        count=-1
        t_bbox=None
        for i in content:
            count+=1#计数
            if i=='':
                if t_bbox==None:#没有底部坐标数据
                    nowy+=pady
                else:
                    nowy=t_bbox[3]+pady
                nowx=pos[0]
                boxes.append((None,'\n',None))
                continue
            x1=nowx+back_line
            y1=nowy+back_line
            x2=nowx+back_line+back_width
            y2=nowy+back_line+back_width
            ar=(x1,y1,x2,y2)
            sign=self.create_oval(ar,width=back_line,fill=bg,outline=fg,tags=uid)
            text=self.create_text((x2+5,nowy),text=i,font=font,fill=fontfg,anchor='nw',tags=uid)
            s_bbox=self.bbox(sign)
            t_bbox=self.bbox(text)
            back=self.create_rectangle((s_bbox[0],s_bbox[1],t_bbox[2],t_bbox[3]),width=0,fill='',tags=uid)
            boxes.append((sign,text,back))
            self.tag_bind(back,'<Enter>',lambda event,ar=ar,sel=count,sign=sign:button_in(ar,sel,sign))
            self.tag_bind(back,'<Leave>',lambda event,ar=ar,sel=count,sign=sign:button_out(ar,sel,sign))
            self.tag_bind(back,'<Button-1>',lambda event,ar=ar,sel=count,sign=sign:sel_it(ar,sel,sign))
            nowx=t_bbox[2]+padx
        return boxes,uid

效果

测试代码

def test8(rbtext):
    print(f'单选组控件选值=>{rbtext}')
if __name__=='__main__':
    #...
    b.add_radiobox((320,1150),content=('1','2','3','','新一行内容','','单选','组','控件'),command=test8)
    #...

最终效果

在这里插入图片描述

2022-10-22新样式

在这里插入图片描述
让它圆那么一点点。。。

2023-12-24新样式

在这里插入图片描述
再圆那么一点点……

2024-6-22

在这里插入图片描述
巨圆⚪……


github项目

TinUI的github项目地址

pip下载

pip install tinui

结语

TinUI接下来的主要任务是改进控件,关于新增模块欢迎通过电子邮箱投递。

🔆tkinter创新🔆

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值