tkinter模块

以下内容大部份出自:Python GUI开发手册(化学工业出版社)
一、创建一个空白窗口:

from tkinter import *
win = Tk()
win.title("My GUI")
win.mainloop()

二、设置窗口属性:
1、设置窗口样式的相关方法及其含义:

title()             设置窗口标题
geoemetry("widthxheight")     设置窗口的大小及位置,单位为pilex
maxsize()                                     窗口的最大尺寸
minsize()                                      窗口的最小尺寸
configure(bg=color)                     为窗口添加背景颜色
resizable(True,True)                    设置窗口大小是否可以更改,第一个表示是否可以更改宽度,第二个是高度,默认True
state("zoomed")                          将窗口最大化
iconify()                                       将窗口最小化
iconbitmap()                                设置窗口的默认图标


示例:给窗口添加标题:

from tkinter import *
win = Tk()
win.title("My GUI")
txt = Label(win,text="\n\ngame over\n\n").pack()
win.mainloop()

三、设置窗口位置:

复制代码
from tkinter import *
win = Tk()
win.title("My GUI")
win.configure(bg="#a7ea90")                                 #窗口背景颜色
winw = 300                                                  #窗口宽度
winh = 220                                                  #窗口高度
scrw = win.winfo_screenwidth()                              #获取屏幕宽度
scrh = win.winfo_screenheight()                             #获取屏幕高度
x = (scrw-winw)/2                                           #计算窗口的水平位置
y = (scrh-winh)/2                                           #计算窗口的垂直位置
win.geometry("%dx%d+%d+%d" %(winw,winh,x,y))                #设置窗口大小和位置
str = "\n\n登高\n\n风急天高猿啸哀,渚清沙白鸟飞回。\n\n无边落木萧萧下,不尽长江滚滚来。\n\n万里悲秋常作客,百年多病独登台。\n\n艰难苦恨繁霜鬓,潦倒新停浊酒杯。"
txt = Label(win,text=str,fg="red",bg="#a7ea90").pack()
win.mainloop()
复制代码

四、tkinter组件分类:
1、文件类组件:
Label:标签组件。主要用于显示文本,添加提示信息
Entry:单行文本组件。只能添加单行文本
Spinbox:输入组件。可以理解为列表菜单与单行文本框的组合体,因为该组件既可以输入内容,也可以直接从现有的选项中选择值
Scale:数字范围组件。可以使用户拖动滑块选择数值,类似于HTML5表单中的range
2、按钮类组件:
Button:按钮组件
Radiobutton:单选组件
Checkbutton:复选框组件
3、选择列表类组件:
Listbox:列表框组件
Scrollbar:滚动条组件
OptionMenu:下拉列表
Combobox:组合框
4、容器类组件:
Frame:框架组件。用于将相关的组件放置在一起,以便于管理
LabelFrame:标签框架组件。将相关组件放置在一起,并给它们一个特定的名称
Toplevel:顶层窗口。重新打开一个新窗口,该窗口显示在根窗口的上方
PanelWindow:窗口布局管理。通过该组件可以手动修改其子组件的大小
Notebook:选项卡。选择不同的内容,窗口中可显示对应的内容
5、会话类组件:
Message:消息框。为用户显示一些短消息,与Label类似,但比Label更灵活
Messagebox:会话框。该组件提供了8种不同场景的会话框
6、菜单类组件:
Menu:菜单组件。可以为窗口添加菜单项以及二级菜单
Toolbar:工具栏。为窗口添加工具栏
Treeview:树菜单
7、进度条组件:
Progressbar:添加进度条

五、虽然tkinter模块中每个组件都有各自的属性,但有些属性是各组件通用的:
foreground或fg:设置组件中文字的颜色
background或bg:设置组件的背景颜色
width:设置组件的宽度
height:设置组件高度
anchor:文字在组件内输出的位置,默认为center
padx:组件的水平间距
pady:组件的垂直间距
font:组件的文字样式
relief:组件的边框样式,主要有:solid,raised,sunken,flat,groove,ridge
cursor:鼠标悬停在组件上时的样式
示例:

复制代码
from tkinter import *
win = Tk()
win.title("My GUI")
win.configure(bg="#a7ea90")                                 #窗口背景颜色
winw = 300                                                  #窗口宽度
winh = 220                                                  #窗口高度
scrw = win.winfo_screenwidth()                              #获取屏幕宽度
scrh = win.winfo_screenheight()                             #获取屏幕高度
x = (scrw-winw)/2                                           #计算窗口的水平位置
y = (scrh-winh)/2                                           #计算窗口的垂直位置
win.geometry("%dx%d+%d+%d" %(winw,winh,x,y))                #设置窗口大小和位置
str = "\n\n登高\n\n风急天高猿啸哀,渚清沙白鸟飞回。\n\n无边落木萧萧下,不尽长江滚滚来。\n\n万里悲秋常作客,百年多病独登台。\n\n艰难苦恨繁霜鬓,潦倒新停浊酒杯。"
txt = Label(win,text=str,fg="red",bg="#a7ea90",width=260,height=260,anchor="nw").pack()
#anchor位置参数说明:
#nw:左上角;w:左中间;sw:左下角;n:上中间;s:下中间;ne:右上角;e:右中间;se:右下角
win.mainloop()
复制代码

font属性的参数及含义:
family:设置字体,例如:Times New Roman
size:设置字号,单位px
weight:设置文字粗细,如bold
slant:设置斜体,如italic
underline:添加下划线,值为True或False
overstrike:添加删除线,值为True或False
示例1:

复制代码
from tkinter import *
win = Tk()
win.title("My GUI")
win.configure(bg="#a7ea90")                                 #窗口背景颜色
winw = 300                                                  #窗口宽度
winh = 220                                                  #窗口高度
scrw = win.winfo_screenwidth()                              #获取屏幕宽度
scrh = win.winfo_screenheight()                             #获取屏幕高度
x = (scrw-winw)/2                                           #计算窗口的水平位置
y = (scrh-winh)/2                                           #计算窗口的垂直位置
win.geometry("%dx%d+%d+%d" %(winw,winh,x,y))                #设置窗口大小和位置
str = "\n\n登高\n\n风急天高猿啸哀,渚清沙白鸟飞回。\n\n无边落木萧萧下,不尽长江滚滚来。\n\n万里悲秋常作客,百年多病独登台。\n\n艰难苦恨繁霜鬓,潦倒新停浊酒杯。"
txt = Label(win,text=str,fg="red",bg="#C3DEEF",font=("华文新魏",16,"bold","italic","underline","overstrike"),padx=200,pady=10).pack()
win.mainloop()
复制代码

示例2:

复制代码
from tkinter import *
win = Tk()
win.title("My GUI")
win.configure(bg="#a7ea90")                                 #窗口背景颜色
winw = 300                                                  #窗口宽度
winh = 220                                                  #窗口高度
scrw = win.winfo_screenwidth()                              #获取屏幕宽度
scrh = win.winfo_screenheight()                             #获取屏幕高度
x = (scrw-winw)/2                                           #计算窗口的水平位置
y = (scrh-winh)/2                                           #计算窗口的垂直位置
win.geometry("%dx%d+%d+%d" %(winw,winh,x,y))                #设置窗口大小和位置
str = "\n\n登高\n\n风急天高猿啸哀,渚清沙白鸟飞回。\n\n无边落木萧萧下,不尽长江滚滚来。\n\n万里悲秋常作客,百年多病独登台。\n\n艰难苦恨繁霜鬓,潦倒新停浊酒杯。"
txt = Label(win,text=str,fg="red",bg="#C3DEEF",relief="groove",cursor="spider")
txt.pack(padx=260,pady=260,side=LEFT)
win.mainloop()
复制代码

六、tkinter布局管理
1、pack()方法。它是常用的一种布局方式,其参数及含义如下:
side:设置组件水平展示或垂直展示,主要有四个属性值:
top:指组件从上到下依次排列,默认值
bottom:指组件从下到上依次排列
left:指组件从左到右依次排列
right:指组件从右到左依次排列
padx:设置组件距离窗口的水平距离
pady设置组件距离窗口的垂直距离
ipadx:设置组件内的文字距离组件边界的水平距离
ipady:设置组件内的文字距离组件边界的垂直距离
fill:设置组件填充所在的空白空间的方式,主要有四个属性值:
x:表示完全填充水平方向的空白空间
y:表示完全填充垂直方向的空白空间
both:表示水平和垂直方向的空白空间都完全填充
none:表示不填充空白空间,默认值
expand:设置组件是否完全填充其余空间(父容器的额外空间),其值为True(1)或False(0)
anchor:设置组件在窗口中的位置,其值如下:
nw:左上角;w:左中间;sw:左下角;n:上中间;s:下中间;ne:右上角;e:右中间;se:右下角
before:设置该组件应该位于指定组件的前面
after:设置该组件应该位于指定组件的后面
示例1:

复制代码
from tkinter import *
win = Tk()
txt1 = "春风吹又生"
txt2 = "夏浅蝉未多"
txt3 = "秋到天空阔"
txt4 = "冬钓寒江雪"
Label(win,text=txt1,bg="#F5DFCC").pack(side="top",padx=20,pady=5,ipadx=10,ipady=5,fill="both")
Label(win,text=txt2,bg="#EDB584").pack(side="top",padx=20,pady=5,ipadx=10,ipady=5)
Label(win,text=txt3,bg="#EF994C").pack(side="top",padx=20,pady=5,ipadx=10,ipady=5)
Label(win,text=txt4,bg="#A7EA90").pack(side="top",padx=20,pady=5,ipadx=10,ipady=5,fill="both")
win.mainloop()
复制代码

示例2:

from tkinter import *
win = Tk()
txt = "野火烧不尽,春风吹又生"
Label(win,text=txt,bg="#E6F5C8",fg="red",font=14).pack(side="top",fill="x",expand=1,anchor="nw")
win.mainloop()

示例3:

复制代码
from tkinter import *
win = Tk()
win.geometry("350x150")     #设置窗口大小
win.title("退出窗口提示")
win.resizable(False,False)  #不可更改窗口大小
txt1 = Label(win,text="确定退出本窗口吗?")
txt2 = Label(win,text="果断退出",bg="#c1ffc1")
txt3 = Label(win,text="我再想想",bg="#cdb5cd")
txt1.pack(fill="x",padx=20)
txt2.pack(side="right",anchor="se",padx=10,pady=20,ipadx=6)
txt3.pack(side="right",anchor="se",padx=10,pady=20,ipadx=6)
win.mainloop()
复制代码

示例4:

复制代码
from tkinter import *
win = Tk()
win.title("动力象棋")
txt1 = Label(win,text="象吃狮",bg="#f1c5c5",font=14)
txt4 = Label(win,text="豹吃狼",bg="#f1c5c5",font=14)
txt3 = Label(win,text="虎吃豹",bg="#cdb5cd",font=14)
txt2 = Label(win,text="狮吃虎",bg="#c1ffc1",font=14)
txt6 = Label(win,text="狗吃猫",bg="#cdb5cd",font=14)
txt7 = Label(win,text="猫吃鼠",bg="#f1c5c5",font=14)
txt5 = Label(win,text="狼吃狗",bg="#c1ffc1",font=14)
txt8 = Label(win,text="鼠吃象",bg="#c1ffc1",font=14)
txt1.pack(side="left",padx=10,ipadx=6,fill="y",expand=1)
#将txt2放在txt1前面
txt2.pack(side="left",padx=10,ipadx=6,fill="y",expand=1,before=txt1)
txt3.pack(side="left",padx=10,ipadx=6,fill="y",expand=1,before=txt2)
txt4.pack(side="left",padx=10,ipadx=6,fill="y",expand=1,before=txt3)
txt5.pack(side="left",padx=10,ipadx=6,fill="y",expand=1,before=txt4)
txt6.pack(side="left",padx=10,ipadx=6,fill="y",expand=1,before=txt5)
txt7.pack(side="left",padx=10,ipadx=6,fill="y",expand=1,before=txt6)
txt8.pack(side="left",padx=10,ipadx=6,fill="y",expand=1,before=txt7)
win.mainloop()
复制代码

2、grid()方法。网格布局,使用row定义组件所在的行,使用column定义组件所在的列,其相关参数及其含义如下:
row:组件所在行,从0开始
column:组件所在列,从0开始
rowspan:组件横向合并的行数
columnspan:组件纵向合并的行数
sticky:组件填充所分配空间空白区域的方式,和anchor类似,不过它只有4个可选的参数:
N:上对齐
S:下对齐
W:左对齐
E:右对齐
也可以组合使用:
N+S:拉长组件高度,使组件的顶端和底端对齐
N+S+E:拉长组件高度,使组件的顶端和底端对齐,同时切齐右边
N+S+W:拉长组件高度,使组件的顶端和底端对齐,同时切齐左边
E+W:拉长组件宽度,使组件的左边和右边对齐
N+S+E+W:拉长组件高度,使组件的顶端和底端对齐,同时切齐左边和右边
padx,pady:组件距离窗口边界的水平方向及垂直方向的距离

示例1:

复制代码
from tkinter import *
win = Tk()
win.title("乘法口诀")
Label(win,text="1 * 1=1",bg="#E0FFFF").grid(row=0,column=0,padx=10)
Label(win,text="1 * 2=2",bg="#E0FFFF").grid(row=1,column=0,padx=10)
Label(win,text="1 * 3=3",bg="#E0FFFF").grid(row=2,column=0,padx=10)
Label(win,text="1 * 4=4",bg="#E0FFFF").grid(row=3,column=0,padx=10)

Label(win,text="2 * 2=4",bg="#EEA9B8").grid(row=1,column=1,padx=10)
Label(win,text="2 * 3=6",bg="#EEA9B8").grid(row=2,column=1,padx=10)
Label(win,text="2 * 4=8",bg="#EEA9B8").grid(row=3,column=1,padx=10)

Label(win,text="3 * 3=9",bg="#F08080").grid(row=2,column=2,padx=10)
Label(win,text="3 * 4=12",bg="#F08080").grid(row=3,column=2,padx=10)

Label(win,text="4 * 4=16",bg="#FFE1FF").grid(row=3,column=3,padx=10)
win.mainloop()
复制代码

合并网格示例:

复制代码
from tkinter import *
win = Tk()
Label(win,text="横向合并4格",width=15,height=1,relief="groove",bg="#EDE19A").grid(row=0,column=0,columnspan=4,sticky="w")

Label(win,text="横向合并2格",width=15,height=1,relief="groove",bg="#EDBE9A").grid(row=1,column=0,columnspan=2,sticky="W")
Label(win,text="横向合并2格",width=15,height=1,relief="groove",bg="#EDBE9A").grid(row=1,column=1,columnspan=2,sticky="W")

Label(win,text="不合并",width=15,height=1,relief="groove",bg="#E5AEAE").grid(row=2,column=0)
Label(win,text="不合并",width=15,height=1,relief="groove",bg="#E5AEAE").grid(row=2,column=1)
Label(win,text="不合并",width=15,height=1,relief="groove",bg="#E5AEAE").grid(row=2,column=2)
Label(win,text="不合并",width=15,height=1,relief="groove",bg="#E5AEAE").grid(row=2,column=3)
win.mainloop()
复制代码

