摘录 python核心编程s

GUI(Graphical User Interface)图形用户界面。

Tcl、Tk和Tkinter

Tkinter是python的默认GUI库。它基于Tk工具包,该工具包最初是为工具命令语言(Tcl)设计的。Tk普及后,被移植到很多其他的脚本语言中,包括Perl(Perl/Tk)、Ruby(Ruby/Tk)和Python(Tkinter)。

安装和使用Tkinter

Tkinter在系统中不是默认必须安装的,可以通过在python解释器中尝试导入Tkinter模块(在Python3中重命名为tkinter)来检查Tkinter是否可用。这里我测试的环境是Python3.6,这里没有发生错误:

>>> import tkinter
>>>

如果失败了的话,就需要重新编译Python解释器以使用tkinter了。

tkinter和Python编程

让GUI程序启动和运行起来需要以下5个步骤:

  1. 导入tkinter模块
  2. 创建一个顶层窗口对象,用于容纳整个GUI应用
  3. 在顶层窗口对象之上构建所有的GUI组件及其功能
  4. 通过底层的应用代码将这些GUI组件连接起来
  5. 进入主事件循环

窗口和控件

在GUI编程中,顶层的根窗口对象包含组成GUI应用的所有的小窗口对象。它们可能是文字标签、按钮、列表框等。这些独立的GUI组件称为控件(widget)。所以当我们说创建一个顶层窗口时,只是表示需要一个地方来摆放所有的控件。一般写成:

top = tkinter.Tk() #或者当采用‘from tkinter import *’ 时,也可直接写成 Tk()

tkinter.Tk()返回的对象通常称为根窗口,这也是一些应用使用root而不是top来指代它的原因。

顶层窗口是那些在应用中独立显示的部分。GUI程序中可以有多个顶层窗口,但是只有一个是根窗口。

控件可以独立存在,也可以作为容器存在。如果一个控件包含其他控件,就可以将其认为是那些控件的父控件。相应的,如果一个控件被其他控件包含,则称这个控件是那个控件的子控件。

事件驱动处理

通常,控件有一些相关的行为,比如按下按钮、将文本写入文本框、敲击回车等。这些行为称为事件。而GUI对这类事件的响应称为回调。

一个GUI程序从开始到结束就是通过整套事件体系来驱动的,这种方式称为事件驱动处理。

比如,最简单的鼠标移动。假设鼠标正停在GUI应用顶层窗口某处,将鼠标移动到另一部分,鼠标移动的行为会被复制到屏幕的光标上,于是看起来像是你的手在移动。系统必须处理的这些鼠标移动事件可以绘制窗口上的指针移动。当释放鼠标时,不再有事件需要处理,此时屏幕会重新恢复闲置的状态。

事件驱动的GUI处理本质上非常适合客户端/服务器架构。当启动一个GUI应用时,需要一些启动步骤来准备核心部分的执行,就像网络服务器启动时必须先分配套接字并将其绑定到本地地址上一样。GUI应用必须先创建所有的GUI组件,然后将他们绘制在屏幕上。这是布局管理器的职责所在。当布局管理器排列好所有空间后,GUI应用进入类似服务器的无限循环。这个循环一直运行,直到出现GUI事件,进行处理。

布局管理器

Tk有3种布局管理器来帮助控件进行定位。最原始的一种称为Placer。他的做法非常直接:你提供控件的大小和位置,然后交给布局管理器。这就存在很大的问题,你必须对所有控件进行这些操作,开发者很大负担。

第二种布局管理器,Packer,也是我们主要是用的。它会把控件填充到正确的位置(即指定的父控件中),然后对于之后的每个控件,回去寻找剩余的空间进行填充。这个处理很像是旅行时往行李箱中填充行李的过程。

第三种布局管理器是Grid。你可以基于网格坐标,使用Grid来指定GUI空间的放置。Grid会在它们的网格位置上渲染GUI应用中的每个对象。

本次学习中我们使用Packer。一旦Packer确定好所有控件的大小和对齐方式,它就会在屏幕上将其位置放置妥当。

当所有的控件摆放好后,可以让应用进入无限主循环中,代码如下:Tkinter.mainloop()

一般来说,这是程序运行的最后一段代码。当进入主循环后,GUI就从这里开始接管程序的执行,所有其他行为都会通过回调来处理,包括退出应用。

顶层窗口:Tkinter.Tk()

之前提到的所有主要控件都是构建在顶层窗口对象之上的,该对象在tkinter中使用Tk类进行创建:

>>> import tkinter
>>> top = tkinter.Tk()

在这个窗口中,可以放置独立的控件,也可以将多个组件拼凑在一起构成GUI程序。下面开始介绍Tk控件

Tk控件

下面介绍了18种Tk控件(更多或者更详细的资料参考Python主站上的Tkinter主题页):

