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

news2024/9/25 6:00:48

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.data
            sx,sy=event.x_root,event.y_root
            #
            maxx,maxy,winw,winh=menu.wind.data
            bbox=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,默认y
        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 unshow(event):#重写菜单
            menu.withdraw()
            menu.unbind('<FocusOut>')
        def show(event):#显示的起始位置
            #初始位置
            maxx,maxy,winw,winh=menu.wind.data
            sx,sy=event.x_root,event.y_root
            #
            maxx,maxy,winw,winh=menu.wind.data
            bbox=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-winw
            else:
                x=sx
            if sy+winh>maxy:
                y=sy-winh
            else:
                y=sy
            menu.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-=1
        outline_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=disable
        funcs.active=active
        return 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.coloradmin.cn/o/1438881.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【C++】基础知识讲解(引用、内联、auto,基于范围for循环)

&#x1f308;个人主页&#xff1a;秦jh__https://blog.csdn.net/qinjh_?spm1010.2135.3001.5343&#x1f525; 系列专栏&#xff1a;http://t.csdnimg.cn/eCa5z 目录 引用 概念 特性 使用场景 作参数 作返回值 传值、传引用效率比较 引用和指针的区别 内联函数 概念…

excel 导出 The maximum length of cell contents (text) is 32767 characters

导出excel报错。错误日志提示&#xff1a;:The maximum length of cell contents (text) is 32767 characters 排查后&#xff0c;发现poi有单元格最大长度校验&#xff0c;超过32767会报错。 解决方案&#xff1a; 通过java反射机制&#xff0c;设置单元格最大校验限制为Int…

Skywalking 应用笔记

概念 Skywalking是一款分布式的系统 性能监视工具&#xff0c;专为微服务、云原生架构和基于容器(Docker、K8s、Mesos)架构而设计。SkyWalking是一款 观察性的分析平台和应用性能管理系统&#xff0c;提供了 分布式追踪、性能指标分析、应用服务依赖分析、可视化一体化等解决方…

CV | SAM在医学影像上的模型调研【20240207更新版】

本文主要是SAM&#xff08;Segment Anything&#xff09;在医学影像上的数据集&#xff0c;模型及评估方法调研【持续更新】~ 1.开源数据集 可参考这篇【数据集 | 基于计算机视觉的医学影像处理数据集_CSDN博客】 2.算法模型 2023.04_SAM 论文&#xff1a;2018.08.05v_Segm…

Win32 SDK Gui编程系列之--弹出式菜单

1.弹出式菜单 例如,在命令提示窗口中点击鼠标右键,会出现如下图所示的弹出菜单(下拉菜单)。 这种弹出式菜单的实现很简单。不创建菜单栏,用CreatePopupMenu函数创建的菜单是最顶端的菜单就可以了。 菜单的显示使用TrackPopupMenu函数进行。 例如,点击鼠标右键显示弹出…

尚硅谷 Java 基础实战—Bank 项目—实验题目 3

实验题目 修改 withdraw 方法以返回一个布尔值&#xff0c;指示交易是否成功。 实验目的 使用有返回值的方法。 提示 修改 Account 类 修改 deposit 方法返回 true&#xff08;意味所有存款是成功的&#xff09;。修改 withdraw 方法来检查提款数目是否大于余额。如果amt小…

十七、vben合并行后操作按钮如何合并

上期我们说了如何在table内部合并行,行内的内容都是字符串,那么如果是多个操作按钮呢,他们是如何合并的,事件是怎么触发的,怎么写呢。 先看效果图 数据上也是和上期一样有9条信息。 下面来看一下我们的具体实现 一、在template里面写table <BasicTable:showIndexCol…

【网络技术】【Kali Linux】Nmap 嗅探(一)简单扫描

一、实验环境 本次实验进行简单的Nmap扫描&#xff0c;实验使用 Kali Linux 虚拟机和 Ubuntu Linux 虚拟机完成&#xff0c;主机操作系统为 Windows 11&#xff0c;虚拟化平台选择 Oracle VM VirtualBox&#xff0c;如下图所示。 二、实验步骤 1、相关配置 Kali Linux 虚拟机…

【python】python实现代码雨【附源码】

欢迎来到英杰社区https://bbs.csdn.net/topics/617804998 系列文章 1新年烟花代码https://blog.csdn.net/m0_73367097/article/details/1354817792爱心代码https://blog.csdn.net/m0_73367097/article/details/136017032 一、效果图&#xff1a; 二、准备工作 &#xff08;1…

第二证券:股市的国家队是谁?股市国家队包括哪些机构?