3、rowconfigure()和columnconfigure()方法设置组件的缩放比例:
tkinter模块添加的窗口默认情况下都可以通过鼠标拖动改变大小的,而当窗口大小改变时,可以通过rowconfigure()和columnconfigure()方法改变某行或某列组件所占空间随窗口缩放的比例。需要注意的是,两个方法是设置在父容器上的,并不是设置在组件上的。用法如下:
rowconfigure(0,weight=1)
columnconfigure(1,weight=1)
0,1分别表示第一行和第二列。weight=1表示随窗口缩放的比例为1
示例:

复制代码
from tkinter import *
win = Tk()
win.rowconfigure(0,weight=1)
win.columnconfigure(1,weight=1)
Label(win,width=15,height=2,relief="groove",bg="pink").grid(row=0,column=0,sticky=N+W) #第一行第一列
Label(win,width=15,height=2,relief="groove",bg="gray").grid(row=0,column=1,sticky=N+E) #第一行第二列
Label(win,width=15,height=2,relief="groove",bg="yellow").grid(row=1,column=0,sticky=N+S+W) #第二行第一列
Label(win,width=15,height=2,relief="groove",bg="blue").grid(row=1,column=1,sticky=N+S+E) #第二行第二列
win.mainloop()
复制代码

4、place()方法。可以设置组件的大小以及组件在容器中的精确位置。其参数及含义如下:
x:设置组件距离窗口左侧的水平距离
y:设置组件距离窗口顶部的垂直距离
width:设置组件的宽度
height:设置组件的高度
relx:设置组件距离容器左侧的相对距离,数值范围0-1
rely:设置组件距离容器顶部的相对距离,数值范围0-1
relwidth:组件相对父容器的宽度,数值范围0-1
relheight:组件相对父容器的高度,数值范围0-1
示例1:

复制代码
from tkinter import *
win = Tk()
win.title("华容道")
win.geometry("240x300")
Label(win,text="赵云",bg="#93edd4",relief="groove",font=14).place(width=60,height=120,x=0,y=0)
Label(win,text="曹操",bg="#a6e3a8",relief="groove",font=14).place(width=120,height=120,x=60,y=0)
Label(win,text="黄忠",bg="#93edd4",relief="groove",font=14).place(width=60,height=120,x=180,y=0)
Label(win,text="张飞",bg="#93edd4",relief="groove",font=14).place(width=60,height=120,x=0,y=120)
Label(win,text="关羽",bg="#93edd4",relief="groove",font=14).place(width=120,height=60,x=60,y=120)
Label(win,text="马超",bg="#93edd4",relief="groove",font=14).place(width=60,height=120,x=180,y=120)
Label(win,text="",bg="#f3f5c4",relief="groove",font=14).place(width=60,height=60,x=60,y=180)
Label(win,text="",bg="#f3f5c4",relief="groove",font=14).place(width=60,height=60,x=120,y=180)
Label(win,text="",bg="#f3f5c4",relief="groove",font=14).place(width=60,height=60,x=0,y=240)
Label(win,text="",bg="#f3f5c4",relief="groove",font=14).place(width=60,height=60,x=180,y=240)
win.mainloop()
复制代码

示例2:

复制代码
from tkinter import *
win = Tk()
win.title("华容道")
win.geometry("240x300")
Label(win,text="赵云",bg="#93edd4",relief="groove",font=14).place(relwidth=0.25,relheight=0.4,relx=0,rely=0)
Label(win,text="曹操",bg="#a6e3a8",relief="groove",font=14).place(relwidth=0.5,relheight=0.4,relx=0.25,rely=0)
Label(win,text="黄忠",bg="#93edd4",relief="groove",font=14).place(relwidth=0.25,relheight=0.4,relx=0.75,rely=0)
Label(win,text="张飞",bg="#93edd4",relief="groove",font=14).place(relwidth=0.25,relheight=0.4,relx=0,rely=0.4)
Label(win,text="关羽",bg="#93edd4",relief="groove",font=14).place(relwidth=0.5,relheight=0.2,relx=0.25,rely=0.4)
Label(win,text="马超",bg="#93edd4",relief="groove",font=14).place(relwidth=0.25,relheight=0.4,relx=0.75,rely=0.4)
Label(win,text="",bg="#f3f5c4",relief="groove",font=14).place(relwidth=0.25,relheight=0.2,relx=0.25,rely=0.6)
Label(win,text="",bg="#f3f5c4",relief="groove",font=14).place(relwidth=0.25,relheight=0.2,relx=0.5,rely=0.6)
Label(win,text="",bg="#f3f5c4",relief="groove",font=14).place(relwidth=0.25,relheight=0.2,relx=0,rely=0.8)
Label(win,text="",bg="#f3f5c4",relief="groove",font=14).place(relwidth=0.25,relheight=0.2,relx=0.75,rely=0.8)
win.mainloop()
复制代码

七、tkinter常用组件:
1、文本类组件:
1.1、Label标签
示例代码:

复制代码
from tkinter import *
win=Tk()                           #添加标题
win.title("斗兽棋游戏的食物链")    #添加标题
# textd定义Label标签里的文本内容,bg表示Label的背景颜色
txt1=Label(win,text="",bg="#FFEBCD",width=5,padx=4,pady=4,font="10")
txt2=Label(win,text="",bg="#c1ffc1",width=5,padx=4,pady=4,font="10")
txt3=Label(win,text="",bg="#FFEBCD",width=5,padx=4,pady=4,font="10")
txt4=Label(win,text="",bg="#c1ffc1",width=5,padx=4,pady=4,font="10")
txt5=Label(win,text="",bg="#FFEBCD",width=5,padx=4,pady=4,font="10")
txt6=Label(win,text="",bg="#c1ffc1",width=5,padx=4,pady=4,font="10")
txt7=Label(win,text="",bg="#FFEBCD",width=5,padx=4,pady=4,font="10")
txt8=Label(win,text="",bg="#c1ffc1",width=5,padx=4,pady=4,font="10")
# foreground设置label组件的文字颜色
txtr1=Label(win,text="",padx=2,pady=2,foreground="#B22222").grid(row=1,column=2)
txtr2=Label(win,text="",padx=2,pady=2,foreground="#B22222").grid(row=1,column=4)
txtb1=Label(win,text="",padx=2,pady=2,foreground="#B22222").grid(row=2,column=5)
txtb2=Label(win,text="",padx=2,pady=2,foreground="#B22222").grid(row=4,column=5)
txtl1=Label(win,text="",padx=2,pady=2,foreground="#B22222").grid(row=5,column=4)
txtl2=Label(win,text="",padx=2,pady=2,foreground="#B22222").grid(row=5,column=2)
txtt1=Label(win,text="",padx=2,pady=2,foreground="#B22222").grid(row=4,column=1)
txtt2=Label(win,text="",padx=2,pady=2,foreground="#B22222").grid(row=2,column=1)
# 设置斗兽棋游戏的棋子的位置
txt1.grid(row=1,column=1)
txt2.grid(row=1,column=3)
txt3.grid(row=1,column=5)
txt4.grid(row=3,column=5)
txt5.grid(row=5,column=5)
txt6.grid(row=5,column=3)
txt7.grid(row=5,column=1)
txt8.grid(row=3,column=1)
win.mainloop()
复制代码

示例-Label标签中添加图片:

from tkinter import *
from PIL import Image,ImageTk
win=Tk()
image = Image.open(f'D:\\temp\\6.png')
img = ImageTk.PhotoImage(image)
Label(win,image=img).pack()
win.mainloop()

如果Label组件中既有文字又有图片,则可以通过Label组件中的compound设置图片与文字的显示位置,具体参数如下:
top:图片位于文字上方
bottom;图片位于文字下方
left:图片位于文字左侧
right:图片位于文字右侧
center:文字位于图片上(图片与文字重叠,且文字在图片的上层)
1.2、Entry单行文本框
使用方法:Entry(win),示例1:

from tkinter import *
win=Tk()
Label(win,text="出发地:",font=14).grid(pady=10,row=0,column=0)
Entry(win).grid(row=0,column=1)
Label(win,text="目的地:",font=14).grid(pady=10,row=1,column=0)
Entry(win).grid(row=1,column=1)
win.mainloop()

如果是密码框,可以用以下方式:Entry(win,show="*")

示例2:

from tkinter import *
win=Tk()
Label(win,text="密码:",font=14).grid(pady=10,row=0,column=0)
Entry(win,show="*").grid(row=0,column=1)
win.mainloop()

示例3-登录窗口:

复制代码
from tkinter import *
win=Tk()
win.configure(bg="#EFE5D2")
Label(win,text="用户:",font=14).grid(pady=10,row=0,column=0)
Entry(win).grid(row=0,column=1)
Label(win,text="密码:",font=14).grid(pady=10,row=1,column=0)
Entry(win,show="*").grid(row=1,column=1)
Label(win,text="确定",relief="groove").grid(row=2,columnspan=2,pady=10)
win.mainloop()
复制代码

Entry提供的三个组件:
get():获取文本框的内容
insert():在文本框的指定位置添加内容,如:entry.insert(index,str)
delete():删除文本框中指定的内容,如:entry.delete(first,end)
示例4-相加计数器:

复制代码
from tkinter import *
win=Tk()
win.configure(bg="#EFE5D2")
def add():
    res.delete(0,END)        #清空显示结果的文本框的内容
    add1 = int(op1.get())
    add2 = int(op2.get())
    res.insert(INSERT,add1+add2)        #INSERT表示从当前光标处开始插入
op1=Entry(win,width=5,relief="groove")
op1.grid(row=0,column=0)
Label(win,text="+").grid(row=0,column=1)
op2=Entry(win,width=5,relief="groove")
op2.grid(row=0,column=2)
Label(win,text="=").grid(row=0,column=3)
res = Entry(win,width=5,relief="groove")
res.grid(row=0,column=4)
Button(win,text="计算",command=add,relief="groove").grid(row=0,column=5,ipadx=10)
win.mainloop()
复制代码

1.3、Text多行文本框
用法Text(win)。可以通过insert()方法添加初始文本。Text组件中添加图片需要创建PhotoImage()对象,然后通过image_create()引入图像。如:
photo = PhotoImage(file="D:\test\ico.png")
text.image_create(END,image=photo)
以上方法只能引入png格式的图片,如果要引入其它格式的请参考:示例-Label标签中添加图片。
示例-在文本框中添加图片、文字及按钮:

复制代码
from tkinter import *
from PIL import Image,ImageTk
i = 0
def show():
    global i
    i += 1
    label.config(text="你点了我\t"+str(i)+"")
win = Tk()
text = Text(win,width=45,height=10,bg="#cae1ff",relief="solid")
image = Image.open(f'E:\\网页素材大全\\按钮\\0034.gif')
photo = ImageTk.PhotoImage(image)
text.image_create(END,image=photo)
text.insert(INSERT,"在这里添加文本:\n")
text.pack()
bt = Button(win,text="你点我试试",command=show,padx=10)
text.window_create("2.0",window=bt) #将按钮放在Text组件中的第2行第1列
label = Label(win,padx=10,text="你点了我0下")
text.window_create("2.end",window=label)    #将Label放在Text组件中的第2行最后一列
win.mainloop()
复制代码

Text组件的索引方式有我种,以下是常见的几种:
line.column:如"2.3"表示第2行第4列
insert:在光标的位置插入,如上面的text.insert(INSERT,"在这里添加文本:\n")
end:最后一个字符的位置,如果字符串为end,表示所有文本的最后一个字符位置;如果字符串为line.end表示当前行的最后一个字符位置
+count chars:指定位置向后移动count个字符。如:"2.1+2 chars"表示第2行第4个字符的位置
-count chars:指定位置向前移动count个字符。如:"2.3-2 chars"表示第2行第2个字符的位置
注意:Text组件中获取字符串的索引位置时,第1行的索引为1,第1列的索引为0
示例:

from tkinter import *
win = Tk()
text = Text(win)
text.insert(INSERT,"I love python")
text.pack()
print(text.get(1.2,1.6)) #获取Text组件的第1行第3列到第1行第7列的字符(love)
win.mainloop()

Text组件提供了一些方法或以获取或编辑Text组件中的内容,常见方法如下:
delete():删除Text组件中的内容
get():获取文本内容
mark_set():添加标记
search():搜索文本
edit_undo():撤销操作
edit_separator():添加分割线。之后再进行撤销操作时不会撤销所有操作,只是撤销上一次的操作
示例-用Ctrl+z和Ctrl+y执行撤销和恢复操作:

复制代码
from tkinter import *
win = Tk()
def undo1(event):
    text.edit_undo()    #撤销操作
def redo1(event):       #恢复操作
    text.edit_redo()
def callback(event):
    text.edit_separator()    #每单击一次键盘就添加一个分割线,否则会撤销或恢复所有操作
text = Text(win,width=50,height=30,undo=True,autoseparators=False)
text.pack()
#添加提示性文字
text.insert(INSERT,'在下方可以添加文本,通过Ctrl+z撤销,Ctrl+y恢复:\n\n')
text.bind('<Key>',callback)
text.bind('<Control-Z>',undo1)
text.bind('<Control-Y>',redo1)
win.mainloop()
复制代码

2、按钮类组件:
2.1、Button按钮
语法:Button(win,command=callback)
同样Button按钮也可以显示图片同样首先需要创建一个PhotoImage()对角,然后在Button按钮中引入该对象。如:
img = PhotoImage(file="test.png")
Button(win,image=img)
示例-单击按钮,增加图片:

复制代码
from tkinter import *
from PIL import Image,ImageTk
def show():
    Label(win,image=img).pack()
win = Tk()
image = Image.open(f'E:\\网页素材大全\\按钮\\0034.gif')
img = ImageTk.PhotoImage(image)
Button(win,text="添加图片",command=show).pack()
win.mainloop()
复制代码

 

Button组件的相关属性及其含义:
activebackground:按钮激活时的背景颜色
activeforeground:按钮激活时的前景颜色
bd:边框的宽度,默认为2像素
command:单击按钮时执行的方法
image:在按钮上添加图片
state:设置按钮的状态,可选的值有NORMAL(默认值)、ACTIVE、DISABELE
wraplength:限制按钮每行显示的字符数量
text:按钮的文本内容
underline:设置哪些文字带下划线。例如,取值为0,表示第一个字符带下划线;值为1表示第二个字符带下划线
示例-模拟密码输入器:

复制代码
from tkinter import *
from PIL import Image,ImageTk
def num(a):
    val = pswshow.get()
    if len(val) < 11:
        #先清除原有内容,然后将原有内容同输入的值一起添加到单行文本框
        pswshow.delete(0,END)
        pswshow.insert(0,val+" "+a)
