25.1. tkinter — Tcl/Tk的Python接口

源代码: Lib/tkinter/__init__.py


The tkinter package (“Tk interface”) is the standard Python interface to the Tk GUI toolkit. Both Tk and tkinter are available on most Unix platforms, as well as on Windows systems. (Tk itself is not part of Python; it is maintained at ActiveState.)

在命令行中运行 python -m tkinter,应该会弹出一个Tk界面的窗口,表明 tkinter 包已经正确安装,而且告诉你 Tcl/Tk 的版本号,通过这个版本号,你就可以参考对应的 Tcl/Tk 文档了。

参见

Tkinter文档:

Python Tkinter 资源

The Python Tkinter Topic Guide 提供了在 Python 中使用 Tk 的很多信息, 同时包含了Tk其他信息的链接。

TKDocs

大量的教程,部分可视化组件的介绍说明。

Tkinter 8.5 reference: a GUI for Python

在线参考资料。

Tkinter docs from effbot

effbot.org 提供的 tkinter 在线参考资料。

使用 Python 编程

由 Mark Lutz 所著的书籍,对 Tkinter 进行了完美的介绍。

为繁忙的 Python 开发者所准备的现代 Tkinter

由 Mark Rozerman 所著的关于如何使用 Python 和 Tkinter 来搭建有吸引力的和现代化的图形用户界面的书籍

Python 和 Tkinter 编程

作者: John Grayson (ISBN 1-884777-81-3).

Tcl/Tk 文档:

Tk 命令

多数命令以 tkinter 或者 tkinter.ttk 类的形式存在。改变 ‘8.6’ 以匹配所安装的 Tcl/Tk 版本。

Tcl/Tk 最新手册页面

www.tcl.tk 上面最新的 Tcl/Tk 手册。

ActiveState Tcl Home Page

Tk/Tcl 的多数开发工作发生在 ActiveState 。

Tcl 及 Tk 工具集

由 Tcl 发明者 John Ousterhout 所著的书籍。

` Tcl 和 Tk 编程实战 <http://www.beedub.com/book/>`_

Brent Welch 所著的百科全局式书籍。

25.1.1. Tkinter 模块

在大多数时候你只需要 tkinter 就足够了,但也有一些额外的模块可供使用。Tk 接口位于一个名字 _tkinter 的二进制模块当中。此模块包含了低层级的 Tk 接口,它不应该被应用程序员所直接使用。它通常是一个共享库(或 DLL),但在某些情况下也可能被静态链接到 Python 解释器。

除了Tk接口, tkinter 也包含了若干 Python 模块,tkinter.constants 是其中最重要的。导入 tkinter 会自动导入 tkinter.constants ,所以,要使用 Tkinter 通常你只需要一条简单的 import 语句:

import tkinter

或者更常用的:

from tkinter import *
class tkinter.Tk(screenName=None, baseName=None, className='Tk', useTk=1)

Tk 类被初始化时无参数。此时会创建一个 Tk 顶级控件,通常是应用程序的主窗口。每个实例都有自己关联的 Tcl 解释器。

tkinter.Tcl(screenName=None, baseName=None, className='Tk', useTk=0)

Tcl() 函数是一个工厂函数,它创建的对象与 Tk 类创建的对象非常相似,只是它不初始化 Tk 子系统。 在不想创建或无法创建(如没有 X Server 的 Unix/Linux 系统)额外的顶层窗口的环境中驱动 Tcl 解释器时,这一点非常有用。 由 Tcl() 对象创建的对象可以通过调用其 loadtk() 方法来创建顶层窗口(并初始化 Tk 子系统)。

提供Tk支持的其他模块包括:

tkinter.scrolledtext

内置纵向滚动条的文本组件。

tkinter.colorchooser

让用户选择颜色的对话框。

tkinter.commondialog

在此处列出的其他模块中定义的对话框的基类。

tkinter.filedialog

允许用户指定文件的通用对话框,用于打开或保存文件。

tkinter.font

帮助操作字体的工具。

tkinter.messagebox

访问标准的 Tk 对话框。

tkinter.simpledialog

基础对话框和便捷功能。

tkinter.dnd

tkinter 提供拖放支持。这是实验性功能,当被 Tk DND 取代时应被废弃。

turtle

Tk 窗口中的海龟绘图库。

25.1.2. Tkinter 拾遗

本节不应作为 Tk 或 Tkinter 的详尽教程。而只是一个补充,提供一些系统指南。

