GUI编程的布局就相当于小孩搭积木,每个积木块放在那里、面积多大,也就是对大小和位置进行管理,二布局管理器就是负责各组件的大小和位置的管理。此外,当用户调整了窗口的大小之后,布局管理器还会自动调整窗口中各组件的大小和位置。
Pack 布局管理器
如果使用Pack布局,那么这些组件是依次向后排列,排列方向即可是水平的,也可是垂直的。
简单示范pack用法
import tkinter
window = tkinter.Tk()
window.title('Pack布局')
window.geometry('300x150')
## 用for循环添加三个标签
for i in range(3):
L = tkinter.Label(window,text='第%d个Label' % (i + 1),bg='green')
L.pack()
window.mainloop()
运行效果:
pack()方法支持的选项:
- anchor:当可用空间大于组件所需求的大小是,该选项决定组件放置在容器的什么地方。该选项支持N(北,代表上)、E(东,代表右)、S(南,代表下)、W(西,代表左)、NW(西北,代表左下)、NE(东北,代表右上)、SW(西南,代表左下)、SE(东南,代表右下)、CENTER(中,默认值)
- expand:父容器增大时是否拉伸组件
- fill:设置组件是否沿水平或垂直方向填充。该选项支持None(不填充)、x(水平填充)、y(垂直填充)、BOTH(两个方向都填充)
- ipadx:指定组件在x方向(水平)上的内部留白(padding)
- ipady:指定组件在y方向(垂直)上的内部留白(padding)
- padx:指定组件在x方向(水平)上与其它组件的间距
- pady:指定组件在y方向(垂直)上与其它组件的间距
- side:设置组件的排列顺序,top(从上到下)、bottom(从下到上)、left(从左到右)、right(从右到左)
当程序界面复杂时,就需要多个容器(Frame)分开布局,然后再将Frame添加到窗口中。
import tkinter
window = tkinter.Tk()
window.title('Pack布局')
window.geometry('500x150')
## 创建第一个容器
fm1 = tkinter.Frame()
## 指定容器从左到右排列,沿水平和垂直方向填充,父容器增大时拉伸组件
fm1.pack(side='left', fill='both', expand='YES')
## 创建三个按钮,放置在fm1容器中,从上到下排列,水平填充,父容器增大时拉伸组件
tkinter.Button(fm1, text='第一个').pack(side='top', fill='x' ,expand='YES')
tkinter.Button(fm1, text='第二个').pack(side='top', fill='x' ,expand='YES')
tkinter.Button(fm1, text='第三个').pack(side='top', fill='x' ,expand='YES')
## 创建第二个容器
fm2 = tkinter.Frame()
## 指定容器从左到右排列,水平上与其它组件的间距为10,父容器增大时拉伸组件
fm2.pack(side='left', padx=10, expand='YES')
## 创建三个按钮,放置在fm2容器中,从左到右排列,垂直填充,父容器增大时拉伸组件
tkinter.Button(fm2, text='第一个').pack(side='right', fill='y' ,expand='YES')
tkinter.Button(fm2, text='第二个').pack(side='right', fill='y' ,expand='YES')
tkinter.Button(fm2, text='第三个').pack(side='right', fill='y' ,expand='YES')
## 创建第三个容器
fm3 = tkinter.Frame()
## 指定容器从右到左排列,水平上与其它组件的间距为10,两个方向都填充,父容器增大时拉伸组件
fm3.pack(side='right',padx=10, fill='both', expand='YES')
## 创建三个按钮,放置在fm3容器中,从下到上排列,垂直填充,父容器增大时拉伸组件
tkinter.Button(fm3, text='第一个').pack(side='bottom', fill='y' ,expand='YES')
tkinter.Button(fm3, text='第二个').pack(side='bottom', fill='y' ,expand='YES')
tkinter.Button(fm3, text='第三个').pack(side='bottom', fill='y' ,expand='YES')
window.mainloop()
运行效果:
通过上面不难发现,Pack布局还是非常灵活的,它完全可以实现很复杂的用户界面。这里有一个界面分解需要说明:无论多么复杂和古怪的界面,其实都可以分解为水平排序或垂直排序,而Pack布局即可实现水平排序,也可以实现垂直排序,然后在通过多个容器进行组合,实现复杂的界面。
Grid布局管理器
Grid是把组件空间分解成一个网格进行维护,即按照行、列的方式排列组件,组件位置由其所在的行号和列号决定:行号相同而列号不同的的几个组件会被依次进行上下排列,列号相同而行号不同的几个组件会被依次进行左右排序
Grid()方法支持的选项:
- column:指定将组件放入哪列。第一列的索引为0
- columnspan:指定组件横跨多少列
- row:指定组件放入哪行,第一行的索引为0
- rowspan:指定组件横跨多少行
- sticky:类似于pack()方法的anchor选项,支持N(北,代表上)、E(东,代表右)、S(南,代表下)、W(西,代表左)、NW(西北,代表左下)、NE(东北,代表右上)、SW(西南,代表左下)、SE(东南,代表右下)、CENTER(中,默认值)
grid布局管理器示范(计算器界面)
import tkinter
window = tkinter.Tk()
window.title('计算器')
E = tkinter.Entry(window,font=('Courier New',24),width=23)
## 放置在第一行,横跨四列
E.grid(row=0,columnspan=4)
keys = (('7','8','9','/'),('4','5','6','*'),('1','2','3','-'),('0','.','+','='))
## 循环方式放置按钮
for i in range(len(keys)):
for j in range(len(keys[i])):
B = tkinter.Button(window,text=keys[i][j],font=('Verdana',20),width=6)
B.grid(row=i+1,column=j)
window.mainloop()
运行效果:
Place布局管理器
Place布局就是其他GUI编程中的“绝对布局”,这种布局方式要求程序指定每个组件的绝对位置或相对于其他组件的位置
place()方法支持的选项:
- x:指定组件的X坐标,x为0代表最左边
- y:指定组件的Y坐标,y为0代表最上边
- relx:以百分比指定组件的X坐标,父容器总宽度为1,该值在0.0~1.0之间,其中0.0代表最左边,0.5代表中间,1.0代表最右边
- rely:以百分比指定组件的Y坐标,父容器总高度为1,该值在0.0~1.0之间,其中0.0代表最上边,0.5代表中间,1.0代表最下边
- width:指定组件的宽度,以pixel为单位
- height:指定组件的高度,以pixel为单位
- relwidth:以百分比指定组件宽度,父容器总宽度为1,该值在0.0~1.0之间,其中1.0代表父容器的全部宽度,0.5代表父容器一半宽度
- relheight:以百分比指定组件高度,父容器总高度为1,该值在0.0~1.0之间,其中1.0代表父容器的全部高度,0.5代表父容器一半高度
- bordermode:是否计算组件的宽度、高度,inside为不计算,outside为计算
- anchor:当可用空间大于组件所需求的大小是,该选项决定组件放置在容器的什么地方。该选项支持N(北,代表上)、E(东,代表右)、S(南,代表下)、W(西,代表左)、NW(西北,代表左下)、NE(东北,代表右上)、SW(西南,代表左下)、SE(东南,代表右下)、CENTER(中,默认值)
简单演示一下吧:
import tkinter
window = tkinter.Tk()
window.title('Place布局')
window.geometry('500x300')
tkinter.Label(window,text='Place',font=('Arial',20)).place(x=50,y=100)
window.mainloop()
运行效果:
(番外篇)事件绑定
简单的事件处理可以通过command选项来绑定一个函数或方法,当用户单击指定按钮时,command指定的函数或方法就会被触发。但程序很多情况需要其他事件(比如鼠标悬停,鼠标双击等)
为了弥补这种不足,Python提供了更灵活的事件绑定方式,所有Widget组件都提供了一个bind()方法,可以为任意组件的事件提供了处理方式
import tkinter
## 导入webbrowser模块
import webbrowser
window = tkinter.Tk()
window.title('简单绑定')
window.geometry('500x300')
## 定义点击按钮触发的函数,event为事件的响应
def one(event):
webbrowser.open('https://www.baidu.com')
def two(event):
webbrowser.open('https://www.taobao.com')
B = tkinter.Button(window,text='单击百度,双击淘宝')
B.pack()
## 按钮单击和one函数绑定(1为鼠标左键,2为鼠标中间(滚轮点击),3为鼠标右键)
B.bind('<Button-1>',one)
## 按钮双击和two函数绑定(1为鼠标左键,2为鼠标中间(滚轮点击),3为鼠标右键)
B.bind('<Double-1>',two)
window.mainloop()
运行效果:
Tkinter支持的各种鼠标,键盘事件介绍
:鼠标单击事件。1为鼠标左键,2为鼠标中间(滚轮点击),3为鼠标右键,4为滚轮上滚(LInux),5为滚轮下滚(Linux) :鼠标按住移动事件。1为鼠标左键按住移动,2为鼠标中键按住移动,3为鼠标右键按住移动 :鼠标按键被释放事件。1为鼠标左键被释放,2为鼠标中键被释放,3为鼠标右键被释放 :鼠标双击事件。1为鼠标左键,2为鼠标中间(滚轮点击),3为鼠标右键,4为滚轮上滚(LInux),5为滚轮下滚(Linux) :鼠标进入组件事件。鼠标悬停组件上方,什么按键也不点情况下 :鼠标移出组件事件 :组件及其子组件获得焦点 :组件及其子组件失去焦点 :按下回车键事件。实际上所有键都绑定了事件, - a
import tkinter
window = tkinter.Tk()
window.title('鼠标事件')
window.geometry('500x130')
## 定义触发函数
def motion(event):
鼠标位置发送到La标签中
La['text'] = '鼠标移动到:(%s %s)' % (event.x,event.y)
def press_motion(event):
La['text'] = '按住鼠标的移动位置:(%s %s)' % (event.x, event.y)
L = tkinter.Label(window,bg='lightgreen',font=('Times',20),width=40,height=3)
L.bind('<Motion>',motion)
L.bind('<B1-Motion>',press_motion)
L.pack()
La = tkinter.Label(window,bg='white',font=('Courier New',20),width=38,height=1)
La.pack()
window.mainloop()
运行效果:
最后升级前面的计算器,使计算器可以真正计算
import tkinter
window = tkinter.Tk()
window.title('计算器')
Text = tkinter.StringVar()
## 定义变量,存放运算符前面的数
Num =None
## 定义按钮触发函数
def click(event):
## 引用外面定义的函数
global Num
## 判断按钮值是否是数字
if event.widget['text'] in ('1','2','3','4','5','6','7','8','9','0','.'):
## Text同步按钮值
Text.set(Text.get() + event.widget['text'])
## 判断按钮值是否为运算符
elif event.widget['text'] in ('/','*','-','+'):
## 判断Num变量是否为空
if Num is None:
## 把Text最新的值和运算符放到Num变量中
Num = Text.get() + event.widget['text']
## 清空Text
Text.set('')
## 否则就先把Num加Text组合的值计算出来
else:
Num = str(eval(Num + Text.get()))
Text.set('')
## 判断按钮值是否为等号
elif event.widget['text'] == '=':
## 这里加了异常处理,否则不该点等号时候点了等号,还报错,看着烦
try:
## Num加Text组合的值计算出来交给Text
Text.set(str(eval(Num + Text.get())))
except:
pass
## 定义双击等号触发函数(这里没有归零,也不想在加个按钮了,就把双击等于定义为归零了)
def Clean(event):
global Num
Text.set('')
Num = None
## 创建文本框,内容为Text值
E = tkinter.Entry(window,textvariable=Text,font=('Courier New',24),width=23)
E.grid(row=0,columnspan=4)
keys = (('7','8','9','/'),('4','5','6','*'),('1','2','3','-'),('0','.','+','='))
for i in range(len(keys)):
for j in range(len(keys[i])):
B = tkinter.Button(window,text=keys[i][j],font=('Verdana',20),width=6)
## 按钮单击绑定click函数
B.bind('<Button-1>',click)
B.grid(row=i+1,column=j)
## 判断按钮值是否为等号
if B['text'] == '=':
## 按钮双击绑定Clean函数
B.bind('<Double-1>',Clean)
window.mainloop()