def back():
    val = pswshow.get()
    if len(val) >= 1:
        #如果文本框的值长度大于2,删除最后一位
        pswshow.delete(len(val) - 2,END)
        pswshow.config(text=val[0:len(val) - 2])
def enter():
    val = pswshow.get()
    #弹出一个顶层窗口
    win2 = Toplevel()
    if len(val) == 12:
        Label(win2,text="\n\n您的密码正确\n\n").pack()
    else:
        Label(win2, text="\n\n密码为6位数字\n\n").pack()

win = Tk()
win.title("密码输入器")
#密码显示部分
pswshow = Entry(win,relief="solid",justify="center")
pswshow.grid(row=1,columnspan=3)
#键盘部分
but1 = Button(win,text="1",command=lambda :num("1"))
but2 = Button(win,text="2",command=lambda :num("2"))
but3 = Button(win,text="3",command=lambda :num("3"))
but4 = Button(win,text="4",command=lambda :num("4"))
but5 = Button(win,text="5",command=lambda :num("5"))
but6 = Button(win,text="6",command=lambda :num("6"))
but7 = Button(win,text="7",command=lambda :num("7"))
but8 = Button(win,text="8",command=lambda :num("8"))
but9 = Button(win,text="9",command=lambda :num("9"))
but0 = Button(win,text="0",height=1,command=lambda :num("9"))
but1.grid(row=5,sticky=W+E)
but2.grid(row=5,column=1,sticky=W+E)
but3.grid(row=5,column=2,sticky=W+E)
but4.grid(row=6,sticky=W+E)
but5.grid(row=6,column=1,sticky=W+E)
but6.grid(row=6,column=2,sticky=W+E)
but7.grid(row=7,sticky=W+E)
but8.grid(row=7,column=1,sticky=W+E)
but9.grid(row=7,column=2,sticky=W+E)
img1 = Image.open(f'D:\\cnblogs\\python\\pic\\back.png')
back1 = ImageTk.PhotoImage(img1)
img2 = Image.open(f'D:\\cnblogs\\python\\pic\\enter.png')
enter1 = ImageTk.PhotoImage(img2)
butback = Button(win,image=back1,command=back)
butok = Button(win,image=enter1,command=enter)
butback.grid(ipady=3,row=8,sticky=W+E)
but0.grid(row=8,column=1,sticky=W+E)
butok.grid(ipady=3,row=8,column=2,sticky=W+E)
win.mainloop()
复制代码

2.2、Radiobutton单选按钮
多个按钮只能选择一个。示例:

from tkinter import *
win = Tk()
vali = IntVar()
vali.set("male")
radio1 = Radiobutton(win,variable=vali,value="male",text="").pack()
radio2 = Radiobutton(win,variable=vali,value="female",text="").pack()
win.mainloop()

Radiobutton组件的常用属性及含义:
image:指定Radiobutton显示的图片
text:指定Radiobutton显示的文本
compound:设置图片和文本的排版方式,具体可以参考Label组件中的compound属性
cursor:当鼠标停在单行按钮上时的样式
indicatoron:指定是否绘制单选按钮前员的小圆圈
selectcolor:选择框的颜色
selectimage:当该单选按钮被选中时显示的状态
state:指定单选按钮的状态
value:表示该按钮的值
variable:设置或获取当前选中的单选按钮
Radiobutton单选按钮也具有和Button一样的属性如:activebackground、activeforeground、bd、command、highlightcolor等
示例-脑筋急转弯:

复制代码
from tkinter import *
def result1():
    print(v.get())
    if v.get() == 1:
        re.config(text="答错了,答案是小狗,因为'旺旺仙贝:)'")
    elif v.get() == 2:
        re.config(text="答对了,因为'旺旺仙贝:)'")
    else:
        re.config(text="请选择一个答案")
win = Tk()
win.title("脑筋急转弯")
win.geometry("300x150")
Label(win,text="老师让小猫和小狗去背书,请问谁先背呢?",font=14).pack(anchor=W)
v = IntVar()
ans1 = Radiobutton(win,text="小猫",variable=v,value=1,selectcolor="#f1d4c9")
ans1.pack(anchor=W)
ans2 = Radiobutton(win,text="小狗",variable=v,value=2,selectcolor="#f1d4c9")
ans2.pack(anchor=W)
Button(win,text="提交",command=result1,font=14,bg="#f1c57e",relief="groove").pack()
re = Label(win)
re.pack()
win.mainloop()
复制代码

2.3、Checkbutton复选框
可以同时选择多个选项,示例:

from tkinter import *
win = Tk()
v1 = IntVar()
Checkbutton(win,variable=v1,text="香蕉").pack()
v2 = IntVar()
Checkbutton(win,variable=v2,text="苹果").pack()
win.mainloop()

如果选项比较多时,可以通过元组或列表存放选项显示的文本:示例:

from tkinter import *
win = Tk()
fruits = ("香蕉","苹果","橙子","百香果","牛油果")
for fruit in fruits:
    var = IntVar()
    checkbox1 = Checkbutton(win,text=fruit,variable=var).pack(side=LEFT)
win.mainloop()

判断复选框是否被选中,实际上是判断复选框绑定的值。如果绑定的变量类型为整型,那么复选框被选中,则变量的值为1,反之值为0;如果绑定的变量类型为布尔型,那么当复选框被选中时,变量值为True,返之为False
示例-问卷调查功能:

复制代码
from tkinter import *
def results():
    sel = ""
    for i in range(len(str1)):
        if check[i].get() == 1:
            sel = sel+str1[i]+" "
    re.config(text=sel)
win = Tk()
win.title("调查问卷")
str1 = ("读书","旅游","追剧","上网","看电影","锻炼","健身","跑步","户外运动","朋友聚会","打球","发呆")
text = Label(win,text="适当放松有益身心健康,请在下方选出自己最喜欢的放松方式:",font=14).grid(row=0,column=0,columnspan=6)
check = []
for i in range(len(str1)):
    v = IntVar()
    checkbox = Checkbutton(win,text=str1[i],variable=v,font=12,selectcolor="#00ffff",padx=5)
    checkbox.grid(row=1,column=i)
    check.append(v)
button = Button(win,text="提交",command=results,font=14,bg="#EFB4DE").grid(row=3,column=0,pady=6,columnspan=12)
re = Label(win,font=12,height=10,width=110,bg="#cfcfcf")
re.grid(row=4,columnspan=12)
win.mainloop()
复制代码

3、菜单列表类组件:
3.1、Listbox列表框组件
可以包含一个或多个文本,以便进午单选或多选。语法:listbox = Listbox(win,option),如:

from tkinter import *
win = Tk()
listbox = Listbox(win)
listbox.insert(END,"合肥")
listbox.insert(END,"上海")
listbox.pack()
win.mainloop()

如果添加的选项较多,可以通过列表存储选项,然后通过for循环向列表框中添加选项。示例:

from tkinter import *
win = Tk()
cities = ["合肥","上海","北京","天津","重庆","香港","台湾"]
listbox = Listbox(win,height=6,width=20,relief="solid")
for city in cities:
    listbox.insert(END,city)
listbox.pack()
win.mainloop()

Listbox组件的相关属性及含义:
listvariable:指向一个StringVar变量,用于存放Listbox组件所有项目
selectbackground:某个选项被选中时的背景颜色
selectmode:选择模式,值可以是single(单选),browse(单选,可以拖动鼠标或使用方向键改变选项),multiple(多选),extended(多先,可以通过<Shift>、<Ctrl>或者拖动鼠标实现多选)
takefocus:指定列表框是否可以通过<Tab>键转移焦点
xscrollcommand:为列表框添加水平滚动条
yscrollcommand:为列表框添加垂直滚动条
示例-双击获取列表框中的选项:

复制代码
from tkinter import *
def show(ele):
    listbox.pack(fill=X)
def typeIn(event):
    enc.delete(0,END)
    enc.insert(INSERT,listbox.get(listbox.curselection()))
win = Tk()
win.geometry("180x150")
val = StringVar()
val.set("合肥 上海 北京 天津 重庆 香港 台湾")
listbox = Listbox(win,bg="#FFF8DC",selectbackground="#2C92DF",selectmode="single",height=6,width=25,listvariable=val)
enc = Entry(win)
enc.pack(fill=X)
#为文本框绑定事件,当鼠标左键单击文本框时,执行show函数
enc.bind("<Button-1>",show)
#为列表框绑定双击事件,当鼠标左键单击文本框时,执行typeIn函数
listbox.bind("<Double-Button-1>",typeIn)
win.mainloop()
复制代码

Listbox组件的相关方法及含义:
insert(index,text):向列表框中指定位置添加选项
delete(start,[end]):删除列表框中start-end区间的选项,如果省略end,则表示删除索引为start的选项
selection_set(start,[end]);选中列表框中start-end区间的选项,如果省略end,则表示选取索引为start的选项
selection_get(index):获取某项的内容
size():获取列表框组的长度
selection_includes():判断某项是否被选中
示例-快捷信号功能:

复制代码
from tkinter import *
def add(from1,to1):
    option = from1.get(from1.curselection())
    to1.insert(END,option)
    print(from1.curselection())
    from1.delete(from1.curselection())

win = Tk()
win.title("添加快捷消息列表")
win.geometry("250x200")
Label(win,text="系统信号").grid(row=0,column=0)
Label(win,text="快捷信号").grid(row=0,column=2)
#列表内容
val1 = StringVar() #系统信号
val1.set("发起进攻 请求集合 小心草丛 跟着我")
val2 = StringVar() #快捷信号
val2.set("开始撤退 清理兵线 回防高地 请求支援")
listbox1 = Listbox(win,bg="#FFF8DC",selectbackground="#D15FEE",selectmode="single",listvariable=val1,height=8,width=10)
listbox2 = Listbox(win,bg="#C1FFC1",selectbackground="#D15FEE",selectmode="single",listvariable=val2,height=8,width=10)
listbox1.grid(row=1,column=0,rowspan=2)
listbox2.grid(row=1,column=2,rowspan=2)
btn1 = Button(win,text=">>>",command=lambda :add(listbox1,listbox2)).grid(row=1,column=1,padx=10)
btn2 = Button(win,text="<<<",command=lambda :add(listbox2,listbox1)).grid(row=2,column=1,padx=10)
win.mainloop()
复制代码

3.2、OptionMenu下拉列表组件
可以单按钮展开下拉列表,并且选择其中的一项,示例:

from tkinter import *
win = Tk()
val = StringVar()
optionmenu = OptionMenu(win,val,"合肥","上海","北京","天津")
optionmenu.pack()
win.mainloop()

下拉列表选项较多时,可以通过元组存储选项内容,如:

from tkinter import *
win = Tk()
val = StringVar()
cities = ("合肥","上海","北京","天津","香港","台湾")
optionmenu = OptionMenu(win,val,*cities)
optionmenu.pack()
win.mainloop()

示例-在下拉列表中显示歌曲列表:

复制代码
from tkinter import *
win = Tk()
win.geometry("220x220")
win.title("我的歌单")
Label(text="我的歌单:").pack(fill="x",anchor="w")
songs_lst = (
    "My way",
    "Trouble is friend",
    "Let it be me",
    "My heart will go on an on",
    "The cup of life",
    "The color of wind",
    "Fly to the moon",
)
val = StringVar()
optionmenu = OptionMenu(win,val,*songs_lst)
optionmenu.pack(fill="x")
win.mainloop()
复制代码

OptionMenu组件主要有两个方法:
set():设置下拉菜单默认被选中的值
get():获取下拉菜单当前被选中的值
示例-逻辑推理谁是小偷:

复制代码
from tkinter import *
def result():
    ans = items[2]
    if v.get() == ans:
        re.config(text=f"答对了,就是{ans}")
    else:
        re.config(text=f"回答错误,小偷是{ans}")
win = Tk()
win.title("谁是小偷")
win.configure(bg="#ffffcc")
text = Text(win,width=50,height=13,bg="#ffffcc",font=14,relief="flat")
question = "一位警察抓获四个盗窃嫌疑犯:张三、李四、王二、麻子,而他们的供词如下:\n\n张三说:“不是我偷的。”\n\n李四说:“是张三偷的。”\n\n王二说:“不是我。”\n\n麻子说“是李四偷的。”\n\n他们四人只有一人说了真话,你知道谁是小偷吗?\n"
text.insert(END,question)
text.grid(row=1,columnspan=4)
text.config(state="disabled")
items = ("张三","李四","王二","麻子")
v = StringVar(win)
v.set(items[0])
om = OptionMenu(win,v,*items)
om.grid(row=2,columnspan=2)
button = Button(win,text="确定",command=result).grid(row=2,column=1,columnspan=2)
re = Label(win,padx=5,pady=5,width=60)
re.grid(row=3,column=0,columnspan=3)
win.mainloop()
复制代码

3.3、Combobox组合框组件
Combobox组件是ttk模块的组件。相当于Entry和OptionMenu组件的组合,用户即可以在文本框中输入内容,也可以单击文本框右侧的按钮展开下拉菜单进行选择,语法如下:
Combobox(win,textvariable=StringVar(),values=("aa","bb","cc"))
示例-在窗口中添加一个组合框:

from tkinter import *
from tkinter.ttk import *
win = Tk()
val = StringVar()
cities = ("合肥","上海","北京","天津","香港","台湾")
Combobox(win,textvariable=val,values=cities).pack(padx=10,pady=10)
win.mainloop()

示例-以管理员身份查看报表:

复制代码
from tkinter import *
from tkinter.ttk import *
win = Tk()
win.title("Combobox使用")
label1 = Label(win,text="选择管理员身份:").grid(row=1,column=0,columnspan=2,pady=10)
items = ("蓝色妖姬","烈焰焚情","寒冰幽兰","岁岁芳华","朝暮盈霄","陌上开花")
useroption = Combobox(win,width=12,values=items)
useroption.grid(row=1,column=2,pady=10)
useroption.current(0) #默认选择当前第一项
label2 = Label(win,text="查看类别:").grid(row=2,column=0,pady=10,columnspan=2)
numberChosen = Combobox(win,width=12,values=("进销总览","销量","库存","进售价","账单"))
numberChosen.grid(row=2,column=2,pady=0)
numberChosen.current(0)
button = Button(win,text="提交").grid(row=3,columnspan=4,pady=10)
win.mainloop()
Combobox组件常用的方法有三个:
get():获取当前被选中的选项
set(value):设置当前选中的值为value
current(index):设置默认选中索引为index的选项
示例-实现添加日程功能:
from tkinter import *
from tkinter.ttk import *
def getMon(event):
    items = monOption.get()
    #当月份为4,6,9,11月时,日期为30天
    if items == "4" or items == "6" or items == "9" or items == "11":
        mon = tuple(range(1,31))
    elif items == "2":
        mon = tuple(range(1, 29))
    else:
        mon = tuple(range(1, 32))
    dateOption["values"] = mon
def getDate():
    info = label3.cget("text")
    temp = monOption.get()+""+dateOption.get()+"日\t"+text.get("0.0",END)
    label3.config(text=info+temp)
    text.delete("0.0",END)
