使用 Tkinter 进行 Python GUI 编程

2023-12-05

Python 有很多图形用户界面框架, 但特金特是唯一内置于 Python 标准库中的框架。 Tkinter 有几个优点。它是跨平台,因此相同的代码适用于 Windows、macOS 和 Linux。视觉元素是使用本机操作系统元素呈现的,因此使用 Tkinter 构建的应用程序看起来像是属于它们运行的​​平台。

尽管 Tkinter 被认为是事实上的 Python GUI 框架,但它并非没有受到批评。一个值得注意的批评是使用 Tkinter 构建的 GUI 看起来已经过时了。如果您想要一个闪亮、现代的界面,那么 Tkinter 可能不是您想要的。

然而,与其他框架相比,Tkinter 是轻量级的并且使用起来相对轻松。这使得它成为在 Python 中构建 GUI 应用程序的一个令人信服的选择,特别是对于不需要现代光泽的应用程序,并且首要任务是快速构建功能性和跨平台的东西。

在本教程中,您将学习如何:

  • 开始使用 Tkinter你好世界应用
  • 与 一起工作小部件,例如按钮和文本框
  • 控制您的应用程序布局几何管理器
  • 制作您的应用程序交互的通过将按钮点击与 Python 函数关联起来

笔记:本教程改编自《图形用户界面》一章Python 基础知识:Python 3 实用介绍.

本书使用Python的内置闲置的编辑器来创建和编辑 Python 文件并与 Python shell 交互。在本教程中,已删除对 IDLE 的引用,以支持更通用的语言。

本教程中的大部分内容均保持不变,您从所选的编辑器和环境中运行示例代码应该不会有任何问题。

一旦您通过完成每个部分末尾的练习掌握了这些技能,您就可以通过构建两个应用程序将所有内容结合在一起。第一个是温度转换器,第二个是文本编辑器。是时候深入学习如何使用 Tkinter 构建应用程序了!

免费奖金: 关于掌握 Python 的 5 个想法,为 Python 开发人员提供的免费课程,向您展示将 Python 技能提升到新水平所需的路线图和思维方式。

参加测验:通过我们的交互式“使用 Tkinter 进行 Python GUI 编程”测验来测试您的知识。完成后,您将收到一个分数,以便您可以跟踪一段时间内的学习进度:

参加测验 »

使用 Tkinter 构建您的第一个 Python GUI 应用程序

Tkinter GUI 的基本元素是窗户。 Windows 是所有其他 GUI 元素所在的容器。这些其他 GUI 元素,例如文本框、标签和按钮,被称为小部件。小部件包含在窗口内部。

首先,创建一个包含单个小部件的窗口。开始一个新的Python外壳会议并跟随!

笔记:本教程中的代码示例均已在 Windows、macOS 和 Ubuntu Linux 20.04(使用 Python 版本 3.10)上进行了测试。

如果你有安装了Python与可用的官方安装程序视窗苹果系统python.org,那么运行示例代码应该没有问题。您可以安全地跳过本说明的其余部分并继续学习本教程!

如果您尚未使用官方安装程序安装 Python,或者您的系统没有官方发行版,那么这里有一些入门提示。

macOS 上的 Python 与 Homebrew:

适用于 macOS 的 Python 发行版可在自制不与捆绑在一起Tcl/TkTkinter 所需的依赖项。而是使用默认的系统版本。此版本可能已过时并阻止您导入 Tkinter 模块。要避免此问题,请使用官方 macOS 安装程序.

Ubuntu Linux 20.04:

为了节省内存空间,Ubuntu Linux 20.04 上预装的默认 Python 解释器版本不支持 Tkinter。但是,如果您想继续使用操作系统附带的 Python 解释器,请安装以下软件包:

$ sudo apt-get install python3-tk

这将安装 Python GUI Tkinter 模块。

其他 Linux 风格:

如果您无法在您的 Linux 风格上安装有效的 Python,那么您可以从源代码使用正确版本的 Tcl/Tk 构建 Python。有关此过程的分步演练,请查看。您也可以尝试使用pyenv管理多个Python版本。

打开 Python shell 后,您需要做的第一件事是导入 Python GUI Tkinter 模块:

>>>
>>> import tkinter as tk

A 窗户是 Tkinter 的一个实例Tk班级。继续创建一个新窗口并将其分配给多变的 window:

>>>
>>> window = tk.Tk()

当您执行上述代码时,屏幕上会弹出一个新窗口。它的外观取决于您的操作系统:

A blank Tkinter application window on Windows 10, macOS, and Ubuntu Linux

在本教程的其余部分中,您将看到 Windows 屏幕截图。

添加小部件

现在您有了一个窗口,您可以添加一个小部件。使用tk.Label类向窗口添加一些文本。创建一个Label带有文本的小部件"Hello, Tkinter"并将其分配给一个名为的变量greeting:

>>>
>>> greeting = tk.Label(text="Hello, Tkinter")

您之前创建的窗口不会改变。您刚刚创建了一个Label小部件,但您尚未将其添加到窗口中。有多种方法可以将小部件添加到窗口。现在,您可以使用Label小部件的.pack()方法:

>>>
>>> greeting.pack()

窗口现在看起来像这样:

Example "Hello, world" Tkinter application on Windows 10

当您将小部件打包到窗口中时,Tkinter 将窗口调整得尽可能小,同时仍然完全包围小部件。现在执行以下命令:

>>>
>>> window.mainloop()

似乎什么也没发生,但请注意 shell 中没有出现新的提示。

window.mainloop()告诉 Python 运行 Tkinter事件循环。此方法侦听事件,例如按钮单击或按键,并且在它之后的任何代码都会运行,直到您关闭调用该方法的窗口为止。继续并关闭您创建的窗口,您将看到 shell 中显示一个新提示。

警告:当您使用 Tkinter 时Python REPL,每行执行时都会应用对窗口的更新。这是not从 Python 文件执行 Tkinter 程序的情况!

如果您不包括window.mainloop()在 Python 文件中的程序末尾,则 Tkinter 应用程序将永远不会运行,并且不会显示任何内容。或者,您可以通过调用在 Python REPL 中增量构建用户界面window.update()在每个步骤之后反映变化。

使用 Tkinter 创建窗口只需要几行代码。但空白窗口并不是很有用!在下一节中,您将了解 Tkinter 中提供的一些小部件,以及如何自定义它们以满足应用程序的需求。

检查你的理解情况

展开下面的代码块来检查您的理解:

编写一个完整的 Python 脚本,使用文本创建 Tkinter 窗口"Python rocks!".

窗口应如下所示:

A Tkinter window containing the text "Python rocks!"

现在尝试这个练习。

您可以展开下面的代码块来查看解决方案:

这是一种可能的解决方案:

import tkinter as tk

window = tk.Tk()
label = tk.Label(text="Python rocks!")
label.pack()

window.mainloop()

请记住,您的代码可能看起来有所不同。

准备好后,您可以继续下一部分。

使用小部件

小部件是 Python GUI 框架 Tkinter 的基础。它们是用户与您的程序交互的元素。每个小部件在 Tkinter 中是由类定义的。以下是一些可用的小部件:

Widget Class Description
Label A widget used to display text on the screen
Button A button that can contain text and can perform an action when clicked
Entry A text entry widget that allows only a single line of text
Text A text entry widget that allows multiline text entry
Frame A rectangular region used to group related widgets or provide padding between widgets

您将在以下部分中了解如何使用其中的每一个,但请记住,Tkinter 的小部件比此处列出的小部件多得多。当您考虑一组全新的组件时,小部件的选择会变得更加复杂主题小部件。在本教程的剩余部分中,您将仅使用 Tkinter 的经典小部件, 尽管。

如果您想了解有关这两种小部件类型的更多信息,则可以展开下面的可折叠部分:

值得注意的是,Tkinter 中目前有两大类小部件:

  1. 经典小部件:可用于tkinter包,例如tkinter.Label
  2. 主题小部件:可用于ttk子模块,例如tkinter.ttk.Label

Tkinter 的经典小部件高度可定制且简单明了,但它们在当今的大多数平台上往往显得过时或有些陌生。如果您想利用原生小部件外观和感觉对于给定操作系统的用户来说很熟悉,那么您可能想查看主题小部件。

大多数主题小部件几乎都是旧版小部件的直接替代品,但具有更现代的外观。您还可以使用一些全新的小部件,例如进度条,以前在 Tkinter 中不可用。同时,您需要继续使用一些没有主题替代方案的经典小部件。

笔记:主题小部件tkinter.ttk模块默认使用操作系统的本机外观。但是,您可以更改它们主题用于定制视觉外观,例如浅色和深色模式。主题是可重用样式定义的集合,您可以将其视为层叠样式表 (CSS)对于 Tkinter。

使新的小部件可主题化意味着将大部分样式信息提取到单独的对象中。一方面,这样一个关注点分离是库设计中所需的属性,但另一方面,它引入了额外的抽象层,这使得主题小部件比经典小部件更难以设计样式。

在 Tkinter 中使用常规和主题小部件时,通常为 Tkinter 包和模块声明以下别名:

>>>
>>> import tkinter as tk
>>> import tkinter.ttk as ttk

像这样的别名可以让你明确地引用tk.Label或者ttk.Label,例如,根据您的需要在一个程序中:

>>>
>>> tk.Label()
<tkinter.Label object .!label>

>>> ttk.Label()
<tkinter.ttk.Label object .!label2>

但是,有时您可能会发现使用通配符导入更方便(*)在可能的情况下自动用主题小部件覆盖所有旧版小部件,如下所示:

>>>
>>> from tkinter import *
>>> from tkinter.ttk import *

>>> Label()
<tkinter.ttk.Label object .!label>

>>> Text()
<tkinter.Text object .!text>

现在,您不必在小部件的类名前面添加相应的 Python 模块。只要主题小部件可用,您就始终会创建它,否则您将退回到经典小部件。上面的两个 import 语句必须按照指定的顺序放置才能生效。因此,通配符导入被认为是一种不好的做法,除非有意识地使用,否则通常应该避免这种做法。

有关 Tkinter 小部件的完整列表,请查看基本小部件更多小部件在里面Tk文档教程。尽管它描述了 Tcl/Tk 8.5 中引入的主题小部件,但其中的大多数信息也应该适用于经典小部件。

有趣的事实:Tkinter 字面意思是“Tk 接口”,因为它是一个 Python捆绑或一个编程接口Tk图书馆在Tcl脚本语言。

现在,仔细看看Label小部件。

显示文本和图像Label小部件

Label小部件用于显示文本或者图片。显示的文本Label用户无法编辑小部件。仅用于显示目的。正如您在本教程开头的示例中看到的,您可以创建一个Label通过实例化小部件Label类并通过细绳text范围:

label = tk.Label(text="Hello, Tkinter")

Label小部件使用默认系统文本颜色和默认系统文本背景颜色显示文本。它们通常分别是黑色和白色,但如果您在操作系统中更改了这些设置,您可能会看到不同的颜色。

你可以控制Label文本和背景颜色使用foregroundbackground参数:

label = tk.Label(
    text="Hello, Tkinter",
    foreground="white",  # Set the text color to white
    background="black"  # Set the background color to black
)

有许多有效的颜色名称,包括:

  • "red"
  • "orange"
  • "yellow"
  • "green"
  • "blue"
  • "purple"

许多HTML 颜色名称与 Tkinter 合作。有关完整参考,包括当前系统主题控制的 macOS 和 Windows 特定系统颜色,请查看颜色手册页.

您还可以使用指定颜色十六进制 RGB 值:

label = tk.Label(text="Hello, Tkinter", background="#34A2FE")

这会将标签背景设置为漂亮的浅蓝色。十六进制 RGB 值比命名颜色更神秘,但也更灵活。幸运的是,有工具可以使获取十六进制颜色代码相对轻松。

如果您不想打字foregroundbackground一直,那么你可以使用简写fgbg设置前景色和背景色的参数:

label = tk.Label(text="Hello, Tkinter", fg="white", bg="black")

您还可以使用以下命令控制标签的宽度和高度widthheight参数:

label = tk.Label(
    text="Hello, Tkinter",
    fg="white",
    bg="black",
    width=10,
    height=10
)

该标签在窗口中的外观如下:

A Tkinter window containing a button with a black background and white text that reads "Hello, Tkinter"

尽管宽度和高度都设置为,但窗口中的标签不是方形的,这可能看起来很奇怪10。这是因为宽度和高度的测量单位为文本单位。一个水平文本单位由字符的宽度决定0,或数字零,采用默认系统字体。同样,一个垂直文本单位由字符的高度决定0.

笔记:对于宽度和高度测量,Tkinter 使用文本单位,而不是英寸、厘米或像素等单位,以确保应用程序跨平台的行为一致。

通过字符宽度测量单位意味着小部件的大小是相对于用户计算机上的默认字体的。这可以确保文本正确地适合标签和按钮,无论应用程序在何处运行。

标签非常适合显示一些文本,但它们不能帮助您获取用户的输入。您将了解的接下来的三个小部件都用于获取用户输入。

显示可点击按钮Button小部件

Button小部件用于显示可点击的按钮。您可以将它们配置为在单击它们时调用函数。您将在下一节中介绍如何通过单击按钮来调用函数。现在,我们来看看如何创建按钮并设置其样式。

之间有很多相似之处ButtonLabel小部件。在许多方面,按钮只是一个可以单击的标签!与您用来创建和设置样式的关键字参数相同Label将与Button小部件。例如,以下代码创建一个具有蓝色背景和黄色文本的按钮。它还将宽度和高度设置为255文本单位分别为:

button = tk.Button(
    text="Click me!",
    width=25,
    height=5,
    bg="blue",
    fg="yellow",
)

该按钮在窗口中的外观如下:

A Tkinter window containing a button with a blue background and yellow text that reads "Click me!"

相当漂亮!您可以使用接下来的两个小部件来收集用户的文本输入。

获取用户输入Entry小部件

当您需要从用户那里获取一些文本(例如姓名或电子邮件地址)时,请使用Entry小部件。它将显示一个小文本框用户可以在其中输入一些文本。创建并设计样式Entry小部件的工作方式几乎与LabelButton小部件。例如,以下代码创建一个具有蓝色背景、一些黄色文本和宽度的小部件50文本单位:

entry = tk.Entry(fg="yellow", bg="blue", width=50)

有趣的是关于Entry不过,小部件并不是如何设计它们的样式。这是如何使用它们来获得来自用户的输入。您可以执行三个主要操作Entry小部件:

  1. 检索文本.get()
  2. 删除文本.delete()
  3. 插入文本.insert()

最好的了解方式Entry小部件就是创建一个小部件并与其交互。打开 Python shell 并按照本节中的示例进行操作。一、导入tkinter并创建一个新窗口:

>>>
>>> import tkinter as tk
>>> window = tk.Tk()

现在创建一个LabelEntry小部件:

>>>
>>> label = tk.Label(text="Name")
>>> entry = tk.Entry()

Label描述了应该包含什么样的文本Entry小部件。它不强制执行任何类型的要求Entry,但它告诉用户您的程序希望他们放在那里的内容。你需要.pack()将小部件放入窗口中,以便它们可见:

>>>
>>> label.pack()
>>> entry.pack()

看起来是这样的:

A Tkinter window containing an Entry widget withe Label "Name"

请注意,Tkinter 自动将标签置于Entry窗口中的小部件。这是一个特点.pack(),您将在后面的部分中了解更多信息。

单击内部Entry使用鼠标并输入小部件Real Python:

A Tkinter window containing an Entry widget with the text "Real Python"

现在你已经输入了一些文本Entry小部件,但该文本尚未发送到您的程序。您可以使用.get()检索文本并将其分配给名为的变量name:

>>>
>>> name = entry.get()
>>> name
'Real Python'

您也可以删除文本。这.delete()方法采用一个整数参数来告诉 Python 要删除哪个字符。例如,下面的代码块显示了如何.delete(0)删除第一个字符Entry:

>>>
>>> entry.delete(0)

小部件中剩余的文本现在是eal Python:

A Tkinter window containing an Entry widget with the text "eal Python"

请注意,就像 Python 一样字符串对象,文本在Entry小部件的索引开始于0.

如果您需要从一个字符中删除几个字符Entry,然后将第二个整数参数传递给.delete()指示删除应停止的字符的索引。例如,以下代码删除了中的前四个字母Entry:

>>>
>>> entry.delete(0, 4)

剩下的文字现在是Python:

A Tkinter window containing an Entry widget with the text "Python"

Entry.delete()就像字符串切片。第一个参数确定起始索引,删除一直持续到 butnot包括作为第二个参数传递的索引。使用特殊常数tk.END对于第二个参数.delete()删除所有文本Entry:

>>>
>>> entry.delete(0, tk.END)

您现在将看到一个空白文本框:

A Tkinter window containing an Entry widget withe Label "Name"

另一方面,您还可以将文本插入到Entry小部件:

>>>
>>> entry.insert(0, "Python")

窗口现在看起来像这样:

A Tkinter window containing an Entry widget with the text "Python"

第一个参数告诉.insert()在哪里插入文本。如果里面没有文字Entry,那么无论您作为第一个参数传递什么值,新文本都将始终插入到小部件的开头。例如,调用.insert()100作为第一个参数而不是0正如您上面所做的那样,会生成相同的输出。

If Entry已经包含一些文本,那么.insert()将在指定位置插入新文本并将所有现有文本向右移动:

>>>
>>> entry.insert(0, "Real ")

小部件文本现在显示为Real Python:

A Tkinter window containing an Entry widget with the text "Real Python"

Entry小部件非常适合捕获用户的少量文本,但由于它们仅显示在一行上,因此并不适合收集大量文本。那就是那里Text小部件进来!

获取多行用户输入Text小部件

Text小部件用于输入文本,就像Entry小部件。不同之处在于Text小部件可能包含多行文本。与一个Text小部件,用户可以输入整个段落甚至几页文本!就像与Entry小部件,您可以执行三个主要操作Text小部件:

  1. 检索文本.get()
  2. 删除文字.delete()
  3. 插入文字.insert()

虽然方法名称与Entry方法,它们的工作原理略有不同。是时候亲自动手创建一个Text小部件并看看它能做什么。

笔记:上一节中的窗口还打开着吗?

如果是这样,那么您可以通过执行以下命令将其关闭:

>>>
>>> window.destroy()

您也可以通过单击手动关闭它关闭按钮。

在您的 Python shell 中,创建一个新的空白窗口并打包Text()小部件进入其中:

>>>
>>> window = tk.Tk()
>>> text_box = tk.Text()
>>> text_box.pack()

文本框比文本框大得多Entry默认情况下是小部件。上面创建的窗口如下所示:

A Tkinter window containing a Text Box widget

Click anywhere inside the window to activate the text box. Type in the word Hello. Then press Enter and type World on the second line. The window should now look like this:

A Tkinter window containing a Text Box widget with the text "Hello World"

就像与Entry小部件,您可以从Text小部件使用.get()。然而,调用.get()不带参数不会像以下那样返回文本框中的全文Entry小部件。它提出了一个例外:

>>>
>>> text_box.get()
Traceback (most recent call last):
  ...
TypeError: get() missing 1 required positional argument: 'index1'

Text.get()需要至少一个参数。呼唤.get()使用单个索引返回单个字符。要检索多个字符,您需要传递一个起始索引结束索引。指数在Text小部件的工作方式与Entry小部件。自从Text小部件可以有多行文本,索引必须包含两条信息:

  1. 行号一个角色的
  2. 位置该行上的一个字符

行号开头为1,字符位置以0。要创建索引,您需要创建一个以下形式的字符串"<line>.<char>", 替换<line>与行号和<char>与字符编号。例如,"1.0"代表第一行的第一个字符,并且"2.3"代表第二行的第四个字符。

使用索引"1.0"从您之前创建的文本框中获取第一个字母:

>>>
>>> text_box.get("1.0")
'H'

这个词有五个字母Hello,以及字符数o4,因为字符编号从0,以及这个词Hello从文本框中的第一个位置开始。就像 Python 字符串切片一样,为了获取整个单词Hello从文本框中,结束索引必须比要读取的最后一个字符的索引大 1。

所以,为了得到这个词Hello从文本框中,使用"1.0"对于第一个索引和"1.5"对于第二个索引:

>>>
>>> text_box.get("1.0", "1.5")
'Hello'

为了得到这个词World在文本框的第二行,将每个索引中的行号更改为2:

>>>
>>> text_box.get("2.0", "2.5")
'World'

要获取文本框中的所有文本,请将起始索引设置为"1.0"并使用特殊的tk.END第二个索引的常数:

>>>
>>> text_box.get("1.0", tk.END)
'Hello\nWorld\n'

请注意,返回的文本.get()包括任何换行符。您还可以从这个示例中看到,a 中的每一行Text小部件末尾有一个换行符,包括文本框中的最后一行文本。

.delete()用于从文本框中删除字符。它的工作原理就像.delete()为了Entry小部件。有两种使用方法.delete():

  1. 与一个单一参数
  2. 两个论点

使用单参数版本,您可以传递给.delete()要删除的单个字符的索引。例如,以下删除第一个字符,H,从文本框中:

>>>
>>> text_box.delete("1.0")

窗口中的第一行文本现在显示为ello:

A Tkinter window containing a Text Box widget with the text "ello World"

对于双参数版本,您可以传递两个索引来删除从第一个索引开始到第二个索引(但不包括)的一系列字符。

例如,删除剩余的ello在文本框的第一行,使用索引"1.0""1.4":

>>>
>>> text_box.delete("1.0", "1.4")

请注意,文本从第一行消失了。这会在单词后面留下一个空行World在第二行:

A Tkinter window containing a Text Box widget with a blank first line and the text "World" on the second line

即使你看不到它,第一行仍然有一个字符。这是一个换行符!您可以使用以下方法验证这一点.get():

>>>
>>> text_box.get("1.0")
'\n'

如果删除该字符,则文本框的其余内容将上移一行:

>>>
>>> text_box.delete("1.0")

现在,World位于文本框的第一行:

A Tkinter window containing a Text Box widget with the text "World"

尝试清除文本框中的其余文本。放"1.0"作为开始索引并使用tk.END对于第二个索引:

>>>
>>> text_box.delete("1.0", tk.END)

文本框现在为空:

A Tkinter window containing a Text Box widget

您可以使用以下命令将文本插入文本框.insert():

>>>
>>> text_box.insert("1.0", "Hello")

这会插入单词Hello在文本框的开头,使用相同的"<line>.<column>"使用的格式.get()指定插入位置:

A Tkinter window containing a Text Box widget with the text "Hello"

看看如果您尝试插入单词会发生什么World在第二行:

>>>
>>> text_box.insert("2.0", "World")

文本不是在第二行插入,而是插入在第一行末尾:

A Tkinter window containing a Text Box widget with the text "HelloWorld"

如果要将文本插入新行,则需要手动将换行符插入到要插入的字符串中:

>>>
>>> text_box.insert("2.0", "\nWorld")

现在World位于文本框的第二行:

A Tkinter window containing a Text Box widget with the text "Hello World"

.insert()将执行以下两件事之一:

  1. 插入文字在指定位置(如果该位置或该位置之后已经有文本)。
  2. 附加文本如果字符数大于文本框中最后一个字符的索引,则转到指定行。

尝试跟踪最后一个字符的索引是什么通常是不切实际的。在末尾插入文本的最佳方法Text小部件是要通过tk.END到第一个参数.insert():

>>>
>>> text_box.insert(tk.END, "Put me at the end!")

不要忘记包含换行符 (\n) 放在文本的开头,如果您想将其放在新行中:

>>>
>>> text_box.insert(tk.END, "\nPut me on a new line!")

Label, Button, Entry, 和Text小部件只是 Tkinter 中可用的一些小部件。还有其他几个小部件,包括复选框、单选按钮、滚动条和进度条的小部件。有关所有可用小部件的更多信息,请参阅附加小部件列表其他资源部分。

将小部件分配给框架Frame小部件

