python实战笔记之(10):使用pyinstaller打包python程序

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/polyhedronx/article/details/81740818

做了一个图形界面的小程序,想要分享给别人的话,就要把它打包成exe文件,这样其他人不用安装python环境还有各种库就可以使用了。

在打包之前,要保证你当前使用的python环境是“干净”的,什么是干净的,为什么要是干净的的呢?这是因为我们打包的时候只需要把程序中用到的包打包就行了,如果你当前使用的环境安装的包太多的话,比如你用的是Anaconda自带的python环境,里面包含了大量的我们根本用不到的各种包,但是打包过程中很有可能把相关的依赖包全都搞在一起了,所以最后生成的exe文件就会非常大,一个非常小的小程序打包完可能都会有200M多。此外,在代码里面尽量不要用import,能from...import...就尽量用这个,因为如果是import的话,会将整个包都打包到exe里面,增大了生成的exe文件的大小。

如果你有Anaconda,为了保证我们的python环境是干净的,可以用conda命令新建一个环境:

conda create -n python36 python=3.6

运行完这条命令,你就拥有了一个新的python3.6环境,它在Anaconda的envs文件夹下,你需要把它添加到环境变量中去(下面的图很直观了)。如有你也可以创建其它版本的python环境,比如python2.7等。conda仅安装了python 3.6相关的必须项,如python、pip等,所以新环境是非常干净的,我们可以根据程序需要再安装一些必要的包就可以了。

想要更新包可以使用这个命令:

pip install --upgrade xxxxx  # xxxxx改为要更新的包

Anaconda 的安装、配置和基本使用可以参考这里:https://blog.csdn.net/imbenben/article/details/78046997

搞好python环境我们就可以开始安装pyinstaller了,这个也很简单,但需要注意的是,你现在已经有了不止一个python环境,可以用“where python”查看当前的所有python环境。

为了保证我们是安装在了正确的python环境中,需要打开python36中的“Scripts”文件夹,在此文件夹中右键并点击“OpenCMDHere”,这时再运行pip命令就是安装在了这个文件夹中。

或者我们可以把该文件夹下的pip.exe复制一份并改成其他名字,如pip36.exe,这样我们就可以使用pip36命令安装了而不用再切换到该文件夹,因为这时候系统只能找到这一个命令,这样就能放心地用而不用担心安装错地方了。

现在我们先来安装pywin32:

pip install pywin32

再安装pyinstaller:

pip install pyinstaller

下面就可以打包文件了,打开CMD,将路径cd到你的文件目录,然后:

pyinstaller -F -w myfile.py -i cat.ico

其中 -F 和 -w 是打包参数,-F 表示生成单个可执行文件,-w 表示去掉控制台窗口,-i 表示可执行文件的图标。想获取图标的话可以从这个网站找:https://www.easyicon.net/

另外要注意,你要打包的程序文件(比如myfile.py)的路径中不要有中文。还有如果你的代码是存在依赖的,即多文件的,而非所有代码都在一个文件中的,不要使用-F。

其它参数及意义如下,原文见:https://pyinstaller.readthedocs.io/en/v3.3.1/usage.html

通用参数

参数名 描述 说明
-h 显示帮助
-v 显示版本号
–distpath 生成文件放在哪里 默认:当前目录的dist文件夹内
–workpath 生成过程中的中间文件放在哪里 默认:当前目录的build文件夹内
-y 如果dist文件夹内已经存在生成文件,则不询问用户,直接覆盖 默认:询问是否覆盖
–upx-dir UPX_DIR 指定upx工具的目录 默认:execution path
-a 不包含unicode支持 默认:尽可能支持unicode
–clean 在本次编译开始时,清空上一次编译生成的各种文件 默认:不清除
–log-level LEVEL 控制编译时pyi打印的信息 一共有5个等级,由低到高分别为TRACE DEBUG INFO(默认) WARN ERROR CRITICAL。也就是默认清空下,不打印TRACE和DEBUG信息

与生成结果有关的参数

参数名 描述 说明
-D 生成one-folder的程序(默认) 生成结果是一个目录,各种第三方依赖、资源和exe同时存储在该目录
-F 生成one-file的程序 生成结果是一个exe文件,所有的第三方依赖、资源和代码均被打包进该exe内
–specpath 指定.spec文件的存储路径 默认:当前目录
-n 生成的.exe文件和.spec的文件名 默认:用户脚本的名称,即main.py和main.spec

