KBEngine 内置 Python 的 Tk 图形界面编程指南:tkinter 体系、核心 API 与源码实现

原创2026-10-18 23:54:241,984 阅读
文章标签:游戏开发后端

KBEngine 内置 Python 的 Tk 图形界面编程指南:tkinter 体系、核心 API 与源码实现

导读

KBEngine 在 kbe/src/lib/python 中随附了一套完整的 Python 标准库源码,其中包含 tkinter(Tk 接口)全套文档与实现。本指南以该文档区入口 tk.rst 为核心骨架,系统讲解 Tk/Tcl 与 Python 的对接方式、tkinter 包的分层架构、Hello World 起步、控件布局与选项设置、事件绑定、ttk 主题控件、tix 扩展控件及配套模块。读完本文,你可以利用这套随引擎分发的 Python 环境,快速编写跨平台(Windows / Unix / macOS)的本地 GUI 工具,并能从 Lib/tkinter/ 与 Modules/_tkinter.c 的源码层面理解其工作原理。


一、Tk 图形界面文档区概览:tkinter 是什么

在 KBEngine 内置 Python 的库文档中,Graphical User Interfaces with Tk 是整个 GUI 章节的入口页。它开篇即给出定位:

Tk/Tcl 长久以来都是 Python 的组成部分。它提供了健壮且跨平台的窗口工具包,Python 程序员可以通过 tkinter 包及其扩展模块 tkinter.tix、tkinter.ttk 来使用它。

几个关键事实(均可在 tk.rst 中找到原文依据):

  • tkinter 是 Tcl/Tk 之上的一层薄型面向对象封装。使用 tkinter 不必编写 Tcl 代码,但需要查阅 Tk 文档(偶尔需要 Tcl 文档);tkinter 是一组把 Tk 控件实现为 Python 类的包装器。
  • 底层 _tkinter 模块提供线程安全机制,允许 Python 与 Tcl 相互交互;它属于内部模块,应用开发者不应直接使用。
  • 优点:tkinter 速度快,而且通常随 Python 一并分发;其官方标准文档较弱,但外部参考资料充足;它一度因外观老旧而闻名,Tk 8.5 起外观已大幅改善。
  • 该章节的文档骨架(toctree) 由六份子文档组成:tkinter.rst、tkinter.ttk.rst、tkinter.tix.rst、tkinter.scrolledtext.rst、idle.rst、othergui.rst。

在仓库中,这些文档与实现一一对应:

文档(仓库相对路径) 对应源码 主题
tkinter.rst Lib/tkinter/init.py、Modules/_tkinter.c Tk 核心接口
tkinter.ttk.rst Lib/tkinter/ttk.py 主题控件集
tkinter.tix.rst Lib/tkinter/tix.py Tix 扩展控件(已弃用)
tkinter.scrolledtext.rst Lib/tkinter/scrolledtext.py 带滚动条文本控件
idle.rst Lib/idlelib/ IDLE 集成开发环境
othergui.rst — 其他 GUI 工具包概览

二、检查 tkinter 是否可用:python -m tkinter

tkinter 的标准用法文档(tkinter.rst)给出了一个最直接的安装自检命令:

python -m tkinter

从命令行运行该命令后,应当会打开一个演示简单 Tk 界面的窗口,这可以:

  1. 确认 tkinter 已正确安装;
  2. 显示当前安装的 Tcl/Tk 版本号,以便你查阅与该版本匹配的 Tcl/Tk 文档。

在 KBEngine 仓库中,这一能力的实现与文档是成套提供的:tkinter 包的 Python 源码位于 Lib/tkinter/,其中既有 __init__.py(tkinter 主包)也有 __main__.py(支持 python -m tkinter 的入口);C 扩展 _tkinter 的源码位于 Modules/_tkinter.c。也就是说,凡是运行在随 KBEngine 分发的这套 Python 上的脚本,都可以直接导入 tkinter 编写本地 GUI 工具。

Tkinter 的模块家族

多数时候只需要导入 tkinter 本身,但围绕它还有一批配套 Python 模块(tkinter.rst 中列出,源码均位于 Lib/tkinter/):

模块 作用
tkinter.constants 常量定义;导入 tkinter 会自动导入它
tkinter.scrolledtext 内置垂直滚动条的文本控件
tkinter.colorchooser 让用户选择颜色的对话框
tkinter.commondialog 其余对话框模块的基类
tkinter.filedialog 打开/保存文件的通用对话框
tkinter.font 字体相关的工具函数
tkinter.messagebox 访问标准 Tk 对话框
tkinter.simpledialog 基础对话框与便捷函数
tkinter.dnd 拖放支持(实验性,未来可能被 Tk DND 取代)
turtle 在 Tk 窗口中绘制海龟图形

Tkinter.constants 是其中最重要的一个:导入 tkinter 会自动导入 tkinter.constants,因此通常只需要一条简单的导入语句即可:

import tkinter

或者更常见的写法:

from tkinter import *

三、核心类:Tk 与 Tcl

tkinter 文档定义了两个顶层工厂:

class Tk(screenName=None, baseName=None, className='Tk', useTk=1)
  • Tk 类通常不带参数实例化,它创建 Tk 的一个顶层控件,通常是应用的主窗口;每个实例都关联一个独立的 Tcl 解释器。
  • 一个应用里 Tk 类一般只实例化一次;即使不显式实例化,系统在实例化其他任何控件类时也会自动创建一个。
class Tcl(screenName=None, baseName=None, className='Tk', useTk=0)
  • Tcl 工厂创建的对象与 Tk 类似,但不初始化 Tk 子系统。这在需要驱动 Tcl 解释器、又不想创建多余顶层窗口的环境中(例如没有 X 服务器的 Unix/Linux 系统)最有用。
  • 之后可以通过调用其 loadtk() 方法来创建 Toplevel 窗口并初始化 Tk 子系统。

四、Tk 与 Tkinter 的分层架构

tkinter.rst 用一张自顶向下的调用链清晰地说明了 Tk 与 Tkinter 的关系:

你的应用(Python)
    │  调用 tkinter
tkinter(Python 包)
    │  解析命令与参数,转换为“像来自 Tk 脚本”的形式
_tkinter(C 扩展模块,注意下划线前缀)
    │  把命令和参数传给 C 函数
Tk Widgets(C 与 Tcl)
    │  Tk 库的 C 函数;Tcl 部分负责绑定默认行为(在导入 tkinter 时执行一次)
Tk(C)
    │  最终映射到
Xlib(C)
        在屏幕上绘制图形

在仓库源码中可以看到这条链的落点:_tkinter 扩展在 Modules/_tkinter.c 中通过 Tcl_CreateInterp() 创建解释器(源码第 622 行附近),并将 Tk 命令封装成 Python 可调用的对象。

Tk 命令速读:classCommand newPathname options

要理解 Tkinter,需要能读懂 Tk 脚本。Tk 脚本就是 Tcl 程序——本质上是用空格分隔的 token 列表。创建控件时命令形式固定为:

classCommand newPathname options
  • classCommand:表示要创建哪种控件(按钮、标签、菜单……)。
  • newPathname:控件的唯一名称。Tk 中所有名称必须唯一,控件用类似文件系统的 pathnames 命名:顶层根控件叫 .(句点),子控件用更多句点分隔,例如 .myApp.controlPanel.okButton。
  • options:以 - 开头的标志加值的列表,配置控件外观与部分行为;多词值需加引号。

示例:

button   .fred   -fg red -text "hi there"

创建之后,控件的 pathname 就成为一个新的命令(widget command)。在 Tk 中对控件执行动作的写法是:

.fred someAction someOptions

而动作的合法取值取决于控件类别,例如 .fred disable 对按钮有效(按钮变灰),对标签无效(Tk 不支持禁用标签)。

把基本 Tk 映射到 Tkinter(tkinter-basic-mapping)

文档给出了一组等价映射表,这是把 Tk 经验迁移到 Tkinter 的关键:

Tk Tkinter
button .fred fred = Button()
button .panel.fred fred = Button(panel)(master 显式指定)
button .fred -fg red fred = Button(panel, fg="red")
.fred configure -fg red fred["fg"] = red 或 fred.config(fg="red")
.fred invoke fred.invoke()
pack .fred -side left fred.pack(side="left")

要点:Tk 中的 class command 对应 Tkinter 的类构造器;Tk 隐式的 master 关系在 Tkinter 中通过构造参数显式指定;Tk 的 -opt val 列表在 Tkinter 中变成构造器/config 的关键字参数或字典式索引;所有 Tkinter 控件都继承自 Packer,因此都拥有 pack 系列方法。


五、Hello World:最小可运行程序

tkinter.rst 给出的经典示例,可直接在 KBEngine 内置 Python 环境中运行:

import tkinter as tk

class Application(tk.Frame):
    def __init__(self, master=None):
        super().__init__(master)
        self.master = master
        self.pack()
        self.create_widgets()

    def create_widgets(self):
        self.hi_there = tk.Button(self)
        self.hi_there["text"] = "Hello World\n(click me)"
        self.hi_there["command"] = self.say_hi
        self.hi_there.pack(side="top")

        self.quit = tk.Button(self, text="QUIT", fg="red",
                              command=self.master.destroy)
        self.quit.pack(side="bottom")

    def say_hi(self):
        print("hi there, everyone!")

root = tk.Tk()
app = Application(master=root)
app.mainloop()

这段代码演示了四个基本动作:子类化 tk.Frame 组织应用结构、用 pack() 进行布局、用字典式索引 widget["text"] 设置选项、用 command 回调把按钮点击连接到 Python 方法。

类层次速览

文档特别提醒,Tkinter 的类层次看起来很复杂,但实际使用时程序员几乎总是引用层次最底部的类。三条组织性规则:

  • 某些类只是把相关函数归入同一命名空间,不应当被独立实例化;
  • Tk 类在一个应用中只实例化一次,且通常无需显式创建;
  • Widget 类不该被实例化,它只用于被子类化产生"真正的控件"(相当于 C++ 的抽象类)。