Credits:

  • Tk 是 John Ousterhout 在伯克利大学时写的。

  • Tkinter 是由 Steen Lumholt 和 Guido van Rossum 编写的。

  • 本拾遗由弗吉尼亚大学的 Matt Conway 撰写。

  • HTML 渲染和一些自由编辑功能,是由 Ken Manheimer 根据 FrameMaker 创建的。

  • Fredrik Lundh 认真研究并修改了类的接口描述,使其与 Tk 4.2 保持一致。

  • Mike Clarkson 将文档转换为 LaTeX 格式,并编写了参考手册的用户界面章节。

25.1.2.1. 本节内容的用法

本节分为两部分:前半部分(大致)涵盖了背景材料,后半部分可以作为手头的参考手册。

当试图回答“如何才能怎么怎么”这种问题时,通常最好是弄清楚如何直接在 Tk 中实现,然后转换回相应的 tkinter 调用。Python 程序员通常可查看 Tk 文档来猜测正确的 Python 命令。这意味着要用好 Tkinter,必须对 Tk 有一定的了解。本文无法完成这个任务,所以最好的做法就是给出目前最好的文档。下面给出一些小提示:

  • 作者强烈建议用户先拿到一份 Tk 手册。具体来说,manN 目录下的 man 文档是最有用的。man3 的 man 文档描述了 Tk 库的 C 接口,因此对脚本编写人员没有什么特别的帮助。

  • Addison-Wesley 出版了一本名为《Tcl 和 Tk 工具包》的书,作者是 John Ousterhout(ISBN 0-201-63337-X),对于新手来说,这是一本很好的 Tcl 和Tk 介绍书籍。该书不算详尽,很多细节还是要看 man 手册。

  • 对大多数人而言,tkinter/__init__.py 是最后一招,但其他手段都无效时也不失为一个好去处。

25.1.2.2. 简单的 Hello World 程序

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()

25.1.3. Tcl/Tk 速览

类的层次结构看起来很复杂,但在实际操作中,应用程序编写人员几乎总是查看最底层的类。

注释:

  • 提供这些类,是为了能在同一个命名空间下将某些功能组织在一起。并不意味着可以独立对其进行实例化。

  • Tk 类在同一个应用程序中仅需作一次实例化。应用程序编程人员不需要显式进行实例化,只要有其他任何类被实例化,系统就会创建一个。

  • Widget 类不是用来实例化的,它仅用于继承以便生成“真正”的部件( C++ 中称为“抽象类”)。

若要充分利用这些参考资料,有时需要知道如何阅读 Tk 的短文,以及如何识别 Tk 命令的各个部分。(参见 将简单的 Tk 映射到 Tkinter 一节,了解以下内容在 tkinter 中的对应部分)。

Tk 脚本就是 Tcl 程序。像所有其他的 Tcl 程序一样,Tk 脚本只是由空格分隔的单词列表。一个Tk 部件只是它的 classoptions 用于进行配置,actions 让它执行有用的动作。

要在 Tk 中制作一个部件,总是采用如下格式的命令:

classCommand newPathname options
classCommand

表示要制作何种部件(按钮、标签、菜单……)。

newPathname

该组件的新名字。Tk 中的所有名字都必须唯一。为了帮助实现这一点,Tk 中的部件都用 路径 命名,就像文件系统中的文件一样。顶层的部件,即 ,名为 .``(句点),而子部件则由更多的句点分隔。比如部件名可能会是 ``.myApp.controlPanel.okButton

options

配置部件的外观,有时也能配置行为。这些选项以标志和值的列表形式出现。标志前带一个“-”,就像 Unix shell 命令的标志一样,如果值超过一个单词,就用引号括起来。

例如

button   .fred   -fg red -text "hi there"
   ^       ^     \______________________/
   |       |                |
 class    new            options
command  widget  (-opt val -opt val ...)

一旦创建成功,部件的路径名就成了一条新的命令。这个新的 部件命令 是程序员让新部件执行某些 action 的句柄。在 C 语言中可表示为someAction(fred, someOptions),在 C++ 中可表示为fred.someAction(someOptions),而在 Tk 中写作:

.fred someAction someOptions

请注意,对象名 .fred 是以句点开头的。

如您所料,someAction 的可用值取决于部件的类别。如果 fred 为按钮,则 .fred disable 有效(fred 会变灰),而当 fred 为标签时则无效(Tk 不支持标签的禁用)。

someOptions 的合法值取决于动作。有些动作不需要参数,比如 disable,其他动作如文本输入框的 delete 命令则需用参数指定要删除的文本范围。