win = Tk()
win.title("添加日程")
number = StringVar()
#1-12月
months = tuple(range(1,13))
monOption = Combobox(win,width=5,textvariable=number,values=months)
monOption.current(0)
monOption.grid(row=1,column=0,sticky="E",columnspan=2)
#为Combobox绑定事件,当进行选择时触发事件
monOption.bind("<<ComboboxSelected>>",getMon)
label1 = Label(win,text="").grid(row=1,column=2,sticky="W")
#默认每月的天数为31天
days = tuple(range(1,32))
dateOption = Combobox(win,width=5,values=days)
dateOption.grid(row=1,column=3,pady=10,columnspan=2)
dateOption.current(0)
label2 = Label(win,text="").grid(row=1,column=5,sticky="w")
text = Text(win,width=40,height=10)
text.grid(row=4,columnspan=8)
button = Button(win,text="确定",command=getDate).grid(row=5,columnspan=8)
label3 = Label(win)
label3.grid(row=6,columnspan=8)
win.mainloop()
复制代码

4、容器类组件:
当窗口中的组件较多时,对组件进行管理就会比较困难,为解决这个问题就需要用到容器类组件。
4.1、Frame组件,语法:Frame(win),win是其父容器,可以省略。
示例-6个Frame容器组件:

复制代码
from tkinter import *
win = Tk()
win.geometry("360x180")
for i in range(6):
    if i % 2 == 0:
        Frame(win,bg="#B1FFBB",width=60,height=40,cursor="cross").grid(row=0,column=i,pady=10)
    else:
        Frame(win, bg="#FFD9C5", width=60, height=40, cursor="plus").grid(row=0, column=i, pady=10)
win.mainloop()
复制代码

将组件放在Frame组件中,也就是将Frame组件做为组件的父容器。示例:

复制代码
from tkinter import *
win = Tk()
win.geometry("360x120")
box = Frame(win,width=100,height=100,relief="groove",borderwidth=5)
box.grid(row=0,column=0,padx=10,pady=10)
txt = "    小明去钓鱼,结果6条无头,8条只有半个身子,9条无尾,请问小明一共钓了几条鱼?"
Label(box,text=txt,wraplength=320,justify="left").grid(columnspan=4)
select = ["0条","6条","8条","9条"]
val = IntVar()
for i in range(len(select)):
    Radiobutton(box,text=select[i],value=i,variable=val).grid(row=1,column=i)
win.mainloop()
复制代码

4.2、LabelFrame标签框架组件,使用该组件可以将一系列相关联的组件放置在一个容器内,默认情况下,该组件会绘制边框将子组件包围,并且为其显示一个标题:语法如下:
labelframe = LabelFrame(win,text="这是标题")
示例-将一组单选按钮放置在一个LabelFrame组件中:

复制代码
from tkinter import *
win = Tk()
win.geometry("240x200")
labelframe = LabelFrame(win,text="选择你的出战英雄:")
labelframe.grid(row=0,column=1,ipadx=10,ipady=10)
hero = StringVar()
hero.set("吕布")
Radiobutton(labelframe,variable=hero,text="吕布",value="吕布").grid(row=1,column=1)
Radiobutton(labelframe,variable=hero,text="刘备",value="刘备").grid(row=2,column=1)
Radiobutton(labelframe,variable=hero,text="关羽",value="关羽").grid(row=3,column=1)
Radiobutton(labelframe,variable=hero,text="张飞",value="张飞").grid(row=4,column=1)
win.mainloop()
复制代码

4.3、Toplevel顶层窗口组件,可以新弹出一个窗口,而这个窗口显示在父窗口的上层,当父窗口被关闭时,Toplevel窗口也会被关闭,但是Toplevel窗口的关闭并不影响父窗口。语法:win2 = Toplevel()
示例-单击根窗口的按钮,弹出一个顶层窗口:

复制代码
from tkinter import *
def creat():
    top = Toplevel()
    top.geometry("150x150")
    top.title("创建顶层窗口")
    top.configure(bg="#D8EBB8")
    Label(top,text="这是Toplevel顶层窗口").pack()
win = Tk()
win.geometry("200x200")
win.configure(bg="#F7D7C4")
Button(win,text="创建顶层窗口",command=creat).pack()
win.mainloop()
复制代码

示例-模拟游戏匹配房间:

复制代码
from tkinter import *
def begin():
    win2 = Toplevel()
    win2.geometry("200x120")
    win2.configure(bg="#FFACAB")
    win2.title("准备游戏")
    Label(win2,text="玩家已就位,请准备!",font=14,bg="#FFACAB").pack(pady=50)
def change():
    win2 = Toplevel()
    win2.geometry("200x120")
    win2.configure(bg="#FFACAB")
    win2.title("2号棋牌室")
    Label(win2, text="进入2号棋牌室", font=14, bg="#FFACAB",width=35).pack(side="top", fill="x")
    Label(win2, text="玩家已就位,请准备!", font=16, bg="#FFACAB").pack(side="top", fill="x",pady=20)
win = Tk()
win.geometry("270x220")
win.title("1号棋牌室")
win.configure(bg="#FFCD63")
label = Label(win,text="欢迎进入1号棋牌室",font=14, bg="#FFFBB5",width=35).grid(row=0,column=0,columnspan=5,ipady=8)
btn1 = Button(win,text="开始对局",bg="#25A837",command=begin).grid(row=2,column=1,pady=10)
btn2 = Button(win,text="更换房间",bg="#FF4A4F",command=change).grid(row=2,column=3,pady=10)
win.mainloop()
复制代码

4.4、Notebook选项卡组件,此组件是ttk模块提供的组件,可以显示多个选项,当用户单击选项时,下方的面板中就会显示对应的内容,语法如下:
note = Notebook(win)
note.add(pane,text="title")
其中,note表示选项卡组件,pane表示向选项卡中添加的子组件,text为该子组件的标题,单击选项卡标题即可显示对应组件。
示例-设置日期和时间的选项卡:

复制代码
from tkinter import *
from tkinter.ttk import Notebook
win = Tk()
win.title("日期和时间")
note = Notebook(win,width=250,height=150)
pane1 = Frame()
Button(pane1,text="更改日期和时间").pack(pady=20)
pane2 = LabelFrame()
Checkbutton(pane2,text="显示此时钟",variable=StringVar()).pack(pady=20)
pane3 = Frame()
Button(pane3,text="更改设置").pack(pady=20)
note.add(pane1,text="日期和时间")
note.add(pane2,text="附加时钟")
note.add(pane3,text="Internet时间")
note.pack()
win.mainloop()
复制代码

示例-实现单击游戏名称显示游戏简介:

复制代码
from tkinter import *
from tkinter.ttk import Notebook
win = Tk()
win.title("游戏介绍")
note = Notebook(win,width=300,height=200)
pane1 = Frame()
img1 = PhotoImage(file="D:\\cnblogs\\python\\pic\\pane1.png")
Label(pane1,image=img1).pack()
Label(pane1,text="脑洞不大,一问便知").pack(pady=20)
Button(pane1,text="现在就玩").pack()
pane2 = Frame()
img2 = PhotoImage(file="D:\\cnblogs\\python\\pic\\pane2.png")
Label(pane2,image=img2).pack()
Label(pane2,text="你到底是哪一派,抽象派还是形象派").pack(pady=20)
Button(pane2,text="现在就玩").pack()
note.add(pane1,text="最强的大脑")
note.add(pane2,text="山水泼墨画")
note.pack()
win.mainloop()
复制代码

八、会话框与菜单:

1、messagebox会话框模块。它是tkinter模块中的一个模块,该模块根据会话框窗口的使用场合,提供了8种会话框,具体如下:
showinfo(title,message,option):显示消息提示
showwarning(title,message,option):显示警告消息
showerror(title,message,option):显示错误消息
askquestion(title,message,option):显示询问消息
askokcancle(title,message,option):显示“确定”或“取消”。确定返回True,取消返回False
askyesno(title,message,option):显示“是”或“否”。是返回True,否返回False
askyesnocancle(title,message,option):显示“是”,“否”和“取消”。是返回True,否返回False,取消返回None
askretrycancle(title,message,option):显示“重试”和“取消”。重试返回True,取消返回False
上述8种会话框的参数基本相同,title表示会话框的标题,message表示会话框的文字内容,option表示可选参数,主要有以下三个参数:
default:设置默认的按钮,即按下回车键时相应的按钮,默认为第一个按钮
icon:设定显示的图标,有INFO,ERROR,QUESTION,WARNING
parent:指定当会话关闭时,焦点指向的父窗口
1.1、showinfo(title,message,option)
示例:

from tkinter import *
from tkinter.messagebox import showinfo
def mess():
    showinfo("Welcom!","好久不见,欢迎回来!")
win = Tk()
win.title("消息会话框")
Button(win,text="进入游戏",command=mess).pack(padx=20,pady=20)
win.mainloop()

1.2、showwarning(title,message,option)
示例:

from tkinter import *
from tkinter.messagebox import showwarning
def mess():
    showwarning("警告!","您进入的网站可能有风险!")
win = Tk()
win.title("警告会话框")
Button(win,text="进入网站",command=mess).pack(padx=20,pady=20)
win.mainloop()

1.3、showerror(title,message,option)
示例:

from tkinter import *
from tkinter.messagebox import showerror
def mess():
    showerror("错误提醒!","程序需要你提供读写权限,\n您拒绝的此项请求,程序将无法继续往下执行!")
win = Tk()
win.title("错误会话框")
Button(win,text="执行程序",command=mess).pack(padx=20,pady=20)
win.mainloop()

1.4、askquestion(title,message,option)
示例:

复制代码
from tkinter import *
from tkinter.messagebox import askquestion
def mess():
    boo = askquestion("是否确认!","请确认是否要执行以下操作!")
    if boo == "yes":
        win2 = Toplevel()
        Label(win2,text="您已确认,程序已执行!").pack()
    else:
        win2 = Toplevel()
        Label(win2, text="您已取消了程序执行!").pack()
win = Tk()
win.title("询问会话框")
Button(win,text="执行程序",command=mess).pack(padx=20,pady=20)
win.mainloop()
复制代码

1.5、askokcancle(title,message,option)
示例:

复制代码
from tkinter import *
from tkinter.messagebox import askokcancel
def mess():
    boo = askokcancel("是否确认!","请确认是否要执行以下操作!")
    if boo == True:
        win2 = Toplevel()
        Label(win2,text="您已确认,程序已执行!").pack()
    else:
        win2 = Toplevel()
        Label(win2, text="您已取消了程序执行!").pack()
win = Tk()
win.title("询问会话框")
Button(win,text="执行程序",command=mess).pack(padx=20,pady=20)
win.mainloop()
复制代码

1.6、askyesno(title,message,option)
示例:

复制代码
from tkinter import *
from tkinter.messagebox import askyesno
def mess():
    boo = askyesno("是否确认!","请确认是否要执行以下操作!")
    if boo == True:
        win2 = Toplevel()
        Label(win2,text="您已确认,程序已执行!").pack()
    else:
        win2 = Toplevel()
        Label(win2, text="您已取消了程序执行!").pack()
win = Tk()
win.title("询问会话框")
Button(win,text="执行程序",command=mess).pack(padx=20,pady=20)
win.mainloop()
复制代码

1.7、askyesnocancle(title,message,option)
示例:

复制代码
from tkinter import *
from tkinter.messagebox import askyesnocancel
def mess():
    boo = askyesnocancel("是否确认!","请确认是否要执行以下操作!")
    if boo == True:
        win2 = Toplevel()
        Label(win2,text="您已确认,程序已执行!").pack()
    elif boo == False:
        win2 = Toplevel()
        Label(win2, text="您已取消了程序执行!").pack()
win = Tk()
win.title("询问会话框")
Button(win,text="执行程序",command=mess).pack(padx=20,pady=20)
win.mainloop()
复制代码

1.8、askretrycancle(title,message,option)
示例:

复制代码
from tkinter import *
from tkinter.messagebox import askretrycancel
def mess():
    boo = askretrycancel("重试提醒!","打开程序出现错误,请选择重试或取消!")
    if boo == True:
        mess()
    else:
        win.quit()
win = Tk()
win.title("重试会话框")
Button(win,text="执行程序",command=mess).pack(padx=20,pady=20)
win.mainloop()
复制代码

2、菜单组件:
2.1、Menu组件,tkinter中创建菜单是通过Menu组件来实现的,语法:menu1 = Menu(win,option)
要在窗口中添加菜单,仅添加菜单组件是不够的,还需要添加菜单项,并且为窗口配置菜单,添加菜单项可以通过add_command()方法实现,如:
menu1.add_command(label="开始",command=callback)
示例:

复制代码
from tkinter import *
def callback():
    pass
win = Tk()
win.title("菜单")
win.geometry("300x300")
menu1 = Menu(win,cursor="hand2")
menu1.add_command(label="开始",command=callback)
menu1.add_command(label="说明",command=callback)
menu1.add_command(label="退出",command=callback)
win.config(menu=menu1)
win.mainloop()
复制代码

2.2、制作二级下拉菜单:

Menu组件的常用方法及含义:
add_comand(option):添加一个命令菜单项
add_cascade(option):添加一个父菜单
add_checkbutton(option):添加一个菜单项,该菜单项为多选按钮
add_radiobutton(option):添加一个菜单项,该菜单项为单选按钮
add_separator(option):添加一条分隔线
delete(index1,index2):删除index1-index2(含)的所有菜单项
entrycget(index,option):获得指定菜单项的某选项的值,index指定菜单项的索引值
entryconfig(index,option):设置指定菜单项的某选项的值,index指定菜单项的索引值
index(index):返回index参数相对应的选项的序号
insert(index,itemType,option):插入指定类型的菜单项到index参数指定的位置
insert_cascade(index,option):在index参数指定的位置添加一个父菜单
insert_checkbutton(index,option):在index参数指定的位置添加一个复选框
insert_radiobutton(index,option):在index参数指定的位置添加一个单选按钮
insert_command(index,option):在index参数指定的位置添加一个子菜单
insert_separator(index,option):在index参数指定的位置添加一个分割线
invoke(index):调用index参数指定的菜单选项相关系的方法
post(x,y):在指定位置显示弹出菜单
type(index):获得index参数指定菜单项的类型,返回值为:command,cascade,checkbutton,radiobutton,separator
unpost():移除弹出菜单
yposition(index):返回index参数指定的菜单项的垂直偏移位

option参数的值及含义:
postcommand:其属性值为一个方法,表示当菜单被打开时调用该函数
tearoff:设置菜单能否从窗口中分离(默认True)
cursor:鼠标悬停Menu组件上时,鼠标的样式
tearoffcommand:当菜单被分离时执行的方法
background(bg):设置背景颜色
selectcolor:当菜单项被选中为单选按钮或多选按钮时,选中标志的颜色
activebackground:当Menu组件处理active状态(通过state设置)的背景颜色
activeborderwidth:当Menu组件处理active状态(通过state设置)的边框宽度
activeforeground:当Menu组件处理active状态(通过state设置)的前景颜色
borderwidth(bd):指定边框宽度
disabledforeground:当Menu组件处理disabled状态(通过state设置)的前景颜色
font:指定Menue组件中的文字样式
foreground(fg):指定Menu组件的前景颜色
relief:指定边框样式
title:被分离的菜单的标题,默认标题为父菜单的名字
示例-为城市列表添加弹出式菜单:

复制代码
from tkinter import *
def pop1():
    menu2_2.post(win.winfo_x()+60,win.winfo_y()+120)
win = Tk()
menu1 = Menu(win)
menu2_1 = Menu(menu1,tearoff=False)
menu1.add_cascade(label="城市",menu=menu2_1)
menu2_1.add_command(label="合肥")
menu2_1.add_command(label="上海")
menu2_1.add_command(label="北京")
menu2_1.add_command(label="天津")
menu2_1.add_command(label="重庆")
menu1.add_command(label="修改",command=pop1)
menu2_2 = Menu(menu1,tearoff=False)
menu2_2.add_command(label="添加城市")
menu2_2.add_command(label="修改城市")
menu1.add_command(label="退出",command=win.quit)
win.config(menu=menu1)
win.mainloop()
复制代码

示例-设置窗口的文字样式及窗口大小:

复制代码
from tkinter import *
from tkinter.ttk import *
def max_win(event):
    win.geometry("600x400")
def normal_win(event):
    win.geometry("300x200")
def txt():
    global val
    global font_size
    global top
    top = Toplevel(win)
    val = StringVar()
    val.set("宋体")
    font_family = ("宋体","黑体","方正舒体","楷体","隶书","方正姚体")
    family = Combobox(top,textvariable=val,values=font_family)
    family.grid(row=0,column=0)
    font_size = Spinbox(top,from_=12,to=30,increment=2,width=10)
    font_size.grid(row=0,column=1)
    btn1 = Button(top,text="确定",command=font_set)
    btn1.grid(row=1,column=1)
def font_set():
    font1 = (val.get(),font_size.get())
    label.config(font=font1)
win = Tk()
win.geometry("300x200")
menu1 = Menu(win)
menu2_1 = Menu(menu1)
menu1.add_cascade(label="窗体",menu=menu2_1)
menu2_1.add_command(label="最大化",accelerator="Ctrl+Up",command=lambda :max_win(""))
menu2_1.add_command(label="恢复窗口",accelerator="Ctrl+Down",command=lambda :normal_win(""))
menu2_1.add_command(label="最小化",command=win.iconify)
menu2_1.add_separator()
menu2_1.add_command(label="关闭",command=win.quit)
menu2_2 = Menu(menu1,tearoff=0)
menu1.add_cascade(label="自定义",menu=menu2_2)
menu2_2.add_command(label="文字设置",command=txt)
win.config(menu=menu1)

label = Label(win,text="这是一个窗口")
label.grid(row=0,column=0)
win.bind_all("<Control-Up>",max_win)
win.bind_all("<Control-Down>",normal_win)
win.mainloop()
复制代码

2.3、制作工具栏:

复制代码
示例-实现猜成语游戏:
num = 0  # 当前游戏多少关
# 通过数组存储成语和成语的含义
idiom = ["别出心裁", "白云苍狗", "暴虎冯河", "鞭长莫及", "并行不悖", "安土重迁", "不耻下问", "不胫而走", "安步当车", "爱莫能助", "白驹过隙"]
idiom_means = ["独出巧思,不同流俗", "比喻世事变幻无常", "比喻有勇无谋,鲁莽冒险", "本意为马鞭虽长,但打不到马肚子上,,比喻虽有力,力量也打不到",
               "彼此同时进行,不相妨碍", "留恋故土,不肯轻易迁移", "比喻谦虚好学,不介意向学识或地位不及自己的人请教", "消息传的很快", "从容的步行,就当乘车一般",
               "心里愿意帮助,但是力量做不到", "形容时间过得很快,像白马在细小的缝隙前一闪而过", ]


# 判断输入成语是否正确
def panduan():
    global num
    a = entry.get()
    if a == idiom[num]:
        num += 1
    if (num >= len(idiom)):
        boo = askyesno("成功过关", "恭喜!已过完所有关卡,是否重新过关?")
        if boo == True:
            num = 0
            panduan()
        else:
            win.quit()
    entry.delete(0, END)
    means.config(text=idiom_means[num])
    level.config(text="" + str(num + 1) + "")

#手动切换至下一关
def next1(event):
    global num
    num += 1
    panduan()

# 重新开始,关卡重置为0
def restart(event):
    global num
    num = 0
    panduan()
# 显示游戏规则
def show1():
    showinfo("游戏规则","根据成语的含义猜成语,正确则自动跳转至下一关")
# 提示当前成语的第一个字
def tip():
    str=idiom[num][0]
    entry.delete(0,END)
    entry.insert(0,str)

from tkinter import *
from tkinter.messagebox import *
win = Tk()
win.geometry("250x200")
win.title("成语猜猜猜")
# 工具栏部分
menu1 = Menu(win)  # 创建顶级菜单
menu2_1 = Menu(menu1)  # 创建第二级菜单
menu1.add_cascade(label="游戏", menu=menu2_1)        # 将第二级菜单添加到顶级菜单并设置显示的内容
menu2_1.add_command(label="下一关", command=lambda:next1(""), accelerator="Ctrl+N")
menu2_1.add_command(label="重新开始", command=lambda :restart(""), accelerator="Ctrl+R")
menu2_1.add_separator()  # 添加分割线
menu2_1.add_command(label="退出", command=win.quit)  # 退出游戏,关闭窗口
menu2_2 = Menu(menu1)                          # 创建第二个二级菜单
menu1.add_cascade(label="帮助", menu=menu2_2)  # 将第二个二级级菜单添加到顶级菜单并设置显示的内容
menu2_2.add_command(label="游戏规则",command=show1)  # 添加二级菜单的子菜单
menu2_2.add_command(label="提示",command=tip)  # 添加二级菜单的子菜单
win.config(menu=menu1)

# 窗口内容
level = Label(win, font=14, text="第 1 关")              # 当前第几关
level.grid(row=0, column=0, columnspan=4, sticky=E)      # 显示成语的含义
means = Label(win, text=idiom_means[0], font=14, width=30, bg="#D8F3F0", height=3, wraplength="200")
means.grid(row=1, column=0, pady=10, columnspan=4)
entry = Entry(win, font=14)                              # 输入成语
entry.grid(row=2, column=1, sticky=E)
btn = Button(win, text="确定", command=panduan).grid(row=2, column=2)
win.bind_all("<Control-n>", next1)         # 绑定键盘事件
win.bind_all("<Control-r>", restart)       # 绑定键盘事件
win.mainloop()
复制代码

2.4、树形菜单,Treeview组件是ttk模块的组件,可以在窗口中添加树形菜单或表格,并且可以对表格和或菜单中的内容进行增删改查。
Treeview组件树状结构和表格与一体,用户可以使用该组件设计表格或树形菜单,并且设置树形菜单时,可以折叠或展开子菜单,语法如下:
tree = Treeview(win,option)
Treeview组件的参数及含义:
columns:其值为列表,列表的每一个元素代表一个列表标识符的名称,列表的长度为列的长度
displaycolumns:设置列表是否显示以衣显示顺序,也可以使用"#all"表示全部显示
height:表格的高度(表格中可以显示几行数据)
padding:标题栏内容距离组件边缘的间距
selectmode:字义选择行的方式,extended可以通过Ctrl+鼠标选择多行(默认值);browse只能选择一行;none表示不能改变选择
show:表示选择哪些列,其值有:tree headings(显示所有列),tree(显示第一列(图标栏));headings(显示除第一列以外的其它列)
示例-在表格中统计游戏中各角色的类型以及操作难易度:

复制代码
from tkinter import *
from tkinter.ttk import *
win = Tk()
tree = Treeview(win,columns=("hero","type","operate"),show="headings",displaycolumns=(0,1,2))
tree.heading("hero",text="英雄",anchor="center")
tree.heading("type",text="类型",anchor="center")
tree.heading("operate",text="操作难易程度",anchor="center")
tree.insert("",END,values=("孙尚香","射手","5"))
tree.insert("",END,values=("孙策","战士","3"))
tree.insert("",END,values=("小乔","辅助","3"))
tree.insert("",END,values=("大乔","法师","4"))
tree.pack()
win.mainloop()
复制代码

添加树形菜单后,需要通过insert()方法添加菜单的子项目item。语法如下:
tree.insert(父对象,插入位置,ID,option)
分别是:
父菜单的ID;插入位置,程序员为菜单设置的ID,若省略,则由Treeview自动分配;option是可选参数,其参数及含义如下:
text:属性菜单中子项目显示的名称
image:子项目前面的图标
values:子项目一行的值,未赋值的列是空列,超过列的长度会被截断
open:子菜单展开或关闭
tags:与item关联的标记
示例-树形显示近一周的天气状况:

复制代码
from tkinter import *
from tkinter.ttk import *

win = Tk()
tree = Treeview(win, columns=("date", "temperature"))
tree.heading("#0", text="天气")  # 设置图标栏的标题
tree.heading("date", text="日期")
tree.heading("temperature", text="气温")
rain = PhotoImage(file="D:\\cnblogs\\python\\pic\\rainheardly.png")  # 定义图标
storm = PhotoImage(file="D:\\cnblogs\\python\\pic\\storm.png")
sunny = PhotoImage(file="D:\\cnblogs\\python\\pic\\sunny.png")
tree.insert("", END, values=("4月1日", "-3~5"), image=rain, text=" 中到暴雨")  # 添加子项目
tree.insert("", END, values=("4月2日", "-3~7"), image=sunny, text="")
tree.insert("", END, values=("4月3日", "0~8"), image=storm, text=" 雷阵雨")
tree.insert("", END, values=("4月4日", "1~04"), image=sunny, text="")
tree.insert("", END, values=("4月5日", "2~04"), image=sunny, text="")
tree.insert("", END, values=("4月6日", "2~05"), image=sunny, text="")
tree.insert("", END, values=("4月7日", "2~04"), image=rain, text="")
tree.pack()
win.mainloop()
复制代码

为树形菜单添加子菜单。
使用Treeview组件添加子菜单时,需要通过ID绑定父元素,这个ID可以通过程序员手动分配,如果程序员忽略了ID,则由Treeview组件自动分配,如下:
tree.insert("",0,"wei",text="魏")
shu = tree.insert("",1,text="蜀")
wu = tree.insert("",2,text="吴")
三行的ID分别为:wei,shu,wu
示例-在树形菜单中显示三国的开国皇帝:

复制代码
from tkinter import *
from tkinter.ttk import *

win = Tk()
tree = Treeview(win)
tree.heading("#0",text="开国皇帝")
tree.insert("",0,"wei",text="")
shu = tree.insert("",1,text="")
wu = tree.insert("",2,text="")
tree.insert("wei",0,text="曹丕")
tree.insert(shu,0,text="刘备")
tree.insert(wu,0,text="孙权")
tree.pack()
win.mainloop()
复制代码

菜单项的获取与编辑。Treeview组件提供了一些虚拟事件和方法,主要如下:
Treeview组件虚拟事件及含义:
TreeviewSelect:当选项发生变化时,触发某事件
TreeviewOpen:当菜单项items的open=True时,触发某事件
TreeviewClose:当菜单项items的open=False时,触发某事件
Treeview组件常用方法及含义:
bbox(item,column=None):返回一个item的范围,如果column指定了列,则返回元素的范围,如果item不可使,则返回空值
get_children(item=None):返回item所有items列表,如果item没有指定,则返回根目录的item
set_children(item,*newchildren):设置item的新的子itemss,这里的设置指的是全部替换
column(column,option=None,**kw):设置或返回各列的属性。column是标识符,option若不设置,则返回所有属性的字典
delete(*item):删除item及子item
detach(*item):取消item和子item的链接,可以在另一个点重新输入,但不会显示。根item的链接无法取消
exists(item):判断item是否在Treeview组件中,若在则返回True
focus(item=None):设置或返回获得焦点的item,若不指定item或无item获得焦点,则返回空值
heading(column,option=None,**kw):查询或修改指定的标题选项,column列为标识符,option若不设置,则返回所有属性的字典,若设置,则返回该属性的属性值
insert(parent,index,iid=none,**kw):创建新的item并返回新建item的标识符
item(item,option=None,**kw):查询或修改指定item的选项
selection():返回所有选中的items列表
selection_set(*item):设置项目为新的选择
selection_add(*item):从选择项中添加项
selection_remove(*item):从选择项中删除项
selection_toggle(*item):切换项目中每个项目的选择状态
set(item,column=None,value=None):指定item,如果不设定column和value,则返回它们的字典;若设置了column,则返对应的value,若value也设定了,则做相应的修改
示例-统计个个出行记录:

复制代码
from tkinter import *
from tkinter.ttk import *
def setdat(event):
    temp = monsel.get()
    if temp == 2:
        dat["value"] = tuple(range(1,29))
    elif temp == 4 or temp == 6 or temp == 9 or temp == 11:
        dat["value"] = tuple(range(1, 31))
    else:
        dat["value"] = tuple(range(1, 32))
def get1():
    if len(entry.get()) == 0:
        return False
    else:
        h = str(horsel.get()) if horsel.get()>=10 else "0"+str(horsel.get())
        m = str(minsel.get()) if minsel.get()>=10 else "0"+str(minsel.get())
        item1 = (str(mon.get())+""+str(dat.get())+"",h+":"+m,entry.get())
        if not tree.focus() == "":
            tree.insert("",tree.index(tree.focus()),values=item1)
            del1()
        else:
            tree.insert("",END,values=item1)
        reset1()

def del1():
    if tree.focus() == "":
        return False
    tree.delete(tree.focus())
def edt(event):
    temp = tree.set(tree.focus())
    d = temp["date"].split("")
    t = temp["time"].split(":")
    monsel.set(d[0])
    datsel.set(int(d[1].split("")[0]))
    horsel.set(t[0])
    minsel.set(t[1])
    entry.delete(0,END)
    entry.insert(INSERT,temp["depart"])
def reset1():
    monsel.set(1)
    datsel.set(1)
    horsel.set(0)
    minsel.set(0)
    entry.delete(0,END)