六、选项设置的三种方式与 config() 返回值

控件选项控制颜色、边框宽度等外观与行为。设置选项有三种方式(tkinter.rst):

方式一:创建对象时用关键字参数

fred = Button(self, fg="red", bg="blue")

方式二:创建后按字典索引赋值

fred["fg"] = "red"
fred["bg"] = "blue"

方式三:用 config() 方法批量更新

fred.config(fg="red", bg="blue")

读懂 config() 的返回值:5 元组

不带参数调用 config()(或调用 keys())会返回一个字典,键是选项名(如 'relief'),值通常是 5 元组:

索引 含义 示例
0 选项名 'relief'
1 数据库查找用的选项名 'relief'
2 数据库查找用的选项类 'Relief'
3 默认值 'raised'
4 当前值 'groove'
>>> print(fred.config())
{'relief': ('relief', 'relief', 'Relief', 'raised', 'groove')}

注意:某些选项(如 bg)是长名称的同义词(bg 是 background 的简写),对同义词调用 config() 返回的是 2 元组 ('bg', 'background'),而非 5 元组。Tk 手册对每个控件都区分 "STANDARD OPTIONS"(通用选项,见 options(3) 手册页)与 "WIDGET SPECIFIC OPTIONS"(该控件特有选项),例如按钮有 command 选项而标签没有。


七、布局:Packer 几何管理器

Packer 是 Tk 的几何管理机制之一,负责指定控件在其容器(master)内的相对位置。与不常用的 placer 相比,packer 接受的是定性关系描述("上方"、"左边"、"填充"等),并自动计算出精确坐标。

关键规则:

  • master 的大小由其内部 slave 控件的大小决定;
  • 可以把控件 pack 进 Frame,再把 Frame 层层嵌套,实现想要的布局;
  • 布局会随配置的增量变化动态调整;
  • 控件必须经过几何管理器(如 pack())才会显示——忘记调用几何管理器是新手最常见的错误:控件创建了,但界面上什么都没出现。

pack() 可用关键字选项控制控件在容器内的位置与窗口缩放时的行为:

fred.pack()            # 默认 side = "top"
fred.pack(side="left")
fred.pack(expand=1)

Packer 选项速查

选项 合法值/含义
anchor 锚点类型,指定每个 slave 在其 parcel 中的放置位置
expand 布尔值,0 或 1
fill 'x'、'y'、'both'、'none'
ipadx / ipady 距离,slave 控件内部每侧的内边距
padx / pady 距离,slave 控件外部每侧的外边距
side 'left'、'right'、'top'、'bottom'

八、变量耦合:StringVar / IntVar / DoubleVar / BooleanVar

某些控件的当前值(如文本输入框)可以通过特殊选项直接连接到应用变量:variable、textvariable、onvalue、offvalue、value。这种连接是双向的:变量一改变,绑定它的控件立即更新。

注意实现限制:tkinter 的当前实现不允许把任意 Python 变量交给控件,只有 tkinter 中定义的 Variable 的子类可以。官方预定义了四个实用子类:StringVar、IntVar、DoubleVar、BooleanVar。读取用 get(),修改用 set(),遵循这一协议控件就会始终跟随变量。

文档示例(输入框与变量联动,回车时打印内容):

class App(Frame):
    def __init__(self, master=None):
        super().__init__(master)
        self.pack()

        self.entrythingy = Entry()
        self.entrythingy.pack()

        # 应用变量
        self.contents = StringVar()
        self.contents.set("this is a variable")
        # 让 entry 控件监视该变量
        self.entrythingy["textvariable"] = self.contents

        # 用户按回车时打印变量值
        self.entrythingy.bind('<Key-Return>',
                              self.print_contents)

    def print_contents(self, event):
        print("hi. contents of entry is now ---->",
              self.contents.get())

九、窗口管理器 Wm 类

Tk 提供 wm 命令与窗口管理器交互,控制标题、位置、图标位图等。在 tkinter 中这些命令被实现为 Wm 类的方法,Toplevel 控件继承自 Wm,可直接调用这些方法。

要获取任意控件所在的顶层窗口,可以调用 _root() 方法(下划线开头表明它是实现细节,而非 Tk 功能接口;如果控件直接 pack 在 master 里,通常直接引用 master 即可,但 pack 在 Frame 内时 master 并不代表顶层窗口)。

典型用法:

import tkinter as tk

class App(tk.Frame):
    def __init__(self, master=None):
        super().__init__(master)
        self.pack()

myapp = App()

myapp.master.title("My Do-Nothing Application")
myapp.master.maxsize(1000, 400)

myapp.mainloop()

十、Tk 选项数据类型速查

Tkinter 的选项值有固定的一套数据类型(tkinter.rst 中 "Tk Option Data Types" 一节),常用类型如下:

类型 说明与合法值
anchor 罗盘点:"n"、"ne"、"e"、"se"、"s"、"sw"、"w"、"nw" 及 "center"
bitmap 8 个内建命名位图:'error'、'gray25'、'gray50'、'hourglass'、'info'、'questhead'、'question'、'warning';X bitmap 文件需完整路径并以 @ 开头,如 "@/usr/contrib/bitmap/gumby.bit"
boolean 整数 0/1 或字符串 "yes" / "no"
callback 不带参数的 Python 函数,如 fred["command"] = print_it
color X 颜色名,或 "#RGB"、"#RRGGBB"、"#RRRGGGBBB"、"#RRRRGGGGBBBB" 形式的 RGB 十六进制串
cursor cursorfont.h 中的 X 光标名去掉 XC_ 前缀,如 "hand2";也可自定位图与掩码文件
distance 像素数字,或带单位后缀的字符串:c(厘米)、i(英寸)、m(毫米)、p(印刷点),如 "3.5i"
font Tk 列表式字体名,如 {courier 10 bold};正数尺寸单位为点,负数单位为像素
geometry widthxheight 字符串,如 fred["geometry"] = "200x100"
justify "left"、"center"、"right"、"fill"
region 四个空格分隔的距离,如 "2 3 4 5"、"3i 2i 4.5i 2i"
relief "raised"、"sunken"、"flat"、"groove"、"ridge"
scrollcommand 几乎总是某个滚动条的 set 方法,也可以是任意接收单参数的控件方法
wrap "none"、"char" 或 "word"

十一、事件绑定与 bind() 方法

控件命令中的 bind 方法用于监听特定事件并在事件发生时触发回调,其签名如下:

def bind(self, sequence, func, add=''):

三个参数:

  • sequence:字符串,表示目标事件类型(如 "<Enter>");
  • func:接收一个参数的 Python 函数,事件发生时被调用,参数是一个 Event 实例(这类函数通常叫 callbacks);
  • add:可选,'' 或 '+'。传空串表示本次绑定替换该事件已有的绑定;传 '+' 表示把函数追加到该事件类型的绑定列表。

示例:

def turn_red(self, event):
    event.widget["activeforeground"] = "red"

self.button.bind("<Enter>", self.turn_red)

回调中通过 event.widget 访问捕获到 X 事件的控件。事件字段与 Tk 记号 %x 的对应关系(查阅 Tk 手册时非常有用):

Tk Tkinter 事件字段 Tk Tkinter 事件字段
%f focus %A char
%h height %E send_event
%k keycode %K keysym
%s state %N keysym_num
%t time %T type
%w width %W widget
%x x %X x_root
%y y %Y y_root

十二、index 参数:Entry / Text / Menu 中的位置引用

许多控件需要 "index" 参数来指向特定位置——Text 控件中的位置、Entry 控件中的字符、Menu 控件中的菜单项:

Entry 控件索引:Entry 有指向文本中字符位置的选项,可用 tkinter 函数访问这些特殊点。

Text 控件索引:索引记号非常丰富,最详细的说明见 Tk 手册页。

Menu 索引(menu.invoke()、menu.entryconfig() 等)可传入:

  • 整数:条目在菜单中的数字位置,从顶部开始、从 0 计数;
  • 字符串 "active":当前位于光标下的菜单位置;
  • 字符串 "last":最后一个菜单项;
  • @ 前缀整数,如 @6:菜单坐标系中的 y 像素坐标;
  • 字符串 "none":表示没有任何菜单项,常用于 menu.activate() 停用全部条目;
  • 文本字符串:从菜单顶部向下与条目 label 做模式匹配;该类型最后才被考虑,因此 label 恰好为 last、active、none 的条目可能被解释为上文中的字面量。

十三、图像:BitmapImage 与 PhotoImage

不同格式的图像通过 tkinter.Image 的对应子类创建:

  • BitmapImage:XBM 格式;
  • PhotoImage:PGM、PPM、GIF 和 PNG 格式(PNG 自 Tk 8.6 起支持)。

两种图像都通过 file 或 data 选项创建。图像对象可被用在任何支持 image 选项的控件上(标签、按钮、菜单等)。注意引用管理:Tk 不会保留对图像的引用——当最后一个 Python 引用被删除时,图像数据随之删除,界面上原本使用该图像的地方会显示空框。因此图像对象需要在 Python 侧保持引用(例如保存为实例属性)。


十四、文件句柄回调:createfilehandler

Tk 允许注册/注销一个回调函数,在文件描述符可进行 I/O 时由 Tk mainloop 调用;每个文件描述符只能注册一个处理器。示例(tkinter.rst 中 tkinter-file-handlers 一节):

import tkinter
widget = tkinter.Tk()
mask = tkinter.READABLE | tkinter.WRITABLE
widget.tk.createfilehandler(file, mask, callback)
# ...
widget.tk.deletefilehandler(file)

方法语义:

  • Widget.tk.createfilehandler(file, mask, func):注册回调。file 可以是带 fileno() 方法的对象(文件或 socket 对象),也可以是整数文件描述符;mask 是下面三个常量的 OR 组合;回调以 callback(file, mask) 形式被调用。
  • Widget.tk.deletefilehandler(file):注销文件句柄回调。

