tkinter绘制组件(27)——标签栏视图

引言

TinUI的又一个规模型控件,而且标签栏视图在应用中很常见:便笺、浏览器、设置界面、分类工作栏等等。所以,一旦TinUI加入了标签栏视图,TinUI的实用性将更上一层楼。

当然,TinUI早就可以进行实用开发了。

为什么说它是规模型控件呢?因为它真的很复杂。

目前,TinUI实现控件代码量最大的是滚动条(scrollbar),但是,(剧透)notebook标签栏视图的代码量跟滚动条差不多。不同的是,滚动条只是在一个小小的地方进行诸多复杂的操作,而notebook这是一个“复杂”的广控制范围控件。因此,notebook控件也是我目前唯一一个打草稿后才编写的TinUI控件。

草稿就不放了,还是往下看看吧。


布局

函数结构

def add_notebook(self,pos:tuple,width:int=400,height:int=400,color='#f9f9fc',fg='#1a1a1a',bg='#f3f3f3',activefg='#595959',activebg='#ededed',onfg='#191919',onbg='#eaeaea'):#绘制标签栏视图

    '''
    pos::位置
    width::页面宽度
    height::页面高度。注意,都是页面,不是控件
    color::大背景颜色
    fg::文本、滚动条颜色
    bg::标签颜色
    activefg::鼠标进入时提示色
    activebg::鼠标进入时提示色
    onfg::鼠标点击时提示色
    onbg::鼠标点击时提示色
    '''

创建主体元素

这一部分和其它控件的操作是一样的,毕竟我们的重点在逻辑部分的渲染和操作。这里直接给代码:

        tbu=BasicTinUI(self,bg=color)
        tbuid=self.create_window((pos[0]+2,pos[1]+2),window=tbu,width=width,height=30,anchor='nw')
        uid='notebook'+str(tbuid)
        self.addtag_withtag(uid,tbuid)
        scro=self.add_scrollbar((pos[0]+5,pos[1]+32),tbu,height=width-5,direction='x',bg=bg,color=fg,oncolor=onfg)
        self.addtag_withtag(uid,scro[-1])
        barheight=self.bbox(scro[-1])[3]
        backpos=(pos[0]+5,pos[1]+3,pos[0]+width+2,pos[1]+3,pos[0]+width+2,barheight+height-3,pos[0]+5,barheight+height-3,pos[0]+5,pos[1]+5)
        back=self.create_polygon(backpos,outline=color,fill=color,width=10,tags=uid)
        self.tkraise(tbuid)
        self.tkraise(scro[-1])
        viewpos=(pos[0]+2,barheight+2)

添加关键变量

这就涉及到逻辑操作了,首先我们需要明白有以下变量是很关键的:

  • nowpage当前显示的页面标识符

  • vdict页面元素字典

  • tbdict标签元素字典

  • flaglist标识符列表

这里说到了标识符,解释一下。就是这个标识符对应了相应的所有标签和页面元素。一般由编写者自行决定,也可以由TinUI自动决定。稍后会谈到。

        nowpage=''
        vdict=dict()#ui,uixml,uiid
        tbdict=dict()#title,cb,pyo
        flaglist=list()
        font='微软雅黑 12'

样式操作

老生常谈:

        def __onenter(flag):
            if flag==nowpage:
                return
            t,c,b=tbdict[flag]
            tbu.itemconfig(t,fill=activefg)
            tbu.itemconfig(c,fill=activefg)
            tbu.itemconfig(b,fill=activebg,outline=activebg)
        def __onleave(flag):
            if flag==nowpage:
                return
            t,c,b=tbdict[flag]
            tbu.itemconfig(t,fill=fg)
            tbu.itemconfig(c,fill=fg)
            tbu.itemconfig(b,fill=bg,outline=bg)
        def __onclick(flag):
            if flag==nowpage:
                return
            t,c,b=tbdict[flag]
            tbu.itemconfig(t,fill=onfg)
            tbu.itemconfig(c,fill=onfg)
            tbu.itemconfig(b,fill=onbg,outline=onbg)

添加页面

这里指的是单纯地往notebook添加一个页面,不是显示。

