本书定位:你还没学过 tkinter,连"控件"是什么都可能是第一次听说;但你已经会 Python 基础、也学过面向对象(类、对象、self、方法)。本书就从这个起点出发,一个控件一个控件地认识,循序渐进,最终能用面向对象的方式独立做出完整的桌面小项目。
主线项目:图形界面通讯录管理器。它不会一上来就出现,而是随章节每章只新增一个控件/能力,慢慢长大——你始终能看见"学到了哪"。
全教程红线(统一遵守):
1. 每个窗口/界面都用类封装:class App:,__init__(self, root)里建控件,方法即事件回调。
2. 所有控件示例一律写在类的方法里,不用脱离类的扁平脚本。
3. 每章代码块尽量可独立运行(带if __name__ == "__main__":)。
4. 每章开头的"红线回顾"请先扫一眼,整本书的写法都按它来。
星级说明:★ 了解 · ★★ 理解 · ★★★ 掌握 · ★★★★ 重点必会。
怎么学:每章先认识控件"是什么",再"在类里写出来",然后学它的常用选项/方法,最后做练习。别跳着读——下一章一定建立在本章之上。
01-第1章-认识GUI与第一个窗口.md02-第2章-显示文字Label.md03-第3章-点击交互Button.md04-第4章-布局管理器pack_grid_place.md05-第5章-单行输入Entry与StringVar.md06-第6章-多行输入Text.md07-第7章-ttk模块与Style.md08-第8章-勾选与单选Checkbutton_Radiobutton.md09-第9章-下拉与调节Combobox_Scale_Spinbox.md10-第10章-列表Listbox与Scrollbar.md11-第11章-表格Treeview.md12-第12章-容器Frame_LabelFrame.md13-第13章-分页Notebook_PanedWindow.md14-第14章-自定义组件复合控件.md15-第15章-事件绑定bind.md16-第16章-标准对话框.md17-第17章-菜单Menu.md18-第18章-画布与进度Canvas_Progressbar.md19-第19章-易错与进阶.md20-第20章-综合项目MVC通讯录.md附录A-常用控件速查表.md附录B-常用事件速查.md附录C-综合项目完整代码.md本章目标:建立三个最基础的认知——① GUI 是什么;② 控件(widget)是什么;③ 一个 tkinter 程序由哪三步组成。然后你就能写出人生第一个窗口,并立刻用面向对象的方式把它重写一遍(这是本书红线,第 1 章就定调)。
是什么:GUI 是 Graphical User Interface(图形用户界面)的缩写。你平时用的软件——微信、记事本、浏览器——那些能"用鼠标点按钮、在框里打字"的界面,就是 GUI。
对比命令行:之前你写的 Python 程序大多是"命令行程序"——在黑框框里输入、屏幕上一行行吐字。GUI 程序没有黑框,它有一个真正的"窗口",里面有按钮、文字、输入框,你用鼠标键盘和它们交互。
为什么学 GUI:同样一个"通讯录",命令行版要靠你记指令、手打格式;GUI 版谁都会用——这就是界面的价值。学会 tkinter,你写的程序就能"像软件一样"交给别人点着用。
是什么:控件(英文叫 widget,也常译作"组件""部件")是界面里每一个可见、可交互的小元素。常见的控件有:
一个关键直觉:界面 = 用控件"搭"出来的。就像用乐高积木拼模型,每个控件是一块积木,你把它们摆进窗口、排好位置,再让它们之间"互动"起来,就是一个完整的程序。本书后面每一章,就是认识一块新"积木"(新控件),并学会怎么把它拼进你的通讯录项目。
有什么(tkinter 的优势):
pip install,写 import tkinter 就能用。为什么不够"好看"(以及对策):tkinter 原生的控件风格偏"复古"。但本书后续会讲 ttk 模块——它提供一套跟随系统的"现代皮肤"控件,一换就好看;还会教你把界面拆成组件类,做出结构清晰的专业软件。所以"丑"不是问题,循序渐进解决。
结论:对零基础、想快速做出能跑的桌面程序的人来说,tkinter 是最顺的那条路。
无论多复杂的 tkinter 程序,骨架永远这三步:
tk.Tk() 创建一个主窗口对象。这个窗口是所有控件的"容器"。窗口.mainloop() 让窗口"活"起来——它开始监听鼠标点击、键盘输入等事件,一直运行直到你关掉窗口。形象理解:Tk()是把舞台搭起来;放控件是把演员请上台;mainloop()是拉开帷幕、大幕拉开后戏才开演(程序才开始响应你操作)。少了mainloop(),窗口一闪就没——这是新手第一个坑。
先不急着用类,用最短的代码让你看到窗口,建立信心:
# 第1章 例1.1:最小窗口(两行核心代码)
import tkinter as tk # ① 导入 tkinter 模块,起个别名 tk,后面都用 tk.xxx 调用
root = tk.Tk() # ② 创建主窗口对象,变量名习惯叫 root("根窗口")
root.mainloop() # ③ 进入主循环:窗口显示出来,等待你操作;关闭窗口后程序才结束逐行说明:
import tkinter as tk。tk 是约定俗成的别名,以后写 tk.Label、tk.Button 都靠它。tk.Tk() 创建一个窗口。此时它还是空的、没标题。root.mainloop()。这是让窗口真正"出来并停留"的关键。如果你把这一行删了,程序会瞬间跑完、窗口根本来不及显示(在命令行里可能什么都看不到就结束了)。运行它,你会看到一个空白的小窗口——恭喜,你的第一个 GUI 程序诞生了。
💡 在哪里运行:tkinter 需要"有桌面的环境"(你的 Windows/Mac 电脑本机)。在服务器、云编辑器、没有显示器的环境里 mainloop() 会卡住或报错——本书所有代码都请在本机 Python 环境运行。
上面的两行代码能跑,但不符合本书的写法约定。本书要求:界面 = 一个类。现在就把例 1.1 改写成面向对象版本:
# 第1章 例1.2:用面向对象方式写第一个窗口(本书标准骨架)
import tkinter as tk
class App: # ① 定义一个"应用"类,整个界面用这个类来管理
def __init__(self, root): # ② 构造方法:创建 App 对象时,传入主窗口 root
self.root = root # ③ 把窗口存到 self.root,这样类里别的方法以后随时能用到它
self.root.title("通讯录管理器") # ④ 给窗口设个标题(不加这句,标题栏是默认的 tk)
if __name__ == "__main__": # ⑤ 当本文件被直接运行时,才执行下面
root = tk.Tk() # ⑥ 创建主窗口
App(root) # ⑦ 把窗口交给 App 类,App 在构造里设置好标题
root.mainloop() # ⑧ 进入主循环,窗口显示出来逐行说明(重点看①②③⑦):
class App:——界面是一个类。以后每章的窗口、按钮、逻辑,都住在这个类里。__init__(self, root):创建 App 对象时必须给一个 root(主窗口)。self 是"这个 App 对象自己"。self.root = root:把窗口"抓"在对象上。以后类里任何方法想改窗口标题、关窗口,写 self.root.xxx 就行——不用再去找那个全局变量 root。这正是用类的好处:相关东西都绑在一起。if __name__ == "__main__":——守卫。意思是"只有直接运行这个文件时才执行下面的启动代码"。如果这个文件被别的文件 import,下面的启动代码不会自动跑(避免一 import 就弹窗)。这是 Python 的工程化好习惯。App 初始化,最后 mainloop。运行例 1.2,你会看到一个标题为"通讯录管理器"的窗口——和例 1.1 的空白窗口相比,它有了名字。这就是"面向对象写法"的第一个可见成果。
本章是全书 OOP 写法的总起,但只先建立两个最简单的认知,别被架构名词吓到:
self.root = root。它把"窗口"这个重要对象存进了 App 实例。假如不用类,你得把 root 写成全局变量,函数之间传来传去、容易乱。用类,所有控件和状态都挂在 self 上,结构清晰、好维护。if __name__ == "__main__": 守卫的作用:保证"启动代码"只在直接运行本文件时执行;被 import 时不误弹窗。后面每章都这样写,看到它就放心。至于"方法即回调""MVC 分层""自定义组件"这些进阶 OOP 概念,不急——它们会在该出现的时候自然登场。
mainloop():窗口一闪而过或根本不显示。记住:建完窗口和控件,最后一定要 root.mainloop()。title 拼成 tittle:Python 不认 tittle,会报 AttributeError。注意拼写。if __name__ == "__main__": 外面:这样文件一旦被 import 就自动弹窗,干扰别的程序。启动代码请收进守卫里。mainloop():如远程服务器、部分在线编辑器。报错或卡住不是你代码错,是环境不支持 GUI。请在本机运行。import tkinter 写成 import Tkinter(首字母大写):Python 3 里模块名全小写 tkinter;大写是 Python 2 的老写法,现在会 ModuleNotFoundError。__init__ 里加一行 self.root.geometry("400x300")(试着运行看看窗口变大了没,"宽x高" 里的 x 是英文字母 x)。App(root) 改成 app = App(root),再在 app 上调用方法会有什么好处?(提示:你以后能通过 app 这个变量访问这个应用对象。)