背景

最近想简单粗暴的用 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 原生提供的东西非常有限,但是可以借鉴很多其他的思路来进行扩展,从而满足我们的需求。

最新文章

  1. error C2065: “CDatabase”: 未声明的标识符
  2. 验证时出错。HRESULT = &#39;8000000A&#39;
  3. Socket通信的理解
  4. Windows与Linux主机之间的连接和交互工具
  5. 利用Jquery使用HTML5的FormData属性实现对文件的上传
  6. CF440C
  7. Oracle游标练手实例
  8. 解决使用jQuery采用append添加的元素事件无效的方法
  9. JUnit + Mockito 单元测试(二)
  10. HDU4289 Control 最大流
  11. netsh命令
  12. SQL中存储过程的例子
  13. H5混合开发二维码扫描以及调用本地摄像头
  14. Excel生成guid、uuid
  15. CSS 换行问题white-space属性 window对象和global对象
  16. (BST 递归) leetcode98. Validate Binary Search Tree
  17. Unity2018 VS2017打开CS脚本,提示全红及无法加载工程等问题解决
  18. IdentityServer4 中文文档 -14- (快速入门)使用 ASP.NET Core Identity
  19. Java测试代码(很不完整,建议大家别看,过几天会再发一次难的版本)
  20. java中如何不自己写排序方法完成排序

热门文章

  1. [DB] Memcache
  2. 实例:使用playbook实现httpd安装、配置、以及虚拟主机的配置
  3. 基于多主机的Web服务
  4. gpgj-19.高级课总结
  5. 2.7循环_while
  6. JavaScript 实现简易版贪吃蛇(Day_13)
  7. Day30 BigInteger和BigDecimal
  8. week-02
  9. GO学习-(30) Go语言操作kafka
  10. jupyter notebook 默认文件路径修改以及启动