在本教程中,您将仅使用五个小部件:

  1. Label
  2. Button
  3. Entry
  4. Text
  5. Frame

这些是您迄今为止见过的四个加上Frame小部件。Frame小部件对于组织非常重要小部件的布局在一个应用程序中。

在详细了解如何布置小部件的视觉呈现之前,请仔细了解一下如何Frame小部件的工作原理,以及如何将其他小部件分配给它们。以下脚本创建一个空白Frame小部件并将其分配给主应用程序窗口:

import tkinter as tk

window = tk.Tk()
frame = tk.Frame()
frame.pack()

window.mainloop()

frame.pack()将框架装入窗口中,以便窗口本身尺寸尽可能小以包围框架。当您运行上面的脚本时,您会得到一些非常无趣的输出:

A Tkinter window containing an empty Frame widget

一个空的Frame小部件几乎是不可见的。框架最好被认为是容器对于其他小部件。您可以通过设置小部件的master属性:

frame = tk.Frame()
label = tk.Label(master=frame)

要了解其工作原理,请编写一个脚本来创建两个Frame小部件称为frame_aframe_b。在这个脚本中,frame_a包含带有文本的标签"I'm in Frame A", 和frame_b包含标签"I'm in Frame B"。这是执行此操作的一种方法:

import tkinter as tk

window = tk.Tk()

frame_a = tk.Frame()
frame_b = tk.Frame()

label_a = tk.Label(master=frame_a, text="I'm in Frame A")
label_a.pack()

label_b = tk.Label(master=frame_b, text="I'm in Frame B")
label_b.pack()

frame_a.pack()
frame_b.pack()

window.mainloop()

注意frame_a之前被打包到窗口中frame_b。打开的窗口显示标签frame_a标签上方frame_b:

A Tkinter window containg two Frame widgets stacked vertically, with the text "I'm in Frame A" in the top Frame, and "I'm in Frame B" in the bottom Frame

现在看看当你交换顺序时会发生什么frame_a.pack()frame_b.pack():

import tkinter as tk

window = tk.Tk()

frame_a = tk.Frame()
label_a = tk.Label(master=frame_a, text="I'm in Frame A")
label_a.pack()

frame_b = tk.Frame()
label_b = tk.Label(master=frame_b, text="I'm in Frame B")
label_b.pack()

# Swap the order of `frame_a` and `frame_b`
frame_b.pack()
frame_a.pack()

window.mainloop()

输出如下所示:

A Tkinter window containg two Frame widgets stacked vertically, with the text "I'm in Frame B" in the top Frame, and "I'm in Frame A" in the bottom Frame

现在label_b在上面。自从label_b被分配给frame_b,它移动到任何地方frame_b已定位。

您所了解的所有四种小部件类型 -Label, Button, Entry, 和Text-有一个master实例化它们时设置的属性。这样,您就可以控制哪个Frame一个小部件被分配给。Frame小部件非常适合以逻辑方式组织其他小部件。相关的小部件可以分配给同一框架,这样,如果框架在窗口中移动,那么相关的小部件就会保持在一起。

笔记:如果您省略master创建新的小部件实例时的参数,那么默认情况下它将被放置在顶级窗口内。

除了按逻辑对小部件进行分组之外,Frame小部件可以为视觉呈现您的申请。继续阅读以了解如何创建各种边框Frame小部件。

通过浮雕调整框架外观

Frame小部件可以配置为relief在框架周围创建边框的属性。您可以设置relief为以下任意值:

  • tk.FLAT:无边框效果(默认值)
  • tk.SUNKEN:打造下沉效果
  • tk.RAISED:创造出凸起的效果
  • tk.GROOVE:创建凹槽边框效果
  • tk.RIDGE:创建脊状效果

要应用边框效果,您必须设置borderwidth属性值大于1。该属性调整边框的宽度(以像素为单位)。感受每种效果的最佳方法是亲自查看它们。这是一个包含五个内容的脚本Frame将小部件放入窗口中,每个小部件都有不同的值relief争论:

 1import tkinter as tk
 2
 3border_effects = {
 4    "flat": tk.FLAT,
 5    "sunken": tk.SUNKEN,
 6    "raised": tk.RAISED,
 7    "groove": tk.GROOVE,
 8    "ridge": tk.RIDGE,
 9}
10
11window = tk.Tk()
12
13for relief_name, relief in border_effects.items():
14    frame = tk.Frame(master=window, relief=relief, borderwidth=5)
15    frame.pack(side=tk.LEFT)
16    label = tk.Label(master=frame, text=relief_name)
17    label.pack()
18
19window.mainloop()

以下是该脚本的详细说明:

  • 第 3 至 9 行创建一个字典其键是 Tkinter 中可用的不同浮雕效果的名称。这些值是相应的 Tkinter 对象。这本词典被分配给border_effects多变的。

  • 13号线开始一个for循环循环遍历中的每个项目border_effects字典。

  • 14号线创建一个新的Frame小部件并将其分配给window目的。这relief属性设置为相应的浮雕border_effects字典,以及border属性设置为5以便效果可见。

  • 15号线包装了Frame进入窗口使用.pack()。这side关键字参数告诉 Tkinter 以哪个方向打包frame对象。您将在下一节中详细了解其工作原理。

  • 16、17号线创建一个Label小部件显示浮雕的名称并将其打包到frame您刚刚创建的对象。

上述脚本生成的窗口如下所示:

A Tkinter window containing 5 Frame widgets, each with one of the five relief values: tk.FLAT, tk.SUNKET, tk.RAISED, tk.GROOVE, and tk.RIDGE

在此图像中,您可以看到以下效果:

  • tk.FLAT创建一个看起来平坦的框架。
  • tk.SUNKEN添加边框,使框架看起来像是凹陷到窗户中。
  • tk.RAISED给框架一个边框,使它看起来从屏幕上伸出来。
  • tk.GROOVE在平坦的框架周围添加一个看起来像凹陷凹槽的边框。
  • tk.RIDGE使框架边缘周围出现凸起的唇缘。

这些效果为您的 Python GUI Tkinter 应用程序提供了一些视觉吸引力。

了解小部件命名约定

当您创建一个小部件时,您可以给它任何您喜欢的名称,只要它是有效的 Python 标识符。通常最好将小部件类的名称包含在分配给小部件实例的变量名称中。例如,如果一个Labelwidget用于显示用户的名字,那么你可以给这个widget命名label_user_name。一个Entry用于收集用户年龄的小部件可能被称为entry_age.

笔记:有时,您可以定义一个新的小部件而不将其分配给变量。你会称其为.pack()方法直接在同一行代码上:

>>>
>>> tk.Label(text="Hello, Tkinter").pack()

当您稍后不打算引用小部件的实例时,这可能会很有帮助。由于自动内存管理,Python通常会垃圾收集此类未分配的对象,但 Tkinter 通过在内部注册每个新小部件来防止这种情况发生。

当您在变量名称中包含小部件类名称时,您可以帮助自己和其他需要阅读您的代码的人了解变量名称所指的小部件类型。但是,使用小部件类的全名可能会导致变量名称很长,因此您可能需要采用简写来引用每个小部件类型。在本教程的其余部分中,您将使用以下速记前缀来命名小部件:

Widget Class Variable Name Prefix Example
Label lbl lbl_name
Button btn btn_submit
Entry ent ent_age
Text txt txt_notes
Frame frm frm_address

在本节中,您学习了如何创建窗口、使用小部件以及使用框架。此时,您可以制作一些显示消息的普通窗口,但您还没有创建一个成熟的应用程序。在下一节中,您将学习如何使用 Tkinter 强大的几何管理器来控制应用程序的布局。

检查你的理解情况

展开下面的代码块进行练习以检查您的理解情况:

编写一个完整的脚本来显示Entry小部件的宽度为 40 个文本单元,具有白色背景和黑色文本。使用.insert()在小部件中显示文本:What is your name?.

输出窗口应如下所示:

A Tkinter window containing an Entry widget with the text "What is your name?"

现在尝试这个练习。

您可以展开下面的代码块来查看解决方案:

有几种方法可以解决这个练习。这是一种使用以下方法的解决方案bgfg参数来设置Entry小部件的背景和前景色:

import tkinter as tk

window = tk.Tk()

entry = tk.Entry(width=40, bg="white", fg="black")
entry.pack()

entry.insert(0, "What is your name?")

window.mainloop()

这个解决方案很棒,因为它明确地设置了背景色和前景色Entry小部件。

在大多数系统上,默认背景颜色Entry小部件为白色,默认前景色为黑色。因此,您也许可以使用以下命令生成相同的窗口bgfg省略参数:

import tkinter as tk

window = tk.Tk()

entry = tk.Entry(width=40)
entry.pack()

entry.insert(0, "What is your name?")

window.mainloop()

请记住,您的代码可能看起来有所不同。

准备好后,您可以继续下一部分。

使用几何管理器控制布局

到目前为止,您一直在向窗口添加小部件,Frame小部件使用.pack(),但你还没有了解这个方法到底是做什么的。让我们把事情弄清楚吧! Tkinter 中的应用程序布局由以下命令控制几何管理器。尽管.pack()是几何管理器的一个示例,但它不是唯一的。 Tkinter 还有另外两个:

  • .place()
  • .grid()

每个窗口或Frame在您的应用程序中只能使用一个几何管理器。但是,不同的框架可以使用不同的几何管理器,即使它们被分配给使用另一个几何管理器的框架或窗口。首先仔细看看.pack().

.pack()几何管理器

.pack()几何管理器使用打包算法将小部件放置在Frame或按指定顺序的窗口。对于给定的小部件,打包算法有两个主要步骤:

  1. 计算一个称为 a 的矩形面积包裹它的高度(或宽度)足以容纳小部件,并用空白空间填充窗口中的剩余宽度(或高度)。
  2. 除非指定了不同的位置,否则将小部件置于地块的中心。

.pack()很强大,但很难想象。感受的最佳方式.pack()就是看一些例子。看看当你.pack()Label小部件到Frame:

import tkinter as tk

window = tk.Tk()

frame1 = tk.Frame(master=window, width=100, height=100, bg="red")
frame1.pack()

frame2 = tk.Frame(master=window, width=50, height=50, bg="yellow")
frame2.pack()

frame3 = tk.Frame(master=window, width=25, height=25, bg="blue")
frame3.pack()

window.mainloop()

.pack()每个地方Frame默认情况下,按照分配给窗口的顺序位于前一个下方:

A Tkinter window with three colored squares packed vertically

每个Frame被放置在最上面的可用位置。因此,红Frame放置在窗口的顶部。然后是黄色Frame位于红色和蓝色的正下方Frame就在黄色的下面。

有三个看不见的包裹,每个包裹都包含三个中的一个Frame小部件。每个包裹都与窗户一样宽,与窗户一样高Frame它包含的。因为没有锚点被指定时.pack()被要求为每个Frame,它们都集中在各自包裹内。这就是为什么每个Frame位于窗口中央。

.pack()接受一些关键字参数以更精确地配置小部件放置。例如,您可以设置fill关键字参数指定其中方向框架应该填满。选项有tk.X填充水平方向,tk.Y垂直填充,并且tk.BOTH填充两个方向。以下是如何堆叠三个框架,以便每个框架水平填充整个窗口:

import tkinter as tk

window = tk.Tk()

frame1 = tk.Frame(master=window, height=100, bg="red")
frame1.pack(fill=tk.X)

frame2 = tk.Frame(master=window, height=50, bg="yellow")
frame2.pack(fill=tk.X)

frame3 = tk.Frame(master=window, height=25, bg="blue")
frame3.pack(fill=tk.X)

window.mainloop()

请注意,width没有设置在任何Frame小部件。width不再需要,因为每个帧都设置.pack()水平填充,覆盖您可能设置的任何宽度。

该脚本生成的窗口如下所示:

A Tkinter window containing three colored frames packed vertically and expanded horizontally to fill the entire window

填充窗口的好处之一.pack()是填充是反应灵敏来调整窗口大小。尝试扩大先前脚本生成的窗口,看看它是如何工作的。当你加宽窗户时,三个的宽度Frame小部件会增长以填充窗口:

A Tkinter window that expands horizontally with window resizing