先来在标签栏里创建一个标签。

        def addpage(title:str,flag=None,scrollbar=False):#创建页面
            if tbu.bbox('all')==None:
                endx=3
            else:
                endx=tbu.bbox('all')[2]+3
            titleu=tbu.create_text((endx,2),text=title,fill=fg,font=font,anchor='nw')
            cbx=tbu.bbox(titleu)[2]+10
            cb=tbu.create_text((cbx,2),text='×',font=font,fill=fg,anchor='nw')
            tbbbox=tbu.bbox(titleu)
            bux=(endx+2,tbbbox[1],cbx+15,tbbbox[1],cbx+15,tbbbox[3],endx+2,tbbbox[3],endx+2,tbbbox[1])
            bu=tbu.create_polygon(bux,fill=bg,outline=bg,width=5)
            tbu.lower(bu)

上面提到的标识符,就是这里的flag参数,这个参数对于后续编写者对这些页面的控制是至关重要的,因此需要做到唯一。如果编写者已经给定了,那就是机器不违背人的意志,随TA,但是,如果需要TinUI自己决定呢?(ta比较懒)

            if flag==None:
                flag='flag'+str(titleu)

很简单。

接下来随便创建页面:

            if scrollbar:
                page=TinUI(self,True,bg=self['background'])
            elif scrollbar==False:
                page=BasicTinUI(self,bg=self['background'])
            uiid=self.create_window(viewpos,window=page,width=width,height=height,anchor='nw',state='hidden')
            uixml=TinUIXml(page)
            bbox=tbu.bbox('all')
            tbu.config(scrollregion=bbox)
            vdict[flag]=(page,uixml,uiid)
            tbdict[flag]=(titleu,cb,bu)
            flaglist.append(flag)
            tbu.tag_bind(titleu,'<Enter>',lambda event:__onenter(flag))
            tbu.tag_bind(cb,'<Enter>',lambda event:__onenter(flag))
            tbu.tag_bind(bu,'<Enter>',lambda event:__onenter(flag))
            tbu.tag_bind(titleu,'<Leave>',lambda event:__onleave(flag))
            tbu.tag_bind(cb,'<Leave>',lambda event:__onleave(flag))
            tbu.tag_bind(bu,'<Leave>',lambda event:__onleave(flag))
            tbu.tag_bind(titleu,'<Button-1>',lambda event:showpage(flag))
            tbu.tag_bind(bu,'<Button-1>',lambda event:showpage(flag))
            tbu.tag_bind(cb,'<Button-1>',lambda event:deletepage(flag))

显示页面

这分为两个步骤:恢复当前显示页面,显示指定页面。

值得注意的是__onclick的逻辑。我们不会对nowpage的页面进行操作,所以我们需要一个中间来替代当前显示页面的标识符,作为一个过渡。

        def showpage(flag):#显示页面
            nonlocal nowpage
            mp=nowpage#中间逻辑变量
            if nowpage!='':
                self.itemconfig(vdict[nowpage][2],state='hidden')
                nowpage=flag
                __onleave(mp)
                nowpage=mp

先释放当前显示页面,再重新恢复原来的显示页面标识符,等页面转化完成后,再彻底更改当前显示页面标识符。

上面所说的过程有点绕,但是事实确实是这样。以后会简化逻辑,但是现在就这么用吧。

(懒,等待issue)

            self.itemconfig(vdict[flag][2],state='normal')
            __onclick(flag)
            nowpage=flag

删除页面

这个也很重要,因为页面可以由编写者关闭,也可以有使用者关闭。

以后会添加是否可删除该页面的创建选项。

UI上删除标签和页面:

        def deletepage(flag):#删除页面
            nonlocal nowpage
            wbbox=tbu.bbox(tbdict[flag][2])
            w=wbbox[2]-wbbox[0]
            w+=1
            for i in tbdict[flag]:
                tbu.delete(i)
            self.delete(vdict[flag][2])
            vdict[flag][0].destroy()

等标签被删除了,我们就应该将后面的标签往前移一点来补位:

            index=flaglist.index(flag)
            if index+1==len(tbdict):
                pass
            else:
                for i in flaglist[index+1:]:
                    for iid in tbdict[i]:
                        tbu.move(iid,-w,0)

页面删除后,我们不可能干巴巴地啥也不显示,除非那是一个唯一界面。

页面删除后,会自动显示下一个页面,如果没有,则显示前面一个页面。

不得不说,字典中使用列表检索有点小复杂,所有后来加了flaglist变量。

            if flag==nowpage:
                if len(tbdict)==1:
                    pass
                    nowpage=''
                elif index+1<len(tbdict):
                    showpage(flaglist[index+1])
                    nowpage=flaglist[index+1]
                elif index+1==len(tbdict):
                    showpage(flaglist[index-1])
                    nowpage=flaglist[index-1]

逻辑上删除页面和标签:

            del tbdict[flag]
            del vdict[flag]
            del flaglist[index]
            bbox=tbu.bbox('all')
            tbu.config(scrollregion=bbox)

一些其它函数

这些都是基本的获取类函数,凭借页面标识符获取。

        def getuis(flag):
            return vdict[flag]
        def gettitles(flag):
            return tbdict[flag]
        def getvdict():
            return vdict
        def gettbdict():
            return tbdict

创建函数结构体

这是为了方便编写者使用。为什么不像之前创建一个函数列表呢?因为notebook的主体功能不在创建时产生,而是运行时产生。

        notebook=TinUINum
        notebook.addpage=addpage
        notebook.showpage=showpage
        notebook.deletepage=deletepage
        notebook.getuis=getuis
        notebook.gettitles=gettitles
        notebook.getvdict=getvdict
        notebook.gettbdict=gettbdict

完整代码函数

    def add_notebook(self,pos:tuple,width:int=400,height:int=400,color='#f9f9fc',fg='#1a1a1a',bg='#f3f3f3',activefg='#595959',activebg='#ededed',onfg='#191919',onbg='#eaeaea'):#绘制标签栏视图
        def __onenter(flag):
            if flag==nowpage:
                return
            t,c,b=tbdict[flag]
            tbu.itemconfig(t,fill=activefg)
            tbu.itemconfig(c,fill=activefg)
            tbu.itemconfig(b,fill=activebg,outline=activebg)
        def __onleave(flag):
            if flag==nowpage:
                return
            t,c,b=tbdict[flag]
            tbu.itemconfig(t,fill=fg)
            tbu.itemconfig(c,fill=fg)
            tbu.itemconfig(b,fill=bg,outline=bg)
        def __onclick(flag):
            if flag==nowpage:
                return
            t,c,b=tbdict[flag]
            tbu.itemconfig(t,fill=onfg)
            tbu.itemconfig(c,fill=onfg)
            tbu.itemconfig(b,fill=onbg,outline=onbg)
        def addpage(title:str,flag=None,scrollbar=False):#创建页面
            if tbu.bbox('all')==None:
                endx=3
            else:
                endx=tbu.bbox('all')[2]+3
            titleu=tbu.create_text((endx,2),text=title,fill=fg,font=font,anchor='nw')
            cbx=tbu.bbox(titleu)[2]+10
            cb=tbu.create_text((cbx,2),text='×',font=font,fill=fg,anchor='nw')
            tbbbox=tbu.bbox(titleu)
            bux=(endx+2,tbbbox[1],cbx+15,tbbbox[1],cbx+15,tbbbox[3],endx+2,tbbbox[3],endx+2,tbbbox[1])
            bu=tbu.create_polygon(bux,fill=bg,outline=bg,width=5)
            tbu.lower(bu)
            if flag==None:
                flag='flag'+str(titleu)
            if scrollbar:
                page=TinUI(self,True,bg=self['background'])
            elif scrollbar==False:
                page=BasicTinUI(self,bg=self['background'])
            uiid=self.create_window(viewpos,window=page,width=width,height=height,anchor='nw',state='hidden')
            uixml=TinUIXml(page)
            bbox=tbu.bbox('all')
            tbu.config(scrollregion=bbox)
            vdict[flag]=(page,uixml,uiid)
            tbdict[flag]=(titleu,cb,bu)
            flaglist.append(flag)
            tbu.tag_bind(titleu,'<Enter>',lambda event:__onenter(flag))
            tbu.tag_bind(cb,'<Enter>',lambda event:__onenter(flag))
            tbu.tag_bind(bu,'<Enter>',lambda event:__onenter(flag))
            tbu.tag_bind(titleu,'<Leave>',lambda event:__onleave(flag))
            tbu.tag_bind(cb,'<Leave>',lambda event:__onleave(flag))
            tbu.tag_bind(bu,'<Leave>',lambda event:__onleave(flag))
            tbu.tag_bind(titleu,'<Button-1>',lambda event:showpage(flag))
            tbu.tag_bind(bu,'<Button-1>',lambda event:showpage(flag))
            tbu.tag_bind(cb,'<Button-1>',lambda event:deletepage(flag))
        def showpage(flag):#显示页面
            nonlocal nowpage
            mp=nowpage#中间逻辑变量
            if nowpage!='':
                self.itemconfig(vdict[nowpage][2],state='hidden')
                nowpage=flag
                __onleave(mp)
                nowpage=mp
            self.itemconfig(vdict[flag][2],state='normal')
            __onclick(flag)
            nowpage=flag
        def deletepage(flag):#删除页面
            nonlocal nowpage
            wbbox=tbu.bbox(tbdict[flag][2])
            w=wbbox[2]-wbbox[0]
            w+=1
            for i in tbdict[flag]:
                tbu.delete(i)
            self.delete(vdict[flag][2])
            vdict[flag][0].destroy()
            index=flaglist.index(flag)
            if index+1==len(tbdict):
                pass
            else:
                for i in flaglist[index+1:]:
                    for iid in tbdict[i]:
                        tbu.move(iid,-w,0)
            if flag==nowpage:
                if len(tbdict)==1:
                    pass
                    nowpage=''
                elif index+1<len(tbdict):
                    showpage(flaglist[index+1])
                    nowpage=flaglist[index+1]
                elif index+1==len(tbdict):
                    showpage(flaglist[index-1])
                    nowpage=flaglist[index-1]
            del tbdict[flag]
            del vdict[flag]
            del flaglist[index]
            bbox=tbu.bbox('all')
            tbu.config(scrollregion=bbox)
        def getuis(flag):
            return vdict[flag]
        def gettitles(flag):
            return tbdict[flag]
        def getvdict():
            return vdict
        def gettbdict():
            return tbdict
        tbu=BasicTinUI(self,bg=color)
        tbuid=self.create_window((pos[0]+2,pos[1]+2),window=tbu,width=width,height=30,anchor='nw')
        uid='notebook'+str(tbuid)
        self.addtag_withtag(uid,tbuid)
        scro=self.add_scrollbar((pos[0]+5,pos[1]+32),tbu,height=width-5,direction='x',bg=bg,color=fg,oncolor=onfg)
        self.addtag_withtag(uid,scro[-1])
        barheight=self.bbox(scro[-1])[3]
        backpos=(pos[0]+5,pos[1]+3,pos[0]+width+2,pos[1]+3,pos[0]+width+2,barheight+height-3,pos[0]+5,barheight+height-3,pos[0]+5,pos[1]+5)
        back=self.create_polygon(backpos,outline=color,fill=color,width=10,tags=uid)
        self.tkraise(tbuid)
        self.tkraise(scro[-1])
        viewpos=(pos[0]+2,barheight+2)
        nowpage=''
        vdict=dict()#ui,uixml,uiid
        tbdict=dict()#title,cb,pyo
        flaglist=list()
        font='微软雅黑 12'
        notebook=TinUINum
        notebook.addpage=addpage
        notebook.showpage=showpage
        notebook.deletepage=deletepage
        notebook.getuis=getuis
        notebook.gettitles=gettitles
        notebook.getvdict=getvdict
        notebook.gettbdict=gettbdict
        return tbu,scro,back,notebook,uid

