Python核心编程笔记————GUI编程(一)

ps:python版本为3.6.2

简介

. 这里主要使用的GUI工具包是Python默认的GUI库Tk,通过接口Tkinter来访问,Tk并不是最新和最好的,也没有包含最强大的GUI构建模块集,但是足够易用。
  查看自己的解释器是否安装了Tkinter的方法是import一下,不过在python3的版本下应该是自动安装的,另外模块的名字是tkinter。

Tkinter和Python编程

Tkinter模块:添加Tk到应用中

. 让一个GUI应用能够启动和运行起来需要以下5个步骤:
  1.Tkinter模块;
  2.创建一个顶层窗口对象,以容纳整个GUI应用;
  3.在顶层窗口对象上(或其中)构建所有的GUI组件(及功能);
  4.通过底层的应用代码将这些组件连接起来;
  5.进入主事件循环。

GUI编程介绍

. 就像画家作画一样,首先需要的是一张干净的画布,然后才能在上面画出不同的画面,这个基础就像是Tkinter中的顶层窗口对象。

窗口和控件

. 在GUI编程中,顶层的根窗口对象包含组成GUI应用的所有小窗口对象,它们可能是标签、按钮、列表框等,这些独立的GUI组件称为控件。一般创建一个顶层窗口所用的语句是:

top = tkinter.Tk()

. 这里返回的对象通常称为根窗口,顶层窗口是那些在应用中独立显示的部分。GUI程序中可以有多个顶层窗口,但是只能有一个根窗口。
  控件可以独立存在,也可以作为容器存在,如果一个控件包含其他控件,就可以将其认为是那些控件的父控件,那些控件也认为是这个控件的子控件
  通常,控件有一些相关的行为,比如按钮可以点击,这些用户行为被称为事件,而GUI对这些事件的响应称为回调

事件驱动处理

. 一个GUI应用从开始到结束就是通过整套事件体系来驱动的,这种方式称为事件驱动处理
  比如说最简单的鼠标移动的例子。假设鼠标处于静止状态,然后被手移动到另一个位置,鼠标移动的行为会被复制到屏幕的光标上,于是看起来像是根据你的手来移动的,系统必须处理这些鼠标移动事件来绘制窗口上的光标移动,当鼠标再次停下来,没有事件需要处理的时候,屏幕会重新恢复到闲置状态。

布局管理器

. Tk有三种布局管理器来帮助控件集进行定位。
  第一种称为Placer:做法很直接,开发者提供控件的大小和摆放位置,然后管理器就会将其摆放好。问题是开发者必须对所有控件进行这些操作,这会加大开发者的负担;
  第二种是Packer,这将是主要使用的:它会将控件填充到正确的位置(即指定的父控件中),然后对于之后的控件,回去寻找剩余的空间填充,就像是打包行李的过程;
  第三种是Grid:基于网格坐标来指定控件的位置,Grid会在它们的网格位置上渲染GUI应用中的每个对象。
  当所有控件摆放好后,就可以让应用进入到无限主循环中,代码一般如下:

tkinter.mainloop()

. 一般这是程序的最后一句话。当程序进入到主循环后,GUI就从这里开始接管程序,所有其他行为都通过回调来处理,甚至包括退出。

Tk控件

. 以下是书中提到的控件:

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

Tkinter示例

Label、Button和Scale控件

. 以下是一个包含标签、按钮以及滑块的GUI程序,通过滑块调节标签中字的大小,通过按钮退出程序:

import tkinter

def resize(ev = None):									#滑动滑块时的回调函数
    label.config(font = 'Helvetica -%d bold' % scale.get())

top = tkinter.Tk()
top.geometry('250x150')									#设置根窗口的大小,是x,不是*

label = tkinter.Label(top,text = 'Hello World',font = 'Helvetica -12 bold')
label.pack(fill = tkinter.Y,expand = 1)

scale = tkinter.Scale(top,from_ = 10,to = 40,orient = tkinter.HORIZONTAL,
                        command = resize)
scale.set(12)												#滑块位置的初始值设置
scale.pack(fill = tkinter.X,expand = 1)

quit = tkinter.Button(top,text = '退出',command = top.quit,
                      activeforeground = 'white',
                      activebackground = 'red')
quit.pack(fill = tkinter.X,expand = 1)
tkinter.mainloop()

. 控件一般都有默认参数,如果不在意可以不用都写,另外在按钮和滑块的pack函数中对fill赋值,表示填充剩余的水平空间, expand 参数则会引导它填充整个水平可视空间,将按钮拉伸到左右窗口边缘。command参数表示给控件安装的回调函数。
  在pack没有收到其它指示的时候,控件都默认是垂直排列的,如果想要水平布局则需要创建一个新的Frame对象来添加按钮。

偏函数应用示例

. 偏函数是函数式编程一系列改进中的一部分。使用偏函数,可以有效的“冻结”那些预先确定的参数来缓存函数参数,然后再运行时,当获得需要的剩余参数后,可以将它们解冻,传递到最终的参数中,从而使用最终确定的所有参数去调用函数。
  偏函数不止可适用于函数,也可以使用于可调用对象(重载了圆括号的那种类或结构体吧),对于那些反复使用相同的参数的可调用对象,使用偏函数会更合适。
  GUI编程是一个很好的偏函数用例,因为通常会希望界面的风格能够一致,比如按钮拥有一致的前景色和背景色。
  以下是一个偏函数的示例,通过交通路标的的不同颜色方案来展示,分为三种:严重、警告和提醒,三种路标有不同的文字、前景色、背景色和回调函数:

from functools import partial as pt         #偏函数相关库
from tkinter import Tk,Button,X,messagebox

WARN = 'warn'
CRIT = 'crit'
REGU = 'regu'

SIGNS = {
    'do not enter':CRIT,
    'railroad crossing':WARN,
    '55\hspeed limit':REGU,
    'wrong way':CRIT,
    'merging traffic':WARN,
    'one way':REGU
}

#点击按钮的回调
critCB = lambda : messagebox.showerror('Error','Error Button Pressed')
warnCB = lambda : messagebox.showwarning('Warn','Warn Button Pressed')
reguCB = lambda : messagebox.showinfo('Regu','Regu Button Pressed')

top = Tk()
top.title('Road Signs')
Button(top,text = 'QUIT',command = top.quit,bg = 'red',fg = 'white').pack()

MyButton = pt(Button,top)
CritButton = pt(MyButton,command = critCB,bg = 'white',fg = 'red')
WarnButton = pt(MyButton,command = warnCB,bg = 'goldenrod1')
ReguButton = pt(MyButton,command = reguCB,bg = 'white')

for eachsign in SIGNS:
    signtype = SIGNS[eachsign]
    cmd = "{0}Button(text = '{1}').pack(fill = X,expand = True)".format(
        signtype.title(),eachsign)

    eval(cmd)

top.mainloop()

. 其中使用了两阶偏函数,第一阶模板化了Button类和根窗口,意味着每次调用MyButton时都会调用Button类,并将top作为它的第一个参数。将它“冻结”为MyButton。
  第二阶偏函数使用第一阶偏函数,并对其模板化,创建不同的按钮时,它就会调用适当的MyButton。最后的eval() 函数用来执行一个字符串表达式。

中级Tkinter示例

. 接下来是一个更复杂的示例,这是一个目录树遍历工具,除了之前的空间还使用到了列表框、文本框和滚动条。此外,还增加了鼠标单击、键盘按下、滚动操作等回调函数:

import os
from time import sleep
from tkinter import *

class DirList(object):

    def __init__(self,initdir = None):
        self.top = Tk()
        self.label = Label(self.top,text = 'Directory Lister v1.1')
        self.label.pack()

        self.cwd = StringVar(self.top)			#用于保存当前所在的目录名		

        self.dirl = Label(self.top,fg = 'blue',font = ('Helvetica' ,12,'bold'))
        self.dirl.pack()

        self.dirfm = Frame(self.top)						#中间部分,包括显示列表,滑块
        self.dirsb = Scrollbar(self.dirfm)
        self.dirsb.pack(side = RIGHT,fill = Y)
        self.dirs = Listbox(self.dirfm,height = 15,width = 50,
                            yscrollcommand = self.dirsb.set)
         #通过使用 Listbox 的 bind()方法,
         #Listbox 的列表项可以与回调函数(setDirAndGo)连接起来                   
        self.dirs.bind('<Double-1>',self.setDirAndGo)				
        self.dirsb.config(command = self.dirs.yview)
        self.dirs.pack(side = LEFT,fill = BOTH)
        self.dirfm.pack()

		#输入框,可以从这里手动输入目录跳转
        self.dirn = Entry(self.top,width = 50,textvariable = self.cwd)
        #绑定了回车键,除了点击按钮,敲击回车也能达到同样效果
        self.dirn.bind('<Return>',self.doLS)
        self.dirn.pack()

        self.bfm = Frame(self.top)
        self.clr = Button(self.bfm,text = 'clear',
                          command = self.clrDir,
                          activeforeground = 'white',
                          activebackground = 'blue')
        self.ls = Button(self.bfm,text = 'List Directory',
                         command = self.doLS,
                         activeforeground='white',
                         activebackground='blue')
        self.quit = Button(self.bfm, text='quit',
                         command=self.top.quit,
                         activeforeground='white',
                         activebackground='red')
        self.clr.pack(side = LEFT)
        self.ls.pack(side=LEFT)
        self.quit.pack(side=LEFT)
        self.bfm.pack()

        if initdir:
            self.cwd.set(os.curdir)
            self.doLS()

	#清空输入框
    def clrDir(self,ev = None):
        self.cwd.set('')

	#设置要遍历的目录
    def setDirAndGo(self,ev = None):
        self.last = self.cwd.get()
        self.dirs.config(selectbackground = 'red')
        check = self.dirs.get(self.dirs.curselection())
        if not check:
            check = os.curdir
        self.cwd.set(check)
        self.doLS()

    def doLS(self,ev = None):
    	#安全检查
        error = ''
        tdir = self.cwd.get()
        if not tdir:
            tdir = os.curdir
        if not os.path.exists(tdir):
            error = tdir + 'no such file'
        elif not os.path.isdir(tdir):
            error = tdir + 'is not a dir'

        if error:
            self.cwd.set(error)
            self.top.update()
            sleep(2)
            if not (hasattr(self,'last') and self.last):
                self.last = os.curdir
            self.cwd.set(self.last)
            self.dirs.config(selectbackground = 'LightSkyBlue')
            self.top.update()
            return
		
		#如果一切正常,就会调用 os.listdir()获取实际文件列表并在 Listbox 中进行替换
        self.cwd.set('Fetching directory contents...')
        self.top.update()
        dirlist = os.listdir(tdir)
        dirlist.sort()
        os.chdir(tdir)

        self.dirl.config(text = os.getcwd())
        self.dirs.delete(0,END)
        self.dirs.insert(END,os.curdir)
        self.dirs.insert(END, os.pardir)
        for eachFile in dirlist:
            self.dirs.insert(END,eachFile)
        self.cwd.set(os.curdir)
        self.dirs.config(selectbackground = 'LightSkyBlue')

def main():
    d = DirList(os.curdir)
    mainloop()

if __name__ == '__main__':
    main()

. 其中bind函数起到绑定的作用,绑定意味着将一个回调函数与按键、鼠标操作或一些其他事件连接起来,当用户发起这类事件时,回调函数就会执行。当双击 Listbox 中的任意条目时,就会调用 setDirAndGo()函数。而 Scrollbar 通过调用 Scrollbar.config()方法与 Listbox 连接起来。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值