但请注意,Frame小部件不会在垂直方向上扩展。

side的关键字参数.pack()指定小部件应放置在窗口的哪一侧。这些是可用的选项:

  • tk.TOP
  • tk.BOTTOM
  • tk.LEFT
  • tk.RIGHT

如果你不设置side, 然后.pack()会自动使用tk.TOP并将新的小部件放置在窗口的顶部,或者放置在尚未被小部件占据的窗口的最顶部。例如,以下脚本从左到右并排放置三个框架,并扩展每个框架以垂直填充窗口:

import tkinter as tk

window = tk.Tk()

frame1 = tk.Frame(master=window, width=200, height=100, bg="red")
frame1.pack(fill=tk.Y, side=tk.LEFT)

frame2 = tk.Frame(master=window, width=100, bg="yellow")
frame2.pack(fill=tk.Y, side=tk.LEFT)

frame3 = tk.Frame(master=window, width=50, bg="blue")
frame3.pack(fill=tk.Y, side=tk.LEFT)

window.mainloop()

这次,您必须指定height至少在其中一个框架上使用关键字参数来强制窗口具有一定的高度。

结果窗口如下所示:

A Tkinter window containing three colored frames packed horizontally and expanded vertically to fill the entire window

就像你设置时一样fill=tk.X要使框架在水平调整窗口大小时做出响应,您可以设置fill=tk.Y使框架在垂直调整窗口大小时做出响应:

A Tkinter window that expands vertically with window resizing

为了使布局真正响应,您可以使用以下命令设置框架的初始大小widthheight属性。然后,设置fill的关键字参数.pack()tk.BOTH并设置expand关键字参数True:

import tkinter as tk

window = tk.Tk()

frame1 = tk.Frame(master=window, width=200, height=100, bg="red")
frame1.pack(fill=tk.BOTH, side=tk.LEFT, expand=True)

frame2 = tk.Frame(master=window, width=100, bg="yellow")
frame2.pack(fill=tk.BOTH, side=tk.LEFT, expand=True)

frame3 = tk.Frame(master=window, width=50, bg="blue")
frame3.pack(fill=tk.BOTH, side=tk.LEFT, expand=True)

window.mainloop()

当您运行上述脚本时,您将看到一个窗口,该窗口最初看起来与上一个示例中生成的窗口相同。不同之处在于,现在您可以根据需要调整窗口大小,并且框架将响应地扩展并填充窗口:

A Tkinter window that expands both horizontally and vertically with window resizing

很酷!

.place()几何管理器

您可以使用.place()来控制精确位置小部件应该占据窗口或Frame。您必须提供两个关键字参数,xy,它指定小部件左上角的 x 和 y 坐标。两个都xy以像素为单位,而不是文本单位。

请记住,起源, 在哪里xy都是0,是左上角Frame或窗口。所以,你可以想到y的论证.place()作为距窗口顶部的像素数,以及x参数为距窗口左边缘的像素数。

下面是一个示例,说明如何.place()几何管理器的工作原理:

 1import tkinter as tk
 2
 3window = tk.Tk()
 4
 5frame = tk.Frame(master=window, width=150, height=150)
 6frame.pack()
 7
 8label1 = tk.Label(master=frame, text="I'm at (0, 0)", bg="red")
 9label1.place(x=0, y=0)
10
11label2 = tk.Label(master=frame, text="I'm at (75, 75)", bg="yellow")
12label2.place(x=75, y=75)
13
14window.mainloop()

这段代码的工作原理如下:

  • 5号线和6号线创建一个新的Frame小部件称为frame, 测量150像素宽和150像素高,并将其打包到窗口中.pack().
  • 8号线和9号线创建一个新的Label被称为label1具有红色背景并将其放置在frame1在位置 (0, 0)。
  • 11、12号线创建第二个Label被称为label2具有黄色背景并将其放置在frame1在位置 (75, 75)。

这是代码生成的窗口:

A Tkinter window containing two Label widgets laid out using the .place() geometry manager

请注意,如果您在使用不同字体大小和样式的不同操作系统上运行此代码,则第二个标签可能会被窗口边缘部分遮挡。这就是为什么.place()不经常使用。除此之外,它还有两个主要缺点:

  1. 布局可能很难管理.place()。如果您的应用程序有很多小部件,则尤其如此。
  2. 使用创建的布局.place()没有反应。它们不会随着窗口大小的调整而改变。

跨平台 GUI 开发的主要挑战之一是使布局无论在哪个平台上查看都看起来不错,并且.place()对于制作响应式和跨平台布局来说是一个糟糕的选择。

这并不是说你永远不应该使用.place()!在某些情况下,它可能正是您所需要的。例如,如果您正在为地图创建 GUI 界面,那么.place()可能是确保小部件在地图上彼此之间保持正确距离的完美选择。

.pack()通常是比.place(),但即使.pack()有一些缺点。小部件的放置取决于其中的顺序.pack()被调用,因此在不完全理解控制布局的代码的情况下修改现有应用程序可能很困难。这.grid()几何管理器解决了很多此类问题,您将在下一节中看到。

.grid()几何管理器

您最常使用的几何管理器是.grid(),它提供了所有的力量.pack()采用更易于理解和维护的格式。

.grid()通过分割窗口或Frame分成行和列。您可以通过调用指定小部件的位置.grid()并将行索引和列索引传递给rowcolumn分别是关键字参数。行索引和列索引都从0,所以行索引为1和列索引2告诉.grid()将小部件放置在第二行的第三列中。

以下脚本创建一个 3 × 3 的框架网格Label装入其中的小部件:

import tkinter as tk

window = tk.Tk()

for i in range(3):
    for j in range(3):
        frame = tk.Frame(
            master=window,
            relief=tk.RAISED,
            borderwidth=1
        )
        frame.grid(row=i, column=j)
        label = tk.Label(master=frame, text=f"Row {i}\nColumn {j}")
        label.pack()

window.mainloop()

结果窗口如下所示:

A Tkinter window containing a 3 x 3 grid of Frame widgets with Label widgets packed into them

在本例中您使用了两个几何管理器。每个框架都附加到window.grid()几何管理器:

import tkinter as tk

window = tk.Tk()

for i in range(3):
    for j in range(3):
        frame = tk.Frame(
            master=window,
            relief=tk.RAISED,
            borderwidth=1
        )
        frame.grid(row=i, column=j)
        label = tk.Label(master=frame, text=f"Row {i}\nColumn {j}")
        label.pack()

window.mainloop()

每个label依附于其主人Frame.pack():

import tkinter as tk

window = tk.Tk()

for i in range(3):
    for j in range(3):
        frame = tk.Frame(
            master=window,
            relief=tk.RAISED,
            borderwidth=1
        )
        frame.grid(row=i, column=j)
        label = tk.Label(master=frame, text=f"Row {i}\nColumn {j}")
        label.pack()

window.mainloop()

这里要认识到的重要一点是,即使.grid()每个都被调用Frame对象,几何管理器应用于window目的。同样,每个的布局frame控制与.pack()几何管理器。

上一示例中的框架彼此紧密相邻放置。要在每个框架周围添加一些空间,您可以设置网格中每个单元格的填充。填充只是围绕小部件的一些空白区域,并在视觉上将其内容分开。

两种类型的填充是外部的内部填充。外部填充在网格单元外部添加一些空间。它由两个关键字参数控制.grid():

  1. padx添加水平方向的填充。
  2. pady在垂直方向添加内边距。

两个都padxpady以像素为单位而不是文本单位进行测量,因此将它们设置为相同的值将在两个方向上创建相同数量的填充。尝试在上一个示例中的框架外部添加一些填充:

import tkinter as tk

window = tk.Tk()

for i in range(3):
    for j in range(3):
        frame = tk.Frame(
            master=window,
            relief=tk.RAISED,
            borderwidth=1
        )
        frame.grid(row=i, column=j, padx=5, pady=5)
        label = tk.Label(master=frame, text=f"Row {i}\nColumn {j}")
        label.pack()

window.mainloop()

这是生成的窗口:

A Tkinter window containing a 3 x 3 grid of Frame widgets with Label widgets packed into them. Each grid cell has 5 pixels of exterior padding.

.pack()也有padxpady参数。以下代码与前面的代码几乎相同,不同之处在于您在两个标签中的每个标签周围添加了五个像素的附加填充xy方向:

import tkinter as tk

window = tk.Tk()

for i in range(3):
    for j in range(3):
        frame = tk.Frame(
            master=window,
            relief=tk.RAISED,
            borderwidth=1
        )
        frame.grid(row=i, column=j, padx=5, pady=5)
        label = tk.Label(master=frame, text=f"Row {i}\nColumn {j}")
        label.pack(padx=5, pady=5)

window.mainloop()

周围的额外填充Label小部件为网格中的每个单元格之间提供了一点喘息空间Frame边框和标签中的文本:

A Tkinter window containing a 3 x 3 grid of Frame widgets with Label widgets packed into them. Each grid cell and Label widget has 5 pixels of exterior padding.

看起来很不错!但是,如果您尝试向任何方向扩展窗口,那么您会注意到布局的响应速度不是很好:

A Tkinter window containing a 3 x 3 grid that does not expand with window resizing

当窗口展开时,整个网格保持在左上角。

通过使用.columnconfigure().rowconfigure()window对象,您可以调整网格的行和列随着窗口大小的调整而增长的方式。请记住,网格附加到window,即使你打电话.grid()在各个Frame小部件。两个都.columnconfigure().rowconfigure()采取三个基本论点:

  1. 指数:您要配置的网格列或行的索引或同时配置多行或多列的索引列表
  2. 重量:一个名为的关键字参数weight确定列或行相对于其他列和行应如何响应窗口大小调整
  3. 最小尺寸:一个名为的关键字参数minsize设置行高或列宽的最小尺寸(以像素为单位)

weight被设定为0默认情况下,这意味着列或行不会随着窗口大小的调整而扩展。如果每列或行的权重为1,那么它们都以相同的速度增长。如果一列的权重为1另一个重量为2,那么第二列的膨胀速度是第一列的两倍。调整之前的脚本以更好地处理窗口大小调整:

import tkinter as tk

window = tk.Tk()

for i in range(3):
    window.columnconfigure(i, weight=1, minsize=75)
    window.rowconfigure(i, weight=1, minsize=50)

    for j in range(0, 3):
        frame = tk.Frame(
            master=window,
            relief=tk.RAISED,
            borderwidth=1
        )
        frame.grid(row=i, column=j, padx=5, pady=5)
        label = tk.Label(master=frame, text=f"Row {i}\nColumn {j}")
        label.pack(padx=5, pady=5)

window.mainloop()

.columnconfigure().rowconfigure()被放置在外部的主体中for环形。您可以显式配置外部的每一列和行for循环,但这需要额外编写六行代码。

在循环的每次迭代中,i-th 列和行的权重配置为1。这可确保每当调整窗口大小时行和列以相同的速率扩展。这minsize参数设置为75对于每一列和50对于每一行。这确保了Label即使窗口尺寸非常小,小部件也始终显示其文本而不会截断任何字符。

结果是网格布局随着窗口大小的调整而平滑地扩展和收缩:

A Tkinter window containing a fully responsive 3 x 3 grid layout

亲自尝试一下,感受一下它是如何工作的!玩转weightminsize参数以查看它们如何影响网格。

默认情况下,小部件位于其网格单元的中心。例如,以下代码创建两个Label小部件并将它们放置在一列两行的网格中:

import tkinter as tk

window = tk.Tk()
window.columnconfigure(0, minsize=250)
window.rowconfigure([0, 1], minsize=100)

label1 = tk.Label(text="A")
label1.grid(row=0, column=0)

label2 = tk.Label(text="B")
label2.grid(row=1, column=0)

window.mainloop()

每个网格单元是250像素宽和100像素高。标签放置在每个单元格的中心,如下图所示:

A Tkinter window with grid geometry manager and custom row and column sizes

您可以使用以下命令更改网格单元内每个标签的位置sticky参数,它接受包含以下一个或多个字母的字符串:

  • "n"或者"N"与单元格的顶部中心部分对齐
  • "e"或者"E"与单元格的右中心对齐
  • "s"或者"S"与单元格的底部中心部分对齐
  • "w"或者"W"与单元格的左中心对齐

