PyQt教程
PyQt是一个GUI组件包,是Qt的一个Python接口,十分强大,并且可跨平台。
PyQt是一个Python编程和Qt库的瑞士军刀。
本教程将指导如何使用PyQt.
创建一个简单的GUi程序,需要包括下面几个步骤:
1 | import sys |
版本格式:主版本号.次版本号.修订号,版本号递增规则如下:
先行版本号及版本编译元数据可以加到“主版本号.次版本号.修订号”的后面,作为延伸。
在软件管理的领域里存在着被称作“依赖地狱”的死亡之谷,系统规模越大,加入的包越多,你就越有可能在未来的某一天发现自己已深陷绝望之中。
在依赖高的系统中发布新版本包可能很快会成为噩梦。如果依赖关系过高,可能面临版本控制被锁死的风险(必须对每一个依赖包改版才能完成某次升级)。而如果依赖关系过于松散,又将无法避免版本的混乱(假设兼容于未来的多个版本已超出了合理数量)。当你专案的进展因为版本依赖被锁死或版本混乱变得不够简便和可靠,就意味着你正处于依赖地狱之中。
作为这个问题的解决方案之一,我提议用一组简单的规则及条件来约束版本号的配置和增长。这些规则是根据(但不局限于)已经被各种封闭、开放源码软件所广泛使用的惯例所设计。为了让这套理论运作,你必须先有定义好的公共 API 。这可以透过文件定义或代码强制要求来实现。无论如何,这套 API 的清楚明了是十分重要的。一旦你定义了公共 API,你就可以透过修改相应的版本号来向大家说明你的修改。考虑使用这样的版本号格式:X.Y.Z (主版本号.次版本号.修订号)修复问题但不影响API 时,递增修订号;API 保持向下兼容的新增及修改时,递增次版本号;进行不向下兼容的修改时,递增主版本号。
我称这套系统为“语义化的版本控制”,在这套约定下,版本号及其更新方式包含了相邻版本间的底层代码和修改内容的信息。
以下关键词 MUST、MUST NOT、REQUIRED、SHALL、SHALL NOT、SHOULD、SHOULD NOT、 RECOMMENDED、MAY、OPTIONAL 依照 RFC 2119 的叙述解读。(译注:为了保持语句顺畅, 以下文件遇到的关键词将依照整句语义进行翻译,在此先不进行个别翻译。)
1.
使用语义化版本控制的软件必须(MUST)定义公共 API。该 API 可以在代码中被定义或出现于严谨的文件内。无论何种形式都应该力求精确且完整。
2.
标准的版本号必须(MUST)采用 X.Y.Z 的格式,其中 X、Y 和 Z 为非负的整数,且禁止(MUST NOT)在数字前方补零。X 是主版本号、Y 是次版本号、而 Z 为修订号。每个元素必须(MUST)以数值来递增。例如:1.9.1 -> 1.10.0 -> 1.11.0。
3.
标记版本号的软件发行后,禁止(MUST NOT)改变该版本软件的内容。任何修改都必须(MUST)以新版本发行。
4.
主版本号为零(0.y.z)的软件处于开发初始阶段,一切都可能随时被改变。这样的公共 API 不应该被视为稳定版。
5.
1.0.0 的版本号用于界定公共 API 的形成。这一版本之后所有的版本号更新都基于公共 API 及其修改内容。
6.
修订号 Z(x.y.Z |
x > 0)必须(MUST)在只做了向下兼容的修正时才递增。这里的修正指的是针对不正确结果而进行的内部修改。
7.
次版本号 Y(x.Y.z |
x > 0)必须(MUST)在有向下兼容的新功能出现时递增。在任何公共 API 的功能被标记为弃用时也必须(MUST)递增。也可以(MAY)在内部程序有大量新功能或改进被加入时递增,其中可以(MAY)包括修订级别的改变。每当次版本号递增时,修订号必须(MUST)归零。
8.
主版本号 X(X.y.z |
X > 0)必须(MUST)在有任何不兼容的修改被加入公共 API 时递增。其中可以(MAY)包括次版本号及修订级别的改变。每当主版本号递增时,次版本号和修订号必须(MUST)归零。
9.
先行版本号可以(MAY)被标注在修订版之后,先加上一个连接号再加上一连串以句点分隔的标识符来修饰。标识符必须(MUST)由 ASCII 字母数字和连接号 [0-9A-Za-z-] 组成,且禁止(MUST NOT)留白。数字型的标识符禁止(MUST NOT)在前方补零。先行版的优先级低于相关联的标准版本。被标上先行版本号则表示这个版本并非稳定而且可能无法满足预期的兼容性需求。范例:1.0.0-alpha、1.0.0-alpha.1、1.0.0-0.3.7、1.0.0-x.7.z.92。
10.
版本编译元数据可以(MAY)被标注在修订版或先行版本号之后,先加上一个加号再加上一连串以句点分隔的标识符来修饰。标识符必须(MUST)由 ASCII 字母数字和连接号 [0-9A-Za-z-] 组成,且禁止(MUST NOT)留白。当判断版本的优先层级时,版本编译元数据可(SHOULD)被忽略。因此当两个版本只有在版本编译元数据有差别时,属于相同的优先层级。范例:1.0.0-alpha+001、1.0.0+20130313144700、1.0.0-beta+exp.sha.5114f85。
11.
版本的优先层级指的是不同版本在排序时如何比较。判断优先层级时,必须(MUST)把版本依序拆分为主版本号、次版本号、修订号及先行版本号后进行比较(版本编译元数据不在这份比较的列表中)。由左到右依序比较每个标识符,第一个差异值用来决定优先层级:主版本号、次版本号及修订号以数值比较,例如:1.0.0 < 2.0.0 < 2.1.0 < 2.1.1。当主版本号、次版本号及修订号都相同时,改以优先层级比较低的先行版本号决定。例如:1.0.0-alpha < 1.0.0。有相同主版本号、次版本号及修订号的两个先行版本号,其优先层级必须(MUST)透过由左到右的每个被句点分隔的标识符来比较,直到找到一个差异值后决定:只有数字的标识符以数值高低比较,有字母或连接号时则逐字以 ASCII 的排序来比较。数字的标识符比非数字的标识符优先层级低。若开头的标识符都相同时,栏位比较多的先行版本号优先层级比较高。范例:1.0.0-alpha < 1.0.0-alpha.1 < 1.0.0-alpha.beta < 1.0.0-beta < 1.0.0-beta.2 < 1.0.0-beta.11 < 1.0.0-rc.1 < 1.0.0。
这并不是一个新的或者革命性的想法。实际上,你可能已经在做一些近似的事情了。问题在于只是“近似”还不够。如果没有某个正式的规范可循,版本号对于依赖的管理并无实质意义。将上述的想法命名并给予清楚的定义,让你对软件使用者传达意向变得容易。一旦这些意向变得清楚,弹性(但又不会太弹性)的依赖规范就能达成。
举个简单的例子就可以展示语义化的版本控制如何让依赖地狱成为过去。假设有个名为“救火车”的函式库,它需要另一个名为“梯子”并已经有使用语义化版本控制的包。当救火车创建时,梯子的版本号为 3.1.0。因为救火车使用了一些版本 3.1.0 所新增的功能, 你可以放心地指定依赖于梯子的版本号大等于 3.1.0 但小于 4.0.0。这样,当梯子版本 3.1.1 和 3.2.0 发布时,你可以将直接它们纳入你的包管理系统,因为它们能与原有依赖的软件兼容。
作为一位负责任的开发者,你理当确保每次包升级的运作与版本号的表述一致。现实世界是复杂的,我们除了提高警觉外能做的不多。你所能做的就是让语义化的版本控制为你提供一个健全的方式来发行以及升级包,而无需推出新的依赖包,节省你的时间及烦恼。
如果你对此认同,希望立即开始使用语义化版本控制,你只需声明你的函式库正在使用它并遵循这些规则就可以了。请在你的 README 文件中保留此页连结,让别人也知道这些规则并从中受益。
最简单的做法是以 0.1.0 作为你的初始化开发版本,并在后续的每次发行时递增次版本号。
当你的软件被用于正式环境,它应该已经达到了 1.0.0 版。如果你已经有个稳定的 API 被使用者依赖,也会是 1.0.0 版。如果你很担心向下兼容的问题,也应该算是 1.0.0 版了。
主版本号为零的时候就是为了做快速开发。如果你每天都在改变 API,那么你应该仍在主版本号为零的阶段(0.y.z),或是正在下个主版本的独立开发分支中。
这是开发的责任感和前瞻性的问题。不兼容的改变不应该轻易被加入到有许多依赖代码的软件中。升级所付出的代价可能是巨大的。要递增主版本号来发行不兼容的改版,意味着你必须为这些改变所带来的影响深思熟虑,并且评估所涉及的成本及效益比。
为供他人使用的软件编写适当的文件,是你作为一名专业开发者应尽的职责。保持专案高效一个非常重要的部份是掌控软件的复杂度,如果没有人知道如何使用你的软件或不知道哪些函数的调用是可靠的,要掌控复杂度会是困难的。长远来看,使用语义化版本控制以及对于公共 API 有良好规范的坚持,可以让每个人及每件事都运行顺畅。
一旦发现自己破坏了语义化版本控制的规范,就要修正这个问题,并发行一个新的次版本号来更正这个问题并且恢复向下兼容。即使是这种情况,也不能去修改已发行的版本。可以的话,将有问题的版本号记录到文件中,告诉使用者问题所在,让他们能够意识到这是有问题的版本。
由于没有影响到公共 API,这可以被认定是兼容的。若某个软件和你的包有共同依赖,则它会有自己的依赖规范,作者也会告知可能的冲突。要判断改版是属于修订等级或是次版等级,是依据你更新的依赖关系是为了修复问题或是加入新功能。对于后者,我经常会预期伴随着更多的代码,这显然会是一个次版本号级别的递增。
自行做最佳的判断。如果你有庞大的使用者群在依照公共 API 的意图而变更行为后会大受影响,那么最好做一次主版本的发布,即使严格来说这个修复仅是修订等级的发布。记住, 语义化的版本控制就是透过版本号的改变来传达意义。若这些改变对你的使用者是重要的,那就透过版本号来向他们说明。
弃用现存的功能是软件开发中的家常便饭,也通常是向前发展所必须的。当你弃用部份公共 API 时,你应该做两件事:(1)更新你的文件让使用者知道这个改变,(2)在适当的时机将弃用的功能透过新的次版本号发布。在新的主版本完全移除弃用功能前,至少要有一个次版本包含这个弃用信息,这样使用者才能平顺地转移到新版 API。
没有,请自行做适当的判断。举例来说,长到 255 个字元的版本已过度夸张。再者,特定的系统对于字串长度可能会有他们自己的限制。
语义化版本控制的规范是由 Gravatars 创办者兼 GitHub 共同创办者 Tom Preston-Werner 所建立。
如果您有任何建议,请到 GitHub 上提出您的问题。
Button(按钮)窗口部件是一个标准的 Tkinter 窗口部件,用来实现各种按钮。
按钮能够包含文本或图象,并与一个 Python 函数或方法相关联。当这个按钮被按下时,Tkinter 自动调用相关联的函数或方法。
按钮仅能显示一种字体,但是这个文本可以跨行。另外,这个文本中的一个字母可以有__下划线__,例如标明一个快捷键。默认情况,Tab 键用于将焦点移动到一个按钮部件。
简而言之,按钮部件用来让用户说“马上给我执行这个任务”,通常我们用显示在按钮上的文本或图象来提示。 按钮通常用在工具条中或应用程序窗口中,并且用来接收或忽略输入在对话框中的数据。
关于按钮和输入的数据的配合,可以参看 Checkbutton 和 Radiobutton 部件。
普通的按钮很容易被创建,仅仅指定按钮的内容(文本、位图、图象)和一个当按钮被按下时的回调函数即可:
1 | b = Button(master, text="OK", command=self.ok) |
没有回调函数的按钮是没有用的,当你按下这个按钮时它什么也不做。你可能在开发一个应用程序的时候想实现这种按钮,比如为了不干扰你的 beta 版的测试者:
1 | b = Button(master, text="Help", state=DISABLED) |
如果你没有指定尺寸,按钮的大小将正好能够容纳它的内容。你可以用 padx 和 pady 选项来增加内容与按钮 边框的间距。你也可以用 height 和 width 选项来显式地设置按钮的尺寸。如果你在按钮中显示文本,那么这些选项将以文本的单位为定义按钮的尺寸。如果你替而代之显示图象,那么按钮的尺寸将是象素 (或其它的屏幕单位)。你实际上甚至能够用象素单位来定义文本按钮的尺寸,但这可能带来意外的结果。下面是指定尺寸 的一段例子代码:
1 | f = Frame(master, height=32, width=32) f.pack_propagate(0) # don't shrink |
按钮能够显示多行文本(但只能用一种字体)。 你可以使用多行或 wraplength 选项来使按钮自己调整文本。 当调整文本时,使用 anchor,justify,也可加上 padx 选项来得到你所希望的格式。一个例子如下:
1 | b = Button(master, text=longtext, anchor=W, justify=LEFT, padx=2) |
为了使一个普通的按钮看起来像凹入的,例如你想去实现某种类型的工具框,你可简单地将 relief 的值从 “RAISED“改变为”SUNKEN:
Button 窗口部件支持标准的 Tkinter 窗口部件接口,加上下面的方法:
tkButtonDown(), tkButtonEnter(), tkButtonInvoke(), tkButtonLeave(), tkButtonUp() 这些方法可以用在定制事件绑定中,所有这些方法接收 0 个或多个形参。
Button 窗口部件支持下面的选项:
类型:颜色;
说明:当按钮被激活时所使用的颜色。
类型:常量;
说明:控制按钮上内容的位置。使用N, NE, E, SE, S, SW, W, NW, or CENTER这些值之一。默认值 是 CENTER。
类型:颜色;
说明:按钮的颜色。默认值与特定平台相关。
类型:位图;
说 明:显示在窗口部件中的位图。如果 image 选项被指定了,则这个选项被忽略。下面的位图在所有平台上 都有 效:error, gray75, gray50, gray25, gray12, hourglass, info, questhead, question, 和 warning.
这后面附加的位图仅在 Macintosh 上有效:document, stationery, edition, application, accessory, folder, pfolder, trash, floppy, ramdisk, cdrom, preferences, querydoc, stop, note, 和 caution.
1 | borderwidth (bd) |
类型:整数; 说明:按钮边框的宽度。默认值与特定平台相关。但通常是 1 或 2 象素。
类型:回调; 说明:当按钮被按下时所调用的一个函数或方法。所回调的可以是一个函数、方法或别的可调用的 Python 对 象。
类型:光标;
说明:当鼠标移动到按钮上时所显示的光标。
类型:常量;
说明:如果设置了,则按钮为默认按钮。注意这个语法在Tk 8.0b2中已改变。
disabledforeground
类型:颜色;
说明:当按钮无效时的颜色。
类型:字体;
说明:按钮所使用的字体。按钮只能包含一种字体的文本。
类型:颜色;
说明:控制焦点所在的高亮边框的颜色。当窗口部件获得焦点的时候,边框为 highlightcolor 所指定的颜 色。否则边框为 highlightbackground 所指定的颜色。默认值由系统所定。
类型:距离; 说明:控制焦点所在的高亮边框的宽度。默认值通常是 1 或 2 象素。
类型:图象;
说明:在部件中显示的图象。如果指定,则 text 和 bitmap 选项将被忽略。
类型:常量;
说明:定义多行文本如何对齐。可取值有:LEFT, RIGHT, 或 CENTER。
类型:距离;
说明:指定文本或图象与按钮边框的间距。
类型:常量;
说明:边框的装饰。通常按钮按下时是凹陷的,否则凸起。另外的可能取值有 GROOVE, RIDGE, 和 FLAT。
类型:常量;
说明:按钮的状态:NORMAL, ACTIVE 或 DISABLED。默认值为 NORMAL。
类型:标志;
说明:表明用户可以 Tab 键来将焦点移到这个按钮上。默认值是一个空字符串,意思是如果按钮有按键绑定的 话,它可以通过所绑定的按键来获得焦点。
类型:字符串;
说明:显示在按钮中的文本。文本可以是多行。如果 bitmaps 或 image 选项被使用,则 text 选项被忽略。
类型:变量;
说明:与按钮相关的 Tk 变量(通常是一个字符串变量)。如果这个变量的值改变,那么按钮上的文本相应更新。
类型:整数; 说明:在文本标签中哪个字符加下划线。默认值为-1,意思是没有字符加下划线。
类型:距离; 说明:按钮的尺寸。如果按钮显示文本,尺寸使用文本的单位。如果按钮显示图象,尺寸以象素为单位(或屏 幕的单位)。如果尺寸没指定,它将根据按钮的内容来计算。
类型:距离; 说明:确定一个按钮的文本何时调整为多行。它以屏幕的单位为单位。默认不调整。
Tkinter
模块是Tk GUI
工具的标准Python
接口。 最开始由Sun Labs
开发。
Tk
和Tkinter
在大多数的Unix
平台上都适用,同时也支持Windows
和Macintosh
。
从8.0版本开始,Tk
支持原生的界面,体验很不错。
Tkinter
包含许多模块。Tk接口由模块 _tkinter
提供。这个模块包含的都是低层次的接口,对于应用程序不需要调用。
公共的接口也是蛮多的,不过最常用的就是Tkinter
模块。
导入方式为:
1 | import tkinter |
更常用的为:
1 | from tkinter import * |
或者
1 | import tkinter as Tk |
While the standard dialogs described in the previous section may be sufficient for many simpler applications, most larger applications require more complicated dialogs. For example, to set configuration parameters for an application, you will probably want to let the user enter more than one value or string in each dialog.
Basically, creating a dialog window is no different from creating an application window. Just use the Toplevel widget, stuff the necessary entry fields, buttons, and other widgets into it, and let the user take care of the rest. (By the way, don’t use the ApplicationWindow class for this purpose; it will only confuse your users).
But if you implement dialogs in this way, you may end up getting both your users and yourself into trouble. The standard dialogs all returned only when the user had finished her task and closed the dialog; but if you just display another toplevel window, everything will run in parallel. If you’re not careful, the user may be able to display several copies of the same dialog, and both she and your application will be hopelessly confused.
In many situations, it is more practical to handle dialogs in a synchronous fashion; create the dialog, display it, wait for the user to close the dialog, and then resume execution of your application. The wait_window method is exactly what we need; it enters a local event loop, and doesn’t return until the given window is destroyed (either via the destroy method, or explicitly via the window manager):
widget.wait_window(window)
(Note that the method waits until the window given as an argument is destroyed; the only reason this is a method is to avoid namespace pollution).
In the following example, the MyDialog class creates a Toplevel widget, and adds some widgets to it. The caller then uses wait_window to wait until the dialog is closed. If the user clicks OK, the entry field’s value is printed, and the dialog is then explicitly destroyed.
1 | from Tkinter import * |
If you run this program, you can type something into the entry field, and then click OK, after which the program terminates (note that we didn’t call the mainloop method here; the local event loop handled by wait_window was sufficient). But there are a few problems with this example:
The root window is still active. You can click on the button in the root window also when the dialog is displayed. If the dialog depends on the current application state, letting the users mess around with the application itself may be disastrous. And just being able to display multiple dialogs (or even multiple copies of one dialog) is a sure way to confuse your users.
You have to explicitly click in the entry field to move the cursor into it, and also click on the OK button. Pressning Enter in the entry field is not sufficient.
There should be some controlled way to cancel the dialog (and as we learned earlier, we really should handle the WM_DELETE_WINDOW protocol too).
To address the first problem, Tkinter provides a method called grab_set, which makes sure that no mouse or keyboard events are sent to the wrong window.
The second problem consists of several parts; first, we need to explicitly move the keyboard focus to the dialog. This can be done with the focus_set method. Second, we need to bind the Enter key so it calls the ok method. This is easy, just use the bind method on the Toplevel widget (and make sure to modify the ok method to take an optional argument so it doesn’t choke on the event object).
The third problem, finally, can be handled by adding an additional Cancel button which calls the destroy method, and also use bind and protocol to do the same when the user presses Escape or explicitly closes the window.
The following Dialog class provides all this, and a few additional tricks. To implement your own dialogs, simply inherit from this class and override the body and apply methods. The former should create the dialog body, the latter is called when the user clicks OK.
1 | from Tkinter import * |
The main trickery is done in the constructor; first, transient is used to associate this window with a parent window (usually the application window from which the dialog was launched). The dialog won’t show up as an icon in the window manager (it won’t appear in the task bar under Windows, for example), and if you iconify the parent window, the dialog will be hidden as well. Next, the constructor creates the dialog body, and then calls grab_set to make the dialog modal, geometry to position the dialog relative to the parent window, focus_set to move the keyboard focus to the appropriate widget (usually the widget returned by the body method), and finally wait_window.
Note that we use the protocol method to make sure an explicit close is treated as a cancel, and in the buttonbox method, we bind the Enter key to OK, and Escape to Cancel. The default=ACTIVE call marks the OK button as a default button in a platform specific way.
Using this class is much easier than figuring out how it’s implemented; just create the necessary widgets in the body method, and extract the result and carry out whatever you wish to do in the apply method. Here’s a simple example (we’ll take a closer look at the grid method in a moment).
1 | import tkSimpleDialog |
And here’s the resulting dialog:
Note that the body method may optionally return a widget that should receive focus when the dialog is displayed. If this is not relevant for your dialog, simply return None (or omit the return statement).
The above example did the actual processing in the apply method (okay, a more realistic example should probably to something with the result, rather than just printing it). But instead of doing the processing in the apply method, you can store the entered data in an instance attribute:
1 | ... |
Note that if the dialog is cancelled, the apply method is never called, and the result attribute is never set. The Dialog constructor sets this attribute to None, so you can simply test the result before doing any processing of it. If you wish to return data in other attributes, make sure to initialize them in the body method (or simply set result to 1 in the apply method, and test it before accessing the other attributes).
While the pack manager was convenient to use when we designed application windows, it may not be that easy to use for dialogs. A typical dialog may include a number of entry fields and check boxes, with corresponding labels that should be properly aligned. Consider the following simple example:
Simple Dialog Layout
To implement this using the pack manager, we could create a frame to hold the label “first:”, and the corresponding entry field, and use side=LEFT when packing them. Add a corresponding frame for the next line, and pack the frames and the checkbutton into an outer frame using side=TOP. Unfortunately, packing the labels in this fashion makes it impossible to get the entry fields lined up, and if we use side=RIGHT to pack the entry field instead, things break down if the entry fields have different width. By carefully using width options, padding, side and anchor packer options, etc., we can get reasonable results with some effort. But there’s a much easier way: use the grid manager instead.
This manager splits the master widget (typically a frame) into a 2-dimensional grid, or table. For each widget, you only have to specify where in this grid it should appear, and the grid managers takes care of the rest. The following body method shows how to get the above layout:
Using the grid geometry maanager
1 | def body(self, master): |
For each widget that should be handled by the grid manager, you call the grid method with the row and column options, telling the manager where to put the widget. The topmost row, and the leftmost column, is numbered 0 (this is also the default). Here, the checkbutton is placed beneath the label and entry widgets, and the columnspan option is used to make it occupy more than one cell. Here’s the result:
If you look carefully, you’ll notice a small difference between this dialog, and the dialog shown by the dialog2.py script. Here, the labels are aligned to the left margin. If you compare the code, you’ll find that the only difference is an option called sticky.
When its time to display the frame widget, the grid geometry manager loops over all widgets, calculating a suitable width for each row, and a suitable height for each column. For any widget where the resulting cell turns out to be larger than the widget, the widget is centered by default. The sticky option is used to modify this behavior. By setting it to one of E, W, S, N, NW, NE, SE, or SW, you can align the widget to any side or corner of the cell. But you can also use this option to stretch the widget if necessary; if you set the option to E+W, the widget will be stretched to occupy the full width of the cell. And if you set it to E+W+N+S (or NW+SE, etc), the widget will be stretched in both directions. In practice, the sticky option replaces the fill, expand, and anchor options used by the pack manager.
The grid manager provides many other options allowing you to tune the look and behavior of the resulting layout. These include padx and pady which are used to add extra padding to widget cells, and many others. See the Grid Geometry Manager chapter for details.
What if the user types bogus data into the dialog? In our current example, the apply method will raise an exception if the contents of an entry field is not an integer. We could of course handle this with a try/except and a standard message box:
1 | ... |
Note that if we left the processing to the calling program (as shown above), we don’t even have to implement the apply method.
废话不多说,直接上代码。
写过代码都知道,第一个肯定就是Hello-world
了。先来一个简单的。
我们的第一个代码helloworld.py
1 | #!/usr/bin/python |
如何运行呢,与python程序一样,如下所述:
1 | $ python helloworld.py |
如果一些顺利,你会看到下面的一幅图片:
如果希望停止该程序,直接点击关闭按钮即可。
首先第一行,我们看到需要导入tkinter
模块。
该模块包含所有的类,函数和其他Tk
工具运行所需要的一切。
大多数情况下,你可以简单地导入Tkinter
的所有到你的模块空间。
1 | import tkinter |
初始化tkinter
,我们需要创建一个Tk根组件。
根组件包含一个title bar
和由窗口管理器提供的装饰组件。
对于每一个程序创建一个根组件即可,并且需要在所有的组件之前创建。
1 | root = tkinter.Tk() |
接下来,我们创建一个根组件的Label
子组件。
1 | w = tkinter.Label(root, text="Hello, world!") |
Label组件可以使用文本、图标和其他图像显示。
在这个示例中,我们使用文本显示。
然后,我们使用pack
方法来将文本显示到组件上。不过直到我们使用Tkinter
的事件主循环,我们才能看到这个窗口。
1 | root.mainloop() |
在我们关闭窗口前,程序将一直处在事件循环中。这个事件循环不仅仅接收来自用户(比如鼠标🖱点击和键盘⌨️输入)和系统(比如重绘事件和窗口配置信息)的事件,也会处理Tkinter
本身的事件。
比如串口重绘或者配置等。这也就意味着如果不进入这个事件循环,之前的程序窗口是无法显示出来的。
如果程序写的大了,一般需要把代码包装到一个或许多类里面。
1 | #!/usr/bin/python |
运行这个程序,将会出现下面的这个窗口。
点击Hello按钮,终端输出信息 “Hi there, welcome to tkinter!”,如果点击Quit按钮,程序将退出。
注意:有些程序可能不会按照上面介绍的运行,这个时候就要检查下Tkinter的说明文档了,看看操作环境相关的配置,然后debug。
这个示例,我们使用了class
方法。初始化init
方法创建一个继承自master
的容器frame
。
1 | class App: |
frame实例存储为局部变量,创建这个组建后,调用pack方法使之可见。
然后创建2个按钮组件,为frame的子组件。
1 | self.button = Button(frame, text='Quit', foreground='red', background='blue', command=frame.quit) |
这次,我们传递了一定数量的选项给结构器,第一个按钮被标记为 “QUIT”,前景色为红色 (其中fg 是foreground的简称)。第二个标记为 “Hello”。两个按钮都有一个command选项,这个选项指定了一个函数或者方法,在按钮被点击的时候调用。
按钮实例存储在实例属性组中。side=LEFT
参数表示这两个按钮在帧中将被分开放置;第一个按钮被放置
在帧的左边缘,第二个被放在第一个的右边(帧的左边缘仍保留着空格)。默认情况下,部件的放置都是相对
于它们的父亲(frame
部件相对于 master
,button
相对于 frame
)。如果 side
选项没指定,side
默认
值为 TOP
。
“hello” 按钮的回调函数如下所示,每次点击的时候都会在终端打印一条消息:
1 | def say_hi(self): |
最后,我们使用一些脚本来创建 Tk root 部件和一个App的实例(使用root作为它的父类):
1 | root = Tk() |
1 | Button(frame, text="Hello", command=self.hello).pack(side=LEFT) |
如果不需要对一个窗口部件进行引用,可以用单独的一行创建并且进行包装,如上所述。
但是如果需要对其进行调用,就需要分开了,如下所示:
1 | w = Button(frame, text="Hello", command=self.hello) |
在上述实例中,我们使用的button或者hi_there都是部件的引用,而不是部件实际的名字,实际的名字大部分是由一系列数字组成的,这个有Tkinter自动为这些新部件赋值。
比如,我们可以打印出来名字如下所示:
1 | >>> print str(ok) |
Tkinter支持下列15种核心组件:
按钮,用于执行命令或其他操作。
结构化的graphics。这个组件用于绘制graphs 和 plots,创建图像编辑器,部署其他组件。
有两个值,通过激活按钮来切换。
用于输入文本。
一个容器窗口部件。帧可以有边框和背景,当创建一个应用程序或 dialog(对话)版面时,帧被用来组织其它 的窗口部件。
用于显示文本或者图像。
显示供选方案的一个列表。listbox 能够被配置来得到 radiobutton 或 checklist 的状态
菜单条,用来实现下拉或者弹出式菜单
菜单按钮,用来实现下拉式菜单,目前基本可以使用Menu来实现
显示文本。与 label 窗口部件类似,但是能够自动地调整文本到给定的宽度或比率。
代表一个变量,为多个值中的一个。点击它将为这个变量设置值,并且清除与这同一变量相关的其它
radiobutton。
允许你通过滑块来设置一数字值
配合canvas, entry, listbox, and text窗口部件使用的标准滚动条。
格式化文本显示。允许你用不同的样式和属性来显示和编辑文本。同时支持内嵌图象和窗口。
一个容器窗口部件,作为一个单独的、最上面的窗口显示。
在Python 2.3 (Tk 8.4),增加了下述组件:
A variant of the Frame widget that can draw both a border and a title.
A container widget that organizes child widgets in resizable panes.
A variant of the Entry widget for selecting values from a range or an ordered set.
Also note that there’s no widget class hierarchy in Tkinter; all widget classes are siblings in the inheritance tree.
所有这些窗口部件提供了 Misc 和几何管理方法、配置管理方法和部件自己定义的另外的方法。此外,Toplevel
类也提供窗口管理接口。这意味一个典型的窗口部件类提供了大约 150 种方法。
The Tkinter module provides classes corresponding to the various widget types in Tk, and a number of mixin and other helper classes (a mixin is a class designed to be combined with other classes using multiple inheritance). When you use Tkinter, you should never access the mixin classes directly.
The Misc class is used as a mixin by the root window and widget classes. It provides a large number of Tk and window related services, which are thus available for all Tkinter core widgets. This is done by delegation; the widget simply forwards the request to the appropriate internal object.
The Wm class is used as a mixin by the root window and Toplevel widget classes. It provides window manager services, also by delegation.
Using delegation like this simplifies your application code: once you have a widget, you can access all parts of Tkinter using methods on the widget instance.
The Grid, Pack, and Place classes are used as mixins by the widget classes. They provide access to the various geometry managers, also via delegation.
Grid
The grid geometry manager allows you to create table-like layouts, by organizing the widgets in a 2-dimensional grid. To use this geometry manager, use the grid method.
Pack
The pack geometry manager lets you create a layout by “packing” the widgets into a parent widget, by treating them as rectangular blocks placed in a frame. To use this geometry manager for a widget, use the pack method on that widget to set things up.
Place
The place geometry manager lets you explicitly place a widget in a given position. To use this geometry manager, use the place method.
The Widget class mixes the Misc class with the geometry mixins, and adds configuration management through the cget and configure methods, as well as through a partial dictionary interface. The latter can be used to set and query individual options, and is explained in further detail in the next chapter.