控件描述
Button于Label类似,但提供额外的功能,如鼠标悬停、按下、释放以及键盘事件
Canvas提供绘制形状的选择(线段、椭圆、多边形、矩形),可以包含图像和位图
Checkbutton一组选框,可以勾选其中的任意个(和HTML中的checkbox类似)
Entry单行文本框,用于收集键盘输入
Frame包含其他控件的纯容器
Label用于包含文本或图像
LabelFrame标签和框架的组合,拥有额外的标签属性
Listbox给用户显示一个选项列表进行选择
Menu按下Menubutton后弹出的选项列表,用户可以从中选择
Menubutton用于包含菜单(下拉、级联等)
Message消息,于Label类似,不过可以显示成多行
PanedWindow一个可以控制其他控件在其中摆放的容器控件
Radiobutton一组按钮,其中只有一个可以按下
Scale线型‘滑块’控件,根据已设定的起始值和终止值,给出当前设定的精确值
Scrollbar为Text、Canvas、Listbox、Enter等支持的控件提供滚动功能
SpinboxEntry和Button的组合,允许对值进行调整
Text多行文本框,用于收集或显示用户输入的文本
Toplevel和Frame类似,不过他提供了一个单独的窗口容器

下面先来看看一下简单的使用:

Label控件

下面tkhello.py脚本中,展示了Tkinter应用如何启动,着重强调了Label控件:

#python 3.6

import tkinter

top = tkinter.Tk()#创建顶层窗口
label = tkinter.Label(top,text='Hello world!')#创建标签label,包含连个参数
label.pack()#使用Packer来管理和显示控件
tkinter.mainloop()#运行GUI程序。这是必须的

运行效果:

pytho GUI编程之Tkinter-LMLPHP

Button控件

下面的tkhello2.py脚本中,展示了按钮控件:

#python 3.6

import tkinter

top = tkinter.Tk()#创建顶层窗口
quit = tkinter.Button(top,text='Hello World!',command=top.quit)#创建按钮quit,包含三个参数,其中第三个参数给按钮安装了一个回调函数:当按下按钮并释放后,程序就会退出
quit.pack()#使用Packer来管理和显示控件
tkinter.mainloop()#运行GUI程序。这是必须的

运行效果:

pytho GUI编程之Tkinter-LMLPHP

Label和Button控件

下面的tkhello3.py脚本中,结合了标签和按钮的功能,并使用了更多的参数:既包括控件的非默认参数,又展示了Packer的一些参数。fill参数告诉Packer让‘退出’按钮占据剩余的水平空间,expand参数则会引导它填充整个水平可视空间,将按钮拉伸到左右窗口边缘。同时,如果Packer没有收到其他指示,所有控件都是垂直排列的(自上而下依次排列):

#python 3.6

import tkinter

top = tkinter.Tk()
hello = tkinter.Label(top,text='Hello Tkinter!') #创建一个标签
hello.pack() quit = tkinter.Button(top,text='退出',command=top.quit,bg='red',fg='white')#创建一个退出按钮,除了绑定一个回调函数外,还设置了背景色以及释放按钮时的颜色
quit.pack(fill = tkinter.X,expand=1)# tkinter.mainloop()

运行效果:

pytho GUI编程之Tkinter-LMLPHPpytho GUI编程之Tkinter-LMLPHP

Label、Button和Scale控件

下面的tkhello4.py脚本中,在之前的基础上增加了Scale控件:

#python 3.6
from tkinter import * #虽然这种写法会污染命名空间,但是这里依然使用这种写法是因为该脚本会大量引用此模块 #该函数依附于Scale控件,当Scale控件的滑块移动时,这个函数就会被激活。该函数会调整Label控件中的文本大小
def resize(ev = None):
label.config(font = 'Helvetica -%d bold' % scale.get()) top = Tk()#顶层窗口
top.geometry('250x150+1000+500')#设置顶层窗口的属性,250x150是指窗口大小,1000+500是指窗口在屏幕上的位置 #创建一个Label控件,拥有默认大小的字体
label = Label(top,text = 'hello world!',font = 'Helvetica -12 bold')
label.pack(fill = Y,expand=1)#通过Packer管理和显示label控件
#创建Scale控件,该控件通过回调resize函数用来控制Label控件中字体大小的;from_属性是滑块最小值,to是滑块最大值
scale = Scale(top,from_=10,to=40,orient=HORIZONTAL,command=resize)
scale.set(30)#设置滑块的初始值
scale.pack(fill=Y,expand = 1)#通过Packer管理和显示Scale控件 #创建一个退出按钮控件,按下按钮前的颜色是白色,释放按钮后的颜色是红色
quit = Button(top,text='退出',command=top.quit,activeforeground='white',activebackground='red')
quit.pack()#通过Packer管理和显示Button控件 mainloop()#主循环

运行效果:

pytho GUI编程之Tkinter-LMLPHPpytho GUI编程之Tkinter-LMLPHP

