tkinter绘制组件(44)——浮出ui控件
- 引言
- 布局
- 函数结构
- ui框架
- 对齐方向
- 绑定已有控件
- 出现和隐藏逻辑
- 出现和隐藏动画
- 完整代码函数
- 效果
- 测试代码
- 最终效果
- github项目
- pip下载
引言
TinUI的浮出ui控件(flyout)其实是一个之间创建在UI框架内的完整BasicTinUI类,且已经绑定了TinUIXml。该控件并不之间占有窗口句柄,因此可以大量创建,包括但不限于:
- 弹出已有控件的说明
- 绑定已有控件,弹出某个操作、用法的说明
- 绑定已有控件,弹出简单的内部对话框或说明
从上面罗列的使用场景可以看出,flyout就是用来作为窗口内提示与简单交互的,只不过是一个轻量的UI框架,和TinUI的内嵌ui控件(ui)非常类似,只不过绑定在了已有的控件。
布局
函数结构
def add_flyout(self, fid, width:int=250, height:int=150, bind='<Button-1>', line='#dcdcdc', bg='#f9f9f9', anchor='n'):# 绘制一个浮出ui控件# 注意,默认布局在fid正上方"""fid::绑定控件的uidwidth::宽度height::高度bind::绑定的事件line::边框颜色bg::背景颜色anchor::展开方向"""
ui框架
上文说过,flyout本质就是一个BasicTinUI。
ui = BasicTinUI(self, bg=bg, highlightbackground=line, highlightthickness=1, relief='flat')
uixml = TinUIXml(ui)
对齐方向
由于是弹出控件,因此flyout的anchor
不再代表控件本身的对齐方向,而是指布局在绑定控件的那一侧。比如n
就代表正上方,nw
代表左上方。
bbox = self.bbox(fid)
if anchor == 'nw':x = bbox[0] - 4y = bbox[1] - 4_anchor = 'se'dxy = (1, 1)
elif anchor == 'n':x = (bbox[0] + bbox[2]) / 2y = bbox[1] - 4_anchor ='s'dxy = (0, 1)
elif anchor == 'ne':x = bbox[2] + 4y = bbox[1] - 4_anchor = 'sw'dxy = (1, 1)
elif anchor == 'e':x = bbox[2] + 4y = (bbox[1] + bbox[3]) / 2_anchor = 'w'dxy = (1, 0)
elif anchor =='se':x = bbox[2] + 4y = bbox[3] + 4_anchor = 'nw'dxy = (1, 1)
elif anchor =='s':x = (bbox[0] + bbox[2]) / 2y = bbox[3] + 4_anchor = 'n'dxy = (0, 1)
elif anchor =='sw':x = bbox[0] - 4y = bbox[3] + 4_anchor = 'ne'dxy = (1, 1)
elif anchor == 'w':x = bbox[0] - 4y = (bbox[1] + bbox[3]) / 2_anchor = 'e'dxy = (1, 0)
else:# 默认为centerx = (bbox[0] + bbox[2]) / 2y = (bbox[1] + bbox[3]) / 2_anchor = 'center'dxy = (1, 1)
uid = self.create_window(x, y, width=width, height=height, window=ui, anchor=_anchor)
dxy
稍后解释
绑定已有控件
self.itemconfig(uid, state='hidden')
self.tag_bind(fid, bind, show)
出现和隐藏逻辑
出现逻辑很显然,就是对于绑定控件的绑定事件,默认就是左键单击。
对于隐藏逻辑,我能够想到的就是左键单击本UI框架,这样做可以减少对其他不确定元素的依赖,且BasicTinUI本身不对单击做出响应,也不应该对单击做出响应(窗口单击获得焦点是系统层决定,这不影响)。单击本UI框架收回flyout是比较合理的。
def show(e):self.tag_unbind(fid, bind)# 避免接下来绑定self <button-1>事件时同步触发self.itemconfig(uid, state='normal')motion(None, 1)self.after(100, go_to_bind)# 避免直接触发控件点击事件
def go_to_bind():self.bind('<Button-1>', hide)
def hide(e):self.unbind('<Button-1>')self.tag_bind(fid, bind, show)motion(None, -1)self.itemconfig(uid, state='hidden')
注意上面代码片段中的两个注释内容,简单来说就是避免绑定控件左键单击和本UI框架左键单击同时触发,导致
show
和hide
调用混乱。
出现和隐藏动画
这就涉及到前文提到的dxy
,且内容就是x和y方向,需要变化的为1,不需要变化的为0。
此外,motion
函数的dis
参数为1表示展开,-1表示收缩。
def motion(e, dis):# 展开/收缩动画# dxy为动画方向,0为不变,1为变化if dis == 1:# 展开_width = width * (1-dxy[0])_height = height * (1-dxy[1])dwidth = width / 10 * dxy[0]dheight = height / 10 * dxy[1]else:# 收缩_width = width_height = heightdwidth = -width / 10 * dxy[0]dheight = -height / 10 * dxy[1]for _ in range(10):time.sleep(0.01)_width += dwidth_height += dheightself.itemconfig(uid, width=_width, height=_height)self.update_idletasks()
完整代码函数
def add_flyout(self, fid, width:int=250, height:int=150, bind='<Button-1>', line='#dcdcdc', bg='#f9f9f9', anchor='n'):# 绘制一个浮出ui控件# 注意,默认布局在fid正上方def show(e):self.tag_unbind(fid, bind)# 避免接下来绑定self <button-1>事件时同步触发self.itemconfig(uid, state='normal')motion(None, 1)self.after(100, go_to_bind)# 避免直接触发控件点击事件def go_to_bind():self.bind('<Button-1>', hide)def hide(e):self.unbind('<Button-1>')self.tag_bind(fid, bind, show)motion(None, -1)self.itemconfig(uid, state='hidden')def motion(e, dis):# 展开/收缩动画# dxy为动画方向,0为不变,1为变化if dis == 1:# 展开_width = width * (1-dxy[0])_height = height * (1-dxy[1])dwidth = width / 10 * dxy[0]dheight = height / 10 * dxy[1]else:# 收缩_width = width_height = heightdwidth = -width / 10 * dxy[0]dheight = -height / 10 * dxy[1]for _ in range(10):time.sleep(0.01)_width += dwidth_height += dheightself.itemconfig(uid, width=_width, height=_height)self.update_idletasks()ui = BasicTinUI(self, bg=bg, highlightbackground=line, highlightthickness=1, relief='flat')uixml = TinUIXml(ui)# 围绕fid进行布局bbox = self.bbox(fid)if anchor == 'nw':x = bbox[0] - 4y = bbox[1] - 4_anchor = 'se'dxy = (1, 1)elif anchor == 'n':x = (bbox[0] + bbox[2]) / 2y = bbox[1] - 4_anchor ='s'dxy = (0, 1)elif anchor == 'ne':x = bbox[2] + 4y = bbox[1] - 4_anchor = 'sw'dxy = (1, 1)elif anchor == 'e':x = bbox[2] + 4y = (bbox[1] + bbox[3]) / 2_anchor = 'w'dxy = (1, 0)elif anchor =='se':x = bbox[2] + 4y = bbox[3] + 4_anchor = 'nw'dxy = (1, 1)elif anchor =='s':x = (bbox[0] + bbox[2]) / 2y = bbox[3] + 4_anchor = 'n'dxy = (0, 1)elif anchor =='sw':x = bbox[0] - 4y = bbox[3] + 4_anchor = 'ne'dxy = (1, 1)elif anchor == 'w':x = bbox[0] - 4y = (bbox[1] + bbox[3]) / 2_anchor = 'e'dxy = (1, 0)else:# 默认为centerx = (bbox[0] + bbox[2]) / 2y = (bbox[1] + bbox[3]) / 2_anchor = 'center'dxy = (1, 1)uid = self.create_window(x, y, width=width, height=height, window=ui, anchor=_anchor)self.itemconfig(uid, state='hidden')self.tag_bind(fid, bind, show)return ui, uixml, hide, uid
效果
测试代码
flylabel = b.add_label((1500,500),text='点击展开浮出UI')[-1]
_, flyxml, flyhide, _ = b.add_flyout(flylabel)
flyxml.funcs['flyhide']=flyhide
flyxml.loadxml('''<tinui><line><paragraph text='浮出UI'></paragraph></line><line><paragraph text='add_flyout(fid, anchor="...")'></paragraph></line><line><paragraph text='使用hide关闭'></paragraph></line>
<line><button2 text='关闭浮出UI控件' command="self.funcs['flyhide']"></button2>
</line></tinui>''')
最终效果
github项目
TinUI的github项目地址
pip下载
pip install tinui
🔆tkinter创新🔆