本书红线(贯穿全教程,回顾):
1. 每个界面都用class App:封装,__init__里建控件,方法即事件回调。
2. 所有控件示例一律写在类里,不用脱离类的扁平脚本。
3. 代码块带if __name__ == "__main__":守卫,可独立运行。
4. 星级:★ 了解 · ★★ 理解 · ★★★ 掌握 · ★★★★ 重点必会。
主线项目:图形界面通讯录管理器,随章节每章新增一个控件慢慢长大。
上节回顾(第 6 章):你认识了 Text 多行输入控件,学会了用 get("1.0", tk.END) 取全部、insert(tk.END, ...) 追加、delete("1.0", tk.END) 清空,还用 tag 给一段文字上色。现在你的通讯录管理器已经能"显示、点击、单行输入、多行输入"了,但界面还是 tkinter 默认的"土味原生"风格。
本章目标:认识 ttk(Themed Tkinter,主题控件)模块 和 Style(样式对象),把界面从土味原生换成随系统更美观的现代风格,并学会统一换肤。本章正式引出 ttk,之后本书优先使用 ttk 控件。
是什么:ttk 是 tkinter 的"主题控件"子模块。它把控件分成"功能(逻辑)"和"外观(主题)"两层——控件长什么样由当前"主题"决定,而不是靠你一个个设 fg/bg。它提供的控件叫"主题控件",如 ttk.Button、ttk.Label、ttk.Entry 等。
为什么要有 ttk:原生 tk.Button 的样式(灰色凸起按钮)又老又土,且在不同系统上观感不一致。ttk 控件随操作系统主题自动变好看,也方便统一换肤。
适用场景:
Style 集中管理)📌 本章起本书的约定:优先用ttk.XXX(如ttk.Button、ttk.Label、ttk.Entry)。但前几章已学的tk.Label等仍然有效,本章先对比二者差别。
ttk 控件选项和 tk 控件不同——它大多没有 fg/bg/font 这些直接样式选项,外观交给 Style 管。下面以 ttk.Button 为例(来自官方 tkinter.ttk 文档):
| 选项 | 作用 | 常用取值 | 重要度 |
|---|---|---|---|
text | 按钮上文字 | "添加" | ★★★★ |
command | 点击回调(同 tk) | self.on_click | ★★★★ |
style | 套用哪个自定义样式名 | "My.TButton" | ★★★ |
state | 可用/禁用 | "normal"/"disabled" | ★★★ |
width | 宽度(字符数) | 整数 | ★★ |
image / compound | 图片及图文位置 | 同 tk | ★ |
padding | 内边距(替代 tk 的 padx/pady 思路) | 整数或元组 | ★★ |
⚠️ ttk 控件没有fg/bg!想改颜色必须用ttk.Style().configure(...)。若你写ttk.Button(..., fg="red")会被忽略(不报错但无效)。这是新手最常踩的坑,见 7.6 节。
本章重点是 ttk.Style 对象的方法:
| 方法 | 作用 | 示例 |
|---|---|---|
ttk.Style() | 创建一个样式管理器对象 | self.style = ttk.Style() |
style.theme_names() | 列出可用主题名 | print(self.style.theme_names()) |
style.theme_use() | 查看当前主题 | self.style.theme_use() |
style.theme_use("clam") | 切换到指定主题 | self.style.theme_use("clam") |
style.configure("TButton", ...) | 设置某类控件的样式 | style.configure("TButton", foreground="red") |
style.map("TButton", foreground=[...]) | 设置"状态变化"时的样式(如悬停变色) | style.map("TButton", foreground=[("active","red")]) |
🔑 样式名规则:ttk 用"T类型"作为类名,如TButton、TLabel、TEntry、TFrame。configure("TButton", ...)就是"改所有按钮的样式"。自定义样式名则写成"我的名字.TButton",再用控件style="我的名字.TButton"套用。
本章先对比tk.Label与ttk.Label,之后示例优先用ttk。注意:用 ttk 控件前要先from tkinter import ttk。
# 第7章 例7.1:并排显示原生 tk.Label 和主题 ttk.Label,直观对比外观
import tkinter as tk
from tkinter import ttk # 引入 ttk 子模块
class App:
def __init__(self, root):
self.root = root
self.root.title("通讯录管理器")
# 原生 tk.Label:样式靠 fg/bg 直接设定
self.tk_label = tk.Label(
self.root, text="我是原生 tk.Label", bg="#3a7afe", fg="white"
)
self.tk_label.pack(padx=10, pady=10)
# ttk.Label:外观由主题决定,不能直接传 fg/bg
self.ttk_label = ttk.Label(self.root, text="我是主题 ttk.Label")
self.ttk_label.pack(padx=10, pady=10)
if __name__ == "__main__":
root = tk.Tk()
App(root)
root.mainloop()逐行说明:
from tkinter import ttk:使用 ttk 控件前必须导入。之后用 ttk.Label、ttk.Button 等。self.tk_label 用 bg/fg 直接上色,这是 tk 的玩法;self.ttk_label 没有 bg/fg,它长什么样由当前主题决定(在不同系统/主题下观感不同)。运行后你会看到两者风格明显不一样——ttk 更"现代"。ttk.Style().theme_use() 能查看/切换主题。常见主题有 'clam'、'alt'、'default'、'classic'(具体可用 theme_names() 查)。
# 第7章 例7.2:用 Style 查看并切换主题,做一个"换皮肤"按钮
import tkinter as tk
from tkinter import ttk
class App:
def __init__(self, root):
self.root = root
self.root.title("通讯录管理器")
# 创建样式管理器
self.style = ttk.Style()
print("当前可用主题:", self.style.theme_names())
print("当前主题:", self.style.theme_use())
self.label = ttk.Label(self.root, text="通讯录")
self.label.pack(padx=10, pady=10)
self.btn = ttk.Button(self.root, text="切换到 clam 主题", command=self.on_switch)
self.btn.pack(padx=10, pady=10)
def on_switch(self):
# 切换到 'clam' 主题,整个界面风格立刻变化
self.style.theme_use("clam")
print("已切换到主题:", self.style.theme_use())
if __name__ == "__main__":
root = tk.Tk()
App(root)
root.mainloop()逐行说明:
self.style = ttk.Style():创建一个"样式管理器"对象,所有换肤操作都通过它。self.style.theme_names():返回当前环境可用的主题列表(不同操作系统结果不同)。self.style.theme_use("clam"):把整套界面切换到 clam 主题。切换后所有 ttk 控件外观统一变化——这就是"换肤"的威力,一行搞定全局。style.configure 设置常态样式;style.map 设置"状态变化"时的样式(如鼠标悬停变红)。
# 第7章 例7.3:用 Style 自定义按钮颜色,并用 map 实现悬停变色
import tkinter as tk
from tkinter import ttk
class App:
def __init__(self, root):
self.root = root
self.root.title("通讯录管理器")
self.style = ttk.Style()
# ① configure:设置所有 TButton 的常态样式(背景、前景、字体)
self.style.configure(
"TButton",
background="#3a7afe",
foreground="white",
font=("微软雅黑", 12),
)
# ② map:当按钮处于 "active"(鼠标悬停/按下)状态时,前景变红
self.style.map(
"TButton",
foreground=[("active", "red")],
background=[("active", "#2a5fd0")],
)
self.btn = ttk.Button(self.root, text="悬停看看我", command=lambda: print("clicked"))
self.btn.pack(padx=20, pady=20)
if __name__ == "__main__":
root = tk.Tk()
App(root)
root.mainloop()逐行说明:
self.style.configure("TButton", ...):"TButton" 代表"所有按钮"。这里设了蓝底白字。注意 ttk 里背景选项叫 background(也可用 foreground 等),由 Style 统一管,而不是写在控件上。self.style.map("TButton", foreground=[("active", "red")]):map 描述"状态→样式"的映射。"active" 是按钮被悬停/激活的状态;("active", "red") 表示此时前景变红。运行后把鼠标移到按钮上,文字会变红——这就是"交互态样式"。本章 OOP 落点:把样式集中放在一个地方(这里放 App.__init__ 开头的 _setup_style 方法),避免样式散落。下面把"标题 + 添加按钮 + 输入框"用 ttk 重写。
# 第7章 例7.4:用 ttk 重写"标题+按钮+输入框",并把样式集中到 _setup_style
import tkinter as tk
from tkinter import ttk
class App:
def __init__(self, root):
self.root = root
self.root.title("通讯录管理器")
self._setup_style() # ① 先集中设好样式
self._build_widgets() # ② 再建界面
def _setup_style(self):
# 把所有样式集中在这里,界面代码就干净了
self.style = ttk.Style()
self.style.theme_use("clam")
self.style.configure("Title.TLabel", font=("微软雅黑", 16),
foreground="#3a7afe", padding=8)
self.style.configure("Action.TButton", foreground="white",
background="#3a7afe", padding=6)
def _build_widgets(self):
# 套用自定义样式名 "Title.TLabel"
self.title = ttk.Label(self.root, text="我的通讯录", style="Title.TLabel")
self.title.pack(pady=5)
self.name_var = tk.StringVar()
self.entry = ttk.Entry(self.root, textvariable=self.name_var, width=20)
self.entry.pack(pady=5)
# 套用自定义样式名 "Action.TButton"
self.add_btn = ttk.Button(
self.root, text="添加", style="Action.TButton", command=self.on_add
)
self.add_btn.pack(pady=5)
def on_add(self):
print("添加:", self.name_var.get())
if __name__ == "__main__":
root = tk.Tk()
App(root)
root.mainloop()逐行说明:
_setup_style():把所有 Style 配置收进这一个方法,样式不散落在界面各处。将来要换配色,只改这里。style="Title.TLabel" / style="Action.TButton":控件用 style= 指定套用哪个自定义样式。样式名以 .TLabel/.TButton 结尾,表示它"继承自"对应控件类型,再叠加我们的定制。ttk.Style() 的配置收进 App.__init__ 或一个独立的 class Theme: 里(如例 7.4 的 _setup_style)。好处是:配色、字体改一处即可全局生效,不会散落在十几个控件里难维护。"Title.TLabel" 不是魔法字符串,而是你在 Style 里注册过的一个样式对象。控件通过 style= 引用它,复用、统一都很容易。一句话:ttk 控件负责"干活",Style 负责"好看";把 Style 收进类的一个方法,界面从此整洁又统一。
fg/bg:写 ttk.Button(self.root, text="x", fg="red", bg="blue") 不会报错但完全无效——颜色被忽略。正确做法是用 ttk.Style().configure("TButton", foreground="red", background="blue")。tk.Button(土味灰)和 ttk.Button(主题风),界面会"一半老一半新"很别扭。本章起请统一优先用 ttk。style.map 的状态名写错无效:常用状态有 "active"(悬停/按下)、"disabled"、"pressed"。map 里写成 ("hover", ...) 是错的(tkinter 没有 hover 这个状态名,用 active)。.T类型 结尾:如 "My.TButton" 才有效;若写成 "MyButton"(没有 .TButton 后缀),tkinter 找不到基类样式,效果可能异常。theme_use 切换到不存在的主题会报错:先用 theme_names() 查清楚有哪些主题再切换。ttk.Style() 查看当前系统有哪些可用主题(打印 theme_names())。ttk.Label + ttk.Button),并用 Style.configure 给按钮设蓝底白字。style.map 让按钮在鼠标悬停时前景变红色。ttk 吗?提示:ttk 没有 Text,Text 仍用 tk.Text,其余 Label/Button/Entry 可 ttk 化),并用一个 Style 统一主题,观察界面风格变化。下节预告:第 8 章我们将认识 Frame 容器控件——把相关控件"装进一个框"做分组,再结合 ttk 做出更具结构感的通讯录界面。