这些信"n", "s", "e", 和"w"来自北、南、东、西四个基本方向。环境sticky"n"在前面的代码中的两个标签上,每个标签都位于其网格单元的顶部中心:

import tkinter as tk

window = tk.Tk()
window.columnconfigure(0, minsize=250)
window.rowconfigure([0, 1], minsize=100)

label1 = tk.Label(text="A")
label1.grid(row=0, column=0, sticky="n")

label2 = tk.Label(text="B")
label2.grid(row=1, column=0, sticky="n")

window.mainloop()

这是输出:

A Tkinter window with grid geometry manager and sticky set to "North"

您可以将多个字母组合在一个字符串中,将每个标签放置在其网格单元的角落:

import tkinter as tk

window = tk.Tk()
window.columnconfigure(0, minsize=250)
window.rowconfigure([0, 1], minsize=100)

label1 = tk.Label(text="A")
label1.grid(row=0, column=0, sticky="ne")

label2 = tk.Label(text="B")
label2.grid(row=1, column=0, sticky="sw")

window.mainloop()

在此示例中,sticky的参数label1被设定为"ne",它将标签放置在其网格单元的右上角。label2通过传递定位在左下角"sw"sticky。这是窗口中的样子:

A Tkinter window with grid geometry manager and sticky set to "Northeast" and "Southwest"

当小部件定位时sticky,小部件本身的大小足以容纳其中的任何文本和其他内容。它不会填充整个网格单元。为了填充网格,您可以指定"ns"强制小部件在垂直方向填充单元格,或者"ew"以水平方向填充单元格。要填充整个单元格,请设置sticky"nsew"。以下示例说明了每个选项:

import tkinter as tk

window = tk.Tk()

window.rowconfigure(0, minsize=50)
window.columnconfigure([0, 1, 2, 3], minsize=50)

label1 = tk.Label(text="1", bg="black", fg="white")
label2 = tk.Label(text="2", bg="black", fg="white")
label3 = tk.Label(text="3", bg="black", fg="white")
label4 = tk.Label(text="4", bg="black", fg="white")

label1.grid(row=0, column=0)
label2.grid(row=0, column=1, sticky="ew")
label3.grid(row=0, column=2, sticky="ns")
label4.grid(row=0, column=3, sticky="nsew")

window.mainloop()

输出如下所示:

A Tkinter window with grid geometry manager and sticky used to fill horizontally, vertically, and along both axes.

上面的例子说明的是.grid()几何管理器sticky使用参数可以达到与.pack()几何管理器fill范围。之间的对应关系stickyfill参数总结如下表:

.grid() .pack()
sticky="ns" fill=tk.Y
sticky="ew" fill=tk.X
sticky="nsew" fill=tk.BOTH

.grid()是一个强大的几何管理器。通常比以下更容易理解.pack()并且比.place()。当您创建新的 Tkinter 应用程序时,您应该考虑使用.grid()作为您的主要几何管理器。

笔记: .grid()提供比您在这里看到的更多的灵活性。例如,您可以将单元格配置为跨越多行和多列。欲了解更多信息,请查看网格几何管理器部分TkDocs 教程.

现在您已经了解了 Python GUI 框架 Tkinter 的几何管理器的基础知识,下一步是将操作分配给按钮以使您的应用程序栩栩如生。

检查你的理解情况

展开下面的代码块进行练习以检查您的理解情况:

下面是使用 Tkinter 制作的地址输入表单的图像:

An address entry form window built with Tkinter

编写重新创建窗口的完整脚本。您可以使用任何您喜欢的几何管理器。

您可以展开下面的代码块来查看解决方案:

有许多不同的方法可以解决这个练习。如果您的解决方案生成一个与练习语句中的窗口相同的窗口,那么恭喜您!您已经成功解决了练习!下面,您可以查看使用以下两个解决方案.grid()几何管理器。

一个解决方案创建了一个LabelEntry每个字段具有所需设置的小部件:

import tkinter as tk

# Create a new window with the title "Address Entry Form"
window = tk.Tk()
window.title("Address Entry Form")

# Create a new frame `frm_form` to contain the Label
# and Entry widgets for entering address information
frm_form = tk.Frame(relief=tk.SUNKEN, borderwidth=3)
# Pack the frame into the window
frm_form.pack()

# Create the Label and Entry widgets for "First Name"
lbl_first_name = tk.Label(master=frm_form, text="First Name:")
ent_first_name = tk.Entry(master=frm_form, width=50)
# Use the grid geometry manager to place the Label and
# Entry widgets in the first and second columns of the
# first row of the grid
lbl_first_name.grid(row=0, column=0, sticky="e")
ent_first_name.grid(row=0, column=1)

# Create the Label and Entry widgets for "Last Name"
lbl_last_name = tk.Label(master=frm_form, text="Last Name:")
ent_last_name = tk.Entry(master=frm_form, width=50)
# Place the widgets in the second row of the grid
lbl_last_name.grid(row=1, column=0, sticky="e")
ent_last_name.grid(row=1, column=1)

# Create the Label and Entry widgets for "Address Line 1"
lbl_address1 = tk.Label(master=frm_form, text="Address Line 1:")
ent_address1 = tk.Entry(master=frm_form, width=50)
# Place the widgets in the third row of the grid
lbl_address1.grid(row=2, column=0, sticky="e")
ent_address1.grid(row=2, column=1)

# Create the Label and Entry widgets for "Address Line 2"
lbl_address2 = tk.Label(master=frm_form, text="Address Line 2:")
ent_address2 = tk.Entry(master=frm_form, width=50)
# Place the widgets in the fourth row of the grid
lbl_address2.grid(row=3, column=0, sticky=tk.E)
ent_address2.grid(row=3, column=1)

# Create the Label and Entry widgets for "City"
lbl_city = tk.Label(master=frm_form, text="City:")
ent_city = tk.Entry(master=frm_form, width=50)
# Place the widgets in the fifth row of the grid
lbl_city.grid(row=4, column=0, sticky=tk.E)
ent_city.grid(row=4, column=1)

# Create the Label and Entry widgets for "State/Province"
lbl_state = tk.Label(master=frm_form, text="State/Province:")
ent_state = tk.Entry(master=frm_form, width=50)
# Place the widgets in the sixth row of the grid
lbl_state.grid(row=5, column=0, sticky=tk.E)
ent_state.grid(row=5, column=1)

# Create the Label and Entry widgets for "Postal Code"
lbl_postal_code = tk.Label(master=frm_form, text="Postal Code:")
ent_postal_code = tk.Entry(master=frm_form, width=50)
# Place the widgets in the seventh row of the grid
lbl_postal_code.grid(row=6, column=0, sticky=tk.E)
ent_postal_code.grid(row=6, column=1)

# Create the Label and Entry widgets for "Country"
lbl_country = tk.Label(master=frm_form, text="Country:")
ent_country = tk.Entry(master=frm_form, width=50)
# Place the widgets in the eight row of the grid
lbl_country.grid(row=7, column=0, sticky=tk.E)
ent_country.grid(row=7, column=1)

# Create a new frame `frm_buttons` to contain the
# Submit and Clear buttons. This frame fills the
# whole window in the horizontal direction and has
# 5 pixels of horizontal and vertical padding.
frm_buttons = tk.Frame()
frm_buttons.pack(fill=tk.X, ipadx=5, ipady=5)

# Create the "Submit" button and pack it to the
# right side of `frm_buttons`
btn_submit = tk.Button(master=frm_buttons, text="Submit")
btn_submit.pack(side=tk.RIGHT, padx=10, ipadx=10)

# Create the "Clear" button and pack it to the
# right side of `frm_buttons`
btn_clear = tk.Button(master=frm_buttons, text="Clear")
btn_clear.pack(side=tk.RIGHT, ipadx=10)

# Start the application
window.mainloop()

这个解决方案没有任何问题。虽然有点长,但是一切都非常明确。如果你想改变什么,那么很清楚要做什么。

也就是说,通过认识到每个Entry具有相同的宽度,并且每个都需要Label是文本:

import tkinter as tk

# Create a new window with the title "Address Entry Form"
window = tk.Tk()
window.title("Address Entry Form")

# Create a new frame `frm_form` to contain the Label
# and Entry widgets for entering address information
frm_form = tk.Frame(relief=tk.SUNKEN, borderwidth=3)
# Pack the frame into the window
frm_form.pack()

# List of field labels
labels = [
    "First Name:",
    "Last Name:",
    "Address Line 1:",
    "Address Line 2:",
    "City:",
    "State/Province:",
    "Postal Code:",
    "Country:",
]

# Loop over the list of field labels
for idx, text in enumerate(labels):
    # Create a Label widget with the text from the labels list
    label = tk.Label(master=frm_form, text=text)
    # Create an Entry widget
    entry = tk.Entry(master=frm_form, width=50)
    # Use the grid geometry manager to place the Label and
    # Entry widgets in the row whose index is idx
    label.grid(row=idx, column=0, sticky="e")
    entry.grid(row=idx, column=1)

# Create a new frame `frm_buttons` to contain the
# Submit and Clear buttons. This frame fills the
# whole window in the horizontal direction and has
# 5 pixels of horizontal and vertical padding.
frm_buttons = tk.Frame()
frm_buttons.pack(fill=tk.X, ipadx=5, ipady=5)

# Create the "Submit" button and pack it to the
# right side of `frm_buttons`
btn_submit = tk.Button(master=frm_buttons, text="Submit")
btn_submit.pack(side=tk.RIGHT, padx=10, ipadx=10)

# Create the "Clear" button and pack it to the
# right side of `frm_buttons`
btn_clear = tk.Button(master=frm_buttons, text="Clear")
btn_clear.pack(side=tk.RIGHT, ipadx=10)

# Start the application
window.mainloop()

在此解决方案中,列表用于存储表单中每个标签的字符串。它们按照每个表单字段应出现的顺序存储。然后,枚举()从每个值中获取索引和字符串labels列表。

准备好后,您可以继续下一部分。

使您的应用程序具有交互性

到目前为止,您已经非常了解如何使用 Tkinter 创建窗口、添加一些小部件以及控制应用程序布局。这很好,但应用程序不应该只是看起来不错——它们实际上需要做一些事情!在本节中,您将学习如何通过在特定情况下执行操作来使您的应用程序栩栩如生。事件发生。

使用事件和事件处理程序

当您创建 Tkinter 应用程序时,您必须调用window.mainloop()开始事件循环。在事件循环期间,您的应用程序会检查事件是否已发生。如果是这样,那么它将执行一些代码作为响应。

Tkinter 为您提供了事件循环,因此您不必自己编写任何检查事件的代码。但是,您必须编写响应事件而执行的代码。在 Tkinter 中,您可以编写名为事件处理程序用于您在应用程序中使用的事件。

笔记:一个事件是事件循环期间发生的任何可能触发应用程序中某些行为的操作,例如按下按键或鼠标按钮时。

当事件发生时,事件对象被发出,这意味着创建了代表该事件的类的实例。您无需担心自己实例化这些类。 Tkinter 将自动为您创建事件类的实例。

您将编写自己的事件循环,以便更好地理解 Tkinter 的事件循环是如何工作的。这样,您就可以了解 Tkinter 的事件循环如何适合您的应用程序,以及您需要自己编写哪些部分。

假设有一个名为events包含事件对象。新的事件对象会自动附加到events每当程序中发生事件时。您不需要实现这种更新机制。在这个概念性示例中,它会自动发生。使用无限循环,您可以不断检查是否有任何事件对象events:

# Assume that this list gets updated automatically
events = []

# Run the event loop
while True:
    # If the event list is empty, then no events have occurred
    # and you can skip to the next iteration of the loop
    if events == []:
        continue

    # If execution reaches this point, then there is at least one
    # event object in the event list
    event = events[0]

现在,您创建的事件循环不执行任何操作event。让我们改变这一点。假设您的应用程序需要响应按键。你需要检查一下event由用户按下键盘上的某个键生成,如果是,则传递event按键事件处理函数。

