tkinter绘制组件(26)——横向翻页视图
引言
目前,TinUI已经完成了基本组件绘制,也就是说TinUI已经成熟,可以作为一个轻量型的窗口框架来使用。自从这一篇文章,也就是TinUI的第一个完全拓展型组件——翻页视图的出现,TinUI开始了对tkinter原生组件的补充和完善。
横向翻页视图并不存在于tkinter的原生组件中,其在winui中的名称为pipspager
。不同于标签页,翻页视图提供了一个可顺序转化,也可以指定序号翻页的视图,用来在有限的地方创建更多的UI框架。
现在,开工吧。
布局
函数结构
def add_pipspager(self,pos:tuple,width:int=200,height:int=200,bg='#f3f3f3',fg='#898989',buttonbg='#f8f8f8',num:int=2):#绘制翻页视图
'''
pos::位置
width::视图宽度
height::视图高度
bg::视图背景
fg::窗口序号标识符颜色
buttonbg::按钮背景色
num::BasicTinUI个数
'''
创建辅助控件
翻页视图的辅助控件主要有两个:
-
一对翻页按钮
-
底部一栏序号标识符,就是若干个小点
首先来创建翻页按钮:
startx=pos[0]+20#按钮与主窗口间隔
doty=pos[1]+height+5#控制点的起始纵坐标
leftbutton=self.add_button((startx-2,pos[1]+width/2),'<',fg=fg,bg=buttonbg,linew=0,activefg=buttonbg,activebg=fg,command=move_left,anchor='e')[-1]
rightbutton=self.add_button((startx+width+2,pos[1]+width/2),'>',fg=fg,bg=buttonbg,linew=0,activefg=buttonbg,activebg=fg,command=move_right,anchor='w')[-1]
uid='pipspager'+str(leftbutton)+str(rightbutton)
self.addtag_withtag(uid,leftbutton)
self.addtag_withtag(uid,rightbutton)
接着创建导航栏,我们选择使用画布来创建导航栏,毕竟对我来说,画布与文本框使用起来更顺手。
直接在循环结构里面创建导航序号标识符,这样方便于待会BasicTinUI框架与标识符一同创立,方便管理。
#...
uilist=list()#[(uiid-1,BasicTinUI-1,TinUIXml-1),(uiid-2,BasicTinUI-2,TinUIXml-2),...]
dotlist=list()#[dot1,dot2,...]
nowui=0#当前显示界面序号
bar=Canvas(self,bg=bg,highlightthickness=0,relief='flat')#导航栏
barid=self.create_window((startx,doty),window=bar,width=width,height=11,anchor='nw',tags=uid)
dotx=3
for i in range(0,num):
dot=bar.create_oval((dotx,3,dotx+5,8),fill=fg,outline=fg,width=0)
dotlist.append(dot)
dotx+=13
此刻我们已经创建了两个列表,范别是UI框架列表,以及导航标识符列表。这些列表都是为了之后的功能能够直接获取这些画布对象。此外,我们还定义了nowui
变量,用于确定当前显示的视图的序号。
TinUI的pipspager中序号从0开始。
创建视图
创建BasicTinUI视图的构成与创建内部框架一样,同样为了一键化建立TinUI与TinUIXml的联系,我们直接实例化TinUIXml:
#...
for i in range(0,num):
ui=BasicTinUI(self,bg=bg)
tinuixml=TinUIXml(ui)
uiid=self.create_window((startx,pos[1]),window=ui,width=width,height=height,state='hidden',anchor='nw',tags=uid)
uilist.append((uiid,ui,tinuixml))
#...
uilist=[(ui-id,ui,ui-xml),…]
导航标识符的功能
先编写这一部分的代码,是因为导航标识符实际上是更加基础的辅助控件,其响应用户时间不会影响到翻页按钮。
先来创建一个基础函数:
def __move_to(number):
nonlocal nowui
self.itemconfig(uilist[nowui][0],state='hidden')
nowui=number
self.itemconfig(uilist[nowui][0],state='normal')
样式绑定:
def __dot_in(dote):
bar.itemconfig(dote,width=3)
def __dot_out(dote):
if dotlist.index(dote)==nowui:
pass
else:
bar.itemconfig(dote,width=0)
标识符选择:
def __dot_select(dote):
number=dotlist.index(dote)
if number==nowui:
pass
else:
__move_to(number)
for i in dotlist:
if i==dote:
continue
else:
bar.itemconfig(i,width=0)
封装一遍开发者可用的move_to
函数:
def move_to(number):
__dot_in(dotlist[nowui])
__dot_select(dotlist[nowui])
__move_to(nowui)
绑定标识符事件:
#...
for i in range(0,num):
#...
bar.tag_bind(dot,'<Enter>',lambda event,dote=dot:__dot_in(dote))
bar.tag_bind(dot,'<Leave>',lambda event,dote=dot:__dot_out(dote))
bar.tag_bind(dot,'<Button-1>',lambda event,dote=dot:__dot_select(dote))
__dot_in(dotlist[nowui])
__dot_select(dotlist[nowui])
__move_to(nowui)
翻页按钮的功能
有了前面的铺垫,两个翻页按钮的功能很好完成,逻辑很简单。
以下是两个功能函数:
def move_left(event):
nonlocal nowui
if nowui==0:
pass
else:
__dot_in(dotlist[nowui-1])
__dot_select(dotlist[nowui-1])
def move_right(event):
nonlocal nowui
if nowui==len(uilist)-1:
pass
else:
__dot_in(dotlist[nowui+1])
__dot_select(dotlist[nowui+1])
虽然听起来很复杂,但是写起来也没什么。
完整代码函数
def add_pipspager(self,pos:tuple,width:int=200,height:int=200,bg='#f3f3f3',fg='#898989',buttonbg='#f8f8f8',num:int=2):#绘制翻页视图
def move_left(event):
nonlocal nowui
if nowui==0:
pass
else:
__dot_in(dotlist[nowui-1])
__dot_select(dotlist[nowui-1])
def move_right(event):
nonlocal nowui
if nowui==len(uilist)-1:
pass
else:
__dot_in(dotlist[nowui+1])
__dot_select(dotlist[nowui+1])
def __move_to(number):
nonlocal nowui
self.itemconfig(uilist[nowui][0],state='hidden')
nowui=number
self.itemconfig(uilist[nowui][0],state='normal')
def move_to(number):
__dot_in(dotlist[nowui])
__dot_select(dotlist[nowui])
__move_to(nowui)
def __dot_in(dote):
bar.itemconfig(dote,width=3)
def __dot_out(dote):
if dotlist.index(dote)==nowui:
pass
else:
bar.itemconfig(dote,width=0)
def __dot_select(dote):
number=dotlist.index(dote)
if number==nowui:
pass
else:
__move_to(number)
for i in dotlist:
if i==dote:
continue
else:
bar.itemconfig(i,width=0)
startx=pos[0]+20#按钮与主窗口间隔
uilist=list()#[(uiid-1,BasicTinUI-1,TinUIXml-1),(uiid-2,BasicTinUI-2,TinUIXml-2),...]
doty=pos[1]+height+5#控制点的起始纵坐标
dotlist=list()#[dot1,dot2,...]
nowui=0#当前显示界面序号
leftbutton=self.add_button((startx-2,pos[1]+width/2),'<',fg=fg,bg=buttonbg,linew=0,activefg=buttonbg,activebg=fg,command=move_left,anchor='e')[-1]
rightbutton=self.add_button((startx+width+2,pos[1]+width/2),'>',fg=fg,bg=buttonbg,linew=0,activefg=buttonbg,activebg=fg,command=move_right,anchor='w')[-1]
uid='pipspager'+str(leftbutton)+str(rightbutton)
self.addtag_withtag(uid,leftbutton)
self.addtag_withtag(uid,rightbutton)
bar=Canvas(self,bg=bg,highlightthickness=0,relief='flat')#导航栏
barid=self.create_window((startx,doty),window=bar,width=width,height=11,anchor='nw',tags=uid)
dotx=3
for i in range(0,num):
ui=BasicTinUI(self,bg=bg)
tinuixml=TinUIXml(ui)
uiid=self.create_window((startx,pos[1]),window=ui,width=width,height=height,state='hidden',anchor='nw',tags=uid)
uilist.append((uiid,ui,tinuixml))
dot=bar.create_oval((dotx,3,dotx+5,8),fill=fg,outline=fg,width=0)
dotlist.append(dot)
dotx+=13
bar.tag_bind(dot,'<Enter>',lambda event,dote=dot:__dot_in(dote))
bar.tag_bind(dot,'<Leave>',lambda event,dote=dot:__dot_out(dote))
bar.tag_bind(dot,'<Button-1>',lambda event,dote=dot:__dot_select(dote))
__dot_in(dotlist[nowui])
__dot_select(dotlist[nowui])
__move_to(nowui)
return uilist,dotlist,move_to,uid
效果
测试代码
#...
#TinUI现在的测试代码量有点大,所以就缩减剩下了必要部分
def test6():
for i in range(0,5):
num=i
xml=f'''
<tinui><line><label text='这是第{num}个BasicTinUI组件'></label></line>
<line><button text='功能按钮' command='lambda event:print("第{i}个功能按钮")'></button>
<combobox width='80' text='可选测试' content='("{i}","其它选项")'></combobox></line></tinui>'''
ppgl[i][2].loadxml(xml)
if __name__=='__main__':
a=Tk()
a.geometry('700x700+5+5')
b=TinUI(a,bg='white')
b.pack(fill='both',expand=True)
#...
ppgl=b.add_pipspager((400,890),num=5)[0]
test6()
a.mainloop()
最终效果
2022-8-8新样式
- 小修标识符
- 对于超过可视范围的标识符,会自动显示到对应范围
2022-8-19新样式
- 使用新的翻页标识符
< >
→◀ ▶
- 页面标识符改用文本
●
,减少粗糙度 - 修复向左越界翻页的页面标识符显示问题
2023-1-9更新
添加翻页切换动画。因为顺序与画布对齐原因,向前和向后的翻页动作有些许不同。
2024-2-11新样式
优化翻页按钮
github项目
pip下载
pip install tinui
结语
TinUI完成了所有基本组件的加入,现在可以替代tkinter原生窗口了,同时搭配TinUIXml使用更加方便!
TinUI即将步入3.0。
🔆tkinter创新🔆