指定打包哪些资源、代码

参数名 描述 说明
–add-data 打包额外资源 用法:pyinstaller main.py –add-data=src;dest。windows以;分割,linux以:分割
–add-binary 打包额外的代码 用法:同–add-data。与–add-data不同的是,用binary添加的文件,pyi会分析它引用的文件并把它们一同添加进来
-p 指定额外的import路径,类似于使用PYTHONPATH 参见PYTHONPATH
–hidden-import 打包额外py库 pyi在分析过程中,有些import没有正确分析出来,运行时会报import error,这时可以使用该参数
–additional-hooks-dir 指定用户的hook目录 hook用法参见其他,系统hook在PyInstaller\hooks目录下
–runtime-hook 指定用户runtime-hook 如果设置了此参数,则runtime-hook会在运行main.py之前被运行
–exclude-module 需要排除的module pyi会分析出很多相互关联的库,但是某些库对用户来说是没用的,可以用这个参数排除这些库,有助于减少生成文件的大小
–key pyi会存储字节码,指定加密字节码的key 16位的字符串

生成参数

参数名 描述 说明
-d 执行生成的main.exe时,会输出pyi的一些log,有助于查错 默认:不输出pyi的log
-s 优化符号表 原文明确表示不建议在windows上使用
–noupx 强制不使用upx 默认:尽可能使用。

其他

参数名 描述 说明
–runtime-tmpdir 指定运行时的临时目录 默认:使用系统临时目录

Windows和Mac特有的参数

参数名 描述 说明
-c 显示命令行窗口 与-w相反,默认含有此参数
-w 不显示命令行窗口 编写GUI程序时使用此参数有用。
-i 为main.exe指定图标 pyinstaller -i beauty.ico main.py

Windows特有的参数

参数名 描述 说明
–version-file 添加版本信息文件 pyinstaller –version-file ver.txt
-m, –manifest 添加manifest文件 pyinstaller -m main.manifest
-r RESOURCE 请参考原文  
–uac-admin 请参考原文  
–uac-uiaccess 请参考原文  

运行结束后会多出两个文件夹和一个spec文件,生成的exe文件就在dist文件夹中。

但是如果直接点击运行的话会出现下面的错误:

这是因为我的程序中调用了一个图片文件:

# 插入背景图片
image = Image.open('python_logo.gif')
bg_img = ImageTk.PhotoImage(image)
label_img = Label(top, image=bg_img, cursor='spider')

所以我们需要把该图片和生成的exe文件放在一个文件夹下:

这样exe文件就能正常运行了:

 

把依赖文件(也就是这个gif图片)拷贝到生成应用的目录这种方法虽然可行,但每次都要拷贝文件未免太过麻烦,而且依赖文件越多拷贝起来就越烦,我们希望的是生成exe文件的同时就把这些图片等依赖文件全部都打包。这时,我们可以通过操作spec文件解决这个问题。

首先,在对应的文件目录下打开CMD,输入如下命令即可生成对应的spec文件:

pyi-makespec xxxxx.py

生成的spec文件的内容如下:

# -*- mode: python -*-

block_cipher = None


a = Analysis(['crawl_video.py'],
             pathex=['E:\\PycharmProjects\\zhihu_video'],
             binaries=[],
             datas=[],
             hiddenimports=[],
             hookspath=[],
             runtime_hooks=[],
             excludes=[],
             win_no_prefer_redirects=False,
             win_private_assemblies=False,
             cipher=block_cipher)
pyz = PYZ(a.pure, a.zipped_data,
             cipher=block_cipher)
exe = EXE(pyz,
          a.scripts,
          exclude_binaries=True,
          name='crawl_video',
          debug=False,
          strip=False,
          upx=True,
          console=True )
coll = COLLECT(exe,
               a.binaries,
               a.zipfiles,
               a.datas,
               strip=False,
               upx=True,
               name='crawl_video')

spec文件中主要包含4个class: Analysis, PYZ, EXE和COLLECT

  • Analysis以py文件为输入,它会分析py文件的依赖模块,并生成相应的信息

  • PYZ是一个.pyz的压缩包,包含程序运行需要的所有依赖

  • EXE根据上面两项生成

  • COLLECT生成其他部分的输出文件夹,COLLECT也可以没有

