Python的门面担当
在大多数时候,我们都在黑黢黢的控制台里执行 Python 脚本。这看起来很酷很 GEEK。但对于部分场景下的用户来说,这样就不大美观和人性化了:我们需要交互更方便的图形化产品,也就是 GUI(图形用户界面,Graphical User Interface)。
Python 有很多可实现 GUI 的库,在之前的文章中有过一个整理:如何用 GUI 提高 python 程序的颜值? 但没有针对某个具体的库做介绍。最近有同学提到希望给讲解下 GUI 的开发,那么今天就来做个简单的“快速上手”。
而我们要介绍的库,就是
Tkinter
相比较其他的 GUI 库,Tkinter 有个优势在于,它是 Python 内置的 GUI 库,无需另行安装,省事了一点点。另外如果你要将开发出的程序打包成 exe,它也比第三方库稍稍更容易一点点。
而功能上,Tkinter 已足够处理大多数小型 GUI 程序的需求。其开发的程序在各主流操作系统上均可运行。Python 的内置编辑器 IDLE 就是使用 Tkinter 开发的。因此,我个人在之前的开发中,是将 Tkinter 作为首选。
(当然,PyQt 也是很强大的 GUI 库,可以做出复杂酷炫的界面效果。而原有 QT 开发经验的人更是很容易上手。)
Hello GUI World
我们从一个最简单的 Tkinter GUI 程序说起:
import tkinter as tk root = tk.Tk() root.mainloop()
1.创建一个 Tk 窗口对象;2.调用这个对象的消息主循环。一个窗口就出现了。在这个窗口之上,可以添加各种输入框、按钮、文本等,可以增加对各种动作的处理。
以往我们写的程序(比如猜数字、罚点球、查天气等)大多是有一个固定的执行流程。而 GUI 程序的不同之处在于,通常它们是由“事件驱动”的:程序运行后,相当于进入一个循环一直运行。如果你不做任何操作,这个窗口就一直在这里。看起来是静止的,但程序实际上是在等待你的操作:通过与窗口中的“控件”进行交互,比如点击按钮、输入文字、勾选选项等,产生不同的“事件”,程序再根据预设的“响应”做处理。就算要结束程序,也是通过“关闭窗口”和“退出”事件。
这就是 mainloop 的意义所在:监听各种事件。理解了这一点,也就理解了 GUI 程序的基本逻辑。
控件
所谓控件,就是 GUI 图形化界面上的对象,或者说功能元素。比如输入框、文本框、按钮、下拉菜单、滚动条等等,窗体本身也可以认为是一个控件。一个控件包含了数据和操作,决定了页面上的元素放在哪里、长什么样、有什么样的效果。
举几个 Tkinter 常用控件的例子:
label = tk.Label(root, text="Hello, world!") label.pack() btn = tk.Button(root, text="OK") btn.pack() entry = tk.Entry(root) entry.pack() root.mainloop()
在调用 mainloop
前,增加了 Label(文本标签)、Button(按钮)、Entry(输入框)三个控件,通过 pack()
方法把它们添加到了窗口之上。
Tkinter 有 15 个核心控件,每个控件有多种设置,这里不展开介绍,网上可以很容易搜到详细的文档说明。
另外除了这些基本控件之外,Tkinter 还提供了一个 ttk 模块,增加了几个控件并对部分已有控件进行了优化。例如:
from tkinter import ttk entry = ttk.Entry(window) entry.pack() combo = ttk.Combobox(window) combo['values'] = ('IDLE', 'PyCharm', 'VSCode', 'SublimeText') combo.pack()
完整示例代码在文末附上。
对于控件属性的设置,有 3 种方法:
在创建时通过参数设置。如
btn = Button(root, text="Click", fg="red", bg="blue", command=click)
通过字典的方式修改。如
btn["fg"] = "green"
通过 config 函数修改。如
btn.config(fg="green", bg="yellow")
布局
如果只是简单的用 pack()
方法将控件添加到窗口上,它们将按顺序从上往下的放置。这显然无法满足复杂的需求。
Tkinter 提供了三种布局方式:
1. Pack
pack 是最简单的布局管理方式,除了像我们前面直接调用外,可以加上 fill、padx、pady、ipadx、ipady、side 等参数,调整放置的边距、填充方式、对齐方式等。
btn.pack(fill=tk.X, padx=5, pady=20, side=tk.LEFT)
2. Place
用 place 替代 pack,可以精确地指定空间的放置坐标及长宽。
btn.place(x=50, y=100, width=120, height=25)
3. Grid
Grid 布局的逻辑在于,将窗口像表格一样划分成不同的格子,将控件放置进去。例如:
当控件数量众多时,这种布局方式更有条理。
btn.grid(row=1, column=0)
顺便提一句,如果你希望可以像 VB 那样所见即所得地设计窗体控件,可以了解下 Visual Tkinter 这个工具。
事件
前面说的都是外在的形式,一个 GUI 程序要能运行,离不开内部的事件响应。即:当用户做了一个操作,程序要做出怎样的反应。
事件要与特定的控件相绑定,比如按钮有点击事件,输入框有按键事件,窗体有关闭事件等。
常用的 2 种绑定方法:
1. command
通过控件的 command 参数指定响应函数:
def onClick(): print('clicked!') btn = Button(root, text='click', command=onClick)
注意这里传递参数时,onClick 后面不能加上括号。(思考下加与不加的区别在哪里?)
2. bind
通过 bind 方法绑定不同的事件:
def onButton(event): print("Clicked:", event.x, event.y) def onKey(event): print("Pressed", event.char) entry.bind('<Button-1>', onButton) entry.bind('<Key>', onKey)
控件、布局、事件响应,就是 GUI 开发的几个重要部分。对此有了整体认识后,剩下的就是查阅相关文档和练习了。
如果有不理解的部分或想要深入了解的细节问题,可以在我们的论坛 bbs.crossincode.com 上发帖讨论,或在知识星球上提问。
运用上述内容,我们把课程最初的猜数字游戏改成一个 GUI 版本。