假使,假设event有一个.type属性设置为字符串"keypress"如果事件是按键事件对象,并且.char包含按下的键的字符的属性。创建一个新的handle_keypress()函数并更新您的事件循环代码:

events = []

# Create an event handler
def handle_keypress(event):
    """Print the character associated to the key pressed"""
    print(event.char)

while True:
    if events == []:
        continue

    event = events[0]

    # If event is a keypress event object
    if event.type == "keypress":
        # Call the keypress event handler
        handle_keypress(event)

你打电话时window.mainloop(),类似上面的循环会为您运行。此方法为您处理循环的两个部分:

  1. 它保持着一个活动清单已经发生的事情。
  2. 它运行一个事件处理程序任何时候有新事件添加到该列表中。

更新您的事件循环以使用window.mainloop()而不是你自己的事件循环:

import tkinter as tk

# Create a window object
window = tk.Tk()

# Create an event handler
def handle_keypress(event):
    """Print the character associated to the key pressed"""
    print(event.char)

# Run the event loop
window.mainloop()

.mainloop()为你处理了很多事情,但是上面的代码缺少一些东西。 Tkinter 如何知道何时使用handle_keypress()? Tkinter 小部件有一个名为的方法.bind()就是为了这个目的。

使用.bind()

要在小部件上发生事件时调用事件处理程序,请使用.bind()。事件处理程序据说是边界事件,因为每次事件发生时都会调用它。您将继续上一节中的按键示例并使用.bind()绑定handle_keypress()到按键事件:

import tkinter as tk

window = tk.Tk()

def handle_keypress(event):
    """Print the character associated to the key pressed"""
    print(event.char)

# Bind keypress event to handle_keypress()
window.bind("<Key>", handle_keypress)

window.mainloop()

在这里,handle_keypress()事件处理程序绑定到"<Key>"事件使用window.bind()。当应用程序运行时,只要按下一个键,您的程序就会打印按下的键的字符。

笔记:上述程序的输出是not在 Tkinter 应用程序窗口中打印。它被打印到标准输出流(stdout).

如果您在空闲状态下运行该程序,那么您将在交互式窗口中看到输出。如果您从终端运行该程序,那么您应该在终端中看到输出。

.bind()总是至少有两个参数:

  1. An 事件由以下形式的字符串表示"<event_name>", 在哪里event_name可以是 Tkinter 的任何事件
  2. An 事件处理程序这是事件发生时要调用的函数的名称

事件处理程序绑定到其上的小部件.bind()叫做。当事件处理程序被调用时,事件对象被传递给事件处理函数。

在上面的示例中,事件处理程序绑定到窗口本身,但您可以将事件处理程序绑定到应用程序中的任何小部件。例如,您可以将事件处理程序绑定到Button每当按下按钮时就会执行一些操作的小部件:

def handle_click(event):
    print("The button was clicked!")

button = tk.Button(text="Click me!")

button.bind("<Button-1>", handle_click)

在此示例中,"<Button-1>"事件于button小部件绑定到handle_click事件处理程序。这"<Button-1>"当鼠标悬停在小部件上时按下鼠标左键时,事件就会发生。还有其他鼠标按钮单击事件,包括"<Button-2>"对于鼠标中键和"<Button-3>"鼠标右键。

笔记:有关常用事件的列表,请参阅事件类型的部分Tkinter 8.5 参考.

您可以将任何事件处理程序绑定到任何类型的小部件.bind(),但是有一种更直接的方法可以使用以下方法将事件处理程序绑定到按钮单击Button小部件的command属性。

使用command

每一个Button小部件有一个command您可以分配给函数的属性。每当按下按钮时,就会执行该功能。

看一个例子。首先,您将创建一个带有Label保存数值的小部件。您将在标签的左侧和右侧放置按钮。左边的按钮将用于减少数值Label,正确的会增加价值。这是窗口的代码:

 1import tkinter as tk
 2
 3window = tk.Tk()
 4
 5window.rowconfigure(0, minsize=50, weight=1)
 6window.columnconfigure([0, 1, 2], minsize=50, weight=1)
 7
 8btn_decrease = tk.Button(master=window, text="-")
 9btn_decrease.grid(row=0, column=0, sticky="nsew")
10
11lbl_value = tk.Label(master=window, text="0")
12lbl_value.grid(row=0, column=1)
13
14btn_increase = tk.Button(master=window, text="+")
15btn_increase.grid(row=0, column=2, sticky="nsew")
16
17window.mainloop()

窗口看起来像这样:

A Tkinter application with increase and decrease buttons that increase and decrease a counter

定义了应用程序布局后,您可以通过向按钮发出一些命令来使其变得栩栩如生。从左按钮开始。按下此按钮时,标签中的值应减一。为此,您首先需要获得两个问题的答案:

  1. 怎么把文字输入进去Label?
  2. 你如何更新文本Label?

Label小部件没有.get()喜欢EntryText小部件可以。但是,您可以通过访问从标签中检索文本text具有字典式下标表示法的属性:

label = tk.Label(text="Hello")

# Retrieve a label's text
text = label["text"]

# Set new text for the label
label["text"] = "Good bye"

现在您已经知道如何获取和设置标签的文本,请编写一个increase()增加值的函数lbl_value通过一:

 1import tkinter as tk
 2
 3def increase():
 4    value = int(lbl_value["text"])
 5    lbl_value["text"] = f"{value + 1}"
 6
 7# ...

increase()获取文本来自lbl_value并将其转换为整数int()。然后,它将该值加一并设置标签的text归因于这个新值。

你还需要decrease()减少值value_label通过一:

 5# ...
 6
 7def decrease():
 8    value = int(lbl_value["text"])
 9    lbl_value["text"] = f"{value - 1}"
10
11# ...

increase()decrease()在你的代码之后import陈述。

要将按钮连接到功能,请将功能分配给按钮的command属性。您可以在实例化按钮时执行此操作。例如,将实例化按钮的两行更新为以下内容:

14# ...
15
16btn_decrease = tk.Button(master=window, text="-", command=decrease)
17btn_decrease.grid(row=0, column=0, sticky="nsew")
18
19lbl_value = tk.Label(master=window, text="0")
20lbl_value.grid(row=0, column=1)
21
22btn_increase = tk.Button(master=window, text="+", command=increase)
23btn_increase.grid(row=0, column=2, sticky="nsew")
24
25window.mainloop()

这就是将按钮绑定到所需要做的全部工作increase()decrease()并使程序正常运行。尝试保存您的更改并运行应用程序!单击按钮可增加和减少窗口中心的值:

A counter app built with Tkinter

以下是完整的应用程序代码供您参考:

import tkinter as tk

def increase():
    value = int(lbl_value["text"])
    lbl_value["text"] = f"{value + 1}"

def decrease():
    value = int(lbl_value["text"])
    lbl_value["text"] = f"{value - 1}"

window = tk.Tk()

window.rowconfigure(0, minsize=50, weight=1)
window.columnconfigure([0, 1, 2], minsize=50, weight=1)

btn_decrease = tk.Button(master=window, text="-", command=decrease)
btn_decrease.grid(row=0, column=0, sticky="nsew")

lbl_value = tk.Label(master=window, text="0")
lbl_value.grid(row=0, column=1)

btn_increase = tk.Button(master=window, text="+", command=increase)
btn_increase.grid(row=0, column=2, sticky="nsew")

window.mainloop()

这个应用程序并不是特别有用,但您在这里学到的技能适用于您将制作的每个应用程序:

  • 使用小部件创建用户界面的组件。
  • 使用几何管理器控制应用程序的布局。
  • 事件处理程序与各种组件交互以捕获和转换用户输入。

在接下来的两节中,您将构建更有用的应用程序。首先,您将构建一个温度转换器,将温度值从华氏度转换为摄氏度。之后,您将构建一个可以打开、编辑和保存文本文件的文本编辑器!

检查你的理解情况

展开下面的代码块进行练习以检查您的理解情况:

编写一个程序来模拟滚动六面骰子。应该有一个带有文本的按钮Roll。当用户单击该按钮时,会出现一个随机整数16应该显示。

暗示:您可以使用生成随机数randint()在里面随机的模块。如果您不熟悉random模块,然后查看在 Python 中生成随机数据(指南)了解更多信息。

应用程序窗口应如下所示:

A Tkinter application with a "Roll" button that produces a random number between 1 and 6

现在尝试这个练习。

您可以展开下面的代码块来查看解决方案:

这是一种可能的解决方案:

import random
import tkinter as tk

def roll():
    lbl_result["text"] = str(random.randint(1, 6))

window = tk.Tk()
window.columnconfigure(0, minsize=150)
window.rowconfigure([0, 1], minsize=50)

btn_roll = tk.Button(text="Roll", command=roll)
lbl_result = tk.Label()

btn_roll.grid(row=0, column=0, sticky="nsew")
lbl_result.grid(row=1, column=0)

window.mainloop()

请记住,您的代码可能看起来有所不同。

准备好后,您可以继续下一部分。

构建温度转换器(示例应用程序)

在本节中,您将构建一个温度转换器应用允许用户输入华氏温度,然后按下按钮将该温度转换为摄氏度。您将逐步浏览代码。您还可以在本节末尾找到完整的源代码以供参考。

笔记:要充分利用本节,请按照以下步骤进行操作Python外壳.

在开始编码之前,您将首先设计应用程序。您需要三个要素:

  1. Entry:一个名为ent_temperature用于输入华氏度值
  2. Label:一个名为lbl_result显示摄氏度结果
  3. Button:一个名为btn_convert从读取值Entry小部件,将其从华氏温度转换为摄氏度,并设置文本Label单击时小部件到结果

您可以将它们排列在网格中,每个小部件具有单行和一列。这会给你一个最低限度工作的应用程序,但它不是很用户友好。一切都需要有标签.

您将直接将标签放在ent_temperature小部件包含华氏温度符号 (℉),以便用户知道该值ent_temperature应以华氏度为单位。为此,请将标签文本设置为"\N{DEGREE FAHRENHEIT}",它使用Python的命名Unicode 字符支持显示华氏度符号。

你可以给btn_convert通过将其文本设置为值来实现一点点天赋"\N{RIGHTWARDS BLACK ARROW}",它显示一个指向右侧的黑色箭头。您还要确保lbl_result标签文本后始终带有摄氏度符号 (℃)"\N{DEGREE CELSIUS}"表明结果以摄氏度为单位。最终窗口如下所示:

A temperature conversion application built with Tkinter

现在您知道需要什么小部件以及窗口的外观,您可以开始编码了!一、导入tkinter并创建一个新窗口:

 1import tkinter as tk
 2
 3window = tk.Tk()
 4window.title("Temperature Converter")
 5window.resizable(width=False, height=False)

window.title()设置现有窗口的标题,同时window.resizable()两个参数都设置为False使窗口具有固定的大小。当您最终运行该应用程序时,窗口将显示文本温度转换器在其标题栏中。接下来,创建ent_temperature带有名为的标签的小部件lbl_temp并将两者分配给Frame小部件称为frm_entry:

 5# ...
 6
 7frm_entry = tk.Frame(master=window)
 8ent_temperature = tk.Entry(master=frm_entry, width=10)
 9lbl_temp = tk.Label(master=frm_entry, text="\N{DEGREE FAHRENHEIT}")

用户将输入华氏度值ent_temperature, 和lbl_temp用于标记ent_temperature带有华氏度符号。这frm_entry容器组ent_temperaturelbl_temp一起。

你要lbl_temp直接放置在ent_temperature。你可以把它们放在frm_entry使用.grid()具有一行和两列的几何管理器:

 9# ...
10
11ent_temperature.grid(row=0, column=0, sticky="e")
12lbl_temp.grid(row=0, column=1, sticky="w")

您已经设置了sticky参数为"e"为了ent_temperature这样它总是粘在网格单元的最右边缘。你还设置了sticky"w"为了lbl_temp使其粘在网格单元的最左边缘。这确保了lbl_temp总是位于紧邻的右侧ent_temperature.

现在,使btn_convertlbl_result用于将输入的温度转换为ent_temperature并显示结果:

12# ...
13
14btn_convert = tk.Button(
15    master=window,
16    text="\N{RIGHTWARDS BLACK ARROW}"
17)
18lbl_result = tk.Label(master=window, text="\N{DEGREE CELSIUS}")

喜欢frm_entry, 两个都btn_convertlbl_result被分配给window。这三个小部件一起构成了主应用程序网格中的三个单元格。使用.grid()现在就开始布置它们:

18# ...
19
20frm_entry.grid(row=0, column=0, padx=10)
21btn_convert.grid(row=0, column=1, pady=10)
22lbl_result.grid(row=0, column=2, padx=10)

最后,运行应用程序:

22# ...
23
24window.mainloop()

看起来很棒!但该按钮还没有执行任何操作。在脚本文件的顶部,就在import行,添加一个名为fahrenheit_to_celsius():

 1import tkinter as tk
 2
 3def fahrenheit_to_celsius():
 4    """Convert the value for Fahrenheit to Celsius and insert the
 5    result into lbl_result.
 6    """
 7    fahrenheit = ent_temperature.get()
 8    celsius = (5 / 9) * (float(fahrenheit) - 32)
 9    lbl_result["text"] = f"{round(celsius, 2)} \N{DEGREE CELSIUS}"
10
11# ...

该函数读取值ent_temperature,将其从华氏温度转换为摄氏度,然后将结果显示在lbl_result.

现在转到您定义的行btn_convert并设置其command参数为fahrenheit_to_celsius:

20# ...
21
22btn_convert = tk.Button(
23    master=window,
24    text="\N{RIGHTWARDS BLACK ARROW}",
25    command=fahrenheit_to_celsius  # <--- Add this line
26)
27
28# ...

就是这样!您仅用二十六行代码就创建了一个功能齐全的温度转换器应用程序!很酷,对吧?

您可以展开下面的代码块以查看完整的脚本:

以下是完整的脚本供您参考:

import tkinter as tk

def fahrenheit_to_celsius():
    """Convert the value for Fahrenheit to Celsius and insert the
    result into lbl_result.
    """
    fahrenheit = ent_temperature.get()
    celsius = (5 / 9) * (float(fahrenheit) - 32)
    lbl_result["text"] = f"{round(celsius, 2)} \N{DEGREE CELSIUS}"

# Set up the window
window = tk.Tk()
window.title("Temperature Converter")
window.resizable(width=False, height=False)

# Create the Fahrenheit entry frame with an Entry
# widget and label in it
frm_entry = tk.Frame(master=window)
ent_temperature = tk.Entry(master=frm_entry, width=10)
lbl_temp = tk.Label(master=frm_entry, text="\N{DEGREE FAHRENHEIT}")

# Layout the temperature Entry and Label in frm_entry
# using the .grid() geometry manager
ent_temperature.grid(row=0, column=0, sticky="e")
lbl_temp.grid(row=0, column=1, sticky="w")

# Create the conversion Button and result display Label
btn_convert = tk.Button(
    master=window,
    text="\N{RIGHTWARDS BLACK ARROW}",
    command=fahrenheit_to_celsius
)
lbl_result = tk.Label(master=window, text="\N{DEGREE CELSIUS}")

# Set up the layout using the .grid() geometry manager
frm_entry.grid(row=0, column=0, padx=10)
btn_convert.grid(row=0, column=1, pady=10)
lbl_result.grid(row=0, column=2, padx=10)

# Run the application
window.mainloop()

是时候让事情更上一层楼了!继续阅读以了解如何构建文本编辑器。

构建文本编辑器(示例应用程序)

在本节中,您将构建一个文本编辑器应用程序可以创建、打开、编辑和保存文本文件。申请中包含三个基本要素:

  1. A Button小部件称为btn_open用于打开文件进行编辑
  2. A Button小部件称为btn_save用于保存文件
  3. A TextBox小部件称为txt_edit用于创建和编辑文本文件

这三个小部件将被排列,以便两个按钮位于窗口的左侧,文本框位于右侧。整个窗口的最小高度应为 800 像素,并且txt_edit最小宽度应为 800 像素。整个布局应该是响应式的,这样如果调整窗口大小,那么txt_edit也调整了大小。但是,容纳按钮的框架的宽度不应改变。

这是窗口外观的草图:

A design sketch for a text editor application

您可以使用以下命令实现所需的布局.grid()几何管理器。该布局包含单行和两列:

  1. 狭窄的柱子左侧为按钮
  2. 更宽的柱子在文本框的右侧

设置窗口的最小尺寸和txt_edit,您可以设置minsize窗口方法的参数.rowconfigure().columnconfigure()800。要处理调整大小,您可以设置weight这些方法的参数1.

为了使两个按钮进入同一列,您需要创建一个Frame小部件称为frm_buttons。根据草图,两个按钮应垂直堆叠在该框架内,btn_open在上面。您可以使用以下任一方法来做到这一点.grid()或者.pack()几何管理器。现在,您将坚持使用.grid()因为它更容易使用。

现在您已经有了计划,就可以开始编写应用程序了。第一步是创建您需要的所有小部件:

 1import tkinter as tk
 2
 3window = tk.Tk()
 4window.title("Simple Text Editor")
 5
 6window.rowconfigure(0, minsize=800, weight=1)
 7window.columnconfigure(1, minsize=800, weight=1)
 8
 9txt_edit = tk.Text(window)
10frm_buttons = tk.Frame(window, relief=tk.RAISED, bd=2)
11btn_open = tk.Button(frm_buttons, text="Open")
12btn_save = tk.Button(frm_buttons, text="Save As...")

以下是此代码的详细说明:

  • 1号线进口tkinter.
  • 3号线和4号线创建一个带有标题的新窗口"Simple Text Editor".
  • 6号线和7号线设置行和列配置。
  • 第 9 至 12 行创建文本框、框架以及打开和保存按钮所需的四个小部件。

仔细看看第 6 行。这minsize的参数.rowconfigure()被设定为800, 和weight被设定为1:

window.rowconfigure(0, minsize=800, weight=1)

第一个参数是0,它将第一行的高度设置为800像素并确保行的高度与窗口的高度成比例增长。应用程序布局中只有一行,因此这些设置适用于整个窗口。

我们还仔细看看第 7 行。在这里,您使用.columnconfigure()设置widthweight带索引的列的属性18001, 分别:

window.columnconfigure(1, minsize=800, weight=1)

请记住,行索引和列索引是从零开始的,因此这些设置仅适用于第二列。通过仅配置第二列,当调整窗口大小时,文本框将自然扩展和收缩,而包含按钮的列将保持固定宽度。

现在您可以处理应用程序布局。首先,将两个按钮分配给frm_buttons框架使用.grid()几何管理器:

12# ...
13
14btn_open.grid(row=0, column=0, sticky="ew", padx=5, pady=5)
15btn_save.grid(row=1, column=0, sticky="ew", padx=5)

这两行代码创建一个网格中有两行和一列frm_buttons框架,因为两者btn_openbtn_save有他们的master属性设置为frm_buttons. btn_open被放在第一行并且btn_save在第二行,这样btn_open出现在上面btn_save在布局中,只是您在草图中规划的。

两个都btn_openbtn_save有他们的sticky属性设置为"ew",这会强制按钮水平扩展在两个方向上并填充整个框架。这可确保两个按钮的大小相同。

您放置五个像素填充通过设置围绕每个按钮padxpady参数为5。仅有的btn_open有垂直内边距。由于它位于顶部,因此垂直填充使按钮从窗口顶部向下偏移一点,并确保它与窗口之间有一个小间隙btn_save.

现在frm_buttons已布局并准备就绪,您可以为窗口的其余部分设置网格布局:

15# ...
16
17frm_buttons.grid(row=0, column=0, sticky="ns")
18txt_edit.grid(row=0, column=1, sticky="nsew")

这两行代码创建一个网格一行和两列window。你放置frm_buttons在第一列和txt_edit在第二列中,以便frm_buttons出现在左侧txt_edit在窗口布局中。

sticky参数为frm_buttons被设定为"ns",这迫使整个框架垂直扩展并填充其列的整个高度。txt_edit填充其整个网格单元,因为您设置了其sticky参数为"nsew",这迫使它向各个方向扩展.

现在应用程序布局已完成,添加window.mainloop()到程序底部并保存并运行文件:

18# ...
19
20window.mainloop()

将显示以下窗口:

A text editor application made with Tkinter

看起来很棒!但它还没有做任何事情,所以您需要开始为按钮编写命令。btn_open需要展示一个文件打开对话框并允许用户选择一个文件。然后它需要打开该文件并设置文本txt_edit到文件的内容。这是一个open_file()函数就是这样做的:

 1import tkinter as tk
 2
 3def open_file():
 4    """Open a file for editing."""
 5    filepath = askopenfilename(
 6        filetypes=[("Text Files", "*.txt"), ("All Files", "*.*")]
 7    )
 8    if not filepath:
 9        return
10    txt_edit.delete("1.0", tk.END)
11    with open(filepath, mode="r", encoding="utf-8") as input_file:
12        text = input_file.read()
13        txt_edit.insert(tk.END, text)
14    window.title(f"Simple Text Editor - {filepath}")
15
16# ...

下面是这个函数的分解:

  • 5 至 7 号线使用askopenfilename()对话框从tkinter.filedialog模块显示文件打开对话框并将所选文件路径存储到filepath.
  • 8号线和9号线检查用户是否关闭对话框或单击取消按钮。如果是这样,那么filepathNone,该函数将return无需执行任何代码来读取文件并设置文本txt_edit.
  • 10号线清除当前内容txt_edit使用.delete().
  • 11、12号线打开所选文件并.read()存储之前其内容text作为字符串。
  • 13号线分配字符串texttxt_edit使用.insert().
  • 14号线设置窗口的标题,使其包含打开文件的路径。

现在您可以更新程序,以便btn_open来电open_file()每当点击它时。您需要执行一些操作来更新程序。一、导入askopenfilename()tkinter.filedialog通过将以下导入添加到程序顶部:

 1import tkinter as tk
 2from tkinter.filedialog import askopenfilename
 3
 4# ...

接下来,设置command的属性btn_opnopen_file:

 1import tkinter as tk
 2from tkinter.filedialog import askopenfilename
 3
 4def open_file():
 5    """Open a file for editing."""
 6    filepath = askopenfilename(
 7        filetypes=[("Text Files", "*.txt"), ("All Files", "*.*")]
 8    )
 9    if not filepath:
10        return
11    txt_edit.delete("1.0", tk.END)
12    with open(filepath, mode="r", encoding="utf-8") as input_file:
13        text = input_file.read()
14        txt_edit.insert(tk.END, text)
15    window.title(f"Simple Text Editor - {filepath}")
16
17window = tk.Tk()
18window.title("Simple Text Editor")
19
20window.rowconfigure(0, minsize=800, weight=1)
21window.columnconfigure(1, minsize=800, weight=1)
22
23txt_edit = tk.Text(window)
24frm_buttons = tk.Frame(window, relief=tk.RAISED, bd=2)
25btn_open = tk.Button(frm_buttons, text="Open", command=open_file)
26btn_save = tk.Button(frm_buttons, text="Save As...")
27
28# ...

保存文件并运行它以检查一切是否正常。然后尝试打开一个文本文件!

btn_open工作了,是时候处理这​​个函数了btn_save。这个需要开一个保存文件对话框以便用户可以选择他们想要保存文件的位置。您将使用asksaveasfilename()对话框中的tkinter.filedialog用于此的模块。该函数还需要提取当前的文本txt_edit并将其写入选定位置的文件中。这是一个执行此操作的函数:

15# ...
16
17def save_file():
18    """Save the current file as a new file."""
19    filepath = asksaveasfilename(
20        defaultextension=".txt",
21        filetypes=[("Text Files", "*.txt"), ("All Files", "*.*")],
22    )
23    if not filepath:
24        return
25    with open(filepath, mode="w", encoding="utf-8") as output_file:
26        text = txt_edit.get("1.0", tk.END)
27        output_file.write(text)
28    window.title(f"Simple Text Editor - {filepath}")
29
30# ...