常量 READABLE、WRITABLE、EXCEPTION 用于构成 mask。该特性在 Windows 上不可用。

一个实践警告:由于无法预知可读字节数,不要使用 io.BufferedIOBase / io.TextIOBase 的 read() 或 readline()(它们坚持读取预定数量的字节);对 socket 用 recv()/recvfrom() 没问题,对其他文件应使用原始读取或 os.read(file.fileno(), maxbytecount)。

在仓库源码中,该方法对应 Modules/_tkinter.c 中的 _tkinter_tkapp_createfilehandler_impl(源码第 2624 行附近),印证了文档所述的注册语义。


十五、配套模块详解:ScrolledText 与更多

tkinter.scrolledtext

tkinter.scrolledtext.rst 介绍 ScrolledText 类:一个"自动配置正确滚动行为"的基础文本控件——内置垂直滚动条,比手动组合 Text + Scrollbar 简单得多。构造函数与 tkinter.Text 相同;文本控件与滚动条被打包在一个 Frame 中,因此 Grid/Pack 几何管理器的方法直接从该 Frame 对象获得。

需要更精细控制时,可访问两个公开属性:

属性 说明
ScrolledText.frame 包裹文本与滚动条控件的 Frame
ScrolledText.vbar 滚动条控件

源码位于 Lib/tkinter/scrolledtext.py。

tix:Tk 扩展控件(已弃用)

tkinter.tix.rst 说明 tkinter.tix(Tk Interface Extension)提供了标准 Tk 所缺的常用控件:HList、ComboBox、Control(即 SpinBox)以及一批可滚动控件,总量超过 40 个,另有 NoteBook、FileEntry、PanedWindow 等通用控件。

重要提示:文档明确标注该扩展自 Python 3.6 起已弃用——"This Tk extension is unmaintained and should not be used in new code. Use tkinter.ttk instead."(该 Tk 扩展已无人维护,新代码不应使用,请改用 tkinter.ttk)。

若确需使用,导入方式为:

from tkinter import tix
from tkinter.constants import *
root = tix.Tk()

tix.Tk() 是代表应用主窗口的 Toplevel 控件,带关联 Tcl 解释器;tix 模块子类化 tkinter 中的类并导入后者,因此只需导入 tix 一个模块。使用前提是 Tix 控件已随 Tk 安装,可用下面代码验证:

from tkinter import tix
root = tix.Tk()
root.tk.eval('package require Tix')

十六、ttk:Tk 8.5 主题控件集

tkinter.ttk 提供 Tk 8.5 引入的主题控件集(tkinter.ttk.rst)。若 Python 未针对 Tk 8.5 编译,只要安装了 Tile 也能访问该模块;而 Tk 8.5 原生支持还额外带来 X11 下的抗锯齿字体渲染和窗口透明(后者需要 X11 合成窗口管理器)。

核心思想:尽可能把实现控件行为的代码与实现外观的代码分离。源码见 Lib/tkinter/ttk.py。

使用与导入

from tkinter import ttk

若要覆盖基础 Tk 控件,导入顺序应为:

from tkinter import *
from tkinter.ttk import *

这会让多个 ttk 控件(Button、Checkbutton、Entry、Frame、Label、LabelFrame、Menubutton、PanedWindow、Radiobutton、Scale、Scrollbar)自动替换同名 Tk 控件,获得跨平台更一致的外观。注意兼容性差异:fg、bg 等样式相关选项在 Ttk 控件中不再存在,应改用 ttk.Style 类实现样式效果。

18 个 ttk 控件

类别 控件
12 个原 tkinter 已有 Button、Checkbutton、Entry、Frame、Label、LabelFrame、Menubutton、PanedWindow、Radiobutton、Scale、Scrollbar、Spinbox
6 个新增 Combobox、Notebook、Progressbar、Separator、Sizegrip、Treeview

全部都是 Widget 的子类。

样式编码对比

Tk 写法:

l1 = tkinter.Label(text="Test", fg="black", bg="white")
l2 = tkinter.Label(text="Test", fg="black", bg="white")

Ttk 写法(用命名样式 "BW.TLabel" 复用外观):

style = ttk.Style()
style.configure("BW.TLabel", foreground="black", background="white")

l1 = ttk.Label(text="Test", style="BW.TLabel")
l2 = ttk.Label(text="Test", style="BW.TLabel")

标准选项与滚动选项

所有 ttk 控件接受以下标准选项:

选项 说明
class 指定窗口类,用于查询选项数据库、决定默认 bindtags、选择默认布局与样式;只读,仅创建时可用
cursor 鼠标光标;默认空串表示继承父控件光标
takefocus 键盘遍历时是否接受焦点:0 跳过、1 接收、空串由遍历脚本决定
style 指定自定义控件样式

受滚动条控制的控件还支持 xscrollcommand / yscrollcommand:视图变化时控件会基于该选项生成 Tcl 命令,通常把它设为某个 Scrollbar 的 set 方法,滚动条即自动同步。

