组合框?你一个绘制类型的TinUI框架,也想通过整合其它的个体组件来创造一个复合组件?
好吧,TinUI是不会尝试绘制可以gird或pack布局的组件的(因为tkinter画布的限制)。本篇文章想要讲的组件,是combobox。
combobox是“组合框”英译名称,而在Windows操作系统中,“combobox”有下拉框、下拉列表的含义,通常作为想给出既定选项,又不希望像单选框一样占用的一定的位置而与用户交互的组件。这次,TinUI将实现该组件。
(剧透) TinUI实现combobox
挺费劲的,是TinUI目前最复杂的个体组件。
因为下拉框使用画布绘制比较复杂,先来想一下该如何实现下拉框。
基本与系统下拉框对齐(除不能够直接输入外),要基本实现以下部分:
额- - -,好像要求有点高,但是远不止这些。还有:各选项维持选定状态、下拉框响应焦点状态、文本显示框的动态更新……
先不管了,一个组件的绘制要求不能这么严格,应该从基础开始,我们就先实现这些功能……
但,还是好复杂啊…(⊙_⊙;)…(引出下文)
我认为,因为TinUI已经有了一定的规模,我们应该尝试运用以往的经验和技巧来简化下拉框的绘制。这样,才能够让之前的只是运用到我们现在要解决的问题。
combobox的整体结构我们是改不了的,但是下拉提示框可以使用add_button
替代,并且实例化其中的按钮文本和按钮背景。
下拉框的响应动作还是得自己写。
展开状态的选项列表可用借鉴add_radiobutton
的相关代码。
以下,就是绘制下拉框的新(简)版方法:
完事。现在开始绘制下拉框。
def add_combobox(self,pos:tuple,width:int=200,text='',content:tuple=(),fg='black',bg='white',activefg='#757F87',activebg='#CCE4F7',font=('微软雅黑',12),command=None):#绘制组合框
'''
pos::位置
width::文本显示框宽度
text::初始文本
content::各个选项组成的元组
fg::文本、边框颜色
bg::背景色
activefg::响应鼠标的文本、边框颜色
activebg::响应鼠标的背景色
font::字体
command::回调函数。必须有一个参数:被选中的选项文本
'''
下拉框在常规状态下,由文本显示框和下拉提示框组成。其中,下拉提示框我们会使用TinUI的按钮替代。
main=self.create_text(pos,text=text,font=font,fill=fg,anchor='nw')
bbox=self.bbox(main)
x1,y1,x2,y2=bbox[0]-3,bbox[1]-3,bbox[0]+width+3,bbox[3]+3
back=self.create_rectangle((x1,y1,x2,y2),fill=bg,outline=fg)
self.tkraise(main)
#下拉提示框
button_text,button_back=self.add_button((x2+3,y1+3),'∨',fg,bg,activefg,activebg,font=font,command=open_box)
其中,函数open_box对应了展开和闭合选项框的操作,后面再讲。
下面的代码借鉴了TinUI单选框的绘制代码。
def button_in(_tag):
self.itemconfig(_tag,fill=activebg,outline=activefg)
def button_out(_tag):
self.itemconfig(_tag,fill=bg,outline=fg)
start_x=bbox[0]#起始x位置
height=bbox[3]+3#变量y位置
for i in content:
choice=self.create_text((start_x+2,height+2),text=i,fill=fg,font=(font[0],10),anchor='nw',width=width-4)
pos=self.bbox(choice)
h=pos[3]-pos[1]+4
cho_back=self.create_rectangle((start_x,height,start_x+width,height+h),outline=fg,fill=bg)
self.tkraise(choice)
height+=h+2
self.tag_bind(choice,'' ,lambda event,back=cho_back:button_in(back))
self.tag_bind(choice,'' ,lambda event,back=cho_back:button_out(back))
self.tag_bind(cho_back,'' ,lambda event,back=cho_back:button_in(back))
self.tag_bind(cho_back,'' ,lambda event,back=cho_back:button_out(back))
self.tag_bind(choice,',lambda event,_text=i,back=cho_back:choose_this(back,_text))
self.tag_bind(cho_back,',lambda event,_text=i,back=cho_back:choose_this(back,_text))
这段代码取自于TinUI的radiobutton绘制代码,有删改。
这段代码的含义在TinUI绘制单选框中提到,这里不再阐述。
函数choose_this代表响应鼠标的选中动作。届时,文本显示框将显示选中的文本。
def choose_this(back,word):
self.itemconfig(main,text=word)
if command!=None:
command(word)
在之后的操作中,我们需要对所有选项框进行统一操作,因此我们将为它们建立一个一致的tag名称。为了避免tag名称重复,我们将使用文本显示框和下拉提示框的组件id进行tag命名。
start_x=bbox[0]#起始x位置
height=bbox[3]+3#变量y位置
box_tagname='combobox>'+str(main)+'>'+str(back)#绑定独立的tag名称
for i in content:
#...
self.addtag_withtag(box_tagname,choice)
self.addtag_withtag(box_tagname,cho_back)
这个动作均有open_box函数进行操作。我们可以通过按钮的文本,来判断当前下拉框的状态。
首先,那些选项框本身是闭合的。
#...
for i in content:
#...
self.itemconfig(box_tagname,state='hidden')
那么,函数的操作如下:
def open_box(event):
if self.itemcget(button_text,'text')=='∨':
self.itemconfig(button_text,text='∧',fill=activefg)
self.itemconfig(box_tagname,state='normal')
else:
self.itemconfig(button_text,text='∨',fill=activefg)
self.itemconfig(box_tagname,state='hidden')
OK,可以去吃饭了。
def add_combobox(self,pos:tuple,width:int=200,text='',content:tuple=(),fg='black',bg='white',activefg='#757F87',activebg='#CCE4F7',font=('微软雅黑',12),command=None):#绘制组合框
def button_in(_tag):
self.itemconfig(_tag,fill=activebg,outline=activefg)
def button_out(_tag):
self.itemconfig(_tag,fill=bg,outline=fg)
def open_box(event):
if self.itemcget(button_text,'text')=='∨':
self.itemconfig(button_text,text='∧',fill=activefg)
self.itemconfig(box_tagname,state='normal')
else:
self.itemconfig(button_text,text='∨',fill=activefg)
self.itemconfig(box_tagname,state='hidden')
def choose_this(back,word):
self.itemconfig(main,text=word)
if command!=None:
command(word)
main=self.create_text(pos,text=text,font=font,fill=fg,anchor='nw')
bbox=self.bbox(main)
x1,y1,x2,y2=bbox[0]-3,bbox[1]-3,bbox[0]+width+3,bbox[3]+3
back=self.create_rectangle((x1,y1,x2,y2),fill=bg,outline=fg)
self.tkraise(main)
button_text,button_back=self.add_button((x2+3,y1+3),'∨',fg,bg,activefg,activebg,font=font,command=open_box)
start_x=bbox[0]#起始x位置
height=bbox[3]+3#变量y位置
box_tagname='combobox>'+str(main)+'>'+str(back)#绑定独立的tag名称
for i in content:
choice=self.create_text((start_x+2,height+2),text=i,fill=fg,font=(font[0],10),anchor='nw',width=width-4)
pos=self.bbox(choice)
h=pos[3]-pos[1]+4
cho_back=self.create_rectangle((start_x,height,start_x+width,height+h),outline=fg,fill=bg)
self.tkraise(choice)
height+=h+2
self.tag_bind(choice,'' ,lambda event,back=cho_back:button_in(back))
self.tag_bind(choice,'' ,lambda event,back=cho_back:button_out(back))
self.tag_bind(cho_back,'' ,lambda event,back=cho_back:button_in(back))
self.tag_bind(cho_back,'' ,lambda event,back=cho_back:button_out(back))
self.tag_bind(choice,',lambda event,_text=i,back=cho_back:choose_this(back,_text))
self.tag_bind(cho_back,',lambda event,_text=i,back=cho_back:choose_this(back,_text))
self.addtag_withtag(box_tagname,choice)
self.addtag_withtag(box_tagname,cho_back)
self.itemconfig(box_tagname,state='hidden')
return main,back,box_tagname
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()
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='lightgreen')
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),fg='blue')
b.add_combobox((600,550),text='你有多大可能去珠穆朗玛峰',content=('20%','40%','60%','80%','100%','1000%'))
a.mainloop()
TinUI的github项目地址
pip install tinui
确实,TinUI的下拉框相比于系统下拉框还有细节上的不足,但是TinUI的下拉框提供了较多的自定义选项,而且不会影响使用效果。
中考考完,居然就直接写了这一篇8000+字的说明文……
欢迎到github上完善TinUI。
tkinter创新