这段代码的工作原理如下:

  • 第 19 至 22 行使用asksaveasfilename()对话框以从用户处获取所需的保存位置。选定的文件路径存储在filepath多变的。
  • 第 23 和 24 行检查用户是否关闭对话框或单击取消按钮。如果是这样,那么filepathNone,并且该函数将返回,而不执行任何将文本保存到文件的代码。
  • 25号线在选定的文件路径中创建一个新文件。
  • 26号线从中提取文本txt_edit.get()方法并将其分配给变量text.
  • 27号线text到输出文件。
  • 28号线更新窗口标题,以便新文件路径显示在窗口标题中。

现在您可以更新程序,以便btn_save来电save_file()当它被点击时。同样,为了更新程序,您需要执行一些操作。一、导入asksaveasfilename()tkinter.filedialog通过更新脚本顶部的导入,如下所示:

 1import tkinter as tk
 2from tkinter.filedialog import askopenfilename, asksaveasfilename
 3
 4# ...

最后,设置command的属性btn_savesave_file:

28# ...
29
30window = tk.Tk()
31window.title("Simple Text Editor")
32
33window.rowconfigure(0, minsize=800, weight=1)
34window.columnconfigure(1, minsize=800, weight=1)
35
36txt_edit = tk.Text(window)
37frm_buttons = tk.Frame(window, relief=tk.RAISED, bd=2)
38btn_open = tk.Button(frm_buttons, text="Open", command=open_file)
39btn_save = tk.Button(frm_buttons, text="Save As...", command=save_file)
40
41btn_open.grid(row=0, column=0, sticky="ew", padx=5, pady=5)
42btn_save.grid(row=1, column=0, sticky="ew", padx=5)
43
44frm_buttons.grid(row=0, column=0, sticky="ns")
45txt_edit.grid(row=0, column=1, sticky="nsew")
46
47window.mainloop()

保存文件并运行它。您现在已经有了一个最小但功能齐全的文本编辑器!

您可以展开下面的代码块以查看完整的脚本:

以下是完整的脚本供您参考:

import tkinter as tk
from tkinter.filedialog import askopenfilename, asksaveasfilename

def open_file():
    """Open a file for editing."""
    filepath = askopenfilename(
        filetypes=[("Text Files", "*.txt"), ("All Files", "*.*")]
    )
    if not filepath:
        return
    txt_edit.delete("1.0", tk.END)
    with open(filepath, mode="r", encoding="utf-8") as input_file:
        text = input_file.read()
        txt_edit.insert(tk.END, text)
    window.title(f"Simple Text Editor - {filepath}")

def save_file():
    """Save the current file as a new file."""
    filepath = asksaveasfilename(
        defaultextension=".txt",
        filetypes=[("Text Files", "*.txt"), ("All Files", "*.*")],
    )
    if not filepath:
        return
    with open(filepath, mode="w", encoding="utf-8") as output_file:
        text = txt_edit.get("1.0", tk.END)
        output_file.write(text)
    window.title(f"Simple Text Editor - {filepath}")

window = tk.Tk()
window.title("Simple Text Editor")

window.rowconfigure(0, minsize=800, weight=1)
window.columnconfigure(1, minsize=800, weight=1)

txt_edit = tk.Text(window)
frm_buttons = tk.Frame(window, relief=tk.RAISED, bd=2)
btn_open = tk.Button(frm_buttons, text="Open", command=open_file)
btn_save = tk.Button(frm_buttons, text="Save As...", command=save_file)

btn_open.grid(row=0, column=0, sticky="ew", padx=5, pady=5)
btn_save.grid(row=1, column=0, sticky="ew", padx=5)

frm_buttons.grid(row=0, column=0, sticky="ns")
txt_edit.grid(row=0, column=1, sticky="nsew")

window.mainloop()

您现在已经用 Python 构建了两个 GUI 应用程序,并应用了您在本教程中学到的许多技能。这是一个不小的成就,所以花一些时间对你所做的事情感到满意。您现在已经准备好自行处理一些应用程序了!

结论

在本教程中,您学习了如何开始使用 Python GUI 编程。特金特对于 Python GUI 框架来说,这是一个引人注目的选择,因为它内置于 Python 标准库中,并且使用该框架开发应用程序相对轻松。

在本教程中,您学习了几个重要的 Tkinter 概念:

  • 如何与小部件
  • 如何控制应用程序布局几何管理器
  • 如何制作您的应用程序交互的
  • 如何使用五个基本的 Tkinter小部件: Label, Button, Entry, Text, 和Frame

现在您已经掌握了使用 Tkinter 进行 Python GUI 编程的基础知识,下一步是构建一些您自己的应用程序。你会创造什么?在下面的评论中分享您有趣的项目!

其他资源

在本教程中,您仅接触了使用 Tkinter 创建 Python GUI 应用程序的基础知识。还有许多其他主题此处未涵盖。在本节中,您将找到一些可帮助您继续旅程的最佳资源。

Tkinter 参考资料

以下是一些值得查看的官方资源:

  • 官方 Python Tkinter 参考文档中等深度地涵盖了 Python 的 Tkinter 模块。它是为更高级的 Python 开发人员编写的,并不是初学者的最佳资源。
  • Tkinter 8.5 参考:Python 的 GUI是涵盖 Tkinter 模块大部分内容的广泛参考。它很详尽,但以参考风格编写,没有注释或示例。
  • Tk 命令Reference 是 Tk 库中命令的权威指南。它是为 Tcl 语言编写的,但它回答了很多关于为什么事情会像 Tkinter 中那样工作的问题。

附加小部件

在本教程中,您了解了Label, Button, Entry, Text, 和Frame小部件。 Tkinter 中还有其他几个小部件,所有这些对于构建实际应用程序都是必不可少的。以下是一些继续学习小部件的资源:

  • The TkDocs Tkinter Tutorial is a fairly comprehensive guide for Tk, the underlying code library used by Tkinter. Examples are presented in Python, Ruby, Perl, and Tcl. You can find several examples of widgets beyond those covered here in two sections:
    • 基本小部件涵盖与本教程相同的小部件,以及更多。
    • 更多小部件涵盖了几个额外的小部件。
  • The official Python docs cover additional widgets:
    • ttk 主题小部件涵盖 Tk 主题小部件集。
    • 滚动文本小部件详情一Text小部件与垂直滚动条相结合。

应用分发

使用 Tkinter 创建应用程序后,您可能希望将其分发给您的同事和朋友。以下是一些帮助您完成该过程的教程:

  • 使用 PyInstaller 轻松分发 Python 应用程序
  • 将 Python 打包为可执行文件的 4 种尝试
  • 使用 PyOxidizer 构建独立的 Python 应用程序

其他 GUI 框架

Tkinter 并不是 Python GUI 框架的唯一选择。如果 Tkinter 不能满足您项目的需求,那么可以考虑以下一些其他框架:

  • 如何使用 wxPython 构建 Python GUI 应用程序
  • Python 和 PyQt:构建 GUI 桌面计算器
  • 使用 Kivy Python 框架构建移动应用程序
  • PySimpleGUI:使用 Python 创建 GUI 的简单方法

参加测验:通过我们的交互式“使用 Tkinter 进行 Python GUI 编程”测验来测试您的知识。完成后,您将收到一个分数,以便您可以跟踪一段时间内的学习进度:

参加测验 »

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用 Tkinter 进行 Python GUI 编程 的相关文章

随机推荐

  • Python 调用免费的百度翻译接口 翻译excel文档中的英文成中文

    usr bin env python3 coding UTF 8 author JHC license None contact JHC000abc gmail com file get coogle translate results p
  • Herriott池的建模

    摘要 在气体光谱学中 为了获得足够灵敏的吸收测量 通常要求具有较长的光程长度 充气体积包裹在反射镜之间的多通道单元是满足这一要求的一种方式 同时在途中控制光束发散 避免了对超大设备的需求 Herriott单元是这种系统的一个例子 其特点是使
  • 题解 | #字符串最后一个单词的长度#

  • 12天带薪年假?外企氛围?杭州小而美急招Prompt工程师!

    Prompt Engineering 需要承担哪些工作 1 负责对 ChatGPT 通义千问等大模型进行提示词开发 并且设计 prompt 解决产品问题效果的 import java io BufferedReader import jav
  • vue中data为什么是一个函数

    vue中的data是一个对象类型 对象类型的数据是按引用传值的 这就会导致所有组件的实例都共享同一份数据 这是不对的 我们要的是每个组件实例都是独立的 为了解决对象类型数据共享的问题 我们需要将 data 定义成一个函数 每个实例需要通过调
  • 【玩转 EdgeOne】| 腾讯云下一代边缘加速CDN EdgeOne 是安全加速界的未来吗?

    目录 前言 边缘加速与安全加固 边缘计算与CDN的融合 EdgeOne优秀的安全特性 EdgeOne卓越的性能表现 灵活的配置和管理 生态系统的支持与发展 技术创新与未来展望 EdgeOne试用 结束语 前言 在当下互联网的迅猛发展的时刻
  • shein笔试第一道编程题,通过率33%,有人知道哪里错了吗

    题解 链表的奇偶重排 struct ListNode int val struct ListNode next ListNode int x 题解 牛牛的新数组求和 include
  • Python mmap:使用内存映射执行文件 I/O(概述)

    这Python之禅有很多智慧可以提供 一个特别有用的想法是 应该有一种 最好只有一种 明显的方法来做到这一点 然而 在 Python 中完成大多数事情有多种方法 而且通常都有充分的理由 例如 有Python 读取文件的多种方法 包括很少使用
  • URL 链接:app_name、路径名和参数

    在本课中 您将了解链接模板一起 命名空间在姜戈中 路径名 并通过URL 中的参数 你还会遇到一个新错误 NoReverseMatch
  • 如何了解更多 VS Code

    在我们的专门课程中了解有关 Visual Studio Code 的更多信息 Visual Studio Code 中的 Python 开发 设置指南
  • Python 的“in”和“not in”运算符:检查成员资格

    目录 Getting Started With Membership Tests in Python Python 的 in 运算符 Python 不在 Operator 中 Using in and not in With Differe
  • 使用 Tkinter 进行 Python GUI 编程

    目录 Building Your First Python GUI Application With Tkinter 添加小部件 检查你的理解情况 Working With Widgets 使用标签小部件显示文本和图像 使用按钮小部件显示可
  • 如何了解更多信息

    在我们的专门课程中了解有关 Thonny 的更多信息 Thonny 适合初学者的 Python 编辑器
  • 回顾你所学的内容

    通过回顾您在课程或教程中学到的内容 您将更深入地处理信息 这有助于长期保留 重要链接 互动测验 书签教程
  • 在 Python 中使用 len() 函数

    目录 Getting Started With Python s len 将 len 与内置序列一起使用 将 len 与内置集合一起使用 探索 len 与其他内置数据类型 Exploring len Further With Some Ex
  • 动画片

    通过使用 r和 b转义序列来控制光标的位置 您可以使用文本创建翻页书样式的动画 以下是如何制作一个旋转器来指示忙碌状态 usr bin env python from time import sleep Show the spinning
  • 使用 Beautiful Soup 和 Python 进行网页抓取(概述)

    互联网上数量惊人的数据对于任何研究领域或个人兴趣来说都是丰富的资源 为了有效地收集这些数据 您需要熟练掌握网页抓取 Python 库requests和 Beautiful Soup 是完成这项工作的强大工具 如果您喜欢通过实践示例进行学习并
  • 在 Ubuntu Linux 16.04 上安装 Python

    了解如何使用 deadsnakes PPA 在 Ubuntu 16 04 中安装 Python 3 7 deadsnakes PPA 是一个包含旧版 Ubuntu 安装的现代 Python 版本的存储库
  • While 循环和列表

    在本课中 您将学习如何使用while 环形 该代码是在视频的实时会话中调试的 一个简单的例子可能如下所示 a fizz baz buzz while a print a pop 1
  • Python 的 map() 函数入门

    以下是有关 lambda 的资源和附加文档 如何使用 Python Lambda 函数 真正的 Python 文章 如何使用 Python Lambda 函数 真正的 Python 视频课程 Lambda 表达式 Python 文档