控件状态标志

ttk 控件的状态是独立状态位的位图,常用标志:

标志 含义
active 鼠标悬停且按下会触发动作
disabled 程序控制下禁用
focus 拥有键盘焦点
pressed 正在被按下
selected Checkbutton/Radiobutton 的"开/真/当前"状态
background 窗口在后台(Windows/Mac 概念)
readonly 不允许用户修改
alternate 控件专属的备选显示格式
invalid 控件值无效

状态规格(statespec)是状态名序列,可用 ! 前缀表示该位关闭。

ttk.Widget 的核心方法:

  • identify(x, y):返回 (x, y) 处元素名,不在任何元素内则返回空串;
  • instate(statespec, callback=None, *args, **kw):无回调时按 statespec 匹配返回 True/False,有回调时匹配则调用;
  • state(statespec=None):传参则按规格设置状态并返回变化的标志,不传则返回当前启用的标志。

十七、ttk 主要控件速查

Combobox(下拉选择框)

ttk.Combobox 把文本框与弹出列表组合,是 Entry 的子类。专用选项:

选项 说明
exportselection 布尔值,选择是否关联 Window Manager 选择
justify "left" / "center" / "right"
height 弹出列表的行数
postcommand 显示列表前立即调用的脚本,可指定要显示的取值
state "normal"(可编辑)/ "readonly"(只能从下拉选)/ "disabled"(不可交互)
textvariable 与控件值双向链接的变量名(见 tkinter.StringVar)
values 下拉列表显示的值列表
width 输入窗口期望宽度(按控件字体的平均字符数)

用户从列表选择时会生成 <<ComboboxSelected>> 虚拟事件。方法:current(newindex=None)(设置/查询当前项索引,不在列表中返回 -1)、get()、set(value)。

Spinbox(微调框)

ttk.Spinbox 是带增减箭头的 Entry,可用于数字或字符串值列表。专用选项:

选项 说明
from 最小值(作参数时必须写 from_,因为 from 是 Python 关键字)
to 最大值
increment 增减步长,默认 1.0
values 若指定,箭头在序列中循环而不是增减数字
wrap 布尔值,True 时在 to/from 之间循环
format 数字格式,形如 "%W.Pf"(W 为宽度、P 为精度)
command 按箭头时被调用的无参 Python 可调用对象

按 <Up>/<Down> 分别生成 <<Increment>> / <<Decrement>> 虚拟事件。方法:get()、set(value)。

Notebook(选项卡)

ttk.Notebook 管理一组窗口并一次只显示一个,每个子窗口关联一个可点击切换的标签页。

  • 选项:height/width(pane 区域尺寸)、padding(四周留白,最多 4 个长度值,省略时 bottom 取 top、right 取 left、top 取 left)。
  • Tab 选项:state("normal"/"disabled"/"hidden")、sticky("n"/"s"/"e"/"w" 组合)、padding、text、image、compound、underline(下划线字符索引,配合 enable_traversal() 做助记键激活)。
  • tab_id 的合法形式:0 到页数间的整数、子窗口名、"@x,y" 位置规格、字面量 "current"、字面量 "end"(仅 index() 返回页数)。
  • 切换标签后生成 <<NotebookTabChanged>> 虚拟事件。
  • 常用方法:add(child, **kw)、forget(tab_id)、hide(tab_id)、identify(x, y)、index(tab_id)、insert(pos, child, **kw)、select(tab_id=None)、tab(tab_id, option=None, **kw)、tabs()、enable_traversal()。
  • enable_traversal() 启用键盘遍历:Control-Tab 切到下一页、Shift-Control-Tab 切到上一页、Alt-K(K 为下划线助记字符)直达对应页。

Progressbar(进度条)

ttk.Progressbar 显示长耗时操作的状态,两种模式:

  • determinate(确定):显示已完成量占总工作量的比例;
  • indeterminate(不确定):动画显示"正在推进"。

选项:orient("horizontal"/"vertical")、length、mode、maximum(默认 100)、value、variable(链接的变量名)、phase(只读,供主题做动画)。方法:start(interval=None)(默认每 50 毫秒 step 一次)、step(amount=None)(默认 +1.0)、stop()。

Separator 与 Sizegrip

  • Separator:水平/垂直分隔条,只有 orient 一个专用选项,无专用方法。
  • Sizegrip(grow box):拖拽调整所在 toplevel 窗口大小,无专用选项/方法。注意事项:macOS 顶层窗口默认自带内建 grip;若窗口位置相对屏幕右/下边缘指定,Sizegrip 无法缩放窗口;只支持"东南向"缩放。

Treeview(树形表格)

ttk.Treeview 显示层级化条目集合:每条目有文本标签、可选图像、可选数据值列表;数据值依次显示在树标签之后的列中。可用 displaycolumns 控制数据列显示顺序与列标题。条目以唯一名称标识(不指定则自动生成);存在一个特殊根条目 {},自身不显示,其子条目出现在层级顶层。每条目还可带 tags,用于绑定事件和控制外观。

