Python中可以使用的主要GUI工具包:
tkinter:开源GUI库,作为Python业界开发移动GUI约定俗成的标准,拥有Pmw, Tix, PIL和ttk等强大的扩展包。 wxPython:一个用于开源wxWidgets库,最初为C++编平台GUI类框架。适合复杂界面搭建,流行度jinciyutkinter。 PyQt:一个全面的GUI库,跨越多个平台。相对复杂,但有更多特性。 PyGTK:用于连接GTK的库,可以跨平台。 Jython:一款针对Java的Python应用,可以将Python转换为Java字节码,完成Python语言与Java类库的无缝连接。所以可以使用Java中的swing和awt等实现用户界面。 IronPython:一款针对.Net环境和runtime engine的Python应用,可以将Python代码转换成.Net字节码,同时支持在.Net架构下用Python脚本搭建GUI界面。
tkinter的特点:
可访问性:一个轻量级的工具包,非常易用。并且易于扩展。 可移植性:能够在不同的平台下提供本地化特色的界面。 可用性:它是Python库中的标准模块,自带编译器。
tkinter扩展包介绍:
Pmw:Python Mega Widgets是用Python建立高级复合组件的扩展工具包。可以提供满足高级GUI开发的需要。该组件的接口与基本dtkinter组件类似。 Tix:含有40多个高级组件的集合,最初为Tcl/TK编写。可以在tkinter.tix中得到它。 ttk:一个比较新的组件集,试图区分两类代码,一类负责执行组件行为,另一类负责实现不同的组件外观。该包在tkinter.ttk中。 PIL:Python Imaging Library是一个开源图像处理扩展包,提供缩放、变形、转换等工具,支持多种图像文件类型。
Python的
tkinter模块在Tk(最初用Tcl语言编写,由Tcl创立者John Outerhout开发)之上增加了一个软件层,该层允许Python脚本与Tk对话,进行界面的构建和配置,再经路径控制回到处理用户事件的Python脚本。内部调用顺序:从Python脚本到tkinter,再到Tk;GUI事件的响应顺序则是从Tk到tkinter再到Python脚本。
Python
tkinter是在C编写的
_tkinter(实际是它与Tk交互)库之上增加了基于类的交互界面。Python/tkinter程序是事件驱动的。
简单的示例:
# 导入Label组件
from tkinter import Label
# 获取Label对象实例
widget = Label(None, text="Hello, tkinter!")
# 布局
widget.pack()
# 开始事件循环
widget.mainloop()
创建组件:
第一个参数是父组件对象,设置为None表示将新的组件设置在程序的默认顶层窗口。当然也可以将组件放置到其他容器中。 第二个参数是配置参数。
http://blog.chriscabin.com/ tkinter几何管理器只是安排一个或多个组件在容器(父组件)内的位置,顶层窗口和框架(一种特殊组件)都可以作为容器;容器可以嵌套,显示出层级结构。常用的有两种:打包pack和网格grid。 在Windows下,.pyw和.py结尾的Python程序在双击运行时表现是不同的,前者不会弹出DOS控制台作为它的标准流。实际上.pyw文件最好用pythonw程序打开。 widget.pack(side=TOP),TOP常量在pack中被调用,它是tkinter的专有名字之一。常量模块由tkinter自动加载。 对组件使用pack时,可以选择组件在父组件中的位置:顶、底、左、右。如果不设置位置,则默认放在父组件的顶部。 root=Tk()可以得到主窗口,即程序运行时就会出现的窗口。组件如果不设置父组件,则默认依附于自动创建的Tk实例(主窗口)。
# 不用显示地指定root,此时master会被设置为自动创建的Tk对象
Label(text='Hello, Left.').pack(side=LEFT)
Label(text='Hello, Right.').pack(side=RIGHT)
Label(text='Hello, Top.').pack(side=TOP)
Label(text='Hello, Bottom.').pack(side=BOTTOM)
mainloop()
Label(text='Hello, Top.').pack(side=TOP, expand=YES, fill=BOTH):
expand=YES要求打包几何管理器为组件扩展空间,通常可以是父组件中任何未被占用的地方。仅使用该项,而不加fill选项,会自动居中组件。 fill选项:可用来拉伸组件,使其充满分配的空间。fill=Y表示垂直拉伸,fill=X表示水平拉伸,fill=BOTH表示都拉伸。
设置组件选项的另外两种方法:
>>> label.config(text="Good")
>>> label['text'] = "Yahoo"
设置窗口标题:Tk().title("Hello, world") tkinter在内部将组件对象交叉连接成一棵可以长期存在的树,该树代表了要显示的对象。所以,即使Python中,对象被垃圾回收,但是在tkinter中还是会被保留下来。 添加按钮和回调函数的方法:
def button_demo():
root = Tk()
# 实际上sys.exit和root.quit都能得到相同的效果
# command就是记录按钮单击后的回调行为
Button(text="Exit", command=root.quit).pack(side=TOP, expand=YES)
root.mainloop()
回调处理器通常可以是任何可调用的对象:函数、lambda表达式生成的匿名函数、类或者类型实例的绑定方法,或者是继承了__call__运算符重载方法的实例。当按钮按下发生回调时,回调处理器不会接受到任何参数(除了自动为绑定方法生成的self参数),回调处理器需要的状态信息必须以其他方式提供,如全局变量、类实例的属性、间接层提供的额外参数等。 使用lambda表达式和对象引用来延迟调用:这时一种比较常见的技巧,可以将不带参数的回调映射到另一个由lambda提供参数的函数上:
def handler(widget, x, y):
widget.configure(text='{} * {} = {}'.format(x, y, y))
def button_demo_2():
label = Label()
label.pack(side=TOP, expand=YES)
# 可以延迟对真正的回调函数的调用
Button(text='Press me!', command=lambda: handler(label, 88, 109)).pack(side=BOTTOM, expand=YES)
mainloop()
延迟回调是有必要的(通常都是需要接受参数的回调函数),如果在按钮创建函数中编写回调处理器调用,那么回调函数会在创建按钮的同时被调用。所以,必须要使用中间函数来延迟调用。 Python的封闭作用域引用模型意味着函数局部作用域内包含lambda函数的局部变量值会被自动保留,并且在按钮按下时使用。 默认参数与外部作用域引用不同,它在函数创建时定值,而不是调用时定值。它可以记录函数创建时封闭作用域内名字的值,而不是封闭作用域内某个地方最后分配的值。当函数的作用域是一个模块而不是另外一个函数时,也是如此。这些是在GUI注册回调函数时需要注意的。
>>> def odd():
funcs = []
for c in 'abcdefg':
# 如果是lambda: c,则在后面调用后会出现同样的信息。
# 使用默认参数传值才可以记住c
funcs.append(lambda c=c: c)
return funcs
>>> for x in odd():
print(x())
总结:封闭作用域名字引用可以作为默认参数传值的替代之选,但是有条件:封闭作用域中的名字不会在内嵌函数创建后变成其他不知道的值。通常,不能在内嵌函数内部引用封闭作用域的循环变量,然而,在其他大多数情况下,封闭作用域的循环变量在其作用域中只有一个值,这样才可以被自用地使用。但相对来说,类是一种更好的处理方法,它可以用来保存需要的额外信息,也不会出现作用域的问题。 使用类中的方法作为回调函数:
class MyGUI:
def __init__(self):
self.x = 100
self.y = 100
self.label = Label()
# 注意,pack返回的是None
self.label.pack(side=TOP, expand=YES)
Button(text="Press to get result", command=self.get_result).pack(side=TOP, expand=YES)
Button(text='Quit', command=sys.exit).pack(side=TOP, expand=YES)
def get_result(self):
self.label.config(text='{} * {} = {}'.format(self.x, self.y, self.x * self.y))
def main():
MyGUI()
mainloop()
再来看看另外一种方法来设定回调函数:可调用类对象
class HelloCallable:
def __init__(self):
self.msg = 'Hello __call__ world!'
def __call__(self, *args, **kwargs):
# 重要的是继承了__call__方法后,就可以让类对象像普通函数那样被调用。
print(self.msg)
sys.exit()
# 注意,`HelloCallable()`才可以工作!
Button(text='Quit', command=HelloCallable()).pack(side=TOP, expand=YES)
tkinter回调协议总结:
按钮的command选项:捕捉按钮按下事件。 菜单的command选项:捕捉菜单按下事件。 滚动条协议:同样使用command注册处理器,但是包含一个独特的事件协议,允许它们与被滚动的组件交叉组合,即移动滚动条就会自动移动组件,反之亦然。 一般的组件bind方法:这种机制可以用来为低级别的界面事件注册回调处理器,比如按键、鼠标等。bind调用会接收一个事件对象参数(Event实例),它会提供事件的环境信息。 窗口管理器协议:捕捉窗口管理器事件。在顶层窗口的对象管理器里面输入protocol方法,为WM_DELETE_WINDOW设置处理器。 预定的事件回调:如定时器过期、输入数据到达、事件循环的空闲状态等。
在框架(Frame)组件中添加多个组件:
class MyGUI:
def __init__(self):
self.win = Frame()
self.win.pack(expand=YES)
Label(self.win, text="Hello, container world!").pack(side=TOP)
Button(self.win, text="Hello", command=lambda: print('Hello, container world!')).pack(side=LEFT)
Button(self.win, text="Quit", command=self.win.quit).pack(side=RIGHT)
def main():
MyGUI()
mainloop()
先打包的组件会在窗口收缩后最后剪裁,所以打包的顺序决定了窗口缩小后那些组件会被先挤出去。 一般地,通过将组件依附于框架,再将框架依附于其他框架,我们可以搭建出任意的GUI布局。 当组件树显示时,孩子组件出现在父组件内部,并且按照打包顺序好打包位置排列。所以说,组件的打包顺序不仅决定了它们的裁切顺序,还决定了它们在生成的显示中相互间边的位置关系。
packer布局系统的工作原理:
packer最初拥有整个父组件容器的可用空间; 随着组件在某条边上被打包,该组件获得了剩余空间要求的一条边,剩余空间缩小; 经过先前的打包要求,空间缩小,后来的打包仅要求获得剩余空间中的一条边; 组件全部分配后,expand选项划分所有剩余空间,fill选项和anchor选项在组件分配的空间内拉伸调整组件。
anchor选项可以定位分配空间中的位置替代
fill选项。可以指定八个方位(指南针:N, NE, NW, W, S等)以及CENTER位置。默认就是CENTER。
fill选项和anchor选项必须在组件分配到所在空间、完成打包顺序、expand要求后才可以使用。通过一起使用打包顺序、边、填充和定位,可以产生多种布局和剪裁效果。 大型GUI界面通常作为Frame子类创建,用回调处理器作为方法。
class HelloFrame(Frame):
def __init__(self, parent=None):
Frame.__init__(self, parent)
self.pack()
self.data = 20
self.make_widgets()
def make_widgets(self):
w = Button(self, text="hello", command=self.msg)
w.pack(side=LEFT)
def msg(self):
self.data += 1
print('Hello frame world!', self.data)
技巧:要改变GUI的行为,可以写一个新类来自定义它的相关部分,而不是改写现有的GUI代码。 tkinter组件类:
|
|
---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
- 重要的类和工具:
- 几何管理器:pack,
grid,
place;
- tkinter连接变量:StringVar,
IntVar,
DoubleVar,
BooleanVar;
- 高级Tk组件:
SpinBox,
LabelFrame,
PanelWindow;
- 复合组件:Dialog,
ScrolledText,
OptionMenu;
- 回调安排:组件的after,
wait,
update方法;
- 其他:标准对话框、剪切板、bind和
Event,组件设置选项,用户对话框、模式对话框、动画等。