win = Tk()
frame = Frame()
frame.grid()
Label(frame,text="日期:").grid(row=0,column=0)
monsel = IntVar()   #绑定月份选项
monsel.set(1)
mon = Combobox(frame,values=tuple(range(1,13)),textvariable=monsel,width=5)
mon.grid(row=0,column=1)
mon.bind("<<ComboboxSelected>>",setdat) #当月份发生变化时对应日期也变化
Label(frame,text="_").grid(row=0,column=2)
datsel = IntVar()
datsel.set(1)
dat = Combobox(frame,values=tuple(range(1,32)),textvariable=datsel,width=5)
dat.grid(row=0,column=3)
Label(frame,text="时间:").grid(row=0,column=4,columnspan=2,sticky=S+E)
horsel = IntVar()
horsel.set(0)
hor = Spinbox(frame,from_=0,to=24,textvariable=horsel,width=5)
hor.grid(row=0,column=6)
Label(frame,text=":").grid(row=0,column=7)
minsel = IntVar()
minsel.set(0)
min = Spinbox(frame,from_=0,to=59,textvariable=minsel,width=5)
min.grid(row=0,column=8)
Label(frame,text="出发地:").grid(row=0,column=9)
entry = Entry(frame)
entry.grid(row=0,column=10)
Button(frame,text="确定",command=get1).grid(row=0,column=11)
Button(frame,text="删除",command=del1).grid(row=0,column=12)
tree = Treeview(win,columns=("date","time","depart"),show="headings")
tree.heading("date",text="日期")
tree.heading("time",text="时间")
tree.heading("depart",text="出发地")
tree.grid(row=1,column=0)
tree.bind("<<TreeviewSelect>>",edt)
win.mainloop()
复制代码

2.5、综合案例-眼力测试小游戏:

复制代码
i = 84
# 提示
def help():
    showwarning("提醒", "第4行")
# 暂停与重新开始游戏
def game():
    boo = askyesnocancel("暂停", "是否停止本游戏,点击是,重新开始游戏,点击否暂停游戏")
    if boo == True:
        i = 0
        label.config(text=i)
    elif boo==False:
        i=84
        label.config(text=i)

#每点击错误一次,得分就减1
def wrong():
    global i
    i -= 1
    label.config(text=i)
# 找到与众不同的汉字
def suc():
    top = Toplevel(win)
    Label(top, text="恭喜,找到了\n,得分为"+str(i), fg="red").grid(row=0, column=0, padx=10, pady=10)

from tkinter import *
from tkinter.messagebox import *
win = Tk()
win.title("为游戏窗口添加菜单")
menu1 = Menu(win)  # 创建顶级菜单
# 添加工具栏
menu1.add_command(label="游戏", command=game)
menu1.add_command(label="帮助", command=help)
menu1.add_command(label="退出", command=win.quit)
win.config(menu=menu1)  # 显示菜单

for c in range(6):
    for j in range(14):
        Button(win, text="", width=1,command=wrong).grid(row=c, column=j)
Button(win, text="", width=1, command=suc).grid(row=3, column=3)
label = Label(win, font=14, fg="red", text=84)
label.grid(row=8, column=0, columnspan=14)
win.mainloop()
复制代码

九、canvas绘图

canvas组件也是tkinter模块中的组件,主要用途就是绘制图形、文字、设计动画,甚至可以将其它的小部件放置在画布上。但在使用canvas组件之前,需要先定义canvas画布。定义canvas画布的语法如下:
canvas = Canvas(win,option)
其中win为canvas组件的父容器,option为canvas组件的相关参数。具体参数如下:
bd:设置边框宽度,默认为2像素
bg:设置背景颜色
confine:如果为true(默认值),则画布不能滚动到可滑动区域外
cursor:设置鼠标悬停canvas组件上时的形状
height:设置画布的高度
width:设置画布的宽度
relief:设置边框的样式
scrollregion:其值为元素tuple(w.n.e.s),分别定义左、上、右、下四个方向可滚动的最大区域
xscrollincrement:水平方向滚动时,请求滚动的数量值
yscrollincrement:垂直方向滚动时,请求滚动的数量值
xscrollcommand:绑定水平滚动条
yscrollcommand:绑定垂直滚动条
示例-在窗口中创建画布:

from tkinter import *
win = Tk()
win.title("创建canvas画布")
win.geometry("300x200")
canvas = Canvas(win,width=200,height=200,bg="#EFEFA3").pack()
win.mainloop()

2、绘制基本图形:
2.1、绘制线条:线条是canvas组件中比较常见的元素之一,canvas组件中的线条可以有多个顶点,在绘制线条时需要按顺序绘制各个顶点。canvas组件中绘制线条是通过create_line()方法实现的。其语法如下:
canvas.create_line(x1,y1,x2,y2...xn,yn,option)
其中x1,y1是线段的起点坐标,x2,y2是第二个顶点坐标……直线的终点。option为线条的可选参数,具体参数及含义如下:
arrow:是否添加箭头,默认为无箭头,另外还可以设置其值为FIRST(起始端右箭头)、LAST(末端右箭头)、BOTH(两端都有箭头)
arrowshap:设置箭头的形状,其值为元素d1、d2、d3,分别表示三角形箭头的底、斜边和高的距离
capstyle:线条终点的样式,其属性值有butt(默认值)、projecting和round
dash:设置线条为虚线,以及虚线的形状,其值为元组x1、x2,表示x1像素的实绩和x2像素的空白交替出现
dashoffset:与dash相近,不过含义为x1像素的空白和x2像素的实绩交替显示
fill:设置线条颜色
joinstyle:设置线条焦点的颜色,其值有round(默认值)、bevel和miter
stipple:绘制位图线条
width:设置线条宽度
示例-使用线条绘制五角星:

from tkinter import *
win = Tk()
win.title("绘制五角星")
win.geometry("300x200")
canvas = Canvas(win,width=200,height=200)
canvas.create_line((14,65,66,65,83,19,99,64,148,64,111,96,126,143,83,113,44,142,58,97,14,65),fill="red")
canvas.pack()
win.mainloop()

2.2、绘制矩形:绘制矩形可以使用create_rectangle()方法,其语法如下:
create_rectangle(x1,y1,x2,y2,option)
其中x1,y1为矩形的左上角坐标;x2,y2为矩形的右下角坐标,当x2-x1=y2-x1时,所绘制的图形为正方形;而option为矩形的可选参数,其中dash,dashoffset,stipple,width等参数的含义和线条的基本类似,另外可以通过outline属性设置矩形的轮廓颜色。
示例-在画布中绘制一个正文形并通过方向键移动:

复制代码
def up1(event):
    #move()方法实现rect向上移动2个单位
    canvas.move(rect, 0, -2)
def down1(event):
    # move()方法实现rect向下移动2个单位
    canvas.move(rect, 0, 2)
def left1(event):
    # move()方法实现rect向左移动2个单位
    canvas.move(rect, -2, 0)
def right1(event):
    # move()方法实现rect向右移动2个单位
    canvas.move(rect, 2, 0)
from tkinter import *
win = Tk()
win.title("键盘控制矩形移动")
win.geometry("300x200")
canvas = Canvas(win,width=200,height=200,relief="solid")
rect = canvas.create_rectangle(10,10,50,50,fill="#C8F7F2")
canvas.pack()
win.bind("<Up>",up1)
win.bind("<Down>",down1)
win.bind("<Left>",left1)
win.bind("<Right>",right1)
win.mainloop()
复制代码

move()方法中三个参数的含义依次为:平移的对象、水平移动距离、垂直移动距离

2.3、绘制椭圆:绘制椭圆和圆形使用的方法相同,用create_oval()方法,其语法如下:
create_oval(x1,y1,x2,y2,option)
其中x1,y1为椭圆的左上角坐标;x2,y2为椭圆的右下角坐标。option参数及其含义可以参考线段的。
示例-绘制人脸简笔画:

复制代码
from tkinter import *
win = Tk()
win.title("绘制人脸")
win.geometry("300x200")
canvas = Canvas(win,width=200,height=200,relief="solid")
cir1 = canvas.create_oval(34,68,143,127,fill="#C8F7F2")
cir2 = canvas.create_oval(59,83,71,99,fil="#E6F1B7")
cir2_1 = canvas.create_oval(61,86,71,94,fill="#000000")
cir3 = canvas.create_oval(101,83,113,99,fil="#E6F1B7")
cir3_1 = canvas.create_oval(100,86,109,94,fill="#000000")
mouth = canvas.create_oval(78,110,92,120,fill="red")
canvas.pack()
win.mainloop()
复制代码

2.4、绘制圆弧与扇形:
绘制圆弧与扇形都使用create_arc()方法,只是使用的参数不同:
2.4.1、绘制圆弧:绘制圆弧除了需要指定圆弧的起始坐标与终点坐标,还需要指定圆弧的角度,其语法如下:
canvas.create_arc(x1,y1,x2,y2,extent=-180,start=30,style=ARC,option)
其中x1,y1为圆弧的起点坐标;x2,y2为圆弧的终点坐标;extent表示圆弧的角度,默认为90度;start为弧形的起始弧度,默认值为0;style表示绘制的类型,其属性值有3个,分别是ARC、CHORD、PIESLICE;option可以参考线段的。
示例-style属性值的演示:

复制代码
from tkinter import *
win = Tk()
win.title("绘制圆弧")
win.geometry("460x200")
canvas = Canvas(win,width=500,height=400,relief="solid")
canvas.create_arc(20,40,150,150,extent=120,outline="red",start=30,width=2,style=ARC)
canvas.create_arc(170,40,300,150,extent=120,outline="red",start=30,width=2,style=CHORD)
canvas.create_arc(320,40,450,150,extent=120,outline="red",start=30,width=2,style=PIESLICE)
canvas.pack()
win.mainloop()
复制代码

2.4.2、绘制扇形:绘制扇形同样使用create_arc()方法,该方法中不仅需要指定扇形的起始坐标和终点坐标,还需要指定style=PIESLICE,以及设定扇形的角度和超始角度,其语法如下:
canvas.create_arc(x1,y1,x2,y2,extent=-180,start=30,style=ARC,style=PIESLICE)
示例-综合使用canvas组件中的方法:

复制代码
from tkinter import *
win = Tk()
win.title("绘制西瓜状雪糕")
win.geometry("300x200")
canvas = Canvas(win,width=400,height=300,relief="solid")
canvas.create_line(95,124,95,194,fill="#E9D39D",width=12,capstyle=ROUND) #雪糕把手
canvas.create_arc(5,-70,185,162,extent=-40,outline="#32e143",fill="#32E143",start=-70,width=2,style=PIESLICE) #西瓜的皮
canvas.create_arc(8,-67,181,155,extent=-40,outline="#E92742",fill="#E92742",start=-70,width=2,style=PIESLICE) #西瓜的瓤
#西瓜的籽:
canvas.create_arc(92,74,97,79,extent=159,fill="#000",width=2,style=ARC)
canvas.create_arc(97,94,102,99,extent=180,start=90,fill="#000",width=2,style=ARC)
canvas.create_arc(110,124,113,127,extent=359,fill="#000",width=2,style=ARC)
canvas.create_arc(90,134,93,137,extent=359,fill="#000",width=2,style=ARC)
canvas.pack()
win.mainloop()
复制代码

2.5、绘制多边形:绘制多边形同绘制线条一样,需要按顺序(顺时针或逆时针方向都可以)依次描绘多边形的各个顶点,而绘制多边形需要使用create_polygon()方法,其语法如下:
canvas.create_polygon(x1,y1,x2,y2,xn,yn,option)
其中(x1,y1),(x2,y2)以及xn,yn为顺时针方向或逆时针方向依次描绘的多边形的顶点;option可以参考线条的。
示例-七巧板拼成的松鼠:

复制代码
from tkinter import *
win = Tk()
win.title("绘制松鼠")
win.geometry("240x260")
canvas = Canvas(win,width=250,height=250,relief="solid")
canvas.create_polygon(27,8,27,62,54,34,fill="#fbfe0d")  #左耳
canvas.create_polygon(54,34,81,8,81,63,fill="red")  #右耳
canvas.create_polygon(81,63,54,35,25,61,53,90,fill="#0001fc")  #
canvas.create_polygon(81,63,81,176,138,121,fill="#32ccfe")  #身体
canvas.create_polygon(81,97,43,135,81,174,fill="#fdcbfe")  #上半身
canvas.create_polygon(139,119,60,198,140,198,fill="#02cd02")  #下半身
canvas.create_polygon(140,198,167,170,223,170,196,198,fill="#9b01ff")  #尾巴
canvas.pack()
win.mainloop()
复制代码

2.6、绘制文字:绘制文字需要使用create_text()方法,其语法如下:
create_text(x,y,text=str,option)
其中x,y为字符串的中心位置;text为输出的字符串;option为文字的相关属性。如font、fill以及justify
示例-绘制随机颜色的文字:

复制代码
from tkinter import *
import random
fill_color = ["#B0E3DD","#E19644","#6689E1","#E16678","#66E1CA"]
font_family = ["方正舒体","方正姚体","华文琥珀","宋体","华文行楷","楷体","华文新魏","隶书"]
def draw():
    canvas.delete("all")    #清空画布
    color = fill_color[random.randint(0,4)]
    family = font_family[random.randint(0,7)]
    canvas.create_text(160,60,text=str,font=(family,20),fill=color)
win = Tk()
win.title("绘制文字")
win.geometry("330x200")
canvas = Canvas(win,width=300,height=160,relief="solid")
str = "人因梦想而伟大"
canvas.pack()
Button(win,text="绘制",command=draw).pack()
win.mainloop()
复制代码

2.7、绘制图像:绘制图像使用的方法是create_image(),具体语法如下:
canvas.create_image(x,y,image=house1,option)
其中x,y中为图像左上角顶点坐标;image为添加的图像;option参数有anchor
示例-用鼠标手动小鸟帮其回家:

复制代码
from tkinter import *
from tkinter.messagebox import *
#拖动鼠标,移动小鸟
def draw(event):
    canvas.coords(bird,event.x,event.y)
#判断小鸟是否回家
def panduan(event):
    canvas.coords(bird,event.x,event.y)
    x1 = abs(event.x-340)
    y1 = abs(event.y-70)
    if x1<70 and y1<75:
        showinfo("小鸟回家","谢谢你成功帮小鸟回家")
win = Tk()
win.title("帮小鸟回家")
win.geometry("400x320")
canvas = Canvas(win,width=400,height=320,relief="solid",bg="#E7D2BB")
bird1 = PhotoImage(file="bird.png")
house1 = PhotoImage(file="house.png")
house = canvas.create_image(340,70,image=house1)
bird = canvas.create_image(150,250,image=bird1)
canvas.grid(row=0,column=0,columnspan=2)
canvas.bind("<B1-Motion>",draw)
canvas.bind("<ButtonRelease-1>",panduan)
win.mainloop()
复制代码

2.8、拖动鼠标绘制图形:
canvas组件中并不能直接通过鼠标绘制线条,但是可以通过为canvas组件绑定鼠标事件。移动鼠标时,在鼠标的坐标位置绘制圆形,然后将一系列圆形连在一起形成线条。
示例-用鼠标写字:

复制代码
from tkinter import *
def draw(event):
    global text1
    text1 = canvas.create_oval(event.x,event.y,event.x+10,event.y+10,fill="green",outline="")
def delete1():
    canvas.delete("all")
    can()
