以代码为例,教你如何利用tkinter快速开发一个简易可用的程序界面或者游戏
首先,什么是tkinter?Tkinter模块是Python的标准Tk GUI工具包的接口,是python自带的GUI库。其他更多的内容网上有大量资料,这里不再累述。先贴出简单项目图给你提提兴趣(这些东西在学完后并查阅网上其他资料后是能很简单的写出来的):
既然你选择了tkinter用于开发程序界面,想必是看中了tkinter简易且方便(不用安装)这一点,相比于pygame之类的库,确实显得简单许多且不用学习太多东西。下面开始讲解一些tkinter开发的技巧,主要分为三大块。
- tkinter运行基础
- 常用控件的介绍
- 事件的使用
tkinter运行基础:
tkinter基础主要就是让你能让tkinter运行起来,明白一个完整的界面需要的操作。
首先,我们通过调用Tk()函数即可创建一个tkinter的根(我们后续的开发就是在这个根上进行的,根窗口有且仅能有一个)。有了这个根后,我们可以通过创建一系列控件,将这个根作为参数传到对应的控件创建函数上,视为绑定。最后通过pack函数,我们能将控件布局在窗口的任意一个角落。而对于每个控件,我们可以通过bind函数将与之相关的事件和函数绑定在一起。这些在事件那里会学到。最后,通过mainloop()函数,我们能让他进行一种响应状态,但是要注意,tkinter不是多线程的,会出现阻塞的情况,要想解决,请查阅网上其他资料,简单的程序基本不会出现阻塞。下面是创建一个简单的窗体的代码:
import tkinter
root = tkinter.Tk()
root.title('title!')
root.geometry('400x300')#窗口大小
root.resizable(0,0)#设置窗口可否拉伸,flase为否
root.mainloop()
顺便介绍一些很可能用到的tkinter根窗口函数:
root.after(1000,funtion)
'''该函数为计时函数,表示经过1000s后执行funtion,通过该函数,利用递归我们可以做出一个每x秒执行一个定时函数'''
root.update()
'''很关键的函数,如果你发现你的界面变化好像跟不上函数,多半是更新晚了,tkinter是每到一个loop才更新界面的,所以在你需要及时回馈数据时,用上它吧'''
root.quit()
root.destroy()
'''一般这两个是配套使用的,效果很简单,就是关闭对应窗口'''
root.protocol("WM_DELETE_WINDOW",func1)
'''将系统事件与func1函数绑定,可以做到关不掉的程序'''
root.withdraw()#
root.deiconify()#
下面控件介绍时,为了节约空间,就略去了tkinter的创建代码,统一以上面的代码作为创建基础,并且控件的布局略去不谈,按默认的来就行(或自行上网查阅资料)。
常用控件介绍
-
button控件
#root创建过程略去 buttons = [] for button in ['开始游戏','速度设置','退出游戏']: buttons.append(tkinter.Button(root,text=button,height = 3,width = 20)) for i in range(3): buttons[i].pack(side='top',pady = '10px') root.mainloop()
是不是发现出现了一个400x300的界面,还有三个按钮整整齐齐地放在界面上了?
buttons的属性主要有:根(也就是要放在哪个东西上),text(文本),height(长),width(宽),command(触发函数,这里为了方便就没加上去了)。很多情况下,如果你是为了创建一组按钮,通过数组的方式可以更快捷而且整齐的创建,减少了排版的需求。
-
label
#root创建过程略去 labels = [] for textx in ['开始游戏','卡组设置','退出游戏']: labels.append(tkinter.Label(root, text = textx, # 标签的文字 bg='Purple', #背景色 fg='#00BFFF', # 字体色 font=('Arial', 11,'bold'), # 字体和字体大小 width=10, height=4, # 标签相对长宽,应该是以字体为准 cursor = 'hand2', #鼠标形状 compound = 'center' #字的位置 )) for label in labels: label.pack(side='top',pady = '5px') root.mainloop()
与button不一样的是,label没有command这一属性。主要用作标签作用。但是,事实上label也可以通过bind函数绑定事件,达到和button一样的效果,这是后话。这两控件其实都差不了太多,看开发情况做选择,一般都是直接用button更方便。
-
toplevel
这时候你可能会想了,很多时候你要多窗口开发,只有根窗口这一个窗口怎么够?当然,tkinter提供了一种toplevel组件助你一臂之力。
#root创建过程略去 test = tkinter.Toplevel() test.title('Duel !') test.geometry('400x300') test.resizable(0,0) root.mainloop()
你会发现此时有两个窗口。
-
menu
#root创建过程略去 menubar = tkinter.Menu(root) filemenu = tkinter.Menu(menubar,tearoff=0) menubar.add_cascade(label='File', menu=filemenu) filemenu.add_command(label='New')#正常使用时,这里肯定会加入参数command filemenu.add_command(label='Open') filemenu.add_command(label='Save') root.config(menu=menubar) root.mainloop()
并不是所有程序都需要菜单栏的,但是主要创建方法还是列出来了。对于加入菜单中的功 能,add_command()还有一个参数是command可以绑定与之对应的函数来实现按键功能。
-
canvas
这个canvas是我要讲的重中之重,其包含的一系列功能实在太好用了,特别是在开发简单的小游戏上,canvas本身是一个画布,这意味这你可以在上面“画”一切你想画的东西。这主要是由于tkinter所有控件本身都可以布局在其他控件上,这就给我们提供了很大的便利。首先是创建。
#root创建过程略去 canvas = tkinter.Canvas(root, width = 200, # 指定Canvas组件的宽度 height = 150, # 指定Canvas组件的高度 bg = 'blue') # 指定Canvas组件的背景色 canvas.place(x=0, y=0, relwidth=1, relheight=1) root.mainloop() '''canvas.place_forget(),这是撤掉画布'''
canvas有多种功能,包括画图,移动元素,还有导入图片等等功能,作为一个控件,还能与很多事件绑定在一起。包括且不局限下面的代码实例:
canvas.create_line((3,3),(3,297),fill='black',width=3)#匿名画一条线 button_return = tkinter.Button(canvas,text="返回菜单",height = 2,width = 12,command = ?)#实名创建一个按钮在canvas上,command指向一个函数 text_p = canvas.create_text((350,100),text = "point : "+str(point),fill = "black",font =('Arial', 12))#创建一个文本 food = canvas.create_image(100,100,image = cardimg)#在100,100的位置创建一个变量名为food的图片cardimg
除开上面列举的功能,还有更多功能,更厉害的是,canvas能对以及创建好的元素进行操作。
canvas.delete(food)#删除命为food的元素 canvas.itemconfig(food,image = xxx)#将food元素重新指向别的图片 canvas.itemconfig(text_p,text = "point : "+str(point))#将text_p文本的内容进行修改 canvas.bind_all("<KeyPress>",fun)#将canvas的按键事件与fun函数绑定
还有许多操作,这里不再列举。单靠上面的这些操作,我们实际上已经可以做出各类小游戏了,比如扫雷。想必你也知道该怎么搞吧,创建一个canvas,往上面对应坐标创建一堆按钮,每当点到对应按钮,通过绑定的函数执行相应的操作,由于是在canvas上,你还可以随时删了这个按钮然后新增别的图标上去,当然,随你创作。
事件的使用
首先是事件的绑定,可以通过调用bind函数将控件与函数相互绑定,或者直接利用command参数来将函数与控件相互绑定。举一个例子:
import tkinter
root = tkinter.Tk()
root.title('title!')
root.geometry('400x300')#窗口大小
root.resizable(0,0)#设置窗口可否拉伸,flase为否
def handlerAdaptor(fun, **kwds):
'''事件处理函数的适配器,相当于中介'''
return lambda fun=fun,kwds=kwds: fun( **kwds)
def button_hit(a,b,c):
print(a,b,c)
button = tkinter.Button(root,text="test",height = 3,width = 20,command = handlerAdaptor(button_hit,a=1,b=2,c=3))
button.pack(side='top',pady = '10px')
root.mainloop()
正常情况下,command指令是无法搞入参数的,我们可以通过万能lambda实现这一功能。
import tkinter
root = tkinter.Tk()
root.title('title!')
root.geometry('400x300')#窗口大小
root.resizable(0,0)#设置窗口可否拉伸,flase为否
def handlerAdaptor(fun, **kwds):
'''事件处理函数的适配器,相当于中介'''
return lambda event,fun=fun,kwds=kwds: fun( **kwds)
def button_hit(a,b,c):
print(a)
button = tkinter.Button(root,text="test",height = 3,width = 20)
button.bind("<Enter>",handlerAdaptor(button_hit,a=3,b=2,c=1))
button.pack(side='top',pady = '10px')
root.mainloop()
类似的代码,但是采用的是bind函数,需要注意的是,适配器也发生了一点改变!!!!
bind函数可以绑定大量事件,键盘按键,鼠标位置等等,下面列举一些常见的:
- 鼠标刚移到控件上触发
- 鼠标离开控件后触发
- 鼠标由按下变为释放触发
- 传入事件的event.x,event.y分别代表指针位置的x,y坐标
- 。。。。。。很多事件和属性网上一查就到,感觉我没必要废话
通过事件将控件与带参数的函数绑定在一起,我们可以实现大量功能。加上canvas强大的功能,我们完全可以通过canvas创建一大堆控件,以及一些图片,图形等,再通过各类事件(如点击,移动)来控制这些控件。这为我们创作简单的2d游戏提供了非常大的便利。
由于是第一篇博文,也不知道重点在哪,可能有错误,排版也有点乱,仅是提供参考讨论,也可以参考我的github代码小游戏项目(开局的图)进行相关的学习(链接:https://github.com/a132980awp/python/tree/master/snack),希望能提供到帮助,有什么疑问可以在评论区提出。