25.1.4. 将简单的 Tk 映射到 Tkinter

Tk 中的类命令对应于 Tkinter 中的类构造函数:

button .fred                =====>  fred = Button()

父对象是隐含在创建时给定的新名字中的。在 Tkinter 中,父对象是显式指定的:

button .panel.fred          =====>  fred = Button(panel)

Tk 中的配置项是以连字符标签列表的形式给出的,后跟着参数值。在 Tkinter 中,选项指定为实例构造函数中的关键字参数,在配置调用时指定为关键字 args,或在已有实例中指定为实例索引,以字典的形式。参见 可选配置项 一节的选项设置部分。

button .fred -fg red        =====>  fred = Button(panel, fg="red")
.fred configure -fg red     =====>  fred["fg"] = red
                            OR ==>  fred.config(fg="red")

在 Tk 中, 要在某个部件上执行动作, 要用部件名作为命令, 并后面附上动作名称, 可能还会带有参数(option)。在 Tkinter 中,则调用类实例的方法来执行部件的动作。部件能够执行的动作(方法)列在 tkinter/__init__.py 中。

.fred invoke                =====>  fred.invoke()

若要将部件交给打包器(geometry manager),需带上可选参数去调用 pack。在 Tkinter 中,Pack 类拥有全部这些功能,pack 命令的各种形式都以方法的形式实现。 tkinter 中的所有部件都是从 Packer 继承而来的,因此继承了所有打包方法。关于Form geometry manager 的更多信息,请参见 tkinter.tix 模块的文档。

pack .fred -side left       =====>  fred.pack(side="left")

25.1.6. 快速参考

25.1.6.1. 可选配置项

配置参数可以控制组件颜色和边框宽度等。可通过三种方式进行设置:

在对象创建时,使用关键字参数
fred = Button(self, fg="red", bg="blue")
在对象创建后,将参数名用作字典索引
fred["fg"] = "red"
fred["bg"] = "blue"
利用 config() 方法修改对象的属性
fred.config(fg="red", bg="blue")

关于这些参数及其表现的完整解释,请参阅 Tk 手册中有关组件的 man 帮助页。

请注意,man 手册页列出了每个部件的“标准选项”和“组件特有选项”。前者是很多组件通用的选项列表,后者是该组件特有的选项。标准选项在 options(3) man 手册中有文档。

本文没有区分标准选项和部件特有选项。有些选项不适用于某类组件。组件是否对某选项做出响应,取决于组件的类别;按钮组件有一个 command 选项,而标签组件就没有。

组件支持的选项在其手册中有列出,也可在运行时调用 config() 方法(不带参数)查看,或者通过调用组件的 keys() 方法进行查询。这些调用的返回值为字典,字典的键是字符串格式的选项名(比如 'relief'),字典的值为五元组。

有些选项,比如 bg 是全名通用选项的同义词(bg 是 “background”的简写)。向 config() 方法传入选项的简称将返回一个二元组,而不是五元组。传回的二元组将包含同义词的全名和“真正的”选项(比如 ('bg', 'background'))。

索引

含义

示例

0

选项名称

'relief'

1

数据库查找的选项名称

'relief'

2

数据库查找的选项类

'Relief'

3

默认值

'raised'

4

当前值

'groove'

示例:

>>> print(fred.config())
{'relief': ('relief', 'relief', 'Relief', 'raised', 'groove')}

当然,输出的字典将包含所有可用选项及其值。这里只是举个例子。

25.1.6.2. The Packer

The packer is one of Tk’s geometry-management mechanisms. Geometry managers are used to specify the relative positioning of the positioning of widgets within their container - their mutual master. In contrast to the more cumbersome placer (which is used less commonly, and we do not cover here), the packer takes qualitative relationship specification - above, to the left of, filling, etc - and works everything out to determine the exact placement coordinates for you.

部件的大小都由其内部的 “从属部件” 的大小决定。包装器用于控制从属部件在主部件中出现的位置。可以把部件包入框架,再把框架包入其他框架中,搭建出所需的布局。此外,只要完成了包装,组件的布局就会进行动态调整,以适应布局参数的变化。

请注意,只有用形状管理器指定几何形状后,部件才会显示出来。忘记设置形状参数是新手常犯的错误,惊讶于创建完部件却啥都没出现。部件只有在应用了类似于打包器的 pack() 方法之后才会显示在屏幕上。

