频道栏目
首页 > 资讯 > Python > 正文

《Python编程》笔记(七)

17-03-02        来源:[db:作者]  
收藏   我要投稿

图形化用户界面

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组件类:
组件类 描述
Label 显示简单消息区域
Button 带有标签的简单的按钮
Frame 用于盛放和布置其他组件对象
Toplevel, Tk 由窗口管理器管理的新窗口
Message 一个多行标签
Entry 简单的单行文本输入区域
Checkbutton 双状态按钮组件,用于多选
Radiobutton 双状态按钮组件,用于单选
Scale 可衡量位置的滑动组件
PhotoImage 图像组件,全彩图
BitmapImage 图像组件,位图
Menu 菜单相关
Menubutton 打开菜单选项和子菜单的按钮
Scrollbar 滚动其他组件的控件
Listbox 选项列表
Text 多行文本浏览、编辑组件,支持字体
Canvas 图形绘画区域,支持线条、圆圈、照片、文字等

- 重要的类和工具:
- 几何管理器:pack, grid, place;
- tkinter连接变量:StringVar, IntVar, DoubleVar, BooleanVar;
- 高级Tk组件:SpinBox, LabelFrame, PanelWindow;
- 复合组件:Dialog, ScrolledText, OptionMenu;
- 回调安排:组件的after, wait, update方法;
- 其他:标准对话框、剪切板、bind和Event,组件设置选项,用户对话框、模式对话框、动画等。

相关TAG标签
上一篇:ASP.NET Core依赖注入解读及使用Autofac替代实现
下一篇:窗体无边框拖动
相关文章
图文推荐

关于我们 | 联系我们 | 广告服务 | 投资合作 | 版权申明 | 在线帮助 | 网站地图 | 作品发布 | Vip技术培训 | 举报中心

版权所有: 红黑联盟--致力于做实用的IT技术学习网站