在a股商场上&#xff0c;投资者大致能够分为散户、游资、主力、组织、国家队这几大类&#xff0c;那么&#xff0c;股市的国家队是谁&#xff1f;股市国家队包含哪些组织&#xff1f; 国家队主要是指以下五大类&#xff1a; 1、中心汇金 中心汇金的全称为中心汇金投资有限责…

【STM32F103】PWM驱动舵机(SG90MG995)

PWM 关于如何发出PWM可以参考我之前的文章。 【STM32F103】TIM定时器&PWM-CSDN博客 SG90&MG995 以这两款舵机为例是因为我手上碰巧只有这两款舵机。不过实际上舵机的操作基本上差不了多少&#xff0c;基本上都是给频率为50Hz的PWM&#xff0c;然后就可以让舵机旋转…

详解C++类和对象(中(类的6个默认成员函数))

文章目录 写在前面1. 类的6个默认成员函数2. 构造函数2.1 构造函数的引入2.1 构造函数的特性 3. 析构函数3.1 析构函数的引入3.2 析构函数的特性 4. 拷贝构造函数4.1 拷贝构造函数概念4.2 拷贝构造函数的特性4.3 拷贝构造函数典型调用场景 5. 赋值运算符重载5.1 运算符重载5.2 …

Javaweb之SpringBootWeb案例之登录校验功能的详细解析

2. 登录校验 2.1 问题分析 我们已经完成了基础登录功能的开发与测试&#xff0c;在我们登录成功后就可以进入到后台管理系统中进行数据的操作。 但是当我们在浏览器中新的页面上输入地址&#xff1a;http://localhost:9528/#/system/dept&#xff0c;发现没有登录仍然可以进…

寻迹模块——红外循迹模式使用介绍

目录 循迹模式——红外循迹模式使用介绍 红外循迹模块介绍 接线 循迹小车原理 安装与接线 实验程序 实验效果 循迹模式——红外循迹模式使用介绍 实验效果&#xff1a; 寻迹模块-CSDN直播 红外循迹模块介绍 传感器的红外发射二极管不断发射红外线&#xff0c;当发射出…

RabbitMQ-5.消费者的可靠性

消费者的可靠性 5.消费者的可靠性5.1.消费者确认机制5.2.失败重试机制5.3.失败处理策略5.4.业务幂等性5.4.1.唯一消息ID5.4.2.业务判断 5.5.兜底方案 5.消费者的可靠性 当RabbitMQ向消费者投递消息以后&#xff0c;需要知道消费者的处理状态如何。因为消息投递给消费者并不代表…

jvm几个常见面试题整理

1. Full GC触发机制有如下5种情况。 (1)调用System.gc()时&#xff0c;系统建议执行Full GC&#xff0c;但是不必然执行。(2)老年代空间不足。(3)方法区空间不足。(4)老年代的最大可用连续空间小于历次晋升到老年代对象的平均大小就会进行Full GC。(5)由Eden区、S0(From)区向S…

前端vite+vue3——自动化配置路由布局

文章目录 ⭐前言&#x1f496;vue3系列文章 ⭐ 自动化配置路由&#x1f496;引入vite版本自定义目录映射&#x1f496;自动化读取文件下的路由&#x1f496;main入口加载路由&#x1f496;入口app.vue配置&#x1f496;layout基础布局配置&#x1f496;效果 ⭐总结⭐结束 ⭐前言…

python实现中国剩余定理

中国剩余定理又称孙子定理&#xff0c;是数论中一个重要定理。最早可见于我国的数学著作《孙子算经》卷下“物不知数”问题&#xff0c;原文如下&#xff1a; 有物不知其数&#xff0c;三三数之剩二&#xff0c;五五数之剩三&#xff0c;七七数之剩二。问物几何&#xff1f;即…

车载网络测试 - 总线基础 - CAN总线负载计算

我想做过CAN总线测试的都有遇到过拉高总线负载相关的测试&#xff0c;这个时候我们一般都会通过增加报文的数量或者减小报文的周期来实现&#xff0c;但是CAN总线上的负载到底是如何计算的呢&#xff1f;我想很多人都会有这个疑问吧&#xff0c;那么今天我们一起来看下如何计算…

CX341A 安装驱动与刷固件

参考 驱动安装1 DPDK编译&#xff1a;支持Mellanox 25Gbps网卡 - 知乎 NVIDIA Mellanox CX网卡固件、驱动系列操作 - 知乎 驱动安装2 Mellanox网卡驱动安装指南 Mellanox OFED_崇尚匀速 追求极致的技术博客_51CTO博客 驱动与固件&#xff1a; 家用万兆网络指南 6 - 比…