【Python】使用Tkinter实现多弹窗图形界面程序并转为.exe文件 && 图片文件转为二进制/数组

最近闲来无事不想学习,就学了一下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_commandself.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 ~

如果你对后续的故事感兴趣

欢迎围观:

工科直女如何优雅地反击来自室友的嘲讽

也欢迎关注我的沙雕学习公众号:

一本正经的搬砖日常

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值