背景

最近想简单粗暴的用 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 并没有一种类似 事件冒泡 的机制,从叶子节点的组件开始向上传递。而只有 bindbind_allbind_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 事件在子元素中共享的更多相关文章

  1. LinearLayout的gravity属性以及其子元素的layout_gravity何时有效;RelativeLayout如何调整其子元素位置只能用子元素中的属性来控制,用RelativeLayout中的gravity无法控制!!!

    LinearLayout的gravity属性以及其子元素的layout_gravity何时有效:RelativeLayout如何调整其子元素位置只能用子元素中的属性来控制,用RelativeLayou ...

  2. js将数组中一个或多个字段相同的子元素中合并

    最近js中遇到js将数组中一个或多个字段相同的子元素中合并,相信很多朋友也有遇到,大家可能有多种方法,我在这里记录一个相对简单的方法,当然大家如有其它更好的方法,请提出来大家共同学习. //将经济事项 ...

  3. 【第三篇】学习 android 事件总线androidEventbus之发布事件,子线程中接收

    发送和接收消息的方式类似其他的发送和接收消息的事件总线一样,不同的点或者应该注意的地方: 1,比如在子线程构造方法里面进行实现总线的注册操作: 2,要想子线程中接收消息的功能执行,必须启动线程. 3, ...

  4. 关于自适应屏幕,设置子元素浮动,父div不能包裹子div,子元素中内容溢出的问题。

    设置HTML适应不同分辨率的屏幕. 需求结构如下: HTML结构代码如下(只是其中一条): <body> <div class="content">< ...

  5. Javascript事件模型(二):Javascript事件的父元素和子元素

    DOM事件标准定义了两种事件流,分别是捕获和冒泡.默认情况下,事件使用冒泡事件流,不使用捕获事件流.你可以指定使用捕获事件流,方法是在注册事件时传入useCapture参数,将这个参数设为true. ...

  6. html 子元素和父元素都监听了 click 事件,点击子元素时为何先触发的是父元素的 click 事件?

    先上一段代码,点击子元素时先触发的是父元素的 click 事件 <html> <head> <script type="text/javascript" ...

  7. 父元素a标签的href默认行为以及子元素绑定的click事件的响应之间存在影响

    原文地址 背景 开发过程中遇到问题,简单写个demo 运行环境为Chrome 68 描述一下这个问题,当a标签内部存在嵌套时, 父元素a标签的href默认行为以及子元素绑定的click事件的响应之间存 ...

  8. a标签嵌套href默认行为与子元素click事件存在影响

    2018-08-07 Question about work 开发过程中遇到问题,简单写个demo 运行环境为Chrome 68 描述一下这个问题,当<a>标签内部存在嵌套时, 父元素&l ...

  9. js 利用事件委托解决mousedown中的click

    有一个需求是这样的: 父元素div绑定一个mousedown事件,子元素a绑定一个click事件. 看解构: <div id="nav"> <a href=&qu ...

随机推荐

  1. PHPcms v9.6.0 文件上传漏洞

    title: PHPcms v9.6.0 文件上传漏洞 date: 2021-4-5 tags: 渗透测试,CVE漏洞复现,文件上传 categories: 渗透测试 CVE漏洞复现 文件上传 PHP ...

  2. 微信小程序组件设计规范

    微信小程序组件设计规范 组件化开发的思想贯穿着我开发设计过程的始终.在过去很长一段时间里,我都受益于这种思想. 组件可复用 - 减少了重复代码量 组件做为抽离的功能单元 - 方便维护 组件作为temp ...

  3. Node.js核心模块API之文件操作

    参考:https://www.runoob.com/nodejs/nodejs-fs.html 异步I/O 1,文件操作 2,网络操作 在浏览器中也存在异步操作 1,定时任务 2,事件处理 3,Aja ...

  4. gdb调试coredump(使用篇)

    gdb调试coredump(使用篇) 看到一个非常好的介绍coredump的文章,做个记录, 参考链接: https://blog.csdn.net/sunxiaopengsun/article/de ...

  5. JQuery 使用教程

    引言 JQuery 是一个 JavaScript 库,它极大地简化了 JavaScript 编程.JQuery 拥有丰富的选择器,可以非常方便的获取和操作 DOM 元素,而在 JQuery 中所有选择 ...

  6. 9.7 top:实时显示系统中各个进程的资源占用状况

    top命令 用于实时地对系统处理器状态进行监控,它能够实时地显示系统中各个进程的资源占用状况.该命令可以按照CPU的使用.内存的使用和执行时间对系统任务进程进行排序显示,同时top命令还可以通过交互式 ...

  7. 灵动微电子ARM Cortex M0 MM32F0010 GPIO 的配置驱动LED灯

    灵动微电子ARM Cortex M0 MM32F0010 GPIO的配置 目录: 1.前言 2.学习方法简要说明 3.要点提示 4.注意事项 5.MM32F0010系统时钟的配置 6.MM32F001 ...

  8. 『动善时』JMeter基础 — 35、JMeter接口关联【JSON提取器】详解

    目录 1.JSON提取器介绍 2.JSON提取器界面详解 3.JSON提取器的使用 (1)测试计划内包含的元件 (2)HTTP Cookie管理器内容 (3)用户登陆请求界面内容 (4)JSON提取器 ...

  9. GO学习-(39) 优雅地关机或重启

    优雅地关机或重启 我们编写的Web项目部署之后,经常会因为需要进行配置变更或功能迭代而重启服务,单纯的kill -9 pid的方式会强制关闭进程,这样就会导致服务端当前正在处理的请求失败,那有没有更优 ...

  10. SparkCore之业务操作逻辑

    在上spark的时候,一开始需要虚拟机模拟真实环境,而spark主要的三种模式:local.standalone.yarn 均可以通过虚拟机模拟. 这里要讨论的是业务逻辑如何和 spark 结合,具体 ...