我不是科班出身的程序员,仅仅因为喜欢python而读过一些书。
但是我还记得我大学时代学VB的时候,第一课就是创建一个叫“Hello,World”的对话框。那一刻,年少轻狂觉得自己无所不能了。
所以,窃认为“没有对话框的编程都是耍流氓”
但是很可惜,即使非常喜欢python的语言风格,基本上没有书去系统介绍如何创建一个交互界面。
连python量化这种高大上的书,用的都是IDLE去输出结果,正如金玉其中,而外面只是一层简陋的纸盒子。简直太不相配了。
好不容易在网上找到关于Python GUI的书,竟然全英文的也只有三本,看来真的是太不受待见了。
所以,也不确定这种Python GUI会不会是鸡肋,毕竟IDLE可以输出好多结果。
所以,先翻译一章看看反映,如果根本没有人关注,那么估计也就这一章翻译了。如果有人关注,可以考虑再往下翻译。
另外,声明,没有得到作者的授权。就是个人的一点兴趣。
还有,不是科班出身,所以一些术语可能翻译不是很准确,请见谅。若有错误请斧正,QQ:284648397
在这一章,我们开始创建非常有趣的Python交互界面,使用的python版本为3.6,以及之前的版本。我们将讲述下列内容
在本章中,我们会开发我们第一个GUI,用最少的代码去建立一个GUI。之后会在此GUI上不断增加内容。
在我们的第一个配方中*【因为书叫cookbook,中文叫菜谱,所以书中所有的例子都会被称为配方,o( ̄︶ ̄)o*】*,我们会显示所有的代码。在之后的配方中,只会显示增加的代码。
在本章的最后,我们会创建一个GUI,包括标签,按钮,文本框,组合框,不同状态的复选框,以及信号按钮去更改GUI的背景色。
Python是一个非常强大的编程语言,内嵌tkinter模块,只需要几行代码,我们就能创建出第一个GUI
为了创建GUI,一个Python开发环境是必不可少的前提。本书的创建环境是Python 3.6,系统是Windows 10 64位系统,但是有鉴于Python跨平台的语言,本书的代码应该都能运行。
【剩下一大堆就是关于Mac和python2兼容性的问题了,暂且略过,有兴趣请联系我,我直接发给你。】
以下就是第一个GUI的创建代码
import tkinter as tk #引用tkinter模块
win=tk.Tk() #创建GUI
win.title("Python GUI") #设置GUI的标题
win.mainloop() #开始GUI,这句在所有代码的最下面,表示开始创建窗口了。本例中是这样,之后都是这样。
第一行:我们引用tkinter模块,并简化为tk
第二行:利用Tk类(class)创建了一个窗口。并将这个窗口分配给变量win(window的缩写)。
因为Python是一个动态语言,我们不需要在分配前声明变量类型,Python会自动判断并分配类型。所以说Python是一个强大而高效的编程语言。
【我简化了翻译,原本比较长,但是大意就是这样。】
第三行:更改了win变量的属性title,可改变GUI的标题。
第四行:通过mainloop方法开始窗口事件循环。之前的代码创建了窗口并更改了一个属性,但是直到这行代码被执行时,窗口才能真正被我们看到。
这是一个无限循环,我们GUI等待着事件,并返回其命令,例如按钮点击等。这个循环会在我们点击窗口的X时被结束。
一个GUI默认是可被调节大小的,但可能不符合某些场景设定,因为GUI上面的控件可能由于窗口大小的调节,导致排版不如预期。所以在这一节,我们学习如何阻止窗口被放大或缩小。
这次的配方会扩展上一节的代码,所以要求你先把上一节的代码敲进Python哦
import tkinter as tk #引用tkinter模块
win=tk.Tk() #创建GUI
win.title("Python GUI") #设置GUI的标题
#==========================================
win.resizable(False,False) #使窗口调节失效
#win.resizable(True,False) #使窗口只能调节x轴,调节不了y轴,反之相反。
#==============================================
win.mainloop() #开始GUI
【结果就是调节不了大小了,就不上图了】
这条代码失效了窗口大小调节,并让最大化按钮灰掉无法使用。
resizable()方法是Tk()类中的一种,传入(False, False),对应(x轴,y轴),使得x轴和y轴都无法调节。任一False变成True,都让对应轴变得可以调节。
标签是GUI上一个很简单的控件,可以去解释其他控件的作用,也可以提供额外的信息。
我们正在扩展第一个配方,但是删除了阻止调节大小的代码。
import tkinter as tk
from tkinter import ttk
win=tk.Tk()
win.title("Python GUI")
#==========================================
ttk.Label(win,text="A Label").grid(column=0,row=0) #增加一个标签
#==============================================
win.mainloop() #开始GUI
首先,我们要从tkinter中引用ttk模块【这个模块可以让我们的GUI更好看】,ttk是themed tk的简称。
然后,利用ttk.Label()函数去增加一个标签。注意,Label第一个字母大写。
grid()在第二章会讲
【不过其实很简单的,就是布局位置】
另外,GUI会变得很窄平,因为我们只增加了一个标签,GUI会随着我们增加内容而自动扩展窗口大小,如果更长的标签(字数更多),那么窗口会自己加长。
另外,增加更多的控件,也会自动扩展窗口大小。
这一节,我们要增加按钮控件,而且利用这个按钮更改另一个控件的属性。这一节我们会引进回叫功能。
【回叫功能,callback:我们给GUI一个命令,例如点击一个按钮,等待GUI回叫我们,即根据命令进行下一个事件。其实就是点击按钮,然后有反应就是callback】
就是1.3中的代码
import tkinter as tk
from tkinter import ttk
win=tk.Tk()
win.title("Python GUI")
#==========================================
a_label=ttk.Label(win,text="A Label") #增加一个标签——1
a_label.grid(column=0,row=0) #设定标签的位置——2
#==============================================
#增加按钮点击事件
def click_me(): #——3
action.configure(text="** I have been Clicked! **") #——4
a_label.configure(foreground='red') #——5
a_label.configure(text='A Red Label') #——6
#增加一个按钮
action=ttk.Button(win,text="Click Me!", command=click_me) #——7
action.grid(column=1,row=0) #——8
#===============================================
win.mainloop() #开始GUI
——1:把标签分配给一个变量a_label
——2:设置标签的位置
——3:更改变量a_label的属性,用click_me()的功能。在默认下,这是模块级别的变量,所以我们可以在函数功能内连接它,只要我们在函数之前声明这个变量就可以了。
——4:设置一个事件,当按钮被点击时触发。
——7:创建了一个按钮,将函数设定的click_me()赋予按钮,用command命令。
GUI是事件驱动,点击一个按钮会创建一个事件,我们用ttk.Button控件中的command属性连接事件发生时什么会发生,用的是回叫功能(callback)。
【谁能告诉我callback在编程中的术语呀,最好能高大上一点,虽然书中解释这就是拨号(点击)——回叫(事件)功能。】
——5:我们也改变了变迁的颜色。
——6&8:使用了grid样式管理,会在第二章讨论。
在tkinter,典型的一行文本框控件称为Entry(输入)。在这个配方里,我们将添加一个Entry(输入).我们会利用标签使Entry对于用户更加易懂。
基于上一节的代码
#==============================================
#增加按钮点击事件
def click_me():
action.configure(text="Hello, "+name.get()) #——1
#更改我们的标签
ttk.Label(win,text='Enter a name:').grid(column=0,row=0) #——2
#增加一个文本框Entry控件
name=tk.StringVar() #S和V必须都大写,不然会报错——3
name_entered=ttk.Entry(win,width=12,textvariable=name) #——4
name_entered.grid(column=0,row=1)
#增加一个按钮
action=ttk.Button(win,text="Click Me!", command=click_me)
action.grid(column=1,row=1) #——5
#===============================================
——1:我们得到了Entry控件的值,我们现在还没有用OPP(面对对象编程),所以当我们还未声明一个变量的时候,如何能读取这个变量的值呢?
如果不用OPP类,那么我们只能从顺序上,将声明变量的代码放在上面。这是怎么工作的呢?
答案是,当按钮被点击时,是一个回叫功能,也就是在按钮之前声明变量即可,之后会被回叫到函数功能上。
【所以,顺序是click_me()的回叫函数,之后是标签,之后是声明name变量,最后是生成一个按钮。这样的顺序保证了在command被激活之前,name变量已经被声明了,不会出现“name没有定义”这样的报错。】
生活很美好哦【作者的文风就是这样的。】
——2:这行代码执行一个标签,而且就在我们文本框的正上方,这么做可以让我们的Entry更有目的性。用的也是grid方法,第二章会介绍到。
——3&4:创建了一个变量name,这个变量和Entry控件绑定在一起,而在click_me()中,我们通过get()来取得Entry控件的值。就像一个咒语一样。
——4:正如我们所见,输出中,即使输入再多内容,Entry控件不会自行调节宽窄,因为我们硬编码了它的宽度为12。
——5:【按钮是上一节的,但是为了美观,row从0变成1,这样排版更合理,书上没有提,但是他代码里改了】
Python是一个动态语言,如果我们将字符分配给name变量,那么name就是字符类型变量。如果分配了整数,就是整数变量。
在使用tkinter,我们不得不首先声明name是什么样的类型变量,用的命令是,tk.StringVar()。因为tkinter不是python,它无法自己辨别变量类型。
随着我们的GUI不断改良,它将更加方便快捷,使得GUI出现时,光标就已经出现在Entry控件里。
上一节的代码
Python真的很强大。【全书一直都在赞美Python,顺便踩一脚Java,O(∩_∩)O】我们可以用focus()方法让控件一开始就获得焦点和光标
在上一节代码的最下面写下面一行就可以【在mainloop()的上面哦,mainloop什么时候基本上都是最下面的】
name_entered.focus()
如果报错,请检查是否有变量声明顺序方面有问题。因为没有用OPP类,顺序很重要。
如果你用的是Mac,获得焦点可能会麻烦一些。
光标一开始就在Entry控件里,且获得焦点,就是最前端的窗口。你马上就可以在Entry里输入字符。
#增加一个按钮
action=ttk.Button(win,text="Click Me!", command=click_me)
action.grid(column=1,row=1)
action.configure(state='disable') #使按钮失效
name_entered.focus()
这样一开,初始化GUI,按钮就失效了。
【如果希望点击之后失效,就把这行代码写在def click_me()的最下面,这样一来,就是点击按钮之后失效。】
这个代码是自我解释的。我们让一个控件获得了焦点,又失效了另一个控件。好的命名规则在编程中可以减少解释的必要。
在这一节的配方中,我们将添加一个下拉选择列表到GUI上,这个列表已经有默认的值。同时,我们还可以限制用户只能从中选择一个,我们也可以让用户自己键入他们想要的任何值。
上一节的代码
#增加一个文本框Entry控件
name=tk.StringVar() #S和V必须都大写,不然会报错
name_entered=ttk.Entry(win,width=12,textvariable=name)
name_entered.grid(column=0,row=1)
#增加一个按钮
action=ttk.Button(win,text="Click Me!", command=click_me)
action.grid(column=1,row=1)
ttk.Label(win,text="Choose a number:").grid(column=1, row=0) #——1
number=tk.StringVar() #——2
number_chosen=ttk.Combobox(win, width=12, textvariable=number) #——3
number_chosen['values']=(1,2,3,42,100) #——4
number_chosen.grid(column=1, row=1)
number_chosen.current(0) #——5
name_entered.focus()
#===============================================
win.mainloop() #开始GUI
我们用一个圆括号把默认值传递给了combobox控件,这些值就会出现在下拉菜单中,我们可以随意改变其值。
——1:增加了第二个标签来匹配我们的combobox控件
——2:声明了一个number变量,指明了其类型。
——3:创建了combobox控件,并将number分配给这个控件。
——4:设置了下拉菜单的默认值,值是字符类型的,因为绑定了number变量,随其类型而来。
——5:设置了初始GUI时,combobox控件所显示的值。
当选择了一个值,这个值可以用number.get()得到。
如果我们想限制用户,只从下拉菜单中选择,我们可以用state属性去限制。
number_chosen=ttk.Combobox(win, width=12, textvariable=number, state='readonly') #——3
在这一节里,我们创造三个初始状态不同的复选框控件
需要扩展上节的代码
我们创造三个初始状态不同的复选框控件。
第一个:已选但灰色失效,用户无法取消勾选
第二个:激活且未选,用户可以通过点击而勾选
第三个:激活且已选,用户可以通过点击而取消勾选。
#创造三个复选框
chVarDis=tk.IntVar() #——1.1
check1=tk.Checkbutton(win,text="Disabled",variable=chVarDis, state='disabled') #——1.2
check1.select() #——1.3
check1.grid(column=0,row=4,sticky=tk.W)
chVarUn=tk.IntVar() #——2.1
check2=tk.Checkbutton(win,text="UnChecked", variable=chVarUn) #——2.2
check2.deselect() #——2.3
check2.grid(column=1, row=4, sticky=tk.W)
chVarEn=tk.IntVar() #——3.1
check3=tk.Checkbutton(win,text="Enabled",variable=chVarEn) #——3.2
check3.select() #——3.3
check3.grid(column=2,row=4,sticky=tk.W)
在——1.1、——2.1和——3.1中,我们创建了三个整数类型的变量。在之后每组跟随着三个变量的大妈中,我们创建了一个复选框(Checkbutton),传递给这些变量。他们会控制复选框的状态(勾选或未勾选)。在默认中,0代表未勾选,1为勾选。所以这三个变量是整数变量。
我们将复选框放置在主窗口内,所以在所有——*.2的语句中,第一个变量位置为win,即主窗口的变量名,text属性即复选框后面所显示的文本。
在grid管理中使用sticky属性:tk.W意味着控件会被分配到主窗口的西方(west),即使用户再次调整窗口大小,此控件都只会在最西边,而不会移动到中间去。这点和JAVA很像。
——*.3语句,select()方法是将对号放入复选框中。
grid管理方法第二章会讲到。
这一节,我们将创建单选框控件,还会增加一些改变主窗口背景颜色的代码。不同的单选框被选择时,会改变不同的颜色。
上一节的代码
#信号按钮——全局 #——1
COLOR1="Blue"
COLOR2="Gold"
COLOR3="Red"
#信号按钮——回叫 #——2
def radCall():
radSel=radVar.get()
if radSel==1:
win.configure(background=COLOR1)
elif radSel ==2:
win.configure(background=COLOR2)
elif radSel ==3:
win.configure(background=COLOR3)
#创造三个信号按钮
radVar=tk.IntVar() #——3
rad1=tk.Radiobutton(win,text=COLOR1, variable=radVar, value=1, command=radCall)
rad1.grid(column=0, row=5, sticky=tk.W)
rad2=tk.Radiobutton(win,text=COLOR2, variable=radVar, value=2, command=radCall)
rad2.grid(column=1, row=5, sticky=tk.W)
rad3=tk.Radiobutton(win,text=COLOR3, variable=radVar, value=3, command=radCall)
rad3.grid(column=2, row=5, sticky=tk.W)
——1的三条代码创建三个全局变量,用来之后创建单选框和更改背景色的回叫函数
我们利用全局变量使其更容易更改代码。相对于搜索-取代的硬编码字符串,这样的全局变量更加符合DRY原则,(Don’t repeat yourself.不要重复自己)。这是一个OPP概念,(面对对象编程)我们之后会用到。
颜色代码需要用到tkinter里的颜色关键字,不然不会起作用。(此处用颜色代码也行,ff开头的那些)
——2:设置一个回叫函数,用来更改主窗口的背景色
——3:我们创建一个tk.IntVar变量,很重要的一点是我们只创建一个变量给三个单选框使用。这样一来,无论用户从主界面上选择哪一个,另外两个都会自动取消选择。
之后的代码:创建三个单选框,位置为主页面win,将变量值传给单选框,单选框利用变量值再传给回叫函数,从而改变了背景色。
【还有两节,这几天一定写完】