——导航边栏)
tkinter绘制组件47——导航边栏引言布局函数结构分部件折叠按钮导航列表折叠与伸展完整代码函数效果2026-10-3 样式更新github项目pip下载引言导航边栏适合作为窗口最顶层的页面视图选择控件。布局函数结构defadd_navigation(self,pos:tuple,minwidth20,maxwidth150,bg#FFFFFF,fg#1A1A1A,activebg#E9E9E9,activefg#191919,onbg#E9E9E9,onfg#191919,oncolor#0067C0,font微软雅黑 14,content((\uE790,Color),(\uE743,Geometry),(\uED58,Iconography)),widgetTrue,commandNone,anchornw): - pos-位置 - minwidth-收缩时宽度 - maxwidth-展开时宽度 - bg-背景颜色 - fg-文本颜色 - activebg-响应鼠标背景颜色 - activefg-响应鼠标文本颜色 - onbg-选中背景颜色 - onfg-选中文本颜色 - oncolor-标识符颜色 - font-字体 - content-内容。(icon, title) - widget-是否显示宽度折叠按钮 - command-回调函数需要接受一个参数title。当折叠按钮显示时会根据即将呈现的状态返回伸缩状态expanding:bool - anchor-对齐方向 分部件折叠按钮导航栏本身的选择元素由图表和文本构成应当允许折叠与展开两种呈现方式这就需要在导航列表上方提供一个折叠按钮当然这个按钮是可以根据widget参数控制有无的。menus[]# (icon, text, back)nowselect-1expandingTrue# 展开状态topiconself.create_text(pos,text\uE700,fontsegoe_font,fillfg,anchorw)uidTinUIString(fnavigation-{topicon})self.itemconfig(topicon,tagsuid)bboxself.bbox(topicon)topbackself.__ui_polygon(((bbox[0],bbox[1]),(bbox[2],bbox[3])),fillbg,outlinebg,width9,tagsuid)foritemin(topicon,topback):self.tag_bind(item,Enter,expand_in)self.tag_bind(item,Leave,expand_out)self.tag_bind(item,Button-1,expand_click)self.lower(topback,topicon)lineself.create_line((0,0,0,font_height/2),filloncolor,width3,tagsuid,capstyleround,statehidden)ifwidget:startypos[1]font_height15else:self.itemconfig(topicon,statehidden)self.itemconfig(topback,statehidden)startypos[1]这里写得这么复杂主要是为了根据不同情况确定接下来导航列表的起始位置。另外直接初始化了标识线元素line因为导航栏会自动锁定在第一个页面也就是无论如何控件初始化后一定会有选项被选中。导航列表TinUI中有很多列表部件的创建方法这里直接给出代码毕竟都48个控件了该很熟练了xpos[0]cnt0foriincontent:ifi[0]:iconself.create_text((x,starty),texti[0],fontsegoe_font,fillfg,anchorw,tagsuid)else:iconself.create_text((x,starty),text ,fontsegoe_font,fillfg,anchorw,tagsuid)textself.create_text((xsegoe_font_width8,starty),texti[1],fontfont,fillfg,anchorw,tagsuid)backself.__ui_polygon(((x,starty-font_height/2),(xmaxwidth,startyfont_height/2)),fillbg,outlinebg,width9,tagsuid)self.tag_lower(back,icon)foritemin(icon,text,back):self.tag_bind(item,Enter,lambda_,indexcnt:mouse_in(index))self.tag_bind(item,Leave,lambda_,indexcnt:mouse_out(index))self.tag_bind(item,Button-1,lambda_,indexcnt:navigate(index))menus.append((icon,text,back))startyfont_height15cnt1折叠与伸展当折叠按钮存在时每按一次就会切换导航列表状态。而这个所谓的“切换”实际上就是隐藏与显示导航栏目的标题而已同时记得更改背景宽度。defexpand_click(event):nonlocalexpanding expandingnotexpandingifexpanding:formenuinmenus:self.itemconfig(menu[1],statenormal)coordsself.coords(menu[2])coords[2]coords[4]coords[0]maxwidth self.coords(menu[2],coords)else:formenuinmenus:self.itemconfig(menu[1],statehidden)coordsself.coords(menu[2])coords[2]coords[4]coords[0]minwidth self.coords(menu[2],coords)ifcommand:command(expanding)完整代码函数defadd_navigation(self,pos:tuple,minwidth20,maxwidth150,bg#FFFFFF,fg#1A1A1A,activebg#E9E9E9,activefg#191919,onbg#E9E9E9,onfg#191919,oncolor#0067C0,font微软雅黑 14,content((\uE790,Color),(\uE743,Geometry),(\uED58,Iconography)),# (icon, title)widgetTrue,commandNone,anchornw,):# 绘制一个导航边栏defexpand_in(event):self.itemconfig(topicon,fillactivefg)self.itemconfig(topback,fillactivebg,outlineactivebg)defexpand_out(event):self.itemconfig(topicon,fillfg)self.itemconfig(topback,fillbg,outlinebg)defexpand_click(event):nonlocalexpanding expandingnotexpandingifexpanding:formenuinmenus:self.itemconfig(menu[1],statenormal)coordsself.coords(menu[2])coords[2]coords[4]coords[0]maxwidth self.coords(menu[2],coords)else:formenuinmenus:self.itemconfig(menu[1],statehidden)coordsself.coords(menu[2])coords[2]coords[4]coords[0]minwidth self.coords(menu[2],coords)ifcommand:command(expanding)defmouse_in(index):ifindexnowselect:returnself.itemconfig(menus[index][1],fillactivefg)self.itemconfig(menus[index][0],fillactivefg)self.itemconfig(menus[index][2],fillactivebg,outlineactivebg)defmouse_out(index):ifindexnowselect:returnself.itemconfig(menus[index][1],fillfg)self.itemconfig(menus[index][0],fillfg)self.itemconfig(menus[index][2],fillbg,outlinebg)defnavigate(index):nonlocalnowselectifindexnowselect:returnold_indexnowselect nowselectindex mouse_out(old_index)self.itemconfig(menus[index][1],fillonfg)self.itemconfig(menus[index][0],fillonfg)self.itemconfig(menus[index][2],fillonbg,outlineonbg)self.moveto(line,x-8,y(font_height15)*(index-1/4))ifcommand:command(self.itemcget(menus[index][1],text))def__layout(x1,y1,x2,y2,expandFalse):nonlocalx,y dx,dyself.__auto_layout(uid,(x1,y1,x2,y2),anchor)xdx ydy fonttkfont.Font(fontfont)font_sizefont.cget(size)font_heightfont.metrics(linespace)segoe_fontf{{Segoe Fluent Icons}}{font_size}segoe_font_widthfont.measure(\uE700)minwidthmax(minwidth,segoe_font_width)menus[]# (icon, text, back)nowselect-1expandingTrue# 展开状态topiconself.create_text(pos,text\uE700,fontsegoe_font,fillfg,anchorw)uidTinUIString(fnavigation-{topicon})self.itemconfig(topicon,tagsuid)bboxself.bbox(topicon)topbackself.__ui_polygon(((bbox[0],bbox[1]),(bbox[2],bbox[3])),fillbg,outlinebg,width9,tagsuid)foritemin(topicon,topback):self.tag_bind(item,Enter,expand_in)self.tag_bind(item,Leave,expand_out)self.tag_bind(item,Button-1,expand_click)self.lower(topback,topicon)lineself.create_line((0,0,0,font_height/2),filloncolor,width3,tagsuid,capstyleround,statehidden)ifwidget:startypos[1]font_height15else:self.itemconfig(topicon,statehidden)self.itemconfig(topback,statehidden)startypos[1]xpos[0]cnt0foriincontent:ifi[0]:iconself.create_text((x,starty),texti[0],fontsegoe_font,fillfg,anchorw,tagsuid)else:iconself.create_text((x,starty),text ,fontsegoe_font,fillfg,anchorw,tagsuid)textself.create_text((xsegoe_font_width8,starty),texti[1],fontfont,fillfg,anchorw,tagsuid)backself.__ui_polygon(((x,starty-font_height/2),(xmaxwidth,startyfont_height/2)),fillbg,outlinebg,width9,tagsuid)self.tag_lower(back,icon)foritemin(icon,text,back):self.tag_bind(item,Enter,lambda_,indexcnt:mouse_in(index))self.tag_bind(item,Leave,lambda_,indexcnt:mouse_out(index))self.tag_bind(item,Button-1,lambda_,indexcnt:navigate(index))menus.append((icon,text,back))startyfont_height15cnt1self.tag_raise(line)dx,dyself.__auto_anchor(uid,pos,anchor)xdx ypos[1]font_height15dyifwidgetelsepos[1]dy self.itemconfig(line,statenormal)navigate(0)delbbox,dx,dy,starty,cnt uid.layout__layout funcsFuncList(1)funcs.navigatenavigatereturntopicon,topback,line,menus,funcs,uid效果导航边栏最好的使用方式是配合TinUI的面板布局这也是其折叠与展开功能的妙处。需要注意的是应当使用横向面板布局并且不指定宽度。rootExpandPanel(u)hpHorizonPanel(u,spacing5)root.set_child(hp)textboxu.add_textbox((0,0))naviewu.add_navigation((0,0),commandcallback)[-1]hp.add_child(naview)expandExpandPanel(u,padding(5,5,5,5))hp.add_child(expand,weight1)expand.set_child(textbox[-1])2026-10-3 样式更新标识线添加过渡动画。github项目TinUI的github项目地址pip下载pipinstalltinui