win = Tk()
win.title("书法秀")
win.geometry("420x420")
canvas = Canvas(win,width=400,height=400,relief="solid",bg="#F1E9D0")
def can():
    rect = canvas.create_rectangle(4,4,400,385,outline="red",width=2)
    line1 = canvas.create_line(2,198,400,198,dash=(2,2),fill="red")
    line2 = canvas.create_line(198,2,198,400,dash=(2,2),fill="red")
    line3 = canvas.create_line(0,0,400,400,dash=(2,2),fill="red")
    line4 = canvas.create_line(0,400,400,0,dash=(2,2),fill="red")
    canvas.pack()
    canvas.bind("<B1-Motion>",draw)
Button(win,text="清屏",command=delete1).pack(side="bottom")
can()
win.mainloop()
复制代码

2.9、canvas组件设计动画:
canvas组件不仅可以绘制基本图形和图像,还可以设计动画,而设计动画主要通过移动或改变canvas画布组件中元素坐标来实现。移动和改变坐标主要通过两个方法,分别是move()和coords(),具体方法如下:
move(ID,x,y):表示ID(canvas中需要移动的形状的编号)水平方向向右移动x单位长度,垂直向下移动y单位长度。
coords(shape,x1,y1,x2,y2):相当于重新设置所绘制图形的坐标。shape为所修改开形状的名称。
除此之外,每当元素的位置改变,需要强制刷新窗口中的内容,强制刷新窗口的方法是update()。
示例-小猫钓鱼游戏:

复制代码
from tkinter import *
from tkinter.messagebox import *
import time
x1 = 350    #鱼的初始水平坐标
step = 2
op = 1  #控制鱼向左移动或向左移动
bar = 1 #当bar=0时鱼不再移动
def move1():
    global bar,x1,fish,op
    bar = 1
    if x1 >= 350:
        op = -1
        canvas.delete(fish)
        fish = canvas.create_image(x1,50,image=fish1)
    if x1 <= 0:
        op = 1
        canvas.delete(fish)
        fish = canvas.create_image(x1, 50, image=fish2)
    x1 = x1+op*step
    canvas.coords(fish,(x1,50))
def catch_fish():
    canvas.coords(cat,(150,50))
    global bar
    bar = 0
    if abs(x1-50)<=160 and abs(x1-50)>=40:
        showinfo("成功提示","恭喜你,成功钓到一条鱼")
    else:
        showinfo("失败提示","遗憾哦,钓鱼失败")
def move_fish():
    while bar:
        move1()
        time.sleep(0.1)
        win.update()
win = Tk()
win.title("小猫钓鱼")
win.geometry("400x400")
canvas = Canvas(win,width=400,height=320,relief="solid",bg="#E7D2BB")
cat1 = PhotoImage(file="cat.png")
fish1 = PhotoImage(file="fish.png")
fish2 = PhotoImage(file="fish1.png")
fish = canvas.create_image(350,50,image=fish1)
cat = canvas.create_image(150,250,image=cat1)
canvas.grid(row=0,column=0,columnspan=2)
Button(win,text="开始",command=move_fish).grid(row=1,column=0)
Button(win,text="钓鱼",command=catch_fish).grid(row=1,column=1)
win.mainloop()
复制代码

示例-碰壁的小球:

复制代码
from tkinter import *
from PIL import Image,ImageTk
import random,math
wid = 500
hig = 340
class ball():
    def __init__(self):
        btn.config(state="disabled")
        self.x1 = random.randint(50,90)
        self.y1 = random.randint(50,90)
        self.img1 = Image.open("balll1.png")
        self.dig = 0
        self.img = ImageTk.PhotoImage(self.img1.rotate(self.dig))
        self.speed_x = 5
        self.speed_y = 5
        self.balls = canvas.create_image(self.x1,self.y1,image=self.img)
        self.move()
    def move(self):
        canvas.delete(self.balls)
        self.img = ImageTk.PhotoImage(self.img1.rotate(self.dig))
        if self.dig >= 360:
            self.dig = 0
        if self.x1 < 60:
            self.speed_x = math.fabs(self.speed_x)
        if self.x1 + 65 > wid:
            self.speed_x = -self.speed_x
        if self.y1 < 60:
            self.speed_y = math.fabs(self.speed_y)
        if self.y1 + 65 > hig:
            self.speed_y = -self.speed_y
        self.x1 += self.speed_x
        self.y1 += self.speed_y
        self.balls = canvas.create_image(self.x1,self.y1,image=self.img)
        win.after(50,self.move)
win = Tk()
win.title("碰壁的小球")
win.geometry("500x380")
canvas = Canvas(win)
canvas.place(x=0,y=0,width=wid,height=hig)
bg = ImageTk.PhotoImage(file="bgball.png")
canvas.create_image(wid,hig,image=bg,anchor="se")
btn = Button(win,text="开始",command=ball)
btn.place(x=200,y=hig+10,width=60,height=30)
win.mainloop()
复制代码

 

十、鼠标键盘事件处理
1、鼠标事件
鼠标相关事件及其含义:
<Button-1>   单击鼠标左键
<Button-2>   单击鼠标中间键
<Button-3>    单击鼠标右键
<Button-4>   向上滚动滑轮
<Button-5>   向下滚动滑轮
<B1-Motion>    按下鼠标左键并拖动鼠标
<B2-Motion>   按下鼠标中键并拖动鼠标
<B3-Motion>    按下鼠标右键并拖动鼠标
<ButtonRelease-1>   释放鼠标左键
<ButtonRelease-2>   释放鼠标中键
<ButtonRelease-3>   释放鼠标右键
<Double-Button-1>   双击鼠标左键
<Double-Button-2>   双击鼠标中键
<Double-Button-3>   双击鼠标右键
<Enter>    鼠标进入控件
<Leave>   鼠标移出控件
示例-鼠标移入移出控制文字显示:

复制代码
from tkinter import *
def show1(event):
    label.config(text="我是LABEL组件")
def hidden1(event):
    label.config(text="")
win = Tk()
label = Label(win,bg="#C5E1EF",width=20,height=3)
label.pack(pady=20,padx=20)
label.bind("<Enter>",show1)
label.bind("<Leave>",hidden1)
win.mainloop()
复制代码

示例-鼠标相关事件的使用:

复制代码
from tkinter import *
def click_mouse_left_key(event):
    label1.config(text="你点击了鼠标左键",fg="red")
def click_mouse_middle_key(event):
    label2.config(text="你点击了鼠标中间键",fg="red")
def click_mouse_right_key(event):
    label3.config(text="你点击了鼠标右键",fg="red")
def move_mouse_right_key(event):
    label4.config(text="你拖动了鼠标左键",fg="red")
def release_mouse_right_key(event):
    label5.config(text="你释放了鼠标左键",fg="red")
def double_mouse_right_key(event):
    label6.config(text="你双击了鼠标左键",fg="red")
win = Tk()
label1 = Label(win,bg="#C5E1EF",width=20,height=3,text="点击鼠标左键")
label1.grid(row=0,column=0,pady=20,padx=20)
label1.bind("<Button-1>",click_mouse_left_key)
label2 = Label(win,bg="#C5E1EF",width=20,height=3,text="点击鼠标中间键")
label2.grid(row=0,column=1,pady=20,padx=20)
label2.bind("<Button-2>",click_mouse_middle_key)
label3 = Label(win,bg="#C5E1EF",width=20,height=3,text="点击鼠标右键")
label3.grid(row=0,column=2,pady=20,padx=20)
label3.bind("<Button-3>",click_mouse_right_key)
label4 = Label(win,bg="#C5E1EF",width=20,height=3,text="按下鼠标左键并拖动")
label4.grid(row=1,column=0,pady=20,padx=20)
label4.bind("<B1-Motion>",move_mouse_right_key)
label5 = Label(win,bg="#C5E1EF",width=20,height=3,text="按下鼠标左键并释放")
label5.grid(row=1,column=1,pady=20,padx=20)
label5.bind("<ButtonRelease-1>",release_mouse_right_key)
label6 = Label(win,bg="#C5E1EF",width=20,height=3,text="双击鼠标左键")
label6.grid(row=1,column=2,pady=20,padx=20)
label6.bind("<Double-Button-1>",double_mouse_right_key)
win.mainloop()
复制代码

2、键盘事件
键盘事件的列表及其含义:
<Focusln>    键盘进入组件
<FocusOut>   键盘离开组件
<Key>      按下某键,键值会作为event对象参数被传递
<Shift-Up>    同时按住<Shift>和<Up>键
<Alt-Up>    同时按住<Alt>和<Up>键
<Control-Up>   同时按住<Control>和<Up>键
示例-输入文字时,统计多行文本框中的字数:

复制代码
from tkinter import *
def prt(event):
    le = len(text.get("0.0",END))
    label.config(text=str(le))
win = Tk()
text = Text(win,width=20,height=5)
text.pack()
label = Label(win)
label.pack()
text.bind("<Key>",prt)
win.mainloop()
复制代码

示例-贪吃蛇:

复制代码
from tkinter import *
w = 10          #蛇体由小正方形组成,w为正方形的边长
x1 = 0          #蛇头的初始位置
y1 = 10
num = 5         #初始状态的蛇由5个小正方形组成
step = 10       #蛇移动的单元距离
def xx(module):
    return int(module.winfo_geometry().split("+")[1])
def yy(module):
    return int(module.winfo_geometry().split("+")[2])
def up1(event):
    for index,ch in enumerate(snake):
        ind = len(snake)-index-1
        if ind == 0:
            snake[ind].place(x=xx(snake[ind]),y=yy(snake[ind])-step)
        else:
            snake[ind].place(x=xx(snake[ind-1]), y=yy(snake[ind-1]))
def down1(event):
    for index, ch in enumerate(snake):
        ind = len(snake) - index - 1
        if ind == 0:
            snake[ind].place(x=xx(snake[ind]), y=yy(snake[ind]) + step)
        else:
            snake[ind].place(x=xx(snake[ind - 1]), y=yy(snake[ind - 1]))
def right1(event):
    for index, ch in enumerate(snake):
        ind = len(snake) - index - 1
        if ind == 0:
            snake[ind].place(x=xx(snake[ind])+step, y=yy(snake[ind]))
        else:
            snake[ind].place(x=xx(snake[ind - 1]), y=yy(snake[ind - 1]))
def left1(event):
    for index, ch in enumerate(snake):
        ind = len(snake) - index - 1
        if ind == 0:
            snake[ind].place(x=xx(snake[ind])-step, y=yy(snake[ind]))
        else:
            snake[ind].place(x=xx(snake[ind - 1]), y=yy(snake[ind - 1]))
win = Tk()
snake = []
for i in range(num):
    item1 = Frame(width=10,height=10,bg="black")
    snake.append(item1)
    item1.place(x=x1,y=y1+i*w)
    snake[0].config(bg="red")
win.bind("<Up>",up1)
win.bind("<Down>",down1)
win.bind("<Left>",left1)
win.bind("<Right>",right1)
win.mainloop()
复制代码

3、绑定多个事件处理程序:
示例-绑定多个事件:

复制代码
from tkinter import *
def fg1():
    button.config(fg="red")
def bg1(event):
    button.config(bg="blue")
def font1(event):
    button.config(font=14)
win = Tk()
button = Button(win,text="这是个按钮",command=fg1)
button.bind("<Button-1>",bg1,add="+")
button.bind("<Button-1>",font1,add="+")
button.pack(pady=10)
win.mainloop()
复制代码

如果不用add="+"参数,最后一次绑定的事件会替代前面的所有事件。
可以用label.unbind("<Button-1>")取消绑定。
示例-方块只能在窗口内移动:

复制代码
from tkinter import *
step = 5
def up1(event):
    print(f"X:{xx(frame)},Y:{yy(frame)}")
    # 如果组件贴着窗口的上边缘,则取消绑定键盘事件
    if yy(frame) <= 0:
        win.unbind("<Up>")
    else:
        frame.place(x=xx(frame), y=yy(frame) - step)
def down1(event):
    print(f"X:{xx(frame)},Y:{yy(frame)}")
    #如果组件贴着窗口的下边缘,则取消绑定键盘事件
    if yy(frame) >= 160:
        win.unbind("<Down>")
    else:
        frame.place(x=xx(frame),y=yy(frame)+step)
def left1(event):
    print(f"X:{xx(frame)},Y:{yy(frame)}")
    if xx(frame) <= 0:
        win.unbind("<Left>")
    else:
        frame.place(x=xx(frame)-step, y=yy(frame))
def right1(event):
    print(f"X:{xx(frame)},Y:{yy(frame)}")
    if xx(frame) >= 260:
        win.unbind("<Right>")
    else:
        frame.place(x=xx(frame) + step, y=yy(frame))
def xx(moudle):
    return int(moudle.winfo_geometry().split("+")[1])
def yy(moudle):
    return int(moudle.winfo_geometry().split("+")[2])
win = Tk()
win.geometry("300x200")
win.resizable(0,0)
frame = Frame(width=40,height=40,bg="#E2ABE5")
frame.place(x=0,y=0)
win.bind("<Up>",up1)
win.bind("<Down>",down1)
win.bind("<Left>",left1)
win.bind("<Right>",right1)
win.mainloop()
复制代码

示例-查看颜色小游戏:

复制代码
from tkinter import *
import random

num = 1 #第多少关
inde = random.randint(0,99) #随机设置与众不同的方块(方块A)
#随机设置颜色
def col():
    arr = ["0","1","2","3","4","5","6","7","8","9","A","B","C","D","E","F"]
    #为保证颜色相近,color1+color2为多数方块的颜色;color1+color3为方块A的颜色
    color1 = ""
    color2 = ""
    color3 = ""
    for i in range(4):
        color1 += arr[random.randint(0,len(arr)-1)]
    for i in range(2):
        color2 += arr[random.randint(0,len(arr)-1)]
    for i in range(2):
        color3 += arr[random.randint(0,len(arr)-1)]
    colorArr = [] #将两种颜色保存到列表
    colorArr.append("#"+color1+color2)
    colorArr.append("#"+color1+color3)
    return colorArr
def panduan(event):
    global num
    global inde
    num += 1    #当前游戏关数
    level.config(text=f"第{num}关")
    sqareBox[inde].config(text="")  #清除上次的提示信息
    sqareBox[inde].unbind("<Button-1>") #取消上一次的事件绑定
    inde = random.randint(0,99) #重新重成方块A
    colorBox = col()
    #print(colorBox,inde+1) #可以打印出两个颜色代码和方块A所在的位置
    for i in sqareBox:
        i.config(bg=colorBox[0])
    sqareBox[inde].config(bg=colorBox[1])
    sqareBox[inde].bind("<Button-1>",panduan) #重新为方块A绑定单击事件
def tishi(event):
    sqareBox[inde].config(text="here")
win = Tk()
win.geometry("270x310")
win.resizable(0,0)
sqareBox = []   #将方块存储在列表中
colorBox = col()
for i in range(10): #
    for j in range(10): #
        label = Label(win,width=3,height=1,bg=colorBox[0],relief="groove")
        sqareBox.append(label)
        label.grid(row=i,column=j)
