最近闲来无事不想学习,就学了一下Python的图形界面编程,顺便做个小玩意逗一下室友,嘿嘿嘿
环境:
系统:Win10
Python:3.6.4
其他工具:PyInstaller
效果
博客不能放视频,先截个图看下吧
点击会:
实际上弹窗有好多个,不过差不多是一样的结构。
Tkinter
这是Python的内置模块,也是这个程序主要用到的包,比较简单容易上手,我大概看了几篇博客就了解了常用的几个功能实现。
主要用到的功能有:各种组件,比如标签(Label,可以是文字图片视频等),按钮(Button),以及新建窗口。
各种组件都有很多可设置的属性,比如内容、长宽、间距、前景背景色等,通过设置属性显示组件。
官方的资料比较少,学习的话可以参考:
Python官网
python界面 | Tkinter图形界面开发库 (我看的这个)
开始
导入包:
from tkinter import *
from PIL import Image, ImageTk
首先建立主窗口:
class APP(Frame):
def __init__(self, master=None):
Frame.__init__(self, master)
self.pack(expand=True, fill=BOTH) # 如果用pack()方法排版必须加这一句
w, h = 400, 300
self.master.geometry('%dx%d' % (w, h)) # 设置窗口大小
self.create_widgets()
def create_widgets(self):
load = Image.open('start.jpg').resize((180, 180))
# load = array_to_image('./icon/start.bin')
render = ImageTk.PhotoImage(load)
self.img = Label(self, image=render, width='100', height='100')
self.img.image = render
self.img.pack(expand=YES, fill=BOTH)
self.label = Label(self, text='XXX会有男朋友吗?', font=('微软雅黑', 16))
self.label.pack(pady=20, fill=X, padx=10)
self.yes_button = Button(self, text='不会', width='10', height='1', command=self.yes_command)
self.yes_button.pack(side='left', expand=True, padx=10, pady=10)
self.no_button = Button(self, text='会', width='10', height='1', command=self.no_command)
self.no_button.pack(side='right', expand=True, padx=10, pady=10)
- Frame类是该窗口所有组件的父容器,可以添加多个组件(Widget)。
- 每个组件的第一个参数是确定这是哪个窗口下的组件,类里就是
self
。 - 图片组件添加时,要先用
PIL.Image.open()
打开,再用PIL.ImageTk.PhotoImage()
转换,具体原- 理我没有查,但是可用。 - Button的触发命令用参数
command
定义,一般是函数,可以自定义,就是当该按钮被点击时触发的事件。 - 每个组件定义完成后需要使用
.pack()
函数添加到窗口,按照先后顺序由上到下依次排列。初始化函数也必须要加上self.pack()
- pack的参数:
方位 side: left,top,right,bottom
拉伸填充 file:X,Y,BOTH,None;水平,竖直,全部,不填充
是否随窗口大小变化 expand:True,False
间距:外部:padx(水平),pady(竖直);内部:ipadx,ipady
pack的逻辑比较简单,也可以使用更复杂的函数,place()
和 grid()
,前者可以按照坐标安排组件,后者按照网格安排。 具体可参考:Python TKinter 布局管理 (Pack Place Grid)
当点击“会”或“不会”按钮时,触发的函数是self.yes_command
和self.no_command
,定义如下:
def yes_command(self):
self.yes_button.destroy()
self.no_button.destroy()
load = Image.open('end.jpg').resize((180, 180))
# load = array_to_image('./icon/end.bin')
render = ImageTk.PhotoImage(load)
self.img.destroy()
self.img = Label(self, image=render, width='100', height='100')
self.img.image = render
self.img.pack(expand=YES, fill=BOTH)
self.label.destroy()
self.label = Label(self, text='真乖 ~', font=('微软雅黑', 16))
self.label.pack(pady=30, fill=X, padx=10)
self.after(1500, self.quit)
return
def no_command(self):
no_window = ChildWindow(self)
self.wait_window(no_window) # 此句必须有
self.quit()
return
点击“不会”按钮,当前窗口图片改变,文本改变,延时1.5s后程序退出。
点击“会”按钮,弹出新窗口。
至此,主窗口的程序已经全部完成,主函数:
if __name__ == '__main__':
app = APP()
app.master.title('嘿嘿嘿') # 主窗口的title
app.mainloop()
弹窗
弹窗需要用到模块Toplevel
(不知道为啥Frame不能弹窗,有了解的可以留言)
实现和主窗口差不多,不过多介绍了。
class ChildWindow(Toplevel):
images = [[1, 2, 3, 4], [-1]] # 图片名称
# images = [[array_to_image('./icon/%d.bin' % i) for i in range(1, 5)],
# [array_to_image('./icon/%d.bin' % -1)]]
labels = [['不对不对!', '再给一次机会', '你好笨哦', '最后一次'], ['不会有的别想了!', '哈哈哈哈承认了吧', '略略略略略']] # 文本标签内容
def __init__(self, parent=None):
super().__init__()
self.title('不会吗?')
self.parent = parent
self.geometry('400x300')
self.create_widgets()
def create_widgets(self):
if ChildWindow.labels[0]:
file_num = ChildWindow.images[0][0]
ChildWindow.images[0].remove(file_num)
load = Image.open('%d.jpg' % file_num).resize((180, 180))
# load = ChildWindow.images[0][0]
ChildWindow.images[0].pop(0) # 使用一个删除一个
render = ImageTk.PhotoImage(load)
self.img = Label(self, image=render, width='100', height='100')
self.img.image = render
self.img.pack(expand=YES, fill=BOTH)
label = ChildWindow.labels[0][0]
ChildWindow.labels[0].remove(label)
self.label = Label(self, text=label, font=('微软雅黑', 16))
self.label.pack(pady=20, fill=X, padx=10)
self.yes_button = Button(self, text='不会', width='10', height='1', command=self.yes_command)
self.yes_button.pack(side='left', expand=True, padx=10, pady=10)
self.no_button = Button(self, text='会', width='10', height='1', command=self.no_command)
self.no_button.pack(side='right', expand=True, padx=10, pady=10)
else:
load = Image.open('-1.jpg').resize((180, 180))
# load = ChildWindow.images[1][0]
render = ImageTk.PhotoImage(load)
self.img = Label(self, image=render, width='100', height='100')
self.img.image = render
self.img.pack(expand=YES, fill=BOTH)
self.label = Label(self, text=ChildWindow.labels[1][0], font=('微软雅黑', 24))
self.label.pack(pady=30, fill=X, padx=10)
self.after(700, self.destroy)
def yes_command(self):
self.yes_button.destroy()
self.no_button.destroy()
self.img.destroy()
self.label.destroy()
self.label = Label(self, text=ChildWindow.labels[1][1], font=('微软雅黑', 16))
self.label.pack(pady=50, fill=X, padx=10)
self.label2 = Label(self, text=ChildWindow.labels[1][2], font=('幼圆', 10))
self.label2.pack(pady=50, padx=30, side=RIGHT)
self.after(1200, self.destroy)
return
def no_command(self):
no_window = ChildWindow(self)
self.wait_window(no_window)
self.destroy()
return
以上就是程序的主体部分,基本功能已经完成。但是,作为朋友玩的小玩意还有点瑕疵:
- 图片文件是直接在文件夹中以.jpg格式出现的,是可以随便替换的
- Python代码也是可以随便更改的
做人就是要有追求完美的精神!所以我们来进行两步优化:
图片文件转二进制(这样就看不出来是图片啦啦啦)
python转exe
图片文件转二进制
本来我是想把图片直接用数组的形式保存到代码里,后面发现可以保存成二进制,感觉后者方便一点。
这里参考了 Python实现图片与数组的转化
直接上代码
图片转二进制
import numpy as np
import os
from PIL import Image
import pickle as p
def image_to_array(file_path):
result = np.array([]) # 新建numpy数组
image = Image.open(file_path).resize((180, 180)) # 打开
r, g, b = image.split() # 通道分离
r_arr = np.array(r).reshape(-1) # 每个通道展开成一维数组
g_arr = np.array(g).reshape(-1)
b_arr = np.array(b).reshape(-1)
result = np.concatenate((r_arr, g_arr, b_arr)) # 三个通道拼在一起
# 以原文件名+ .bin保存
file_name = os.path.basename(file_path).split('.')[0]
with open(file_name + '.bin', 'wb') as f:
p.dump(result, f)
print('ok')
从二进制文件读图片
def array_to_image(file_path):
with open(file_path, 'rb') as f:
image_array = p.load(f)
image_array = image_array.reshape(3, 180, 180)
r = Image.fromarray(image_array[0]).convert('L')
g = Image.fromarray(image_array[1]).convert('L')
b = Image.fromarray(image_array[2]).convert('L')
image = Image.merge('RGB', (r, g, b))
return image
这样就可以先用第一个函数把图片全部转为二进制文件,然后删掉图片:)
我这里给图片的二进制文件建了个单独的文件夹。
当然,主程序的读图代码也要修改,直接换成注释的部分即可。
Python 转 exe 文件
在命令提示符中输入
pip install pyinstaller
进入工程目录下输入:
pyinstaller -F -w gui.py
就可以在生成的dist文件夹下找到gui.exe啦
pyinstaller 的使用:
-F 生成单独的可执行文件
-w 去掉运行时的黑色命令行窗口(一般调试时可以看到窗口的输出)
-i 生成的可执行文件的图标,后面加图标地址
-h 帮助
图标大小必须是32*32,颜色256。
可以使用这个网站转换图片。
这部分参考:
如何将python3 gui界面(py文件)打包成exe文件
[Python] Pyinstaller将python代码打包成exe | Pyinstaller错误合集
代码下载
整个工程的全部代码:https://github.com/chenyr0021/Tkinter_GUI_Program
欢迎 star ~
如果你对后续的故事感兴趣
欢迎围观:
也欢迎关注我的沙雕学习公众号: