内容正文:
第10章 图形用户界面程序设计
10.1 GUI编程
1.窗口与组件
在进行GUI开发时,首先需要创建一个顶层窗口。该窗口充当容器的角色,可以存放程序所需的各种组件,例如按钮、下拉框、单选框等。
顶层窗口包含了所有的组件,而组件本身也可以作为容器使用,包含其他组件。
包含其他组件的组件被称为父组件,而被包含在其中的组件则被称为子组件。
2.事件驱动与回调机制
事件:用户使用GUI程序进行的操作,如移动鼠标、点击鼠标、按下键盘上的按键等。
回调机制:当某个事件发生时,程序将调用对应的事件处理函数并做出相应的响应。事件对应的处理函数被称为回调函数。
10.2 Tkinter的主要组件
使用Tkinter库创建一个GUI程序只需要以下步骤:
(1)导入Tkinter模块。
(2)创建GUI应用程序的主窗口(顶层窗口)。
(3)添加完成程序功能所需要的组件。
(4)编写回调函数。
(5)进入主事件循环,对用户触发的事件做出响应。
10.2.1 标签
标签(Label)是用来显示图片和文本的组件。
在top主窗口中创建标签:label = Label(top, text=属性值)
text属性:设置显示的文本
例10-1在标签内显示两行文字
from tkinter import *
top = Tk()
top.title(u"主窗口")
label = Label(top, text="Hello World,
from Tkinter")
label.pack()
top.mainloop()
10.2.2 框架
框架(Frame)是其他组件的一个容器,通常是用来包含一组控件的主体。
frame = Frame(top,属性1=表达式1,属性2=表达式2….)
例 10-2
from tkinter import *
top = Tk()
top.title(u"主窗口")
for relief_setting in ["raised", "flat", "groove", "ridge", "solid",
"sunken"]:
frame = Frame(top, borderwidth=2, relief=relief_setting)
Label(frame, text=relief_setting, width=10).pack()
frame.pack(side=LEFT, padx=5, pady=5)
top.mainloop()
10.2.3 按钮
在主窗口w中创建了一个按钮控件:
btn=Button(w,text="Quit",command=w.quit)
Command属性:指定按钮的事件处理函数,将按钮与某个函数或方法关联起来。
例10-3
from tkinter import *
top = Tk()
top.title(u"主窗口")
bt1 = Button(top, text=u"禁用", state=DISABLED)
bt2 = Button(top, text=u"退出", command=top.quit)
bt1.pack(side=LEFT)
bt2.pack(side=LEFT)
top.mainloop()
10.2.4 输入框
输入框(Entry)是用来接收用户文本输入的组件。
E1 = Entry(f1, width=30)
例10-4
from tkinter import *
top = Tk()
top.title(u"登录")
f1 = Frame(top)
Label(f1, text=u"用户名").pack(side=LEFT)
E1 = Entry(f1, width=30)
E1.pack(side=LEFT)
f1.pack()
f2 = Frame(top)
Label(f2, text=u"密 码").pack(side=LEFT)
E2 = Entry(f2, width=30)
E2.pack(side=LEFT)
f2.pack()
f3 = Frame(top)
Button(f3, text=u"登录").pack()
f3.pack()
top.mainloop()
10.2.5 单选按钮和复选按钮
单选按钮(Radiobutton)和复选按钮(Checkbutton)比较:
相同点:都是用于用户进行选择输入。
区别:前者是互斥性选择,即用户只能从一组选项中选择一个选项;而后者支持用户选择多个选项。
(1)单选按钮的创建:需要将这些按钮与一个相同的变量关联起来,以设定或获取单选按钮组当前的选中状态。
r = Radiobutton(f1, text=txt, value=val, variable=choice)
(2)复选框的创建:需要将每个选项与一个不同的变量关联起来,以表示每个选项的选中状态。
r = Checkbutton(f1, text=txt, variable=ch, command=update_cstr)
from tkinter import *
top = Tk()
top.title(u"单选")
f1 = Frame(top)
choice = IntVar(f1) #定义动态绑定变量
for txt, val in [('1', 1), ('2', 2), ('3', 3)]:
#将所有的选项与变量choice绑定起来
r = Radiobutton(f1, text=txt, value=val, variable=choice)
r.pack()
choice.set(1) #设定默认选项
Label(f1, text=u"您选择了:").pack()
Label(f1, textvariable=choice).pack() #将标签与变量动态绑定起来
f1.pack()
top.mainloop()
例10-5
例10-6
from tkinter import *
top = Tk()
top.title(u"多选")
f1 = Frame(top)
choice = {} # 存放绑定变量的字典
cstr = StringVar(f1)
cstr.set("")
def update_cstr():
selected = [str(i) for i in [1, 2, 3] if choice[i].get() == 1]
cstr.set(",".join(selected))
for txt, val in [('1', 1), ('2', 2), ('3', 3)]:
ch = IntVar(f1) # 建立与每个选项绑定的变量
choice[val] = ch # 将绑定的变量加入字典choice
r = Checkbutton(f1, text=txt, variable=ch, command=update_cstr)
r.pack()
Label(f1, text=u"您选择了:").pack()
Label(f1, textvariable=cstr).pack()
f1.pack()
top.mainloop()
10.2.6 列表框与滚动条
列表框(Listbox):用列表的形式展示多个选项以供用户选择。
滚动条(Scrollbar):用来处理组件界面上一次性显示不全的情况。
scrollbar = Scrollbar(top) #创建滚动条
mylist = Listbox(top, yscrollcommand=scrollbar.set)
from tkinter import *
top = Tk()
top.title(u"列表框")
scrollbar = Scrollbar(top) #创建滚动条
scrollbar.pack(side=RIGHT, fill=Y) #设置滚动条布局
#将列表与滚动条绑定,并加入主窗体
mylist = Listbox(top, yscrollcommand=scrollbar.set)
for line in range(20):
mylist.insert(END, str(line))
mylist.pack(side=LEFT, fill=BOTH) #设置列表布局
scrollbar.config(command=mylist.yview) #将滚动条行为与列表绑定
mainloop()
例10-7
10.3 对象的布局方式
10.3.1 pack 布局管理器
pack布局管理器将所有控件组织为一行或一列,控件的添加顺序决定了它们在父控件中的位置。
side属性:改变控件的排列位置,LEFT表示左侧对齐,RIGHT表示右侧对齐。
fill属性:设置填充空间。
X:在水平方向填充,
Y:在垂直方向填充,
BOTH:在水平和垂直两个方向上填充,
NONE:不填充。
ipadx/ipady:用于设置控件内部在水平/垂直方向的间距。
padx/pady用于设置控件外部在水平/垂直方向的间距。
expand属性:指定如何使用额外的“空白”空间。
=1:随着父控件的大小变化而变化,
=0:子控件大小不能扩展。
from tkinter import *
w=Tk()
w.geometry('250x100') #改变 w 的大小为 250x100
Lbl1=Label(w,text='北京',bg='yellow3')
Lbl1.pack(expand=1,side=LEFT,ipadx=20)
Lbl2=Label(w,text='天津',bg='red')
Lbl2.pack(fill=BOTH,expand=1,side=LEFT,padx=10)
Lbl3=Label(w,text='上海',bg='green')
Lbl3.pack(fill=X,expand=0,side=RIGHT,padx=10)
w.mainloop()
例10-8
10.3.2 grid 布局管理器
grid布局管理器将窗口或框架视为一个由行和列构成的二维表格,并将控件放入行列交叉处的单元格中。
row:指定控件所要放置的行编号。
column:指定控件所要放置的列编号。
Sticky:用来改变对齐方式。如N、S、E、W、CENTER表示上、下、左、右、中心点,还可以取NE、SE、NW、SW,表示右上角、右下角、左上角、左下角。
Rowspan:指定在行方向上的跨度。
Columnspan:指定在列方向上的跨度。
例10-9
from tkinter import *
w=Tk()
var1=IntVar()
var2=IntVar()
Label(w,text="姓名").grid(row=0,column=0,sticky=W)
Label(w,text="住址").grid(row=1,column=0,sticky=W)
Entry(w).grid(row=0,column=1)
Entry(w).grid(row=1,column=1)
lframe=LabelFrame(w,text='性别')
radiobutton1=Radiobutton(lframe,text='男',variable=var1)
radiobutton2=Radiobutton(lframe,text='女',variable=var2)
lframe.grid(sticky=W)
radiobutton1.grid(sticky=W)
radiobutton2.grid(sticky=W)
photo=PhotoImage(file="e:\\mypython\\photo.png")
label=Label(image=photo)
label.image=photo
label.grid(row=2,column=1,sticky=W+E+N+S,padx=5,pady=5)
w.mainloop()
10.3.3 place 布局管理器
place布局管理器可以直接指定控件在父控件(窗口或框架)中的位置坐标。
为使用这种布局,只需先创建控件,再调用控件的place()方法,并使用该方法的选项x和y设定坐标。父控件的坐标系以左上角为原点(0,0),x轴方向向右,y轴方向向下。
>>> Label(w,text="Hello").place(x=0,y=0)
>>> Label(w,text="World").place(x=199,y=199,anchor=SE)
>>> Label(w,text="Hello").place(relx=0.25,rely=0.5,anchor=SW)
10.4 对话框
10.4.1 自定义对话框
主要步骤:
(1)先创建顶层窗口对象
(2)添加所需的按钮和其他控件。
例10-10 简易对话框应用示例。
from tkinter import *
def Msg():
top=Toplevel(width=400,height=200)
Label(top,text='Python').pack()
w=Tk()
Button(w,text='OK',command=Msg).pack()
w.mainloop()
10.4.2 标准对话框
1.messagebox子模块
提供一系列用于显示信息或进行简单对话的消息框,可通过调用函数askyesno()、askquestion()、askyesnocancel()、askokcancel()、askretrycancel()、showerror()、showinfo()和 showwarning()来创建。
from tkinter.messagebox import *
ask=askyesno(title='消息框演示',message='是否继续? ')
if ask:
showinfo(title='信息提示',message='继续! ')
else:
showinfo(title='信息提示',message='终止! ')
例子:
2.filedialog 子模块
提供用于文件浏览、打开和保存的对话框,可通过调用函数askopenfilename()、asksaveasfilename()等函数来创建。
from tkinter.filedialog import *
askopenfilename(title='文件对话框',\
filetypes=[('Python 源文件' ,'.py')])
例子:打开对话框
3.colorchooser 子模块
提供用于选择颜色的对话框,可通过函数 askcolor()来创建。
from tkinter.colorchooser import *
askcolor(title='颜色对话框')
10.5 事件处理
10.5.1 事件处理程序
1.事件的描述
一般形式是:<修饰符>-<类型符>-<细节符>
修饰符:描述鼠标的单击、双击,以及键盘组合按键等情况。
类型符:事件类型,最常用的类型有分别表示鼠标事件和键盘事件的Button和Key。
细节符:具体的鼠标键或键盘按键,如鼠标的左、中、右三个键分别用1、2、3表示,键盘按键用相应字符或按键名称表示。
例如:<Double-Button-1>
(1)常用鼠标事件
<ButtonPress-1>:按下鼠标左键,可简写为<Button-1>或<1>。类似的有<Button-2>(按下鼠标中键)和<Button-3>(按下鼠标右键)。
<B1-Motion>:按下鼠标左键并移动鼠标。类似的有<B2-Motion>和<B3-Motion>。
<Double-Button-1>:双击鼠标左键。
<Enter>:鼠标指针进入控件。
<Leave>:鼠标指针离开控件。
(2)常用键盘事件
<KeyPress-a>:按下a键。可简写为<Key-a>或a(不用尖括号)。
注意:空格键对应的事件是<space>,小于号键对应的事件是<less>。
<Return>:按下回车键。不可显示字符都可像回车键这样用<键名>表示对应事件,例如<Tab>、<Shift_L>、<Control_R>、<Up>、<Down>、<F1>等。
<Key>:按下任意键。
<Shift-Up>:同时按下Shift键和↑键。类似的还有Alt键组合、Ctrl键组合。
2.事件对象
每个事件都导致系统创建一个事件对象,并将该对象传递给事件处理函数。
常用的属性
x和y:鼠标单击位置相对于控件左上角的坐标,单位是像素。
x_root和y_root:鼠标单击位置相对于屏幕左上角的坐标,单位是像素。
num:单击的鼠标键号,1、2、3分别表示左、中、右键。
char:如果按下可显示字符键,此属性是该字符。如果按下不可显示键,此属性为空串。
keysym:如果按下可显示字符键,此属性是该字符。如果按下不可显示键,此属性设置为该键的名称。
keycode:所按键的ASCII码。注意,此编码无法得到上挡字符的ASCII码。
keysym_num:这是keysym的数值表示。对普通单字符键来说,就是ASCII码。
3.事件处理函数(回调(call back)函数)的一般形式
def 函数名(event):
函数体
10.5.2 事件绑定
1.事件绑定的方式
(1) 对象绑定和窗口绑定
形式如下:
控件对象.bind(事件描述符, 事件处理程序)
from tkinter import *
def callback(event):
print("clicked at",event.x,event.y)
w=Tk()
w.bind("<Button-1>",callback)
w.mainloop()
例10-11 窗口绑定应用示例。
(2) 类绑定
一般形式如下:
控件对象.bind_class(控件类描述符,事件描述符,事件处理程序)
例10-12 类绑定应用示例。
from tkinter import *
def callback(event):
print("Python")
w=Tk()
b1=Button(w,text="OK1")
b2=Button(w,text="OK2")
b1.bind_class("Button","<Button-1>",callback)
b1.pack()
b2.pack()
w.mainloop()
应用程序绑定:对应程序中的所有控件都有效。
一般形式如下:
控件对象.bind_all(事件描述符,事件处理程序)
例10-13 应用程序绑定应用示例。
from tkinter import *
def callback(event):
print("Python")
w=Tk()
b1=Button(w,text="OK1")
b2=Button(w,text="OK2")
l=Label(w,text="OK3")
b1.bind_all("<Button-1>",callback)
b1.pack()
b2.pack()
l.pack()
w.mainloop()
2.键盘事件与焦点
焦点(focus)是指当前正在操作的对象。
在处理键盘事件时,需要执行一个额外的步骤,即设置焦点。
设置焦点的两种方法:
调用对象的focus_set()方法
使用Tab键来移动焦点
例10-14 焦点应用示例。
from tkinter import *
def show1(event):
print("pressed",(event.char).lower())
def show2(event):
print("pressed",(event.char).upper())
w=Tk()
b1=Button(w,text='OK1')
b1.bind('<Key>',show1)
b2=Button(w,text='OK2')
b2.bind('<Key>',show2)
b1.focus_set()
b1.pack()
b2.pack()
w.mainloop()
$$