调用 pack() 方法时可以给出由关键字/参数值组成的键值对,以便控制组件在其容器中出现的位置,以及主程序窗口大小变动时的行为。下面是一些例子:

fred.pack()                     # defaults to side = "top"
fred.pack(side="left")
fred.pack(expand=1)

25.1.6.3. 包装器的参数

关于包装器及其可接受的参数,更多信息请参阅 man 手册和 John Ousterhout 书中的第 183 页。

anchor

锚点类型。 表示包装器要放置的每个从属组件的位置。

expand

布尔型,01

fill

合法值为:'x''y''both''none'

ipadx 和 ipady

距离值,指定从属部件每一侧的内边距。

padx 和 pady

距离值,指定从属部件的外边距。

side

合法值为:'left''right''top''bottom'

25.1.6.4. 部件与变量的关联

通过一些特定参数,某些组件(如文本输入组件)的当前设置可直接与应用程序的变量关联。这些参数包括 variabletextvariableonvalueoffvaluevalue。这种关联是双向的:只要这些变量因任何原因发生变化,其关联的部件就会更新以反映新的参数值。

不幸的是,在目前 tkinter 的实现代码中,不可能通过 variabletextvariable 参数将任意 Python 变量移交给组件。变量只有是 tkinter 中定义的 Variable 类的子类,才能生效。

已经定义了很多有用的 Variable 子类: StringVarIntVarDoubleVarBooleanVar。调用 get() 方法可以读取这些变量的当前值;调用 set() 方法则可改变变量值。只要遵循这种用法,组件就会保持跟踪变量的值,而不需要更多的干预。

例如

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

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

        # here is the application variable
        self.contents = StringVar()
        # set it to some value
        self.contents.set("this is a variable")
        # tell the entry widget to watch this variable
        self.entrythingy["textvariable"] = self.contents

        # and here we get a callback when the user hits return.
        # we will have the program print out the value of the
        # application variable when the user hits return
        self.entrythingy.bind('<Key-Return>',
                              self.print_contents)

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

25.1.6.5. 窗口管理器

Tk 有个实用命令 wm,用于与窗口管理器进行交互。wm 命令的参数可用于控制标题、位置、图标之类的东西。在 tkinter 中,这些命令已被实现为 Wm 类的方法。顶层部件是 Wm 类的子类,所以可以直接调用 Wm 的这些方法。

要获得指定部件所在的顶层窗口,通常只要引用该部件的主窗口即可。当然,如果该部件是包装在框架内的,那么主窗口不代表就是顶层窗口。为了获得任意组件所在的顶层窗口,可以调用 _root() 方法。该方法以下划线开头,表明其为 Python 实现的代码,而非 Tk 提供的某个接口。

以下是一些典型用法:

import tkinter as tk

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

# create the application
myapp = App()

#
# here are method calls to the window manager class
#
myapp.master.title("My Do-Nothing Application")
myapp.master.maxsize(1000, 400)

# start the program
myapp.mainloop()

25.1.6.6. Tk 参数的数据类型

anchor

合法值是罗盘的方位点:"n""ne""e""se""s""sw""w""nw""center"

位图

内置已命名的位图有八个:'error''gray25''gray50''hourglass''info''questhead''question''warning' 。若要指定位图的文件名,请给出完整路径,前面加一个 @,比如 "@/usr/contrib/bitmap/gumby.bit"

布尔值

可以传入整数 0 或 1,或是字符串 "yes""no"

callback – 回调

指任何无需调用参数的 Python 函数。 例如:

def print_it():
    print("hi there")
fred["command"] = print_it
color

可在 rgb.txt 文件中以颜色名的形式给出,或是 RGB 字符串的形式,4 位 :"#RGB" ,8 位 :"#RRGGBB",12 位:"#RRRGGGBBB",16 位:"#RRRRGGGGBBBB",其中R、G、B 为合法的十六进制数值。 详见 Ousterhout 书中的第 160 页。

cursor

可采用 cursorfont.h 中的标准光标名称,去掉 XC_ 前缀。 比如要获取一个手形光标(XC_hand2),可以用字符串 "hand2"。也可以指定自己的位图和掩码文件作为光标。参见 Ousterhout 书中的第 179 页。

distance

屏幕距离可以用像素或绝对距离来指定。像素是数字,绝对距离是字符串,后面的字符表示单位:c 是厘米,i 是英寸,m 是毫米,p 则表示打印机的点数。例如,3.5 英寸可表示为 "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""3c 2c 4c 10.43c" 都是合法的区域值。

