tkinter绘制组件(41)——菜单按钮

tkinter绘制组件(41)——菜单按钮

  • 引言
  • 布局
    • 函数结构
    • 按钮部分
    • 菜单显示
    • 完整代码函数
  • 效果
    • 测试代码
    • 最终效果
  • github项目
  • pip下载
  • 结语

引言

TinUI5的新控件,菜单按钮,menubutton

这是一个与TinUI菜单(menubar)相关联的控件,可以当作按钮(button2)放置在窗口的任意位置。只需要单击就可以展开菜单。

在TinUI中,菜单按钮有两种展开方式,由参数side=x/y决定,稍后会详细说明。当然,两种对齐方式在超出显示屏时,均会做出调整,以便全部可见。

我才知道,在WinUI3里,像菜单、选择器、浮窗这些脱离窗口的控件,叫“浮出控件(ContextFlyout)”。不过在tkinter里无法实现,原理决定了。


布局

函数结构

def add_menubutton(self,pos:tuple,text:str,side='y',fg='#1b1b1b',bg='#fbfbfb',line='#CCCCCC',linew=1,activefg='#5d5d5d',activebg='#f5f5f5',activeline='#e5e5e5',font=('微软雅黑',12),cont=(('command',print),'-'),tran='#01FF11'):#绘制按钮展开菜单
'''
此段略过,样式是从button2挪过来的,cont是menubar的
side - 展开方向。y在下方展开,x在右侧展开
'''

按钮部分

说实话,menubar就是一个“缝合怪”,TinUI样式部分就是button2。

但是情况有变,为了给menubar提供新的样式,button2也在事件绑定等方面做出了调整,不过在这里体现不大,可以直接照抄。

        def in_button(event):self.itemconfig(outline,outline=activeline,fill=activeline)self.itemconfig(uid+'button',fill=activefg)def out_button(event):self.itemconfig(back,fill=bg,outline=bg)self.itemconfig(outline,outline=line,fill=line)self.itemconfig(uid+'button',fill=fg)def on_click(event):self.itemconfig(back,fill=activebg,outline=activebg)self.itemconfig(uid+'button',fill=activefg)self.after(500,lambda : out_button(None))show(event)#从menu那偷过来的    ...def disable(fg='#9d9d9d',bg='#f5f5f5'):self.itemconfig(uid+'button',state='disable',fill=fg)self.itemconfig(back,state='disable',disabledfill=bg)self.itemconfig(outline,state='disable')def active():self.itemconfig(uid+'button',state='normal')self.itemconfig(back,state='normal')self.itemconfig(outline,state='normal')out_button(None)button=self.create_text(pos,text=text,fill=fg,font=font,anchor='nw')uid='menubutton'+str(button)self.itemconfig(button,tags=(uid,uid+'button'))x1,y1,x2,y2=self.bbox(uid)if side=='y':self.create_text((x2+5,(y1+y2)/2),text='\uE70D',fill=fg,font='{Segoe Fluent Icons} 12',anchor='w',tags=(uid,uid+'button'))elif side=='x':self.create_text((x2+5,(y1+y2)/2),text='\uE76C',fill=fg,font='{Segoe Fluent Icons} 12',anchor='w',tags=(uid,uid+'button'))...#创建菜单menu=self.add_menubar(uid,'<Button-1>',font=font,fg=fg,bg=bg,line=line,activefg=activefg,activebg=activebg,cont=cont,tran=tran)[0]self.tag_unbind(uid,'<Button-1>')#重新绑定事件...

引用自己很久以前写的代码,但现在应该写不出来的……

按钮主体代码不是重点。

在创建完uid后,有对于side的判断。这里是严格的判断,只接受小写字母x和y,默认为y。

然后是菜单部分(menu=...),没错,这次不仅是抄,而且干脆直接调用。不过需要注意,因为菜单在创建的时候会自动绑定参数给的事件,因此我们需要对这个事件(<Button-1>)解绑。

菜单显示

然后才是重点。注意到on_click函数的最后一行,有show(event),就代表我们需要重写菜单的显示方式,为什么呢?因为,我们需要菜单像滚动选择框(picker)那样在按钮周边展开,要么下方,要么在右侧。

那么问题来了,TinUI的控件是函数式创建,如何重写菜单的显示方式呢?

很简单,直接在menubar的第一个返回值,menu窗口添加一个属性wind,记录窗口数据。

反正我是TinUI开发者,内部代码想怎么改就怎么改,毕竟这个属性又不用提供该使用TinUI的人。

menu修改代码见最新TinUI库。

通过在picker里的积累,在元素周围展现浮出窗口并不难。只是需要浅浅地判断一下side的方向就行了。

        def unshow(event):#重写菜单menu.withdraw()menu.unbind('<FocusOut>')def show(event):#显示的起始位置#初始位置maxx,maxy,winw,winh=menu.wind.datasx,sy=event.x_root,event.y_root#maxx,maxy,winw,winh=menu.wind.databbox=self.bbox(uid)scx,scy=event.x_root,event.y_root#屏幕坐标if side=='y':dx,dy=round(self.canvasx(event.x,)-bbox[0]),round(self.canvasy(event.y)-bbox[3])#画布坐标差值elif side=='x':dx,dy=round(self.canvasx(event.x,)-bbox[2]),round(self.canvasy(event.y)-bbox[1])#画布坐标差值sx,sy=scx-dx,scy-dy#...