效果

测试代码

def test7():
    ntvdict=ntb.getvdict()
    num=1
    for i in ntvdict:
        uxml=ntvdict[i][1]#tinuixml
        xml=f'''
<tinui><line><button text='这是第{num}个BasicTinUI组件' command='print'></button></line>
<line><label text='TinUI的标签栏视图'></label><label text='每个都是单独页面'></label></line>
</tinui>'''
        uxml.loadxml(xml)
        num+=1
if __name__=='__main__':
    a=Tk()
    a.geometry('700x700+5+5')
    b=TinUI(a,bg='white')
    b.pack(fill='both',expand=True)
    ntb=b.add_notebook((800,900))[-2]
    for i in range(1,11):
        ntb.addpage('test'+str(i),'t'+str(i))
    test7()
    a.mainloop()

最终效果

在这里插入图片描述

细看,标签栏的标签是圆角哦。

customtkinter于2022年10月添加tabview控件,很别致。

2022-6-11新样式

在这里插入图片描述
可指定该页面能否被删除,如5

2022-7-3新样式

在这里插入图片描述

  • 标签栏新配色
  • 可指定是否响应新界面按钮的事件函数

2022-7-13新功能

在这里插入图片描述

  • 可更改标签标题

github项目

TinUI的github项目地址

pip下载

pip install tinui

结语

TinUI完成了所有基本组件的加入,现在可以替代tkinter原生窗口了,同时搭配TinUIXml使用更加方便!

🔆tkinter创新🔆

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值