sqareBox[inde].config(bg=colorBox[1])
sqareBox[inde].bind("<Button-1>",panduan) #为颜色与众不同的方块添加事件
level = Label(win,text="第1关",font=14)
level.grid(row=11,column=0,columnspan=10,pady=10)
que = Button(win,text="提示一下下",font=14)
que.bind("<Button-1>",tishi)
que.grid(row=12,column=0,columnspan=10,pady=10)
win.mainloop()
复制代码

示例-一键着色:

复制代码
from tkinter import *
import random
# 随机生成颜色
def col():
    arr=["0","1","2","3","4","5","6","7","8","9","A","B","C","D","E","F"]
    color1="#"
    for i in range(6):
        color1+=arr[random.randint(0,15)]
    return color1
# 第一部分Label组件添加颜色
def go1():
    a=col()
    for i in box1:
        i.config(bg=a)
# 第二部分Label组件添加颜色
def go2(event):
    a=col()
    for i in box2:
        i.config(bg=a)
win=Tk()
win.geometry("330x200")
box1=[]
box2=[]
# 通过循环定义多个Label组件
for i in range(8):
    for j in range(2):
        label=Label(win,width=5,height=1,relief="groove")
        label.grid(row=j,column=i)
        if (i+j)%2==0:
            box1.append(label)
        else:
            box2.append(label)
btn=Button(win,text="一键着色",command=go1)
btn.grid(row=9,column=0,columnspan=8)
btn.bind("<Button-1>",go2,add="+")    #绑定第二个事件
win.mainloop()
复制代码

十一、综合案例-滚动大抽奖:

复制代码
from tkinter import *
import random
def comeon():
    global timer,temp
    i = random.randint(0,len(box)-1)
    temp = box[i]
    canvas.itemconfigure(re,text=temp)
    timer = win.after(100,comeon)
def callback(event):
    global timer,boo,re3,box,i,temp
    if not boo:
        comeon()
        boo = True
    else:
        timer = win.after_cancel(timer)
        box.remove(temp)
        boo = False
win = Tk()
win.geometry("380x270")
bg_img = PhotoImage(file="bg.png")
btn_img = PhotoImage(file="button.png")
canvas = Canvas(win,width=380,height=270)
canvas.pack()
temp = ""
timer = ""
boo = False
bg = canvas.create_image(190,135,image=bg_img)
btn = canvas.create_image(190,215,image=btn_img)
re = canvas.create_text(190,110,text="",font="宋体 16 bold")
canvas.tag_bind(btn,"<Button-1>",callback)
with open("all.txt","r",encoding="utf-8") as f:
    box = f.readlines()
win.mainloop()
复制代码

after()方法类型于定时器,可以实现每间隔一段时间调用一次方法;而要让动画停下来就必须先清除定时器,清除定时器的方法为after_cancel()

 十二、综合案例-音乐机器人

复制代码
from tkinter import *
from tkinter.messagebox import *
import winsound
import random
def count():
    global tim,countdown
    cav.delete(text)
    entry.destroy()
    cav.itemconfig(showTime,image=imgbox[tim + 1])
    tim -= 1
    countdown = win.after(1000,count) #定时哭器,第过1000毫秒就调用一次count
    if tim < 0:
        win.after_cancel(countdown)
        play()
def start(event):
    global tim,showTime
    tim = time1.get()
    if tim != "" and tim in ["0","1","2","3","4","5","6","7","8","9"]:
        tim = int(tim)
    else:
        showerror("错误提示","请输入0到9之间的数字!")
        return
    showTime = cav.create_image(958,228)
    if tim > 0 and tim < 10:
        count()
    elif tim == 0:
        cav.delete(text)
        entry.destroy()
        play()
def play():
    cav.itemconfigure(showTime,image=imgbox[-1])
    cav.tag_bind(showTime,"<Button-1>",quitwin)
    music = ["m1.wav","m2.wav","m3.wav","m4.wav","m5.wav","m6.wav",]
    one = "wav/"+random.choice(music)
    winsound.PlaySound(one,winsound.SND_ASYNC)
def quitwin(event):
    win.quit()
win =Tk()
win.geometry("1920x1080+0+0")
win.state("zoomed")
win.title("音乐机器人")
imgbox = [] #倒计时数字图片对象
for i in range(0,11):
    img = PhotoImage(file="image/bg"+str(i)+".png")
    imgbox.append(img)
quit1 = PhotoImage(file="image/quit.png")
imgbox.append(quit1)
pao = PhotoImage(file="image/pao.png")
#实现窗口布局:
cav = Canvas(win,width=1920,height=1080,bg="yellow")
cav.place(x=0,y=0,relwidth=1,relheight=1)
bg = cav.create_image(960,530,image=imgbox[0])
#用于倒计时显示:
text = cav.create_image(470,280,image=pao)
time1 = StringVar()
entry = Entry(win,textvariable=time1,font=("宋体",20,"normal"),relief="groove",bd=2)
entry.place(x=400,y=240,width=110,height=40)
#当变量发生变化时调用start()方法:
entry.bind("<Return>",start)
win.mainloop()
View Code
复制代码

十三、九宫格切图器

复制代码
import tkinter as tk
from PIL import Image,ImageTk
import tkinter.filedialog
import tkinter.messagebox
def select_button():
    global a,img
    fileType = [("jpg文件","*.jpg"),("png文件","*.png")]
    a = tk.filedialog.askopenfilename(title="选择图片",filetypes=fileType)
    img = Image.open(a)
    image_width = img.size[0]   #获取原图片的宽度
    image_height = img.size[1]  #获取原图片的高度
    if image_width > image_height:  #如果原图片的宽度大于高度,那么设置预览图的宽度为310,高度等比例缩小
        image_height = int(image_height * 310 / image_width)
        image_width = 310
    else:   #如果原图的高度大于宽度,那么设置预览图的高度为280,宽度等比例缩小
        image_width = int(image_width * 280 / image_height)
        image_height = 280
    out = img.resize((image_width,image_height))
    img = ImageTk.PhotoImage(out)
    label_image.config(image=img)
    label_image.place(x=120,y=80)
    txt.set(a)  #将图片路径显示在文本框中
def cut_image(image):
    width,height = image.size
    colWidth = int(width / 3) #一行三张
    colHeight = int(height / 3)
    image_grid = []
    for i in range(0,3):
        for j in range(0,3):
            row = (j*colWidth,i*colHeight,(j+1)*colWidth,(i+1)*colWidth)
            image_grid.append(row)
    image_list = [image.crop(row) for row in image_grid]
    return image_list
def save_images(image_list):
    index = 1
    for image in image_list:
        image.save(str(index)+".png","PNG")
        index += 1
def cut_button():
    file_path = txt.get()
    if file_path == "":
        tk.messagebox.showerror("错误提供","所选文件为空,请重新选择文件!")
    else:
        image = Image.open(file_path)
        image_list = cut_image(image)
        save_images(image_list)
        tk.messagebox.showinfo("切图成功","切图成功,请在程序所在目录查看")

main =tk.Tk()
main.configure(bg="#F2F1D7")
main.geometry("550x400")
main.title("九宫格切图器")
label1 = tk.Label(main,text="选择文件:",font=("bold",14),fg="#f00",bg="#F2F1D7")
label1.place(x=20,y=25)
txt = tk.StringVar()
txt_entry = tk.Entry(main,width=55,textvariable=txt,relief=tk.GROOVE)
txt_entry.place(x=120,y=20,width=220,height=30)
button1 = tk.Button(main,text="浏览",fg="#f00",bg="#E8FFE8",font=14,command=select_button,relief=tk.GROOVE)
button1.place(x=350,y=20,width=60,height=32)
button2 = tk.Button(main,text="我选好了",fg="#f00",bg="#DDF3FF",font=13,relief=tk.GROOVE,command=cut_button)
button2.place(x=430,y=20,width=90,height=32)
label_image = tk.Label(main,bg="#F2F1D7")
main.mainloop()
View Code
复制代码

十四、模拟斗地主发牌

复制代码
from tkinter import *
from tkinter.messagebox import *
import random
from PIL import Image,ImageTk,ImageSequence

class poker():
    def __init__(self):
        self.win = Tk()
        self.win.geometry("1000x500-200-200")
        self.win.title("斗地主")
        self.pkBox_num = [] #所有扑克编号
        self.pkBox = []
        self.moveCenter = True
        self.pb = []    #底牌
        self.pb_box = []    #玩家手里的底牌
        self.pb_num = []    #底牌编号
        self.pb1_num = []   #左边玩家牌的编号
        self.pb2_num = []  #右边玩家牌的编号
        self.pb3_num = []  #中间玩家牌的编号
        self.pbback = []    #发牌时显示牌背面
        self.temp = 0   #所发出去的牌的数量
        self.backbox = []   #将绘制的扑克背面存储在此列表中
        self.boo = 0    #判断谁是地主,0表示没人叫地主,1表示左侧玩家、2表示中间玩家、3表示右侧玩家
        self.imgback1 = Image.open("border/back.png")   #牌的背面
        self.imgback2 = self.imgback1.resize((70,120))  #设置扑克牌大小
        for i in range(1,55):
            self.img1 = Image.open("border/"+str(i)+".png")
            self.img2 = self.img1.resize((70,120))
            self.img = ImageTk.PhotoImage(self.img2)
            self.pkBox.append(self.img)
        self.imgback = ImageTk.PhotoImage(self.imgback2)    ##牌的背面图片对象
        self.p1 = PhotoImage(file="border/people1.png")
        self.p2 = PhotoImage(file="border/people2.png")
        self.p3 = PhotoImage(file="border/people3.png")
        self.p4 = PhotoImage(file="border/people4.png")
        self.canvas = Canvas(self.win,bg="#DDF3FF")
        self.canvas.place(x=0,y=0,width=1000,height=500)
        #左侧的叫地主按钮:
        self.call1 = Button(self.win,text="叫地主",command=lambda: self.dizhu(1),wraplength=20,bg="#efefda",relief="groove")
        #中间的叫地主按钮:
        self.call2 = Button(self.win, text="叫地主", command=lambda: self.dizhu(2),bg="#efefda",relief="groove")
        #右侧的叫地主按钮:
        self.call3 = Button(self.win, text="叫地主", command=lambda: self.dizhu(3), wraplength=20, bg="#efefda",relief="groove")
        self.dizhubox = [self.call1,self.call2,self.call3]
        self.btn1 = Button(self.win,text="发牌",command=self.fapai,relief="groove",bg="#E8E8FF")
        self.btn2 = Button(self.win, text="码牌", command=self.mapai, relief="groove", bg="#bdbdec")
        self.btn3 = Button(self.win, text="重新开始", command=self.reset, relief="groove", bg="#66cccc")
        self.btn1.place(x=360,y=180,width=80,height=30)
        self.btn2.place(x=500, y=180, width=80, height=30)
        self.btn3.place(x=400, y=230, width=150, height=30)
        self.reset()
        self.win.mainloop()
    def dizhu(self,who):
        if who == 1:
            self.canvas.itemconfig(self.left,image=self.p1)
        elif who == 2:
            self.canvas.itemconfig(self.center,image=self.p1)
            self.moveCenter = False
        elif who == 3:
            self.canvas.itemconfig(self.right,image=self.p2)
        self.boo = who
        for it in self.dizhubox:
            it.place_forget()
    def fapai(self):
        if self.boo == 0:
            showerror("错误", "请先确定地主")
            return False
        a = random.randint(0, len(self.pkBox) - 1)
        if (a not in self.pb2_num) and (a not in self.pb3_num) and (a not in self.pb1_num):
            if self.temp % 3 == 0:
                lt = self.canvas.create_image(150, 70 + self.temp // 3 * 20, image=self.pkBox[a])
                self.pb1_num.append(a)
                self.pb_box.append(lt)
            elif self.temp % 3 == 1:
                rt = self.canvas.create_image(335 + self.temp // 3 * 20, 400, image=self.pkBox[a])
                self.pb2_num.append(a)
                self.pb_box.append(rt)
            else:
                ct = self.canvas.create_image(770, 70 + self.temp // 3 * 20, image=self.pkBox[a])
                self.pb3_num.append(a)
                self.pb_box.append(ct)
            self.pb.remove(a)
            self.canvas.delete(self.backbox[-1])
            del self.backbox[-1]
        else:
            self.temp -= 1
        self.temp += 1
        if self.temp < len(self.pkBox) - 3:
            self.win.after(30, self.fapai)
        else:

            self.canvas.create_image(360, 100, image=self.pkBox[self.pb[0]])
            self.canvas.create_image(460, 100, image=self.pkBox[self.pb[1]])
            self.canvas.create_image(560, 100, image=self.pkBox[self.pb[2]])
            for i in self.backbox:
                self.canvas.delete(self.backbox[-1])
                del self.backbox[-1]

            self.canvas.delete(self.backbox[-1])
            del self.backbox[-1]
            print(len(self.backbox))
    def mapai(self):
        self.btn2.config(state="disabled")
        # self.canvas.delete("all")
        for it in self.pb_box:
            self.canvas.delete(it)
            self.pb_box.remove(it)
        if self.boo == 1:
            self.pb1_num.extend(self.pb)
        elif self.boo == 2:
            self.pb2_num.extend(self.pb)
        else:
            self.pb3_num.extend(self.pb)
        self.pb1_num.sort()
        self.pb2_num.sort()
        self.pb3_num.sort()
        for i in range(len(self.pb1_num)):
            self.canvas.create_image(150, 70 + i * 20, image=self.pkBox[self.pb1_num[i]])

        for i in range(len(self.pb3_num)):
            self.canvas.create_image(770, 70 + i * 20, image=self.pkBox[self.pb3_num[i]])
        if self.moveCenter:
            for i in range(len(self.pb2_num)):
                self.canvas.create_image(335 + i * 20, 400, image=self.pkBox[self.pb2_num[i]])
        else:
            for i in range(len(self.pb2_num)):
                self.canvas.create_image(310 + i * 20, 400, image=self.pkBox[self.pb2_num[i]])
    def reset(self):
        self.temp = 0
        self.canvas.delete("all")
        self.pb = []
        self.boo = 0
        self.pb1_num = []
        self.pb2_num = []
        self.pb3_num = []
        self.moveLeft = True
        self.left = self.canvas.create_image(70,80,image=self.p3)
        self.right = self.canvas.create_image(850, 80, image=self.p4)
        self.center = self.canvas.create_image(225, 400, image=self.p3)
        for it in self.dizhubox:
            it.config(state="normal")
        for i in range(54):
            self.back1 = self.canvas.create_image(160 + i * 11, 90, image=self.imgback)
            self.backbox.append(self.back1)
            self.pb.append(i)
        self.call1.place(x=275, y=160, width=30, height=100)  # 左侧的叫地主
        self.call2.place(x=415, y=290, width=120, height=30)  # 中间的叫地主
        self.call3.place(x=650, y=160, width=30, height=100)  # 右侧的叫地主
if __name__ == "__main__":
    poker()
View Code
复制代码

 

posted @   donfag  阅读(106)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
点击右上角即可分享
微信分享提示