[Toc]
# 第10章 图形化界面
今天墨博士和小墨一起做了两个程序:多色板和二维码生成器。第一次做出有图形化界面的小墨非常兴奋,一起来看看吧。
小墨:墨博士,我想编一些带界面的程序,你能教教我吗?
墨博士:你想做什么呢?
小墨:做什么都行,只要带界面就行。
墨博士:也好,虽然现在早已不是桌面为王的时代了,但是学点开发桌面应用程序的技能,做一些日常的小工具也不错。
> 墨博士提醒:简而言之,需要在本地计算机上安装应用程序的一般可以认为是桌面应用程序,比如word、记事本、图片查看器等。
小墨:是啊,学了这么多的内容,还不知道怎么做界面呢。
墨博士:前面的都是基础,现在基础学的差不多了,可以教给你一些界面相关的知识了。
小墨:太好了,我们快点开始吧。
墨博士:Python中有很多受欢迎的GUI库,比如wxPython和PyQt。GUI是图形用户界面(Graphical User Interface)的简称,就是你说的界面。对于新手来说,除wxPython和PyQt外,使用Python自带的tkinter也是个不错的选择。今天我们就来使用tkinter做几个小应用。先来看第一个,效果如图10.1所示。
![](http://p7moyixbi.bkt.clouddn.com/%E5%9B%BE10.1.png)
图10.1 第一个小应用运行结果
小墨:哇,看起来有点像瓷砖,不过颜色有点多。
墨博士:我们就叫它多色板吧。下面我们使用tkinter来制作多色板。
## 10.1 多色板
想使用tkinter,需要先将它导入进来:
```
from tkinter import *
```
因为我们可能会用到tkinter中的很多内容,所以这里使用`from tkinter import *`将tkinter中的所有内容全部引入进来。
然后创建一个窗口:
```
root = Tk()
```
这里的Tk()是创建了一个窗口,然后赋值给了变量root,这样root就表示这个窗口了。
创建窗口后运行程序是看不到任何效果的,想看到窗口的效果,还需要加上这句:
```
root.mainloop()
```
这句话字面意思是调用主循环方法,可以理解为在实时刷新我们编写的界面然后呈现出来。此时运行效果如图10.2所示。
![](http://p7moyixbi.bkt.clouddn.com/%E5%9B%BE10.2.png)
图10.2 第一个小窗口
小墨:终于看到窗口了,这是我的第一个小窗口!
### 10.1.1 修改窗口标题和大小
墨博士:你还可以修改下窗口的标题,这个使用root的title方法:
```
root.title('我的小窗口')
```
运行效果如图10.3所示。
![](http://p7moyixbi.bkt.clouddn.com/%E5%9B%BE10.3.png)
图10.3 修改标题后的窗口
小墨:这个我需要把它拖大一点才能看到我起的标题。能不能用程序把它调大一点呢?这样运行起来就是个大窗口了。
墨博士:想设置窗口的大小,需要使用root的geometry方法:
```
root.geometry('600x400')
```
这个方法的参数`600x400`就表示创建的窗口宽度是600,高度是400,单位是像素。运行结果如图10.4所示。
![](http://p7moyixbi.bkt.clouddn.com/%E5%9B%BE10.4.png)
图10.4 修改大小后的窗口
你还可以传入`600x400+300+200`作为参数,后面的300和200指的是窗口最左上角的点在屏幕上的坐标,也即窗口在屏幕上的位置。其中300指x轴坐标的大小,200指y轴坐标的大小。对于tkinter来说,屏幕左上角是原点,从原点出发,横向向右是x轴,纵向向下是y轴。如图10.5所示。
![](http://p7moyixbi.bkt.clouddn.com/%E5%9B%BE10.5.png)
图10.5 电脑屏幕的坐标系,左上角绿点就是原点。
小墨:这样就能定义窗口出现的任意大小和在屏幕上的任意位置了。
墨博士:接下来我们给窗口上个色,先做个单色板出来。
### 10.1.2 单色板
想给窗口上色,就要了解做桌面应用的思路。一般来说,做GUI程序就像画画,这里的root窗口就像画板,一般我们不在画板上直接画画,而是在画板上铺上一张画纸,然后在画纸上画。
这里要做的单色板也用这种思路,现在定义一张画纸出来,在tkinter中,画纸的角色由Frame担任。
```
# 定义基础画纸
frame_basic = Frame(width=600, height=400, bg='red')
```
这样就定义好了一张画纸。width=600和height=400用于指定画纸的宽度和高度分别是是600和400,和画板的大小是一样的,这样能正好贴在画板上。bg='red'用于指定这张画纸的背景颜色,bg是background的简写,red表示红色,当然你也可以使用其他颜色,如blue、yellow等。
> 墨博士提醒:定义Frame时传入的参数和前面的有所不同,是将key=value的形式一块做了参数,如width=600。这称为关键字参数。
小墨:我喜欢蓝色,我要指定蓝色,使用bg='blue',运行,嗯?什么都没有?
墨博士:上面一句我们只是定义好了画纸,指定了大小和颜色,此时的画纸和画板还没有直接的关系,也就是画纸还没有贴上去。
小墨:那怎样才能贴上去呢?快说吧,我都等不及了。
墨博士:这就要说说布局的概念了。对于画板来说,画纸贴在哪是有讲究的,不能乱贴。这种如何贴画纸的规矩,称为布局。比如tkinter中有个grid布局,它会将画板分成几行几列的网格状,你可以选择将画纸贴住第几行第几列的格子里。就像下图10.6所示。
![](http://p7moyixbi.bkt.clouddn.com/%E5%9B%BE10.6.png)
图10.6 tkinter中的grid布局,将画板分成了n行n列
这个表格就好像画板,我们可以将定义好的画纸贴进去了:
```
frame_basic.grid(row=0, column=0)
```
这里的row就是行,column就是列,需要注意的是行号和列号都是从0开始的。
小墨:这样就把画纸贴在了第1行第1列的格子上,那此时这个画板一共分成了多少行多少列,一共有多少个格子呢?
墨博士:一般一个界面会有很多控件构成,如输入框、按钮等,这就好比这里的画板上,可能会贴上多张画纸。在使用grid布局去贴这些画纸的时候,不需要去事先指定共分了多少行多少列,而是根据要写的界面的特点,将这些画纸分别贴在不同的单元格就行了,这样就指定好了画纸与画纸的相对位置关系,比如想做一个输入框后面有个按钮,就将输入框放在第1行第1列,而按钮放在第1行第2列就可以了。另外grid并不是把画板平均分成了多少个小格子,每个小格子的大小要看实际贴进来的画纸的大小,也即放入的控件的大小。就像这里的frame_basic,它和画板一样大,这样它自己就占满整个画板了。
现在我把背景颜色改为orange,完整代码如下:
```
from tkinter import *
root = Tk()
root.title('我的小窗口')
root.geometry('600x400+300+200')
# 定义画纸,背景颜色为orange橘黄色
frame_basic = Frame(width=600, height=400, bg='orange')
# 将frame_basic放在画板的第一行第一列,也即第一个单元格
frame_basic.grid(row=0, column=0)
root.mainloop()
```
运行结果如图10.7所示。
![](http://p7moyixbi.bkt.clouddn.com/%E5%9B%BE10.7.png)
图10.7 单色板
这个就是所谓的单色板了。
### 10.1.3 四色板
接下来来做一块四色板:屏幕分成四等份,每份一种颜色。
小墨:这个我来做。首先定义4个Frame,因为root窗口大小是600x400,所以每个Frame的大小应该是300x200,也即宽高各减半,这样4个Frame就占满全窗口了。然后将这4个Frame分别放在root的第1行第1列、第1行第2列、第2行第1列和第2行第2列上就可以了。代码如下:
```
from tkinter import *
root = Tk()
root.title('我的小窗口')
root.geometry('600x400+300+200')
# 定义4个Frame,指定4种不同的背景颜色
# 600 / 2 表示600除以2,作为定义的Frame的宽度
frame1 = Frame(width=600 / 2, height=400 / 2, bg='orange')
frame2 = Frame(width=600 / 2, height=400 / 2, bg='green')
frame3 = Frame(width=600 / 2, height=400 / 2, bg='pink')
frame4 = Frame(width=600 / 2, height=400 / 2, bg='white')
# 将4个Frame依次放入指定位置
frame1.grid(row=0, column=0)
frame2.grid(row=0, column=1)
frame3.grid(row=1, column=0)
frame4.grid(row=1, column=1)
root.mainloop()
```
运行效果如图10.8所示。
![](http://p7moyixbi.bkt.clouddn.com/%E5%9B%BE10.8.png)
图 10.8 四色板
墨博士:做的不错!加大点难度,每次运行,这4个色块的颜色都是随机的,这个怎么做呢?
### 10.1.4 随机颜色的四色板
小墨:可以定义一个list,里面存很多颜色,然后使用random模块每次去随机一个颜色的坐标,这样就得到了随机的颜色。
墨博士:是个好主意,但是这样生成的颜色数量有限,我们换一种背景颜色的表达方式,能够将颜色数量大大增加:
```
frame1 = Frame(width=600 / 2, height=400 / 2, bg='#F08080')
```
这里的背景我们设置为`#F08080`,它也表示一种颜色,为什么颜色能这样表示呢?这就要说说RGB色彩模式了。
RGB色彩模式是一种颜色标准,目前的显示器大都采用了这种颜色标准。这里的RGB是红色Red,绿色Green和蓝色Blue的首字母组合。电脑屏幕上的所有颜色,都由这三种色光按照不同的比例混合而成的。
小墨:这个跟绘画的时候调制颜料差不多。
墨博士:是的,在电脑中,RGB的所谓“多少”是指亮度,并使用整数来表示。通常情况下,RGB各有256级亮度,用数字表示为从0、1、2...直到255。注意虽然数字最高是255,但0也是数值之一,因此共256级。
使用红绿蓝的这256个数值,我们就能“调制”颜色了。比如黑色可以表示为“000”,第一个0表示Red的亮度值,第2个表示绿色的亮度值,第3个表示蓝色的亮度值。
小墨:那如果是白色,就是“256256256”?
墨博士:理论上是这样的,但是这样写不直观,还容易出错,所以最后颜色就统一成了六位的十六进制。小墨,你知道十六进制吗?
小墨:不知道,但是我知道二进制。我们平时用的是十进制。
墨博士:十六进制是计算机中数据的一种表示方法,它由0-9,A-F组成,字母不区分大小写。与10进制的对应关系是:0-9对应0-9;A-F对应10-15。
小墨:那大于15的十进制用十六进制如何表示呢?比如16。
墨博士:十进制是逢十进位,二进制是逢二进位,十六进制道理也一样:逢十六进位。十进制的16用十六进制表示就是进了一,结是“10”,同理17是“11”,18是“12”,255就是“FF”。
你看,RGB的最大亮度值255对应的十六进制是“FF”,也即一种颜色最多使用六位十六进制就可以表示了,前两位表示Red的亮度,中间两位表示Green的亮度,后面两位表示Blue的亮度。
小墨:那不够两位的怎么办?
墨博士:不够两位的就前面补0,比如黑色就是“000000”,白色就是“FFFFFF”,红色就是“FF000000”。最后前面再拼接上一个“#”号,表示这是一种颜色。
小墨:明白了,这样的话,上面的“#F08080”就是由红色提供F0的亮度、绿色提供80亮度、蓝色也提供80亮度后混合的结果。
墨博士:是的。你能算出来这种表示方式一共能表示多少颜色吗?
小墨:嗯……,一共能表示256乘以256再乘以256种颜色,我来用python算一下……,结果是16777216,一共一千六百多万种颜色呀!
墨博士:是的,接下来我们就定义一个方法,用于产生一种随机颜色,其实就是产生一个六位的十六进制字符串。代码如下:
```
def get_color():
color = ''
colors = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F']
for i in range(6):
# 随机0-15内的数,作为下标去取colors中的字符
index = random.randint(0, 15)
# 拼接成6位的字符串
color += colors[index]
return '#' + color
```
然后把Frame的bg参数指定为`bg=get_color()`就能取得一个随机颜色,这样每次程序运行就都能得到不同的颜色了,如图10.9所示。
![](http://p7moyixbi.bkt.clouddn.com/%E5%9B%BE10.9.png)
图10.9 随机颜色的四色板
> 动动手:根据四色板程序编写九色板程序,也即窗口平分为3行3列共9块,每块颜色随机。
### 10.1.5 多色板
墨博士:在4色板的基础上,只要稍加改造这段代码,就可以做出任意多行任意多列的多色板来。
```
frame1 = Frame(width=600 / 2, height=400 / 2, bg=get_color())
frame2 = Frame(width=600 / 2, height=400 / 2, bg=get_color())
frame3 = Frame(width=600 / 2, height=400 / 2, bg=get_color())
frame4 = Frame(width=600 / 2, height=400 / 2, bg=get_color())
frame1.grid(row=0, column=0)
frame2.grid(row=0, column=1)
frame3.grid(row=1, column=0)
frame4.grid(row=1, column=1)
```
比如想开发个九色板,那定义的Frame的宽和高要除以3:
```
frame1 = Frame(width=600 / 3, height=400 / 3, bg=get_color())
…… # 定义其他Frame,此处省略
```
再把定义好的9个Frame放入画板中:
```
frame1.grid(row=0, column=0)
frame2.grid(row=0, column=1)
frame3.grid(row=0, column=2)
frame4.grid(row=1, column=0)
frame5.grid(row=1, column=1)
frame6.grid(row=1, column=2)
frame7.grid(row=2, column=0)
frame8.grid(row=2, column=1)
frame9.grid(row=2, column=2)
```
小墨你看,上述两个过程做的都是相同或类似的事,对于重复性的事情,就可以考虑循环来解决。思考一下,这里应该如何用循环呢?
小墨:嗯……,从将Frame放入画板的9条语句的row和column的值可以看出,这里适合用双重for循环。外层循环循环3次,内层循环也循环3次。
```
for i in range(3):
for j in range(3):
frame = Frame(width=600 / 3, height=400 / 3, bg=get_color())
frame.grid(row=i, column=j)
```
墨博士:将你的这段程序中的行数和列数换成变量,就能变成任意多行任意多列的多色板了。
```
# 定义行数
row_num = 4
# 定义列数
column_num = 3
# 循环创建Frame,并指定其位置
for i in range(row_num):
for j in range(column_num):
frame = Frame(width=600 / column_num, height=400 / row_num, bg=get_color())
frame.grid(row=i, column=j)
```
注意程序中Frame的宽度是600除以列数,高度是400除以行数,这个别弄混了。
运行,结果如图10.10所示。
![](http://p7moyixbi.bkt.clouddn.com/%E5%9B%BE10.10.png)
图10.10 一个4行3列的色板
小墨:博士,你看我做了个20行20列的,效果如图10.11所示。
![](http://p7moyixbi.bkt.clouddn.com/%E5%9B%BE10.11.png)
图 10.11 20行20列的色板
为什么和你最初展示的不一样呢?色块与色块之间没有白线分割。另外,这个窗口拉大之后效果如图10.12所示,我能设置窗口大小不可变吗?
![](http://p7moyixbi.bkt.clouddn.com/%E5%9B%BE10.12.png)
图10.12 窗口拉大后的效果
墨博士:如果想在色块间有分割线,只要在调用grid方法的时候传入padx和pady两个参数就可以了:
```
frame.grid(row=i, column=j, padx=1, pady=1)
```
padx用于设置x轴方向色块间的间隔,pady用于设置y轴方向上色块间的间隔,单位都是像素。加上这两个参数之后再运行,就能看到分割线了。不过需要注意的是,加了分割线后,分割线的宽度加上色块的宽度就超过了画板的宽度(高度也是),色块可能就会被挤出去一些,这时只需调整下画板的宽度值和高度值即可。
而对于设置窗口不可改变,可以在定义root之后,使用:
```
# 此时因为添加了分割线,就加大了宽度和高度,当然这个要根据实际情况调整
root.maxsize(width=620, height=420)
root.minsize(width=620, height=420)
```
设置root的最大大小和最小大小都是620x420,这样窗口就不能被调整大小了。完整代码如下:
```
from tkinter import *
import random
root = Tk()
root.title('多色板')
root.geometry('620x420+300+200')
root.maxsize(width=620, height=420)
root.minsize(width=620, height=420)
def get_color():
color = ''
colors = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F']
for i in range(6):
index = random.randint(0, 15)
color += colors[index]
return '#' + color
row_num = 10
column_num = 10
for i in range(row_num):
for j in range(column_num):
frame = Frame(width=600 / column_num, height=400 / row_num, bg=get_color())
frame.grid(row=i, column=j, padx=1, pady=1)
root.mainloop()
```
> 想一想:这段代码中为了避免最后一行和最后一列被挤出画板,将画板的大小设置为了620x420,思考一下,如果不改变这里的画板大小,如何既能添加行和列的间隔,又能不被挤出去呢?
## 10.2 二维码生成器
墨博士:接下来我们乘胜追击,再做个二维码生成器,效果如图10.13所示。
![](http://p7moyixbi.bkt.clouddn.com/%E5%9B%BE10.13.png)
图10.13 二维码生成器
用户在输入框中输入内容,然后点击后面的按钮,就生成了对应内容的二维码。
小墨:我来扫一扫。滴~,果然是Hello World。
墨博士:想做这样的一款小应用,首先需要知道按钮及按钮的点击事件。
### 10.2.1 按钮及点击事件
和上面的单色板一样,我们定义一个按钮放在画板上。
```
from tkinter import *
root = Tk()
root.title('按钮')
root.geometry('300x100+300+200')
# 定义按钮
btn = Button(root, text='点我')
# 指定按钮的位置
btn.grid(row=0, column=0)
root.mainloop()
```
运行效果如图10.14所示。
![](http://p7moyixbi.bkt.clouddn.com/%E5%9B%BE10.14.png)
图10.14 按钮
因为这里我们并没有指定按钮的大小,所以它是默认大小显示在窗口的左上角。
小墨:这个按钮怎么点击一点反应都没有?
墨博士:想要有反应,就需要指明当按钮被点击时,程序做什么,这个通过在定义按钮时指定“command=callback”实现。
```
from tkinter import *
root = Tk()
root.title('按钮点击效果')
root.geometry('300x100+300+200')
def callback():
print('hello ')
# 添加command=callback参数
btn = Button(root, text='点我', command=callback)
btn.grid(row=0, column=0)
root.mainloop()
```
这里的callback是一个函数的名字,在定义按钮时添加“command=callback”,就表示点击按钮时执行callback函数。
你来试试吧小墨。
小墨:现在再点击按钮就会输出hello了。
### 10.2.2 输入框和标签
墨博士:接下来说说二维码生成器中的用于输入的输入框和用于图片显示的标签怎么做。
```
from tkinter import *
root = Tk()
root.title('按钮点击效果')
# 输入框
entry = Entry(root)
entry.grid(row=0, column=0)
def callback():
print('hello')
# 按钮
btn = Button(root, text='点我', command=callback)
btn.grid(row=0, column=1)
# 标签,用于显示文字或图片
label = Label(root)
label.grid(row=1, column=0, columnspan=2)
root.mainloop()
```
这段程序定义了一个输入框Entry,放在了第1行第1列的位置,然后定义了一个按钮Button,放在了第1行第2列的位置,也即放在了输入框的后面。然后定义了一个标签Label,放在了第2行第1列的位置,同时指定了一个“columnspan=2”的参数,表示这个控件横跨2列。
程序运行结果如图10.15所示。
![](http://p7moyixbi.bkt.clouddn.com/%E5%9B%BE10.15.png)
图15:输入框、按钮和标签
现在我们给这套界面加点功能:输入框中输入任意内容,当点击按钮时,将输入框中的内容显示在下面横跨两列的标签上。这需要三步来完成。
第一步,定义一个StringVar():
```
text = StringVar()
```
StringVar是tkinter内部定义的字符串变量类型,在这里用于管理按钮或标签上面的字符。改变StringVar,按钮或标签上的文字也随之改变。
第二步,在Label标签上加上“textvariable=text”参数,将标签和StringVar通过textvariable参数关联:
```
label = Label(root, textvariable=text)
```
这里的text就是第一步中定义的text。
最后,我们在按钮的callback函数中改变StringVar的值,标签上的文字也就随着改变:
```
text.set(entry.get())
```
这里的entry.get()表示获取输入框中的内容。
完整代码如下:
```
from tkinter import *
root = Tk()
root.title('点击按钮显示输入的内容')
entry = Entry(root)
entry.grid(row=0, column=0)
# 第一步:定义StringVar
text = StringVar()
def callback():
# 第三步:改变text的值,此时标签跟着改变。
text.set(entry.get())
btn = Button(root, text='点我', command=callback)
btn.grid(row=0, column=1)
# 第二步:将标签和StringVar通过textvariable参数关联。
label = Label(root, textvariable=text)
label.grid(row=1, column=0, columnspan=2)
root.mainloop()
```
运行结果如图10.16所示。
![](http://p7moyixbi.bkt.clouddn.com/%E5%9B%BE10.16.png)
图10.16 点击按钮显示输入的内容
小墨:这个和二维码生成器很接近了,接下来要把标签换成图片了吧?
墨博士:是的,不过换图片之前,我们需要先安装一个第三方的库。
### 10.2.3 图片处理库Pillow的安装和使用
对于Python官方标准库来说,可以直接使用或导入进来后直接使用,比如我们现在所使用的tkinter。对于第三方库,则需要先下载安装,再导入使用。
> 墨博士提醒:第一章中我们说过,除Python官方人员和你之外的人就是第三方,他们写的库就是第三方库,你可以下载安装后使用。
Python中提供了一个包管理工具叫pip,第三方库的查找、下载、安装及卸载等都可以使用它。使用方式非常简单,在cmd窗口中敲入对应指令就行了。
对于我们的程序而言,想要把一张图片展示在标签上,就需要先安装一个第三方的图片处理库,叫Pillow。使用pip下载并安装Pillow只需要在cmd中输入“pip install Pillow”就可以了,如图10.17所示。
![](http://p7moyixbi.bkt.clouddn.com/%E5%9B%BE10.17.png)
图10.17 使用pip自动下载并安装Pillow
结尾提示安装成功。
安装好之后就可以使用了,如下:
```
from tkinter import *
from PIL import Image
from PIL import ImageTk
root = Tk()
root.title('标签上图片的展示')
img = Image.open('C:/Users/Administrator/Desktop/momeimei.jpg')
img = ImageTk.PhotoImage(img)
label = Label(root, text="", image=img, width=500, height=500)
label.grid(row=0, column=0)
root.mainloop()
```
这里的第2、3行代码就表示从Pillow库中引入Image和ImageTk。第8、9两行代码就是使用Image和ImageTkinter来操作图片,最终得到的img就表示桌面上的momeimei.jpg,这是墨妹妹的一张照片。
然后给标签设置“image=img”参数,表示将得到的img图片和标签绑定到了一起。此时运行程序,结果如图10.18所示。
![](http://p7moyixbi.bkt.clouddn.com/%E5%9B%BE10.18.png)
图10.18 标签上展示图片
### 10.2.4 二维码库qrcode的安装和使用
墨博士:接下来说说Python中二维码库qrcode的安装和使用。这是一个第三方库,和Pillow一样,需要使用pip进行下载和安装。如图10.19所示。
![](http://p7moyixbi.bkt.clouddn.com/%E5%9B%BE10.19.png)
图10.19 qrcode库的安装
安装成功之后就可以来使用了:
```
import qrcode
# 将hello world生成二维码
img2 = qrcode.make('hello, world')
# 将二维码图片保存到D盘下的qrcode文件夹下,命名为hello.png
img2.save('D:\\qrcode\\hello.png')
```
运行效果如图10.20所示。
![](http://p7moyixbi.bkt.clouddn.com/%E5%9B%BE10.20.png)
图10.20 生成的二维码图片
### 10.2.5 二维码生成器
把上面界面、按钮事件、图片显示和二维码生成等内容综合起来,就能得到二维码生成器了。代码如下:
```
from tkinter import *
from PIL import Image
from PIL import ImageTk
import qrcode
root = Tk()
root.title('二维码生成器')
entry = Entry(root, width=60)
entry.grid(row=0, column=0)
def callback():
# 获取输入的内容
text_input = entry.get()
# 将输入的内容生成二维码
img_new = qrcode.make(text_input)
# 保存生成的二维码图片
img_new.save('D:\\qrcode\\' + text_input + '.png')
# 处理成标签能使用的类型
img_new = Image.open('D:\\qrcode\\' + text_input + '.png')
img_new = ImageTk.PhotoImage(img_new)
# 这两步是关键
label.configure(image=img_new)
label.image = img_new
btn = Button(root, text='点我', command=callback)
btn.grid(row=0, column=1)
img = qrcode.make('hello, qrcode')
img.save('D:\\qrcode\\hello.png')
img = Image.open('D:\\qrcode\\hello.png')
img = ImageTk.PhotoImage(img)
label = Label(root, text="", image=img, width=500, height=500)
label.grid(row=1, column=0, columnspan=2)
root.mainloop()
```
其中第9行输入框定义时指定的“width=60”中的60不是指60像素,而是指输入框中能够输入的字符的个数,这个也就影响了输入框的长度。
第31-35行代码用于在程序启动的时候就生成了一个二维码显示出来,二维码的内容是“hello qrcode”。
从13行开始的callback是点击按钮时执行的函数,它会获取输入框的内容然后生成二维码并保存在本地,然后再将本地的二维码显示在标签上。这里第24、25两行是关键。24行用于将img_new和标签关联,25行用于将img_new替换原标签上的图片,这样我们每次生成二维码都能直接显示出来。
这样就完成了二维码生成器。
## 10.3 本章小结
墨博士总结:本章你学习了tkinter库,并使用这个库做了两个小应用:多色板和二维码生成器。多色板中,画板和画布的换算关系要计算清楚,另外就是grid布局的使用,这些你都掌握的不错。在二维码生成器中,主要是第三方库Pillow和qrcode的安装和使用,以及按钮、输入框、标签等控件的使用。有了这些知识,你就能做出很多有意思的程序了。