Tkinter 吐槽之二:Event 事件在子元素中共享
背景
最近想简单粗暴的用 Python 写一个 GUI 的小程序。因为 Tkinter 是 Python 自带的 GUI 解决方案,为了部署方便,就直接选择了 Tkinter。
本来觉得 GUI 发展这么多年以来,就算功能简陋,但也应该大差不差才对,何况我的需求本就十分简单。但事实上 Tkinter 的简陋仍然超出了我的想象。因此想写几篇文章,记录一下踩到的坑,权当吐槽。
同系列的其它文章:
Tkinter 吐槽之一:多线程与 UI 交互
问题
在一些情况下,Tkinter 原生的组件因为过于简单而无法满足我们的需求,所以可能需要通过 Frame 自定义一些组件,并响应一些事件。
比如,自定义一个带多个信息的可点击的 Frame:
import tkinter as tk
class PersonFrame(tk.Frame):
def __init__(self, master, name, age):
super().__init__(master=master, width=100, borderwidth=2, padx=5)
tk.Label(self, text=name).pack(side=tk.LEFT, fill=tk.X)
tk.Label(self, text=age, foreground='red').pack(side=tk.RIGHT)
self.bind('<Button-1>', self._on_click)
def _on_click(self, _):
print('clicked')
app = tk.Tk()
frame = PersonFrame(app, 'Tom', '27').pack(fill=tk.X)
frame = PersonFrame(app, 'Jerry', '16').pack(fill=tk.X)
app.mainloop()
这个例子看上去一切都很美好,通过自定义 Frame 的方式创建了一个组件,并且绑定了组件的 鼠标单击 事件。
但是经过实际测试发现,只有当鼠标点击在 Frame 的空白位置时,才会触发 on_click 的调用。如果鼠标点在文字上,并不会有任何效果。这说明, bind 操作只对 Frame 本身生效,并不会对覆盖在其上的子元素生效,即使逻辑上存在父子关系。
因此,这个自定义的组件没有办法真正意义上被当作可点击的组件使用。
方案
根本原因,在于 Tkinter 并没有一种类似 事件冒泡 的机制,从叶子节点的组件开始向上传递。而只有 bind,bind_all, bind_class 这三种形式。很显然,这三种形式都是无法满足要求的。
那么思路就演变为,有没有可能把 Frame 及 Frame 的所有子元素,都绑定上同一个事件的方法呢?
def bind_recursively(widget: tk.Misc, event: str, callback: Callable):
"""Binds event recursively with it's all children. So a widget and all
it's children will share one event and callback.
"""
widget.bind(event, callback)
for w in widget.children.values():
bind_recursively(w, event, callback)
这段代码就是采用类似的思路,首先找到 widget 的所有子元素,然后以递归的方式绑定同一个事件的 callback。这样可以保证一个 widget 中的所有子元素都可以响应同样的事件。从而实现在哪里点击都一致的效果。
总结
工程设计其实就是这样,找到一些理论可行的方法,然后通过抽象、封装的方式转换成一个优雅的解决方案。 Tkinter 原生提供的东西非常有限,但是可以借鉴很多其他的思路来进行扩展,从而满足我们的需求。
Tkinter 吐槽之二:Event 事件在子元素中共享的更多相关文章
- LinearLayout的gravity属性以及其子元素的layout_gravity何时有效;RelativeLayout如何调整其子元素位置只能用子元素中的属性来控制,用RelativeLayout中的gravity无法控制!!!
LinearLayout的gravity属性以及其子元素的layout_gravity何时有效:RelativeLayout如何调整其子元素位置只能用子元素中的属性来控制,用RelativeLayou ...
- js将数组中一个或多个字段相同的子元素中合并
最近js中遇到js将数组中一个或多个字段相同的子元素中合并,相信很多朋友也有遇到,大家可能有多种方法,我在这里记录一个相对简单的方法,当然大家如有其它更好的方法,请提出来大家共同学习. //将经济事项 ...
- 【第三篇】学习 android 事件总线androidEventbus之发布事件,子线程中接收
发送和接收消息的方式类似其他的发送和接收消息的事件总线一样,不同的点或者应该注意的地方: 1,比如在子线程构造方法里面进行实现总线的注册操作: 2,要想子线程中接收消息的功能执行,必须启动线程. 3, ...
- 关于自适应屏幕,设置子元素浮动,父div不能包裹子div,子元素中内容溢出的问题。
设置HTML适应不同分辨率的屏幕. 需求结构如下: HTML结构代码如下(只是其中一条): <body> <div class="content">< ...
- Javascript事件模型(二):Javascript事件的父元素和子元素
DOM事件标准定义了两种事件流,分别是捕获和冒泡.默认情况下,事件使用冒泡事件流,不使用捕获事件流.你可以指定使用捕获事件流,方法是在注册事件时传入useCapture参数,将这个参数设为true. ...
- html 子元素和父元素都监听了 click 事件,点击子元素时为何先触发的是父元素的 click 事件?
先上一段代码,点击子元素时先触发的是父元素的 click 事件 <html> <head> <script type="text/javascript" ...
- 父元素a标签的href默认行为以及子元素绑定的click事件的响应之间存在影响
原文地址 背景 开发过程中遇到问题,简单写个demo 运行环境为Chrome 68 描述一下这个问题,当a标签内部存在嵌套时, 父元素a标签的href默认行为以及子元素绑定的click事件的响应之间存 ...
- a标签嵌套href默认行为与子元素click事件存在影响
2018-08-07 Question about work 开发过程中遇到问题,简单写个demo 运行环境为Chrome 68 描述一下这个问题,当<a>标签内部存在嵌套时, 父元素&l ...
- js 利用事件委托解决mousedown中的click
有一个需求是这样的: 父元素div绑定一个mousedown事件,子元素a绑定一个click事件. 看解构: <div id="nav"> <a href=&qu ...
随机推荐
- printf/scanf格式
(1)打印字符 char c; printf("%c",c); (2)打印整形 int i; printf("%d",i); //有符号十进制数 printf( ...
- 2020BUAA-团队介绍-采访
团队作业-团队介绍和采访 项目 内容 课程:北航2020软件工程 博客园班级地址 作业要求 团队作业-团队介绍和采访 团队介绍 姓名 有图有真相 个人介绍 刘y 精通(没那么熟悉)c++和python ...
- Ribbon导航
简介 最近都在弄微服务的东西,现在来记录下收获.我从一知半解到现在能从0搭建使用最大的感触有两点 1.微服务各大组件的版本很多,网上很多博客内容不一定适合你的版本,很多时候苦苦琢磨都是无用功 2.网上 ...
- 『动善时』JMeter基础 — 23、JMeter中使用“用户自定义变量”实现参数化
目录 1.用户自定义变量介绍 2.使用"用户自定义变量"实现参数化 (1)测试计划内包含的元件 (2)数据文件内容 (3)测试计划界面内容 (4)线程组元件内容 (5)CSV数据文 ...
- [刷题] 剑指offer 面试题18:删除链表节点
要求 给定单向链表的头指针和一个节点指针,在O(1)时间内删除该节点 常规思路:从头节点a开始顺序遍历,发现p指向要删除的节点i,然后把p的m_pNext指向i的下一个节点j,时间复杂度O(n) O( ...
- 用户添加到sudoer列表## Allow root to run any commands anywhere root ALL=(ALL) ALL Iron ALL=(ALL) ALL
将用户添加到sudoer列表 李序锴关注 2017.12.20 15:03:25字数 605阅读 4,067 默认情况下,linux没有将当前用户列入到sudoer列表中(在redhat系列的linu ...
- jmeter线程组扩展空间——Stepping Thread Group
安装方法跟安装其他插件一样,不复赘述 各个配置含义: 举个例子:一个线程组下包含了登陆和抽奖两个接口 1.继续:如果登陆接口失败,会继续执行抽奖接口 2.start next thread loop: ...
- 059.Python前端Django组件cooki和session
一 会话跟踪技术 1.1 什么是会话 会话是指一个终端用户(服务器)与交互系统(客户端)进行通讯的过程. 1.2 什么是会话跟踪 对同一个用户对服务器的连续的请求和接受响应的监视.(将用户与同一用户发 ...
- Linux进阶之综合练习
综合练习: 1.准备2台centos7系统的服务器,远程互相免密登录,以下所有题目过程中开启防火墙 2.给1号机和2号机使用光盘搭建本地yum源(永久生效) 3.给服务器1添加2块硬盘,1块1GB,1 ...
- 2.5if语句
判断(if)语句 目标 开发中的应用场景 if 语句体验 if 语句进阶 综合应用 01. 开发中的应用场景 生活中的判断几乎是无所不在的,我们每天都在做各种各样的选择,如果这样?如果那样?-- 程序 ...