我们上面说过,有时候pyInstaller自动生成的spec文件并不能满足我们的需求,最常见的情况就是我们的程序依赖我们本地的一些数据文件,这个时候就需要我们自己去编辑spec文件来添加数据文件了。
上面的spec文件解析中Analysis中的datas就是要添加到项目中的数据文件,我们可以编辑datas:

a = Analysis(
    ...
    datas = [('you/source/file/path','file_name_in_project'),
    ('source/file2', 'file_name2')]
    ...
    )

可以认为datas是一个List,每个元素是一个二元组。元组的第一个元素是你本地文件索引,第二个元素是拷贝到项目中之后的文件名字。将其写作[(a,b)],a表示依赖所在的文件夹路径或者文件路径,如果a表示文件夹路径,那么文件夹下面的所有文件都会被拷贝到应用文件夹;b表示应用文件夹下的目标路径,b参数空缺表示依赖文件全部拷贝到根目录下。如果要拷贝到特定的目录下,那么需要写作‘.\\icon’,icon就是文件夹的名字(当然可以换成其他名字)。

除了上面那种写法,也可以将其提出来。

added_files = [...]

a = Analysis(
    ...
    datas = added_files,
    ...
    )

比如我们想把一个图片添加进去,就要修改datas参数:

datas=[('E:\\PycharmProjects\\zhihu_video\\python_logo.gif','')]

注意,如果你不想生成带命令行窗口的应用,还需要把“console”参数的值改为False;如果想要给应用更换图标的话,需要添加icon参数,COLLECT部分也可以去掉:

# -*- mode: python -*-

block_cipher = None


a = Analysis(['crawl_video.py'],
             pathex=['E:\\PycharmProjects\\zhihu_video'],
             binaries=[],
             datas=[('E:\\PycharmProjects\\zhihu_video\\python_logo.gif', '')],
             hiddenimports=[],
             hookspath=[],
             runtime_hooks=[],
             excludes=[],
             win_no_prefer_redirects=False,
             win_private_assemblies=False,
             cipher=block_cipher)
pyz = PYZ(a.pure, a.zipped_data,
             cipher=block_cipher)
exe = EXE(pyz,
          a.scripts,
          a.binaries,
          a.zipfiles,
          a.datas,
          name='crawl_video',
          debug=False,
          strip=False,
          upx=True,
          runtime_tmpdir=None,
          console=False , icon='mimi.ico')

编辑完保存,再执行如下语句即可生成应用:

pyinstaller xxxxx.spec

但是...虽然生成了exe文件,但还是会报错,仍然需要把依赖图片拷贝到和exe相同的文件夹下:

WTF。。。我也不知道这是为什么!

找了半天,终于在一个贴吧里找到了一个解决方案,解决方法就是对图片文件进行编码转换成py文件,然后就能一起打包了,而且不需要操作spec文件:

#  pic_to_py.py

import base64


def gif_to_py(picture_name):
	open_gif = open("%s.gif" % picture_name, 'rb')
	b64str = base64.b64encode(open_gif.read())
	open_gif.close()
	write_data = 'img = "%s"' % b64str.decode()
	f = open('%s.py' % picture_name, 'w+')
	f.write(write_data)
	f.close()


if __name__ == '__main__':
	picture = ['python_logo']
	for picture_position in picture:
		gif_to_py(picture_position)

我转换的是gif图片,如果需要转换别的类型的图片自己在程序里修改就行了。

然后在你的tkinter文件内导入你生成的py文件:

import base64
from pictures.python_logo import img as logo


tmp = open('tmp.gif', 'wb+')  # 临时文件用来保存gif文件
tmp.write(base64.b64decode(logo))
tmp.close()
image = Image.open('tmp.gif')
bg_img = ImageTk.PhotoImage(image)
label_img = Label(top, image=bg_img, cursor='spider')
os.remove('tmp.gif')

接着使用pyinstaller正常操作,不需要对spec文件作出修改,生成的exe文件就能正常使用了:

pyinstaller -F -w crawl_video.py -i mimi.ico

参考内容:

https://www.landui.com/help/show-3768.html

https://jingyan.baidu.com/article/63f2362827511d0209ab3d51.html

https://jingyan.baidu.com/article/b2c186c8d9856dc46ff6ff54.html

https://my.oschina.net/u/150309/blog/123262

http://tieba.baidu.com/p/3060401749

猜你喜欢

转载自blog.csdn.net/polyhedronx/article/details/81740818