主要选项:columns、displaycolumns(或 "#all")、height(可见行数)、padding、selectmode("extended" 默认可多选 / "browse" 单选 / "none" 不改选择)、show("tree"/"headings",默认全部;列 #0 恒为树列)。

条目选项:text、image、values(与列数对应,多则忽略、少则补空)、open、tags。标签选项:foreground、background、font、image。

列标识符三种形式:columns 列表中的符号名、整数 n(第 n 个数据列)、#n 字符串(第 n 个显示列)。

虚拟事件:<<TreeviewSelect>>(选择变化)、<<TreeviewOpen>>(焦点条目展开前)、<<TreeviewClose>>(收起后)。

常用方法(节选):

  • bbox(item, column=None)、get_children(item=None)、set_children(item, *newchildren);
  • column(column, option=None, **kw):列选项含 id(只读)、anchor、minwidth、stretch、width,树列用 column("#0", ...);
  • heading(column, option=None, **kw):含 text、image、anchor、command;
  • identify(component, x, y)、identify_row(y)、identify_column(x)、identify_region(x, y)(返回 heading/separator/tree/cell,Tk 8.6 起)、identify_element(x, y)(Tk 8.6 起);
  • insert(parent, index, iid=None, **kw)、item(item, option=None, **kw)、move(item, parent, index)、reattach(item, parent, index)(move 别名)、next/prev/parent/see;
  • selection() 系列:selection_set、selection_add、selection_remove、selection_toggle(注意:旧式 selection() 修改选择已在 3.6 弃用、3.8 移除);
  • set(item, column=None, value=None)、tag_bind、tag_configure、tag_has(Tk 8.6 起)、xview/yview。

十八、Ttk 样式体系:Style 类与 Layouts

每个 ttk 控件都分配一个样式(style),它定义组成控件的一组元素、元素排列方式以及元素选项的动态/默认设置。默认样式名与控件类名相同,可用 style 选项覆盖;不确定类名时用 Misc.winfo_class()(即 somewidget.winfo_class())查询。

Style 类操作样式数据库,核心方法:

configure(style, query_opt=None, **kw):查询或设置样式的默认选项值。例如把默认按钮改为扁平、带内边距、灰底:

from tkinter import ttk
import tkinter

root = tkinter.Tk()

ttk.Style().configure("TButton", padding=6, relief="flat",
   background="#ccc")

btn = ttk.Button(text="Sample")
btn.pack()

root.mainloop()

map(style, query_opt=None, **kw):查询或设置样式的动态值。每个键是选项,值一般是包含 statespec 的列表/元组。statespec 的顺序有讲究:

import tkinter
from tkinter import ttk

root = tkinter.Tk()

style = ttk.Style()
style.map("C.TButton",
    foreground=[('pressed', 'red'), ('active', 'blue')],
    background=[('pressed', '!disabled', 'black'), ('active', 'white')]
    )

colored_btn = ttk.Button(text="Test", style="C.TButton").pack()

root.mainloop()

如果把 foreground 的顺序换成 [('active', 'blue'), ('pressed', 'red')],结果会变成 active/pressed 状态下前景色均为蓝色——因此 (states, value) 序列的顺序决定匹配优先级。

lookup(style, option, state=None, default=None):查询样式中的选项值。例如:

from tkinter import ttk

print(ttk.Style().lookup("TButton", "font"))

layout(style, layoutspec=None):定义/查询样式布局。布局规格是序列(非字符串),每项为 (layoutname, layout) 元组。文档示例(重建 TMenubutton 布局):

from tkinter import ttk
import tkinter

root = tkinter.Tk()

style = ttk.Style()
style.layout("TMenubutton", [
   ("Menubutton.background", None),
   ("Menubutton.button", {"children":
       [("Menubutton.focus", {"children":
           [("Menubutton.padding", {"children":
               [("Menubutton.label", {"side": "left", "expand": 1})]
           })]
       })]
   }),
])

mbtn = ttk.Menubutton(text='Text')
mbtn.pack()
root.mainloop()

element_create(elementname, etype, *args, **kw):在当前主题创建元素,etype 为 "image"、"from" 或 "vsapi"(后者仅 Tk 8.6a 的 Windows XP/Vista 可用)。"image" 时 args 为默认图像名加 statespec/value 对,kw 可带 border(四边整数 padding)、height、padding、sticky("n"/"s"/"w"/"e" 组合)、width;"from" 时克隆已有元素。

主题管理:theme_create(themename, parent=None, settings=None)、theme_settings(themename, settings)(临时切换主题应用 settings 后恢复)、theme_names()、theme_use(themename=None)(设置/查询当前主题,切换时刷新所有控件并发出 <<ThemeChanged>> 事件)。

theme_settings 示例——微调默认主题的 Combobox:

from tkinter import ttk
import tkinter

root = tkinter.Tk()

style = ttk.Style()
style.theme_settings("default", {
   "TCombobox": {
       "configure": {"padding": 5},
       "map": {
           "background": [("active", "green2"),
                          ("!disabled", "green4")],
           "fieldbackground": [("!disabled", "green3")],
           "foreground": [("focus", "OliveDrab1"),
                          ("!disabled", "OliveDrab2")]
       }
   }
})

combo = ttk.Combobox().pack()

root.mainloop()

Layouts 布局机制:布局可为 None,或是指定元素排布方式的 dict;它使用 pack 几何管理器的简化版:给定初始 cavity,每个元素被分配一个 parcel。合法键:side(top/right/bottom/left,省略则占满整个 cavity)、sticky(元素在其 parcel 内的位置)、unit(0/1,为 1 时元素及其后代被当作单元素处理,用于带手柄的滚动条滑块等)、children(子元素列表,每项为 (layoutname, layout) 元组)。


十九、IDLE:基于 tkinter 的 Python IDE

tkinter.rst 所在章节的 toctree 还包含 idle.rst,介绍 IDLE——Python 自带的集成开发与学习环境。它与 tkinter 的关系是实现级的:IDLE 用 100% 纯 Python 编写,基于 tkinter GUI 工具包;跨平台(Windows、Unix、macOS 行为基本一致)。源码位于 Lib/idlelib/。

主要特性:

  • Python shell 窗口(交互式解释器),代码输入、输出、错误信息均带着色;
  • 多窗口文本编辑器:多级撤销、Python 着色、智能缩进、call tips、自动补全等;
  • 任意窗口内搜索、编辑器内替换、多文件搜索(grep);
  • 支持持久断点、单步、查看全局/局部命名空间的调试器;
  • 配置、浏览器与其他对话框。

IDLE 有两类主窗口:Shell 窗口与 Editor 窗口(可同时打开多个编辑器窗口)。输出窗口(如 "Edit => Find in Files")是编辑器窗口的子类型。


二十、其他 GUI 工具包横向概览

othergui.rst 罗列了 Python 可用的其他跨平台 GUI 工具包,供对比选型:

工具包 说明
PyGObject 为基于 GObject 的 C 库提供内省绑定,其中之一是 GTK+ 3 控件集;控件数量远多于 Tkinter
PyGTK 较老版本 GTK+ 2 的绑定,提供略高于 C 层的面向对象接口,另有 GNOME 绑定
PyQt 基于 sip 封装 Qt 工具包(Unix/Windows/macOS 可用的 C++ GUI 框架)
PySide 较新的 Qt 绑定,许可证对比 PyQt 对非开源应用更友好
wxPython 基于 wxWidgets C++ 工具包,尽量使用各平台原生控件,实现 Windows/macOS/Unix 的原生外观

文档指出:PyGTK、PyQt、wxPython 的外观与控件数量都胜过 Tkinter;更完整的清单见 Python Wiki 的 "GUI Programming" 页。


二十一、仓库中的实现证据:随附源码与扩展模块

以上内容并非空谈——本仓库中 tkinter 的完整实现与文档是一一对应的:

  • Python 层实现:Lib/tkinter/ 目录下有 __init__.py、constants.py、scrolledtext.py、colorchooser.py、commondialog.py、dialog.py、dnd.py、filedialog.py、font.py、messagebox.py、simpledialog.py、tix.py、ttk.py 以及 __main__.py 入口,文档中描述的每一个控件与选项都能在对应源码中找到实现。
  • C 扩展层:Modules/_tkinter.c 是 _tkinter 扩展的源码,内含 Tcl_CreateInterp()(第 622 行附近创建 Tcl 解释器)与 _tkinter_tkapp_createfilehandler_impl(第 2624 行附近实现文件句柄回调注册)等关键逻辑,印证了文档所述"线程安全机制允许 Python 与 Tcl 交互"以及"内部模块不应被直接使用"的定位;配套的 Modules/tkinter.h 与 Modules/tkappinit.c 提供初始化支持。
  • 测试代码:Lib/tkinter/test/ 存放 tkinter 的测试套件,可帮助你在修改/阅读源码时验证行为。

对使用 KBEngine 内置 Python 编写运维或开发辅助工具的开发者而言,这意味着无需额外安装即可获得一整套文档齐备、源码在手的 GUI 开发能力;编写纯本地(本机窗口)的工具时可直接依赖 tkinter,需要更现代外观时优先选择 ttk,而 tix 仅建议在维护遗留代码时使用。


小结

围绕 tk.rst 这一 Tk 图形界面章节入口,本文完整覆盖了 tkinter 的定位与架构、Tk/Tcl 核心类、Tk 到 Tkinter 的映射、Hello World、选项设置与 5 元组返回值、Packer 布局、变量耦合、窗口管理器、选项数据类型、事件绑定、index 参数、图像与文件句柄回调,以及 scrolledtext、tix、ttk(含 Style 样式体系)、IDLE 与其他 GUI 工具包。仓库内 Lib/tkinter/ 与 Modules/_tkinter.c 提供了从 Python 封装到 C 扩展的完整实现证据,你可以对照文档逐行验证,并在此基础上自由扩展自己的 GUI 工具。

登录后查看全文
kbengine