relief

决定了组件的边框样式。 合法值包括:"raised""sunken""flat""groove""ridge"

scrollcommand

这几乎就是带滚动条部件的 set() 方法,但也可是任一只有一个参数的部件方法。

wrap:

只能是以下值之一:"none""char""word"

25.1.6.7. 绑定和事件

部件命令中的 bind 方法可觉察某些事件,并在事件发生时触发一个回调函数。bind 方法的形式是:

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

这里:

sequence – 序列

是一个表示事件的目标种类的字符串。(详情请看 bind 的手册页和 John Outsterhout 的书的第 201 页。)

func

is a Python function, taking one argument, to be invoked when the event occurs. An Event instance will be passed as the argument. (Functions deployed this way are commonly known as callbacks.)

add

is optional, either '' or '+'. Passing an empty string denotes that this binding is to replace any other bindings that this event is associated with. Passing a '+' means that this function is to be added to the list of functions bound to this event type.

例如

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

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

Notice how the widget field of the event is being accessed in the turn_red() callback. This field contains the widget that caught the X event. The following table lists the other event fields you can access, and how they are denoted in Tk, which can be useful when referring to the Tk man pages.

Tk

Tkinter Event Field

Tk

Tkinter Event Field

%f

焦点

%A

char

%h

height

%E

send_event

%k

keycode

%K

keysym

%s

状况

%N

keysym_num

%t

time

%T

type – 类型

%w

宽度

%W

widget

%x

x

%X

x_root

%y

y

%Y

y_root

25.1.6.8. The index Parameter

A number of widgets require “index” parameters to be passed. These are used to point at a specific place in a Text widget, or to particular characters in an Entry widget, or to particular menu items in a Menu widget.

Entry widget indexes (index, view index, etc.)

Entry widgets have options that refer to character positions in the text being displayed. You can use these tkinter functions to access these special points in text widgets:

Text widget indexes

The index notation for Text widgets is very rich and is best described in the Tk man pages.

Menu indexes (menu.invoke(), menu.entryconfig(), etc.)

Some options and methods for menus manipulate specific menu entries. Anytime a menu index is needed for an option or a parameter, you may pass in:

  • an integer which refers to the numeric position of the entry in the widget, counted from the top, starting with 0;

  • the string "active", which refers to the menu position that is currently under the cursor;

  • the string "last" which refers to the last menu item;

  • An integer preceded by @, as in @6, where the integer is interpreted as a y pixel coordinate in the menu’s coordinate system;

  • the string "none", which indicates no menu entry at all, most often used with menu.activate() to deactivate all entries, and finally,

  • a text string that is pattern matched against the label of the menu entry, as scanned from the top of the menu to the bottom. Note that this index type is considered after all the others, which means that matches for menu items labelled last, active, or none may be interpreted as the above literals, instead.

25.1.6.9. Images

Images of different formats can be created through the corresponding subclass of tkinter.Image:

  • BitmapImage for images in XBM format.

  • PhotoImage for images in PGM, PPM, GIF and PNG formats. The latter is supported starting with Tk 8.6.

Either type of image is created through either the file or the data option (other options are available as well).

The image object can then be used wherever an image option is supported by some widget (e.g. labels, buttons, menus). In these cases, Tk will not keep a reference to the image. When the last Python reference to the image object is deleted, the image data is deleted as well, and Tk will display an empty box wherever the image was used.

参见

The Pillow package adds support for formats such as BMP, JPEG, TIFF, and WebP, among others.

25.1.7. File Handlers

Tk allows you to register and unregister a callback function which will be called from the Tk mainloop when I/O is possible on a file descriptor. Only one handler may be registered per file descriptor. Example code:

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

This feature is not available on Windows.

Since you don’t know how many bytes are available for reading, you may not want to use the BufferedIOBase or TextIOBase read() or readline() methods, since these will insist on reading a predefined number of bytes. For sockets, the recv() or recvfrom() methods will work fine; for other files, use raw reads or os.read(file.fileno(), maxbytecount).

Widget.tk.createfilehandler(file, mask, func)

Registers the file handler callback function func. The file argument may either be an object with a fileno() method (such as a file or socket object), or an integer file descriptor. The mask argument is an ORed combination of any of the three constants below. The callback is called as follows:

callback(file, mask)
Widget.tk.deletefilehandler(file)

Unregisters a file handler.

tkinter.READABLE
tkinter.WRITABLE
tkinter.EXCEPTION

Constants used in the mask arguments.