Python程序打包为exe可执行文件
这是Python学习笔记系列的第五篇。
欢迎关注零壹研究 【微信号: Research_LAB】
全文约6000字,阅读约需9分钟
一、引言
函数式编程是提高效率的好办法,因为把一些需要重复执行的代码进行了封装,以后可以重复调用。此外,函数式编程也有助于形成自顶向下的设计思维。打包成exe可执行文件主要是使程序能够在非python环境下也能得到执行,便于程序的分享使用。
二、turtle库:画图
turtle是python自带的标准库,不需要额外安装,可直接import turtle使用。这里以turtle画图作例子,演示函数式编程与打包。
在画图之前,需要了解这个画布呈现的方式。
画布就是一个小窗口,在调用turtle的时候作为画图呈现的平台。在画图中,无论是设置画布的位置大小,还是画图中使用到的画笔,都要使用像素作为单位。画图的第一步就是设置画布的位置。如上图,画布这个小窗口的定位和大小主要利用了四个字参数,startx、starty、width和height。后两者就是画布的宽度和长度,前两者是画布左上角顶点距离屏幕左边和屏幕顶部的距离。
为了准确定位画布,我们需要知道屏幕的width和height。值得注意的是,在程序中屏幕分辨率并不一定是屏幕设置中的参数,显示设置1920*1080的屏幕在这里并不一定就是这一分辨率。因此需要检测。
这里需要调用win32api检测,需要安装pywin32包。(在cmd中执行pip install pywin32)
然后输入:
import win32api,win32con # 需要安装pywin32包
print("Width:",win32api.GetSystemMetrics(win32con.SM_CXSCREEN))
print("Height:",win32api.GetSystemMetrics(win32con.SM_CYSCREEN))
(注:文中代码界面均可左右移动)
在输出界面可以看到当前屏幕的分辨率:
因此,尽管是1920*1080的屏幕。但是在这里像素只有1280*720。
根据这一分辨率,就可以设置画布了:
t.setup(0.5,0.5,320,180)# Display Size is 1280*720,So the canvas is 640*360
t.bgcolor("black")
# turtle.setup(width=0.5, height=0.5, startx=None, starty=None)
# 参数:width, height: 输入宽和高为整数时, 表示像素; 为小数时, 表示占据电脑屏幕的比例
# (startx, starty): 这一坐标表示矩形窗口左上角顶点的位置, 如果为空,则窗口位于屏幕中心。
这里将画布设置成640*360,长度宽度各取屏幕一般,然后通过换算使得画布显示在屏幕正中央。画布中,画笔的默认初始状态是这样的:
画笔朝向:右
画笔位置:(0,0)
在画布中,又有一套自己的坐标系,以中心为(0,0)点,右边为正x轴,上方为正y轴。一切效果都是这个画笔实现的,随着画笔的轨迹变动,相应的图画也就产生了。
三、函数式编程: def
对于一些重复性的东西,完全可以用函数封装起来,只需要输入参数,就可以返回对应的结果,这样就可以极大的提高开发效率。在Python中,一个个标准库和第三方库就是大型的函数集合。
比如我要让画笔画出字母“C”,我可以用函数封装起来,只要调用这个函数,输入参数,就能让画笔画出对应的字母,在需要绘画大量字母的时候就省去了每次都要重新编程的冗余工作。
编写函数最重要的就是标准化,并且适应性要强。在不同的场景中,我肯定需要不同大小的字母“C”,那么就要设定参数使得字母“C”可以根据输入的参数变化大小。为了标准化,可以将字母C放置在一个矩形里面,类似于:
统一设定画笔在左下角,朝向右边,以此为起点进行绘制,绘制完毕后回到该点,朝向不变。C可以用半圆表示,为了控制其大小,可以将高度设定为参数x,一切距离用这个x表示,那么整个字母大小就可以等比例控制而不会变形。这里的思路为:
抬起画笔(不留轨迹)——前进0.5x——放下画笔(开始有轨迹)——在该点顺时针画半圆——抬起画笔——顺着轨迹回到0.5x点——返回原点——放下画笔(使画笔回到初始状态)
这样一来,画笔从开始到绘制结束,这个函数调用以后仍然保持了初始状态,可以很好的与下一步进行对接,提高效率。否则,下一个函数调用还要把画笔调整到对应位置,十分麻烦。
对于这个操作,可以命名为C_Image(x=80)。设定的x=80是一个默认参数,如果不输入数字,将会默认为矩形高度为80像素。也可以输入其他数字从而改变字母C的大小。代码如下:
import turtle as t
def C_Image(x=80):#python中用def定义函数
t.up()#抬起画笔
t.fd(0.5*x)#前进0.5x距离
t.down()#放下画笔
t.circle(0.5*x,-180)#逆时针画半圆
t.up()#抬起画笔
t.circle(0.5 * x, 180)#半圆原路返回
t.bk(0.5*x)#后退0.5x
t.down()#放下画笔
同理,可以设计N_Image(x=80)、I_Image(x=80)等等。
字母N:
import turtle as t
def N_Image(x=80):
t.left(90)
t.fd(x)
t.right(180-26.565)
t.fd(pow(1.25,0.5)*x)
t.left(180-26.565)
t.fd(x)
t.up()
t.left(90)
t.fd(0.5*x)
t.left(90)
t.fd(x)
t.left(90)
t.down()
字母I:
import turtle as t
def I_image(x=80):
t.fd(0.5*x)
t.bk(0.25*x)
t.left(90)
t.fd(x)
t.right(90)
t.fd(0.25*x)
t.bk(0.5*x)
t.right(90)
t.up()
t.fd(x)
t.left(90)
t.down()
至于一些稍复杂的图形绘制,就纯粹是数学问题了。无非是计算出各个“路线”的长度,让画笔按照相应的路径进行绘制。
四、调用函数绘图:main()
封装好函数以后,需要一个主程序进行调用,构成最终绘图。这里可以设计一个main()函数对各个函数进行调用,构成最终图像。
由于在字母和字母之间经常需要不留轨迹地让画笔前进后退移动,这里就封装了两个函数right_move(x=40)和left_move(x=40),让画笔抬起——右移(左移)x距离——放下画笔,便于主函数这里挪动画笔位置。
import turtle as t
def left_move(x=40):
t.up()
t.bk(x)
t.down()
def right_move(x=40):
t.up()
t.fd(x)
t.down()
主函数可以这样设计:
import turtle as t
def main():
# set canvas.
t.setup(0.5, 0.5, 320, 180) # Display Size is 1280*720,So the canvas is 640*360
left_move(220)# move the pen.
C_Image(x=80)
right_move(100)
N_Image(x=80)
right_move(80)
I_image()
right_move(80)
Love_Image()
right_move(130)
U_image()
t.hideturtle()# hide the pen.
t.up()
t.setx(150)
t.sety(-60)
t.color("red")
t.write("Jerry Mei, ECNU.", move=True, align="left", font=("Arial", 10, "normal"))# write my name.
t.down()
运行效果:
五、打包为exe可执行文件
重头戏来了。
这样一个程序,在安装python环境中执行也不太容易,比如用pycharm运行还需要设置运行名称,py文件地址,python解释器指定等等。用cmd运行还得打开cmd命令,键入py文件地址等等,还要考虑python有没有放到环境变量里面。
要是能够打包成exe就好了,双击运行,只要是windows终端都可以。
pyinstaller包就提供了这么一种方法,可以把python程序打包成exe可执行文件,极大方便了程序的跨平台运行。
首先需要安装pywin32和pyinstaller包。可以在cmd中用以下命令安装:
pip install pywin32
pip install pyinstaller
安装好以后就可以用cmd键入命令打包文件。命令格式为:
pyinstaller -F D:\Desktop\xxxx.py
回车后,
在运行这条命令的路径下面会生成两个文件夹,一个build是工程文件,可以删除,另一个dist文件夹下面就存放着需要的exe文件。也可以给程序添加图标等等,百度pyinstaller参数设置即可。
需要注意的是,文件路径要求比较高,路径中最好不要出现空格或者中文字符等特殊字符,否则可能会报错。
双击打开exe,会跳出cmd界面,会有一小会延迟,过一会就会开始自动执行。
这样以来,编写的python文件就可以在没有安装python环境的计算机中运行了。当然,需要注意的是这个打包程序应该只适用于比较简单的程序(比如这里的画图),如果涉及大量前端界面交互,或者需要调用较为复杂的接口等,可能无法实现一键打包。
上述python文件完整代码如下:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#-------------------------------
# Time :2019/1/29 21:23
# Author :Jerry Mei, ECNU
# Email :meicczj@163.com
# IDE :PyCharm
#-------------------------------
import time
import turtle as t
import win32api,win32con # 需要安装pywin32包
print(time.strftime("%Y-%m-%d %H:%M:%S:", time.localtime()), "程序开始!")
StartTime = time.perf_counter()
print("Display Width:",win32api.GetSystemMetrics(win32con.SM_CXSCREEN))
print("Display Height:",win32api.GetSystemMetrics(win32con.SM_CYSCREEN))
# turtle.setup(width=0.5, height=0.5, startx=None, starty=None)
# 参数:width, height: 输入宽和高为整数时, 表示像素; 为小数时, 表示占据电脑屏幕的比例
# (startx, starty): 这一坐标表示矩形窗口左上角顶点的位置, 如果为空,则窗口位于屏幕中心。
def C_Image(x=80):
t.up()
t.fd(0.5*x)
t.down()
t.circle(0.5*x,-180)
t.up()
t.circle(0.5 * x, 180)
t.bk(0.5*x)
t.down()
def N_Image(x=80):
t.left(90)
t.fd(x)
t.right(180-26.565)
t.fd(pow(1.25,0.5)*x)
t.left(180-26.565)
t.fd(x)
t.up()
t.left(90)
t.fd(0.5*x)
t.left(90)
t.fd(x)
t.left(90)
t.down()
def I_image(x=80):
t.fd(0.5*x)
t.bk(0.25*x)
t.left(90)
t.fd(x)
t.right(90)
t.fd(0.25*x)
t.bk(0.5*x)
t.right(90)
t.up()
t.fd(x)
t.left(90)
t.down()
def Love_Image(h=80):# height=(3*2^(0.5)+2)/4*x=80,x=4*h/(3*2^(0.5)+2)
x = 4*h / (3 * pow(2,0.5) + 2)
t.up()
t.fd((0.5+0.25*pow(2,0.5))* x)
t.down()
t.color("red","pink")
t.begin_fill()
t.left(45)
t.fd(x)
t.circle(0.5*x,180)
t.right(90)
t.circle(0.5*x,180)
t.fd(x)
t.end_fill()
t.left(45)
t.up()
t.bk((0.5+ 0.25 * pow(2,0.5)) * x)
t.down()
t.color("black")
def U_image(x=80):
t.left(90)
t.fd(x)
t.right(90)
t.up()
t.fd(0.5*x)
t.down()
t.right(90)
t.fd(x)
t.left(90)
t.bk(0.5*x)
def left_move(x=40):
t.up()
t.bk(x)
t.down()
def right_move(x=40):
t.up()
t.fd(x)
t.down()
def main():
#set canvas.
t.setup(0.5, 0.5, 320, 180) # Display Size is 1280*720,So the canvas is 640*360
left_move(220)#move the pen.
C_Image(x=80)
right_move(100)
N_Image(x=80)
right_move(80)
I_image()
right_move(80)
Love_Image()
right_move(130)
U_image()
t.hideturtle()# hide the pen.
t.up()
t.setx(150)
t.sety(-60)
t.color("red")
t.write("Jerry Mei, ECNU.", move=True, align="left", font=("Arial", 10, "normal"))#write my name.
t.down()
main()
# t.right(180)
# t.done() # 使得画面停留
time.sleep(5)#使画面停留5秒
EndTime = time.perf_counter()
print("程序总共用时:{:.2f}秒".format(EndTime - StartTime))
print(time.strftime("%Y-%m-%d %H:%M:%S:", time.localtime()), "程序结束!")
--End--
好看!