完整代码函数

    def add_menubutton(self,pos:tuple,text:str,side='y',fg='#1b1b1b',bg='#fbfbfb',line='#CCCCCC',linew=1,activefg='#5d5d5d',activebg='#f5f5f5',activeline='#e5e5e5',font=('微软雅黑',12),cont=(('command',print),'-'),tran='#01FF11'):#绘制按钮展开菜单#Segoe Fluent Icons x右侧展开\uE76B \uE76C,y下方展开\uE70D \uE70E,默认ydef in_button(event):self.itemconfig(outline,outline=activeline,fill=activeline)self.itemconfig(uid+'button',fill=activefg)def out_button(event):self.itemconfig(back,fill=bg,outline=bg)self.itemconfig(outline,outline=line,fill=line)self.itemconfig(uid+'button',fill=fg)def on_click(event):self.itemconfig(back,fill=activebg,outline=activebg)self.itemconfig(uid+'button',fill=activefg)self.after(500,lambda : out_button(None))show(event)#从menu那偷过来的def unshow(event):#重写菜单menu.withdraw()menu.unbind('<FocusOut>')def show(event):#显示的起始位置#初始位置maxx,maxy,winw,winh=menu.wind.datasx,sy=event.x_root,event.y_root#maxx,maxy,winw,winh=menu.wind.databbox=self.bbox(uid)scx,scy=event.x_root,event.y_root#屏幕坐标if side=='y':dx,dy=round(self.canvasx(event.x,)-bbox[0]),round(self.canvasy(event.y)-bbox[3])#画布坐标差值elif side=='x':dx,dy=round(self.canvasx(event.x,)-bbox[2]),round(self.canvasy(event.y)-bbox[1])#画布坐标差值sx,sy=scx-dx,scy-dy#if sx+winw>maxx:x=sx-winwelse:x=sxif sy+winh>maxy:y=sy-winhelse:y=symenu.geometry(f'{winw+15}x{winh+15}+{x}+{y}')menu.attributes('-alpha',0)menu.deiconify()menu.focus_set()for i in [0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9,1]:menu.attributes('-alpha',i)menu.update()time.sleep(0.05)menu.bind('<FocusOut>',unshow)def disable(fg='#9d9d9d',bg='#f5f5f5'):self.itemconfig(uid+'button',state='disable',fill=fg)self.itemconfig(back,state='disable',disabledfill=bg)self.itemconfig(outline,state='disable')def active():self.itemconfig(uid+'button',state='normal')self.itemconfig(back,state='normal')self.itemconfig(outline,state='normal')out_button(None)button=self.create_text(pos,text=text,fill=fg,font=font,anchor='nw')uid='menubutton'+str(button)self.itemconfig(button,tags=(uid,uid+'button'))x1,y1,x2,y2=self.bbox(uid)if side=='y':self.create_text((x2+5,(y1+y2)/2),text='\uE70D',fill=fg,font='{Segoe Fluent Icons} 12',anchor='w',tags=(uid,uid+'button'))elif side=='x':self.create_text((x2+5,(y1+y2)/2),text='\uE76C',fill=fg,font='{Segoe Fluent Icons} 12',anchor='w',tags=(uid,uid+'button'))x1,y1,x2,y2=self.bbox(uid+'button')linew-=1outline_t=(x1-linew,y1-linew,x2+linew,y1-linew,x2+linew,y2+linew,x1-linew,y2+linew)outline=self.create_polygon(outline_t,width=9,tags=uid,fill=line,outline=line)back_t=(x1,y1,x2,y1,x2,y2,x1,y2)back=self.create_polygon(back_t,width=7,tags=uid,fill=bg,outline=bg)#创建菜单menu=self.add_menubar(uid,'<Button-1>',font=font,fg=fg,bg=bg,line=line,activefg=activefg,activebg=activebg,cont=cont,tran=tran)[0]self.tag_unbind(uid,'<Button-1>')#重新绑定事件self.tag_bind(uid+'button','<Button-1>',on_click)self.tag_bind(uid+'button','<Enter>',in_button)self.tag_bind(uid+'button','<Leave>',out_button)self.tag_bind(back,'<Button-1>',on_click)self.tag_bind(back,'<Enter>',in_button)self.tag_bind(back,'<Leave>',out_button)self.tag_bind(outline,'<Button-1>',on_click)self.tag_bind(outline,'<Enter>',in_button)self.tag_bind(outline,'<Leave>',out_button)self.tkraise(uid+'button')funcs=FuncList(2)funcs.disable=disablefuncs.active=activereturn uid+'button',back,outline,funcs,uid

效果

测试代码

b.add_menubutton((1500,50),'menubutton',cont=(('command',print),('menu',test1),'-',('TinUI文本移动',test)))

最终效果

在这里插入图片描述


github项目

TinUI的github项目地址

pip下载

pip install tinui

结语

后续,menubutton或许会和menubar混合联用。

🔆tkinter创新🔆

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/458592.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Springboot项目报文加密(AES、RSA、Filter动态加密)

Springboot项目报文加密(AES、RSA、Filter动态加密) 一、痛点1.1、初版报文加密二、前期准备2.1、AES加密2.2、RSA加密2.3、国密算法概述2.4、国密SM22.5、国密SM32.6、国密SM42.7、JAVA中的拦截器、过滤器2.8、请求过滤器2.9、响应过滤器2.10、登录验证码2.11、BCrypt非对称…

基于Java (spring-boot)的学生成绩管理系统

一、项目介绍 (1) 课程信息的管理&#xff0c;包括课程信息的条件查询、录入、修改和删除。 (2) 课程表的管理&#xff0c;包括排课、录入课程表、修改课程表。 (3) 用户信息管理。包括对系统管理员、教师和学生的信息进行维护管理&#xff0c;可以新增、修改、删除和条件分…

电动汽车雷达技术概述 —— FMCW干扰问题

一、电动汽车上有多少种传感器&#xff1f; 智能电动汽车&#xff08;包括自动驾驶汽车&#xff09;集成了大量的传感器来实现高级驾驶辅助系统&#xff08;ADAS&#xff09;、自动驾驶功能以及车辆状态监测等功能。以下是一份相对全面的智能电动汽车中可能使用到的传感器列表…

AUTOSAR CP--chapter4从一个VCU需求开始Autosar的工程创建

从一个VCU需求开始Autosar的工程创建 1、VCU需求2、Autosar工作任务之间的关系&#xff1a;2.1、工程阶段配置阶段集成阶段调试阶段 1、VCU需求 通过建立整车控制器的工程&#xff0c;搭建Autosar工程的框架&#xff0c;开发的软件是搭载于微控制上运行的。 首先&#xff0c;看…

ArcGIS的UTM与高斯-克吕格投影分带要点总结

UTM&#xff08;通用横轴墨卡托投影、等角横轴割椭圆柱投影&#xff09;投影分带投影要点&#xff1a; 1&#xff09;UTM投影采用6度分带 2&#xff09;可根据公式计算&#xff0c;带数&#xff08;经度整数位/6&#xff09;的整数部分31 3&#xff09;北半球地区&#xff0…

家政小程序系统开发:从构思到实现

随着科技的快速发展&#xff0c;移动互联网已经深入到我们生活的方方面面。特别是在家政服务领域&#xff0c;传统的服务方式已经不能满足现代人的需求。因此&#xff0c;开发一款家政小程序系统显得尤为重要。本文将介绍家政小程序系统的开发过程&#xff0c;包括需求分析、设…

数据结构——D/二叉树

&#x1f308;个人主页&#xff1a;慢了半拍 &#x1f525; 创作专栏&#xff1a;《史上最强算法分析》 | 《无味生》 |《史上最强C语言讲解》 | 《史上最强C练习解析》 &#x1f3c6;我的格言&#xff1a;一切只是时间问题。 ​ 1.树概念及结构 1.1树的概念 树是一种非线性的…

最新话费充值系统源码,附带系统安装教程

搭建教程 亲测环境&#xff1a;PHP7.0MySQL5.6 PHP扩展安装&#xff1a;sg11 数据库配置文件路径&#xff1a;/config/database.php 伪静态设置为thinkphp 后台地址&#xff1a;/admin 账号密码&#xff1a;admin/123456

力扣面试题 05.06. 整数转换(位运算)

Problem: 面试题 05.06. 整数转换 文章目录 题目描述思路及解法复杂度Code 题目描述 思路及解法 1.通过将两个数进行异或操作求出两个数中不同的位(不同的位异或后为二进制1); 2.统计异或后不同的位的个数(即异或后二进制为1的个数) 复杂度 时间复杂度: O ( 1 ) O(1) O(1) 空间…

实战分享:SpringBoot在创新创业项目管理中的应用

✍✍计算机编程指导师 ⭐⭐个人介绍&#xff1a;自己非常喜欢研究技术问题&#xff01;专业做Java、Python、微信小程序、安卓、大数据、爬虫、Golang、大屏等实战项目。 ⛽⛽实战项目&#xff1a;有源码或者技术上的问题欢迎在评论区一起讨论交流&#xff01; ⚡⚡ Java实战 |…

Python操作Word表格对齐、单元格对齐

通过Table的alignment可以设置表格居左对齐、居中对齐、居右对齐。通过Cell的vertical_alignment可以设置垂直位置。通过单元格里段落的alignment可以设置文本的左右对齐方式。 import docx from docx.enum.table import WD_TABLE_ALIGNMENT, WD_CELL_VERTICAL_ALIGNMENT from…

Compose | UI组件(十四) | Navigation-Data - 页面导航传递数据

文章目录 前言传参流程实例说明普通方式传值定义接受参数格式定义接受参数类型获取参数传入参数传参和接受参数效果图 结合 ViewModel 传递参数定义ViewModel在 navigation 定义 ViewModel 实例&#xff0c;并且传入 LoginScreen传入输入框中的值&#xff0c;并且跳转传值获取值…