tkinter结合偏函数应用实例

何谓偏函数:当函数的参数个数比较多的时候,可以使用functools.partial创建一个新的函数,这个新函数会固定住(冻住)原函数的部分参数,之后,当获得需要的剩余参数后,可以将它们解冻,一起传入到最终的参数中,从而使用最终确定的所有参数去调用函数。简单说就是:。functools.partial会把一个函数的某些参数设置默认值,并返回一个新函数,我们可以直接调用这个新函数。友情链接:偏函数

偏函数的一大优点就是它不局限于函数,而是可以用于任何可调用对象(任何包括函数接口的对象),只需要使用圆括号即可,包括类、方法或可调用实例。

对于很多可调用对象,并且许多调用都反复使用相同参数的情况,使用偏函数会更合适。

GUI编程是一个很好的偏函数用例,因为你很有可能需要GUI控件在外观上具有某种一致性,而这种一致性来自于使用相同参数创建相似的对象时。想象有这么一个应用,有很多按钮拥有相同的背景色和前景色,对于这种只有细微差别的按钮,每次都使用相同的参数创建相同的实例简直是一种负担和浪费。

下面的pfaGUI2.py脚本中,展示了一个交通路标的实例,该应用会尝试创建文字版本的路标,并将其根据标志类型进行区分,比如严重、警告、通知。而标志类型决定了创建时的颜色方案:严重级别的是白底红字,警告级别的是黄底黑字,通知级别的是白底黑字。这里,‘严禁驶入’和‘错误路线’属于严重级别,‘交通拥堵’‘火车交汇口’属于警告级别,而‘限速’和‘单行线’属于通知级别。该应用会创建这些标志的按钮,当用户按下按钮时,会弹出相应的对话框。

这里要注意的版本问题,python2.x中的tkMessageBox模块在python3.x中被重命名为messagebox,并被整合进tkinter模块中:

#python 3.6

from functools import partial#使用偏函数
from tkinter import Tk,X,Button#仅仅导入tkinter模块中用到的属性
from tkinter.messagebox import showinfo,showwarning,showerror #python 3.xb版本中对tkMessageBox重命名。这里仅仅导入三个对话框
#python2.x中使用from tkMessageBox import showinfo,showwarning,showerror #定义了三个标志 警告、严重、通知
WARN = 'warn'
CRIT = 'crit'
REGU = 'regu'
SIGNS = {
'严谨驶入': CRIT,
'火车交汇': WARN,
'限速': REGU,
'错误路线': CRIT,
'交通拥堵': WARN,
'单行线': REGU,
'do not': CRIT
} #对话框用作按钮的回调函数,将在创建每个按钮时使用他们
critCB = lambda:showerror('Error','按下错误按钮')
warnCB = lambda:showwarning('Warning','按下警告按钮')
infoCB = lambda:showinfo('Info','按下通知按钮')
#启动Tk,设置标题、位置
top = Tk()
top.title('路标')
top.geometry('250x250+700+500')#设置顶层窗口的属性,250x250是指窗口大小,1000+500是指窗口在屏幕上的位置
Button(top,text='退出',command = top.quit,bg = 'red',fg = 'white').pack() #创建一个退出按钮。设置前景色、背景色,并用Packer管理 MyButton = partial(Button,top)#创建了一个一阶偏函数,模板化Button类和根窗口top。MyButton效果相当于tkinter.Button() 并将top作为他的第一个参数
#下面是三个二阶偏函数。二阶偏函数是对一阶偏函数的再次模板化。最终效果相当于使用top、回调函数和颜色这几个参数去调用Button。
CritButton = partial(MyButton,command = critCB,bg = 'white',fg = 'red')
WarnButton = partial(MyButton,command = warnCB,bg = 'yellow')
ReguButton = partial(MyButton,command = infoCB,bg = 'white') for eachSign in SIGNS:
signType = SIGNS[eachSign]
#构建一个可求值字符串cmd,该字符串包含按钮名、传给按钮标签的文本参数 和 pack()操作组成。如果是严重级别,会把字符大写,否则按照标题格式输出。这里还使用了三元操作符。格式化字符串的时候要注意%s和%r的区别.
#标题化函数title(),即所有单词的首字母都大写,其他的字母都小写
cmd = '%sButton(text = %r%s).pack(fill = X,expand = True)' % \
(signType.title(),eachSign,'.upper()' if signType ==CRIT else '.title()') #
eval(cmd)#该函数用于执行一个字符串表达式,这里是实例化按钮 top.mainloop()#主循环,用于启动GUI程序

运行效果图:

pytho GUI编程之Tkinter-LMLPHPpytho GUI编程之Tkinter-LMLPHPpytho GUI编程之Tkinter-LMLPHPpytho GUI编程之Tkinter-LMLPHP

自此,我们算是入门了。下节我们展示一个中级的tkinter示例~咩

05-22 16:03