本章中,涉及大量的 API 讲解。学习 API 最好的来源就是官方提供的文档:tkinter 官方网址:
https://docs.python.org/3.7/library/tk.html或者:http://effbot.org/tkinterbook/ (相对规整,适合初学者查找)。
基于 tkinter 模块创建 GUI 程序包含如下 4 个核心步骤:
(1) 通过类 Tk 的无参构造函数
from tkinter import *
root = Tk()
btn01 = Button(root)
btn01["text"] = "点我就送花"
btn01.pack()
(1) 通过绑定事件处理程序,响应用户操作所触发的事件(比如:单击、双击等)
def songhua(e):
messagebox.showinfo("Message","送你一朵玫瑰花,请你爱上我")
print("送你 99 朵玫瑰花")
btn01.bind("",songhua)
【示例】使用 tkinter 模块,创建 GUI 应用程序,并实现点击按钮的事件处理
from tkinter import *
from tkinter import messagebox
root = Tk()
btn01 = Button(root)
btn01["text"] = "点我就送花"
btn01.pack()
def songhua(e):
messagebox.showinfo("Message","送你一朵玫瑰花,请你爱上我")
print("送你 99 朵玫瑰花")
btn01.bind("",songhua)
root.mainloop() #调用组件的 mainloop 方法,进入事件循环
主窗口位置和大小
通过 geometry(‘wxh±x±y’)进行设置。w 为宽度,h 为高度。+x 表示距屏幕左边的距离;-x 表示距屏幕右边的距离;+y 表示距屏幕上边的距离;-y 表示距屏幕下边的距离。
【示例】测试 tkinter 主窗口位置和大小的设置
from tkinter import *
root = Tk()
root.title("测试主窗口的位置和大小")
root.geometry("500x400+100+200") #宽度 500,高度 400;距屏幕左边 100,距屏幕上边
200
root.mainloop()
执行结果:
图形用户界面是由一个个组件组成,就像小孩“搭积木”一样最终组成了整个界面。有的组件还能在里面再放置其他组件,我们称为“容器”。Tkinter 的 GUI 组件关系图如下:
[外链图片转存失败(img-EcrnVCCL-1563689228532)(C:\Users\Administrator\Documents\gui组件关系图.jpg)]
Misc 和 Wm:
Tkinter 的 GUI 组件有两个根父类,它们都直接继承了 object 类:
·Misc:它是所有组件的根父类。
·Wm:它主要提供了一些与窗口管理器通信的功能函数。
Tk
Misc 和 Wm 派生出子类 Tk,它代表应用程序的主窗口。一般应用程序都需要直接或间接使用 Tk。
Pack、Place、Grid
Pack、Place、Grid 是布局管理器。布局管理器管理组件的:大小、位置。通过布局管理器可以将容器中的组件实现合理的排布。
BaseWidget
BaseWidget 是所有组件的父类
Widget
Widget 是所有组件类的父类。Widget 一共有四个父类:BaseWidget、Pack、Grid、Place。意味着,所有 GUI 组件同时具备这四个父类的属性和方法。
【注】想观察类的层次结构可以在类定义处的类名上单击右键,选择 Diagram–>showDiagram。
Tkinter 类 | 名称 | 简介 |
---|---|---|
Toplevel | 顶层 | 容器类,可用于为其他组件提供单独的容器;Toplevel 有点类似于窗口 |
Button | 按钮 | 代表按键组件 |
Canvas | 画布 | 提供绘画功能,包括直线、矩形、椭圆、多边形、位图等 |
Checkbutton | 复选框 | 可供用户勾选的复选框 |
Entry | 单行输入框 | 用户可输入内容 |
Frame | 容器 | 用于装载其他GUI组件 |
Label | 标签 | 用于显示不可编辑的文本或图标 |
LabelFrame | 容器 | 也是容器组件,类似于Frame,但它支持添加标题 |
Listbox | 列表框 | 列出多个选项,供用户选择 |
本节程序也是 GUI 应用程序编写的一个主要结构,采用了面向对象的方式,更加合理的组织代码。
通过类 Application 组织整个 GUI 程序,类 Application 继承了 Frame 及通过继承拥有了父类的特性。通过构造函数__init__()初始化窗口中的对象,通过 createWidgets()方法创建窗口中的对象。Frame 框架是一个 tkinter 组件,表示一个矩形的区域。Frame 一般作为容器使用,可以放置其他组件,从而实现复杂的布局。
【栗子】 标准的 GUI 程序类的写法
"""测试一个经典的 GUI 程序的写法,使用面向对象的方式"""
from tkinter import *
from tkinter import messagebox
class Application(Frame):
"""一个经典的 GUI 程序的类的写法"""
def __init__(self, master=None):
super().__init__(master) # super()代表的是父类的定义,而不是父类对象
self.master = master
self.pack()
self.createWidget()
def createWidget(self):
"""创建组件"""
self.btn01 = Button(self)
self.btn01["text"] = "点击送花"
self.btn01.pack()
self.btn01["command"] = self.songhua
# 创建一个退出按钮
self.btnQuit = Button(self, text="退出", command=root.destroy)
self.btnQuit.pack()
def songhua(self):
messagebox.showinfo("送花","送你 99 朵玫瑰花")
if __name__ == '__main__':
root = Tk()
root.geometry("400x100+200+300")
root.title("一个经典的 GUI 程序类的测试")
app = Application(master=root)
root.mainloop()
Label(标签)主要用于显示文本信息,也可以显示图像。
Label(标签)有这样一些常见属性:
【栗子】
from tkinter import *
from tkinter import messagebox
class Application(Frame):
def __init__(self, master=None):
super().__init__(master)
self.master = master
self.pack()
self.create_widget()
def create_widget(self):
self.label01 = Label(self,text = "百战程序员", width=8, height=2,
fg="blue", bg="white")
#对上面的字典参数,重新赋值改变
#self.label01["text"] = "ccc"
#self.label01.config(fg="red", bg="green")
self.label01.pack()
self.label02 = Label(self, text="Mr_huang,你好", width=15, height=2,
fg="red",bg="black",font=("楷体",18))
self.label02.pack()
# 显示图片
global photo #声明为全局变量,如果是局部变量,本方法执行完毕后自动销毁,窗口显示不出来
photo = PhotoImage(file="imgs/logo.gif")
self.label03 = Label(self, image=photo)
self.label03.pack()
#多行显示
self.label04 = Label(self,text = "北京尚学堂\nPython培训\n黄璞",
borderwidth=3,relief="ridge", justify="left")
self.label04.pack()
if __name__ == '__main__':
root = Tk()
root.geometry("400x300+200+100")
root.title("Gui/Lable组件测试")
app = Application(master=root)
root.mainloop()
通过学习 Label 组件,我们发现可以通过 Options 设置组件的属性,从而控制组件的各种状态。比如:宽度、高度、颜色、位置等等。
我们可以通过三种方式设置 Options 选项,这在各种 GUI 组件中用法都一致。
fred = Button(self, fg="red", bg="blue")
fred["fg"] = "red"
fred["bg"] = "blue"
创建对象后,使用 config()方法
fred.config(fg="red", bg="blue")
常用的组件汇总如下:
选项名 (别名) 含义
activebackground 指定组件处于激活状态时的背景色
activeforeground 指定组件处于激活状态时的前景色
anchor
指定组件内的信息(比如文本或图片)在组件中如何显示(当所在组件
比信息大时,可以看出效果)。必须为下面的值之一:N、NE、E、SE、
S、SW、W、NW 或 CENTER。比如 NW(NorthWest)指定将信息显示在组件的左上角
background(bg) 指定组件正常显示时的背景色
bitmap
指定在组件上显示该选项指定的位图,该选项值可以是 Tk_GetBitmap
接收的任何形式的位图。位图的显示方式受 anchor、justify 选项的
影响。如果同时指定了 bitmap 和 text,那么 bitmap 覆盖文本;如果
同时指定了 bitmap 和 image,那么 image 覆盖 bitmap
borderwidth 指定组件正常显示时的 3D 边框的宽度,该值可以是 Tk_GetPixels 接
收的任何格式
cursor
指定光标在组件上的样式。该值可以是 Tk_GetCursors 接受的任何格
式
command 指定按组件关联的命令方法,该方法通常在鼠标离开组件时被触发调
用
disabledforeground 指定组件处于禁用状态时的前景色
font 指定组件上显示的文本字体
foreground(fg) 指定组件正常显示时的前景色
highlightbackgroun
d 指定组件在高亮状态下的背景色
highlightcolor 指定组件在高亮状态下的前景色
highlightthickness
指定组件在高亮状态下的周围方形区域的宽度,该值可以是
Tk_GetPixels 接收的任何格式
height
指定组件的高度,以 font 选项指定的字体的字符高度为单位,至少为
1
image
指定组件中显示的图像,如果设置了 image 选项,它将会覆盖 text、
bitmap 选项
justify
指定组件内部内容的对齐方式,该选项支持 LEFT(左对齐)、CENTER
(居中对齐)或 RIGHT(右对齐)这三个值
padx
指定组件内部在水平方向上两边的空白,该值可以是 Tk_GctPixels 接
收的任何格式
pady
指定组件内部在垂直方向上两地的空白,该值可以是 Tk_GctPixels 接
收的任何格式
relief
指定组件的 3D 效果,该选项支持的值包括 RAISED、SUNKEN、FLAT、
RIDGE、SOLID、GROOVE。该值指出组件内部相对于外部的外观样式,
比如 RAISED 表示组件内部相对于外部凸起
selectbackground 指定组件在选中状态下的背景色
selectborderwidth 指定组件在选中状态下的 3D 边框的宽度,该值可以是 Tk_GetPixels
接收的任何格式
selectforeground 指定组在选中状态下的前景色
state
指定组件的当前状态。该选项支持 NORMAL(正常)、DISABLE(禁用)
这两个值
takefocus
指定组件在键盘遍历(Tab 或 Shift+Tab)时是否接收焦点,将该选
项设为 1 表示接收焦点;设为 0 表示不接收焦点
text 指定组件上显示的文本,文本显示格式由组件本身、anchor 及justify 选项决定
textvariable
指定一个变量名,GUI 组件负责显示该变量值转换得到的字符串,文
本显示格式由组件本身、anchor 及 justify 选项决定
underline
指定为组件文本的第几个字符添加下画线,该选项就相当于为组件绑
定了快捷键
width 指定组件的宽度,以 font 选项指定的字体的字符高度为单位,至少为
1
wraplength 对于能支持字符换行的组件,该选项指定每行显示的最大字符数,超
过该数量的字符将会转到下行显示
xscrollcommand
通常用于将组件的水平滚动改变(包括内容滚动或宽度发生改变)与
水平滚动条的 set 方法关联,从而让组件的水平滚动改变传递到水平
滚动条
yscrollcommand
通常用于将组件的垂直滚动改变(包括内容滚动或高度发生改变)与
垂直滚动条的 set 方法关联,从而让组件的垂直滚动改变传递到垂直
滚动条
Button(按钮)用来执行用户的单击操作。Button 可以包含文本,也可以包含图像。按钮被单击后会自动调用对应事件绑定的方法。
【栗子】:
from tkinter import *
from tkinter import messagebox
class Application(Frame):
def __init__(self, master=None):
super().__init__(master)
self.master = master
self.pack()
self.create_widget()
def create_widget(self):
#创建组件
self.btn01 = Button(root,text="登录", width=10, height=2,
command=self.login)
self.btn01.pack()
#显示图片
global photo #声明全局变量
photo = PhotoImage(file="imgs/start.gif")
self.btn02 = Button(root, image=photo, width=200, height=150, command=self.login)
self.btn02.pack()
# self.btn02.config(state="disabled") # 设置按钮为禁用
def login(self):
messagebox.showinfo("登录", "登录成功!")
if __name__ == '__main__':
root = Tk()
root.geometry("400x300+200+100")
root.title("Gui/Lable组件测试")
app = Application(master=root)
root.mainloop()
运行结果如下:
Entry 用来接收一行字符串的控件。如果用户输入的文字长度长于 Entry 控件的宽度时, 文字会自动向后滚动。如果想输入多行文本, 需要使用 Text 控件。
from tkinter import *
from tkinter import messagebox
class Application(Frame):
def __init__(self, master=None):
super().__init__(master)
self.master = master
self.pack()
self.create_widget()
def create_widget(self):
"""建登录界面的组件 """
self.label01 = Label(self,text="用户名")
self.label01.pack()
# StringVar 变量绑定到指定的组件。
# StringVar 变量的值发生变化,组件内容也变化;
# 组件内容发生变化,StringVar 变量的值也发生变化。
v1 = StringVar()
self.entry01 = Entry(self, textvariable=v1)
self.entry01.pack()
v1.set("admin")
print(v1.get());print(self.entry01.get())
#创建密码框
self.label02 = Label(self, text="密码")
self.label02.pack()
v2 = StringVar()
self.entry02 = Entry(self, textvariable=v2)
self.entry02.pack()
# 登录按钮
Button(self, text="登录", command=self.login).pack()
def login(self):
username = self.entry01.get()
pwd = self.entry02.get()
print("去数据库比对用户密码!")
print("用户名:"+username)
print("密码:"+pwd)
if username=="huangpu"and pwd=="123456":
messagebox.showinfo("测试登录系统", "登录成功,你好棒!")
else:
messagebox.showinfo("测试登录系统", "登录失败,请核对用户名和密码!")
if __name__ == '__main__':
root = Tk()
root.geometry("400x300+200+100")
root.title("Gui/Lable组件测试")
app = Application(master=root)
root.mainloop()
Text(多行文本框)的主要用于显示多行文本,还可以显示网页链接, 图片, HTML 页面, 甚至 CSS 样式表,添加组件等。因此,也常被当做简单的文本处理器、文本编辑器或者网
页浏览器来使用。比如 IDLE 就是 Text 组件构成的。
【示例】Text 多行文本框基本用法(文本输入、组件和图像显示
from tkinter import *
from tkinter import messagebox
import webbrowser
class Application(Frame):
def __init__(self, master=None):
super().__init__(master)
self.master = master
self.pack()
self.create_widget()
def create_widget(self):
self.w1 = Text(root,width=150,height=50,bg="gray")
self.w1.pack()
self.w1.insert(1.0,"0123456789\nabcdefg")
self.w1.insert(2.7,"欲穷千里目,更上一层楼。")
Button(self,text="重复插入文本",command=self.insertText).pack(side="left")
Button(self,text="返回文本",command=self.returnText).pack(side="left")
Button(self,text="添加图片",command=self.addImage).pack(side="left")
Button(self,text="添加组件",command=self.addWidget).pack(side="left")
Button(self,text="通过 tag 精确控制文本",command=self.textTag).pack(side="left")
def insertText(self):
# INSERT 索引表示在光标处插入
self.w1.insert(INSERT, 'huangpu')
#END 索引号表示在最后插入
self.w1.insert(END, '[python]')
self.w1.insert(1.8, 'Mr_huang')
def returnText(self):
# Indexes(索引)是用来指向Text组件中文本的位置,Text的组件索引也是对应实际字符之间的位置。
# 核心:行号以1开始 列号以0开始
print(self.w1.get(1.2, 1.6))
print("所有文本内容:\n"+self.w1.get(1.0, END))
def addImage(self):
# global photo
self.photo = PhotoImage(file="imgs/logo.gif")
self.w1.image_create(END,image=self.photo)
def addWidget(self):
b1 = Button(self.w1,text="爱python")
#在text创建组件的命令
self.w1.window_create(INSERT, window=b1)
def textTag(self):
self.w1.delete(1.0, END)
self.w1.insert(INSERT, "good good study,day day up!\n 人生苦短\n 快用python\n百度,嗖一下就知道")
self.w1.tag_add("good", 1.0, 1.9)
self.w1.tag_config("good", background="yellow", foreground="red")
self.w1.tag_add("baidu", 4.0, 4.2)
self.w1.tag_config("baidu", underline=True)
self.w1.tag_bind("baidu", "" , self.webshow)
def webshow(self,event):
webbrowser.open("http://www.baidu.com")
if __name__ == '__main__':
root = Tk()
root.geometry("860x640+300+100")
root.title("Gui/Lable组件测试")
app = Application(master=root)
root.mainloop()
Radiobutton 控件用于选择同一组单选按钮中的一个。Radiobutton 可以显示文本,也可以
显示图像。
from tkinter import *
from tkinter import messagebox
class Application(Frame):
def __init__(self, master=None):
super().__init__(master) # super()代表的是父类的定义,而不是父类对象
self.master = master
self.pack()
self.createWidget()
def createWidget(self):
self.v = StringVar();
self.v.set("F")
self.r1 = Radiobutton(self, text="男性", value="M", variable=self.v)
self.r2 = Radiobutton(self, text="女性", value="F", variable=self.v)
self.r1.pack(side="left");self.r2.pack(side="left")
Button(self, text="确定", command=self.confirm).pack(side="left")
def confirm(self):
messagebox.showinfo("测试","选择的性别:"+self.v.get())
if __name__ == '__main__':
root = Tk()
root.geometry("400x50+200+300")
app = Application(master=root)
root.mainloop()
Checkbutton 控件用于选择多个按钮的情况。Checkbutton 可以显示文本,也可以显示图像。
from tkinter import *
from tkinter import messagebox
class Application(Frame):
def __init__(self, master=None):
super().__init__(master) # super()代表的是父类的定义,而不是父类对象
self.master = master
self.pack()
self.createWidget()
def createWidget(self):
self.codeHobby = IntVar()
self.videoHobby = IntVar()
print(self.codeHobby.get()) # 默认值为0
# offvalue 是没选中为0, onvalue为选中为1
self.c1 = Checkbutton(self, text="敲代码",variable=self.codeHobby, onvalue=1, offvalue=0)
self.c2 = Checkbutton(self, text="看视频",variable=self.videoHobby, onvalue=1, offvalue=0)
self.c1.pack(side="left");self.c2.pack(side="left")
Button(self, text="确定", command=self.confirm).pack(side="left")
def confirm(self):
if self.videoHobby.get() == 1:
messagebox.showinfo("测试","看视频,都是正常人有的爱好!你喜欢看什么类型?")
if self.codeHobby.get() == 1:
messagebox.showinfo("测试","抓获野生程序猿一只,赶紧送给他尚学堂的视频充饥")
if __name__ == '__main__':
root = Tk()
root.geometry("400x50+200+300")
app = Application(master=root)
root.mainloop()
canvas(画布)是一个矩形区域,可以放置图形、图像、组件等。
from tkinter import *
from tkinter import messagebox
import random
class Application(Frame):
def __init__(self,master=None):
super().__init__(master)
self.pack()
self.master = master
self.createWidget()
def createWidget(self):
self.canvas = Canvas(self,width=300, height=200, bg="green")
self.canvas.pack()
# 画一条直线
line = self.canvas.create_line(10,10,30,20,40,50)
# 画一个矩形:
rect = self.canvas.create_rectangle(50,50,100,100)
ocal = self.canvas.create_oval(50,50,100,100)
global photo
photo = PhotoImage(file="imgs/logo.gif")
self.canvas.create_image(150,170,image=photo)
Button(self,text="画十个矩形",command=self.draw50Recg).pack()
def draw50Recg(self):
for i in range(10):
x1 = random.randrange(int(self.canvas["width"])/2)
y1 = random.randrange(int(self.canvas["height"])/2)
x2 = random.randrange(int(self.canvas["width"])/2)
y2 = random.randrange(int(self.canvas["height"])/2)
self.canvas.create_rectangle(x1,y1,x2,y2)
if __name__ == '__main__':
root = Tk()
root.geometry("840x620+200+300")
app = Application(master=root)
root.mainloop()
一个 GUI 应用程序必然有大量的组件,这些组件如何排布?这时候,就需要使用 tkinter提供的布局管理器帮助我们组织、管理在父组件中子组件的布局方式。tkinter 提供了三种管理器:pack、grid、place。
grid 表格布局,采用表格结构组织组件。子组件的位置由行和列的单元格来确定,并且可以跨行和跨列,从而实现复杂的布局。
grid()方法提供的选项
选项 说明 取值范围
column 单元格的列号 从 0 开始的正整数
columnspan 跨列,跨越的列数 正整数
row 单元格的行号 从 0 开始的正整数
rowspan 跨行,跨越的行数 正整数
ipadx, ipady 设置子组件之间的间隔,x 方向或者 y 方向,
默认单位为像素
非负浮点数,默认 0.0
padx, pady 与之并列的组件之间的间隔,x 方向或者 y 方
向,默认单位是像素
非负浮点数,默认 0.0
sticky 组件紧贴所在单元格的某一角,对应于东南西
北中以及 4 个角
“n”, “s”, “w”, “e”, “nw”, “sw”, “se”, “ne”, “center”(默认)
【栗子】
from tkinter import *
from tkinter import messagebox
import random
class Application(Frame):
def __init__(self,master=None):
super().__init__(master)
self.pack()
self.master = master
self.createWidget()
def createWidget(self):
self.label01 = Label(self,text="用户名")
self.label01.grid(row=0, column=0)
self.entry01 = Entry(self)
self.entry01.grid(row=0,column=1)
Label(self, text="用户手机号").grid(row=0,column=2)
Label(self,text="密码").grid(row=1,column=0)
Entry(self,show="*").grid(row=1,column=1)
Button(self,text="登录").grid(row=2,column=1,sticky=EW)
Button(self,text="取消").grid(row=2,column=2,sticky=E)
if __name__ == '__main__':
root = Tk()
root.geometry("840x620+200+300")
app = Application(master=root)
root.mainloop()
pack 按照组件的创建顺序将子组件添加到父组件中,按照垂直或者水平的方向自然排布。如果不指定任何选项,默认在父组件中自顶向下垂直添加组件。pack 是代码量最少,最简单的一种,可以用于快速生成界面。
pack()方法提供的选项
名称 描述 取值范围
expand
当值为“yes”时,side 选项无效。组件显示在父配
件中心位置;若 fill 选项为”both”,则填充父组件
的剩余空间
“yes”, 自然数,”no”, 0
(默认值”no”或 0)
fill 填充 x(y)方向上的空间,当属性 side=”top”或”
bottom”时,填充 x 方向;当属性 side=”left”或”
right”时,填充”y”方向;当 expand 选项为”yes”
时,填充父组件的剩余空间
“x”, “y”, “both”,
“none”
(默认值为 none)
ipadx
,
ipady
设置子组件之间的间隔,x 方向或者 y 方向,默认单
位为像素
非负浮点数,默认 0.0
padx,
pady
与之并列的组件之间的间隔,x 方向或者 y 方向,默
认单位是像素
非负浮点数,默认 0.0
side 定义停靠在父组件的哪一边上 “ top ” , “ bottom ” , “left”, “right”
(默认为”top”)
befor
e
将本组件于所选组建对象之前 pack,类似于先创建本
组件再创建选定组件
已经 pack 后的组件对象
after 将本组件于所选组建对象之后 pack,类似于先创建选
定组件再本组件
已经 pack 后的组件对象
in_ 将本组件作为所选组建对象的子组件,类似于指定本
组件的 master 为选定组件
已经 pack 后的组件对象
anchor
对齐方式,左对齐”w”,右对齐”e”,顶对齐”n”,
底对齐”s”
“n”, “s”, “w”, “e”, “nw”, “sw”, “se”, “ne”, “center”(默认)
【建议】如上列出了 pack 布局所有的属性,但是不需要挨个熟悉,了解基本pack 适用于简单的垂直或水平排布,如果需要复杂的布局可以使用 grid 或 place。
【栗子】制作钢琴按键布局
from tkinter import *
root = Tk(); root.geometry("800x300")
# Frame是一个矩形区域,就是用来防止其他组件
f1 = Frame(root)
f1.pack()
f2 = Frame(root); f2.pack()
btntext = ("流行风","中国风","古典风","轻音乐","重金属")
for txt in btntext:
Button(f1, text=txt).pack(side="left", padx=10)
for i in range(1,15):
Button(f2, width=5, height=10, bg="black" if i % 2 == 0 else "white").pack(side="left")
root.mainloop()
place 布局管理器可以通过坐标精确控制组件的位置,适用于一些布局更加灵活的场景。
place()方法的选项
选项 说明 取值范围
x,y 组件左上角的绝对
坐标(相对于窗口)
非负整数
x 和 y 选项用于设置偏移(像素),如果同时设置
relx(rely) 和 x(y),那么 place 将优先计算 relx 和
rely,然后再实现 x 和 y 指定的偏移值
relx
rely
组件左上角的坐标
(相对于父容器)
relx 是相对父组件的位置。0 是最左边,0.5 是正中间,1
是最右边;
rely 是相对父组件的位置。0 是最上边,0.5 是正中间,1
是最下边;
width,
height
组件的宽度和高度 非负整数
relwid
th,
relhei
组件的宽度和高度
(相对于父容器)
与 relx、rely 取值类似,但是相对于父组件的尺寸
ght
anchor 对齐方式,左对齐”
w”,右对齐”e”,
顶对齐”n”,底对
齐”s”
“n”, “s”, “w”, “e”, “nw”, “sw”, “se”, “ne”, “center”(默认)
【栗子】用palce 布局管理-- 基本用法
#coding=utf-8
from tkinter import *
root = Tk(); root.geometry("500x300")
root.title("place 布局管理器"); root["bg"]= "white"
f1 = Frame(root,width=200,height= 200, bg="green")
f1.place(x=30, y= 30 )
Button(root,text="python").place(relx=0.5,rely=0, x=100, y=200, relwidth= 0.2, relheight=0.2)
Button(f1,text='程序猿').place(relx=0.6, rely=0.7)
Button(f1,text="python程序猿").place(relx=0.2,rely=0.2)
root.mainloop()
【栗子】place 布局管理 - 扑克游戏 demo
# coding = utf-8
from tkinter import *
class Application(Frame):
def __init__(self,master=None):
super().__init__(master)
self.pack()
self.createWidget()
def createWidget(self):
""" 通过布局管理器实现puke 位置控制 """
# self.photo = PhotoImage(file="imgs/puke/puke1.gif")
# self.puke1 = Label(self.master,image=self.photo)
# self.puke1.pack()
self.photos = [PhotoImage(file = "imgs/puke/puke"+str(i+1)+".gif") for i in range(10)]
self.pukes = [Label(self.master,image=self.photos[i]) for i in range(10)]
for i in range(10):
self.pukes[i].place(x=55+i*40,y=50)
# 为所有的Label 增加时间处理
self.pukes[0].bind_class("Label","" ,self.chupai)
def chupai(self,event):
print(event.widget.winfo_geometry())
print(event.widget.winfo_y())
if event.widget.winfo_y() == 50:
event.widget.place(y=30)
else:
event.widget.place(y=50)
if __name__ == '__main__':
root=Tk()
root.geometry("600x270+200+300")
app = Application(master=root)
root.mainloop()
一个 GUI 应用整个生命周期都处在一个消息循环 (event loop) 中。它等待事件的发生,并作出相应的处理。Tkinter 提供了用以处理相关事件的机制. 处理函数可被绑定给各个控件的各种事件。widget.bind(event, handler)
如果相关事件发生, handler 函数会被触发, 事件对象 event 会传递给 handler 函数。
代码 说明
<1>
鼠标左键按下。 2 表示右键,3 表示中键;
鼠标左键释放
按住鼠标左键移动
双击左键
鼠标指针进入某一组件区域
鼠标指针离开某一组件区域
滚动滚轮;
按下 a 键,a 可用其他键替代
释放 a 键。
按下 A 键(大写的 A)
同时按下 alt 和 a;alt 可用 ctrl 和 shift 替代
快速按两下 a
CTRL 和 V 键被同时按下,V 可以换成其它键位
event 对象常用属性
名称 说明
char 按键字符,仅对键盘事件有效
keycode 按键编码,仅对键盘事件有效
keysym 按键名称,仅对键盘事件有效
比如按下空格键:
键的 char: 键的 keycode:32 键的 keysym:space
比如按下 a 键:
键的 char:a 键的 keycode:65 键的 keysym:a
num 鼠标按键,仅对鼠标事件有效
type 所触发的事件类型
widget 引起事件的组件
width,height 组件改变后的大小,仅 Configure 有效
x,y 鼠标当前位置,相对于父容器
x_root,y_root 鼠标当前位置,相对于整个屏幕
【栗子】键盘和鼠标事件的测试
from tkinter import *
root = Tk(); root.geometry("530x300")
c1 = Canvas(root, width=200, height=200, bg="green")
c1.pack()
def mouseTest(event):
print("鼠标左键单击位置(相对于父容器):{0},{1}".format(event.x,event.y))
print("鼠标左键单击位置(相对于屏幕):{0},{1}".format(event.x_root,event.y_root))
print("事件绑定的组件:{0}".format(event.widget))
def testDrg(event):
c1.create_oval(event.x, event.y, event.x+1, event.y+1)
def keyboardTest(event):
print("键的keycode:{0},键的char:{1},键的keysym:{2}".format(event.keycode, event.char, event.keysym))
def press_a_test(event):
print("press a")
def release_a_test(event):
print("release a")
c1.bind("" , mouseTest)
c1.bind("" , testDrg) # 鼠标左键拖动事件
root.bind("" ,keyboardTest)
root.bind("" , press_a_test)
root.bind("" ,release_a_test)
root.mainloop()
lambda 表达式定义的是一个匿名函数,只适合简单输入参数,简单计算返回结果,不适合功能复杂情况。
lambda 定义的匿名函数也有输入、也有输出,只是没有名字。语法格式如下:
lambda 参数值列表:表达式
参数值列表即为输入,表达式计算的结构即为输出。
我们写一个最简单的案例:
add3args = lambda x,y,z:x+y+z
#print(add3args(10,20,30))
上面的 lambda 表达式相当于如下函数定义:
def add3args(x,y,z):
return x+y+z
lambda 表达式的参数值列表可以为如下内容:
lambda 格式 说明
lambda x, y: xy 函数输入是 x 和 y,输出是它们的积 xy
lambda:None 函数没有输入参数,输出是 None
lambda:aaa(3,4) 函数没有输入参数,输出是 aaa(3,4)的结果
lambda *args: sum(args) 输入是任意个数的参数,输出是它们的和
lambda **kwargs: 1 输入是任意键值对参数,输出是 1
我们在平时使用时,注意 lambda 只是一个匿名函数(没有名字的函数),功能不强,不要过度使用;
使用 lambda 表达式实现传参
【示例】使用 lambda 帮助 command 属性绑定时传参
# coding=utf-8
# 测试 command 属性绑定事件,测试 lambda 表达式帮助传参
from tkinter import *
root = Tk();root.geometry("270x50")
def mouseTest1():
print("command 方式,简单情况:不涉及获取 event 对象,可以使用")
def mouseTest2(a,b):
print("a={0},b={1}".format(a,b))
Button(root, text="测试 command1",command=mouseTest1).pack(side="left")
Button(root, text="测试 command2",command=lambda:mouseTest2("gaoqi","xixi")).pack(side="left")
root.mainloop()
Button(root,text=”登录”,command=login)
通过 bind()方法绑定(适合需要获取 event 对象)
c1 = Canvas(); c1.bind(“”,drawLine) ·
组件类的绑定调用对象的 bind_class 函数,将该组件类所有的组件绑定事件:
w.bind_class(“Widget”,”event”,eventhanler)
比如:btn01.bind_class(“Button”,””,func)
【栗子】组件对象的绑定
#coding=utf-8
# 多种事件绑定汇总
from tkinter import *
root = Tk();root.geometry("320x40")
def mouseTest1(event):
print("bind()方式绑定可以,可以获取event对象")
print(event.widget)
def mouseTest2(a,b):
print("a={0},b={1}".format(a,b))
print("command 方式绑定,不能直接获取event对象")
def mouseTest3(event):
print("右击事件,绑定所有按钮!")
print(event.widget)
b1 = Button(root,text= "测试bind()绑定")
b1.pack(side= "left")
b1.bind("" ,mouseTest1)
b2 = Button(root, text="测试command2", command=lambda: mouseTest2("zhangsan","lisi"))
b2.pack(side="left")
b1.bind_class("Button", "" , mouseTest3)
root.mainloop()
我们在前面介绍了最常用的几个组件,接下来我们介绍其他一些组件。
OptionMenu(选择项)用来做多选一,选中的项在顶部显示。
from tkinter import *
root = Tk(); root.geometry("200x100")
v = StringVar(root)
v.set("程序猿")
om=OptionMenu(root,v,"程序猿","python程序猿","java程序猿","go程序猿")
om["width"] = 10
om.pack()
def test1():
print("最喜爱的计算机语言:",v.get())
Button(root,text="确定",command=test1).pack()
root.mainloop()
Scale(移动滑块)用于在指定的数值区间,通过滑块的移动来选择值。
from tkinter import *
root = Tk(); root.geometry("400x150")
def test1(value):
print("滑块的值:",value)
newFont= ("宋体",value)
a.config(font=newFont)
s1 = Scale(root,from_=10, to=50,length=200, tickinterval=5, orient=HORIZONTAL, command=test1)
s1.pack()
a = Label(root,text="python程序猿",width=20, height=1, bg="black", fg="white")
a.pack()
root.mainloop()
颜色选择框可以帮助我们设置背景色、前景色、画笔颜色、字体颜色等等。
from tkinter import *
from tkinter.colorchooser import *
root = Tk();root.geometry("400x200")
def test1():
s1 = askcolor(color="red",title="选择背景")
print(s1)
print(s1[0])
root.config(bg=s1[1])
Button(root,text = "选择背景色",command=test1).pack()
root.mainloop()
文件对话框帮助我们实现可视化的操作目录、操作文件。最后,将文件、目录的信息传入到
程序中。文件对话框包含如下一些常用函数。
函数名 对话框 说明
askopenfilename(**options) 文 件 对 话框
返回打开的文件名
askopenfilenames(**options) 返回打开的多个文件名列表
askopenfile(**options) 返回打开文件对象
askopenfiles(**options) 返回打开的文件对象的列表
askdirectory(**options) 目 录 对 话
框
返回目录名
asksaveasfile(**options) 保 存 对 话
框
返回保存的文件对象
asksaveasfilename(**options) 返回保存的文件名
命名参数 options 的常见值如下:
参数名 说明
defaultextension 默认后缀:.xxx
用户没有输入则自动添加
filetypes=[(label1,pattern1),(labe2,patt
ern2)]
文件显示过滤器
initialdir 初始目录
initialfile 初始文件
parent 父窗口,默认根窗口
title 窗口标题
【栗子】文件对话框获取文件
from tkinter import *
from tkinter.filedialog import *
root = Tk(); root.geometry("400x200")
def test1():
f = askopenfilename(title ="上传文件", initialdir="e:/",filetypes=[("视频文件",".mp4")])
print(f)
show["text"] = f
Button(root,text="选择文件",command=test1).pack()
show = Label(root, width=40, height=3, bg="green")
show.pack()
root.mainloop()
simpledialog(简单对话框)包含如下常用函数:
函数名 | 说明 |
---|---|
askfloat(title,prompt,**kw) | 输入并返回浮点数 |
askinteger(title,prompt,**kw) | 输入并返回整数 |
askstring(title,prompt,**kw) | 输入并返回字符串 |
参数中,title 表示窗口标题;prompt 是提示信息;命名参数 kw 为各种选项:initialvalue(初始值)、minvalue(最小值)、maxvalue(最大值)。
【栗子】简单对话框的基本用法
from tkinter.simpledialog import *
root = Tk(); root.geometry("400x200")
def test1():
a = askinteger(title ="输入年龄", prompt="请输入年龄", initialvalue=18, minvalue=1, maxvalue=100)
show["text"]=a
Button(root,text="你多大了?请输入", command=test1).pack()
show = Label(root, width=40, height=3, bg="green")
show.pack()
root.mainloop()
messagebox(通用消息框)用于和用户简单的交互,用户点击确定、取消。如下列出了
messagebox 的常见函数:
函数名 说明 例子
askokcancel(title,message,**opt
ions)
OK/Cancel 对话框
askquestion(title,message,opt
ions)
Yes/No 问题对话框
askretrycancel(title,message,
options)
Retry/Cancel问题对
话框
showerror(title,message,**optio
ns)
错误消息对话框
showinfo(title,message,**option
s)
消息框
showwarning(title,message,**opt
ions)
警告消息框
【栗子】通用消息框的基本用法
from tkinter import *
from tkinter.messagebox import *
root = Tk(); root.geometry("400x200")
def test1():
a = showwarning(title ="python", message="哈哈哈~ 你被骗了。")
print(a)
Button(root,text="点进去有惊喜~~", command=test1).pack()
root.mainloop()
我们再前面学的组件是 tkinter 模块下的组件,整体风格较老较丑。为了弥补这点不足,推出了 ttk 组件。ttk 组件更加美观、功能更加强大。 新增了 LabeledScale(带标签的Scale)、Notebook(多文档窗口)、Progressbar(进度条)、Treeview(树)等组件。使用 ttk 组件与使用普通的 Tkinter 组件并没有多大的区别,只要导入 ttk 模块即可。
ttk 子模块的官方文档:
https://docs.python.org/3.7/library/tkinter.ttk.html
【注】此处我们不展开细讲 ttk。如果你的项目确实需要用到复杂的界面,推荐大家使用wxpython 或者 pyQt。
GUI 程序通常都有菜单,方便用户的交互。我们一般将菜单分为两种:
主菜单一般包含:文件、编辑、帮助等,位于 GUI 窗口的上面。创建主菜单一般有如下 4步:
【栗子】记事本软件的主菜单
""" 开发记事本软件的菜单 """
from tkinter import *
from tkinter.colorchooser import *
class Application(Frame):
def __init__(self,master=None):
super().__init__(master)
self.master = master
self.textpad = None
self.pack()
self.createWidget()
def createWidget(self):
# 创建主菜单
menubar = Menu(root)
menuFile = Menu(menubar)
menuEdit = Menu(menubar)
menuHelp = Menu(menubar)
# 将子菜单加入主菜单栏
menubar.add_cascade(label="文本(F)", menu=menuFile)
menubar.add_cascade(label="编辑(E)", menu=menuEdit)
menubar.add_cascade(label="帮助(H)", menu=menuHelp)
# 添加菜单项
menuFile.add_command(label="新建文件",accelerator="ctrl+n",command=self.test)
menuFile.add_command(label="打开文件",accelerator="ctrl+o",command=self.test)
menuFile.add_command(label="保存文件",accelerator="ctrl+s",command=self.test)
menuFile.add_separator() # 添加分割线
menuFile.add_command(label="退出文件",accelerator="ctrl+q",command=self.test)
# 将主菜单栏加入根窗口
root["menu"] = menubar
#文本编辑区
self.textpad = Text(root,width=80,height=50)
self.textpad.pack()
def test(self):
pass
if __name__ == '__main__':
root = Tk();root.geometry("450x300+300+200")
root.title("Mr_huang的简易记事本")
app = Application(master=root)
root.mainloop()
快捷菜单(上下文菜单)是通过鼠标右键单击组件而弹出的菜单,一般是和这个组件相关的操作,比如:剪切、复制、粘贴、属性等。创建快捷菜单步骤如下:
#coding=utf-8
from tkinter import *
from tkinter.colorchooser import *
from tkinter.filedialog import *
root = Tk();root.geometry("400x400")
def openAskColor():
s1 = askcolor(color="red", title="选择背景色")
root.config(bg=s1[1])
#创建快捷菜单
menubar2 = Menu(root)
menubar2.add_command(label="颜色",command=openAskColor)
menuedit = Menu(menubar2,tearoff=0)
menuedit.add_command(label="剪切")
menuedit.add_command(label="复制")
menuedit.add_command(label="粘贴")
menubar2.add_cascade(label="编辑",menu=menuedit)
def test(event):
#菜单在鼠标右键单击的坐标处显示
menubar2.post(event.x_root,event.y_root)
#编辑区
w1 = Text(root,width=50,height=30)
w1.pack()
w1.bind("" ,test)
root.mainloop()
将主菜单栏加入根窗口
root[“menu”] = menubar
#文本编辑区
self.textpad = Text(root,width=80,height=50)
self.textpad.pack()
def test(self):
pass
if name == ‘main’:
root = Tk();root.geometry(“450x300+300+200”)
root.title(“Mr_huang的简易记事本”)
app = Application(master=root)
root.mainloop()
#### 上下文菜单
快捷菜单(上下文菜单)是通过鼠标右键单击组件而弹出的菜单,一般是和这个组件相关的操作,比如:剪切、复制、粘贴、属性等。创建快捷菜单步骤如下:
1. 创建菜单
menubar = tk.Menu(root)
menubar.add_command(label=”字体”)
2. 绑定鼠标右键单击事件
def test(event):
menubar.post(event.x_root,event.y_root) #在鼠标右键单击坐标处显示菜单
root.bind(“”,test)
```python
#coding=utf-8
from tkinter import *
from tkinter.colorchooser import *
from tkinter.filedialog import *
root = Tk();root.geometry("400x400")
def openAskColor():
s1 = askcolor(color="red", title="选择背景色")
root.config(bg=s1[1])
#创建快捷菜单
menubar2 = Menu(root)
menubar2.add_command(label="颜色",command=openAskColor)
menuedit = Menu(menubar2,tearoff=0)
menuedit.add_command(label="剪切")
menuedit.add_command(label="复制")
menuedit.add_command(label="粘贴")
menubar2.add_cascade(label="编辑",menu=menuedit)
def test(event):
#菜单在鼠标右键单击的坐标处显示
menubar2.post(event.x_root,event.y_root)
#编辑区
w1 = Text(root,width=50,height=30)
w1.pack()
w1.bind("",test)
root.mainloop()