PyInstaller打包成exe
使用插件:
PyInstaller
安装
安装方式
pip install PyInstaller
问题
ImportError: 'module' object has no attribute 'check_specifier
如果出现上面的问题,先尝试更新:
pip install --upgrade setuptools
再执行安装指令
如果上述操作存在问题,使用 管理员 试试!
使用
windows用法
windows下使用gb2312保存文件
echo off
rem 请把python:path/Scripts添加到系统路径
rem cxfreeze,pyinstaller都在这个路径下
rem !!!在windows下编写bat,路径使用[\],不要使用 [/]
rem py文件,不要带后缀[.py]
set py_file=test
rem 中间目录
set out_dir=.\dist
rem 自己的模块位置
set my_py_module_path=..\
rem 图标icon
set icon=test.ico
echo %cd%
rem warning: 在执行完毕会删除中间目录,同时会把exe拷贝到上层目录
rem 所以建议把中间目录就设置在工作目录下
rem 文档:https://pyinstaller.readthedocs.io/en/v3.5/
rem LEVEL may be one of TRACE, DEBUG, INFO, WARN, ERROR, CRITICAL (default: INFO).
pyinstaller -F -c --distpath=%out_dir% --workpath=%out_dir%\build --specpath=%out_dir%\build --path=%my_py_module_path% --name=%py_file%.exe -y --clean --log-level=WARN --icon=..\..\%icon% %py_file%.py
rem 复制到当前目录
copy /Y /B %out_dir%\%py_file%.exe .\
rem 删除目录
rd /Q /s %out_dir%
echo "finish!"
linux用法
#!/bin/sh
# py文件,不要带后缀[.py]
py_file=test
# 中间目录
out_dir=./dist
# 自己的模块位置
my_py_module_path=.
# 获取当前工作目录
cd=$(cd `dirname $0`; pwd)
echo $cd
pyinstaller -F -c --distpath=$out_dir --workpath=$out_dir/build --specpath=$out_dir/build --path=$my_py_module_path --name=$py_file.app -y --clean --log-level=WARN --add-data=$cd/"resource/gdb_show_details.cmd:resource" $py_file.py
cp -f $out_dir/$py_file.app ./
rm -rf $out_dir
echo "finish!"
上面的命令行是个人使用,放在这里仅供参考!
PyInstaller
命令解释参考文档!
最终成功生成exe
问题
上面的命令行能执行成功,但是有坑
files_copy.py和copy.ico在同一个目录,但是2个文件指定路径不一样
如果要使用同一目录: –icon=copy.ico files_copy.py
测试结果是去掉 –specpath 就好了
对比
名字 | 安装难度 | 使用难度 | 单文件 | 文件大小 | 坑 |
---|---|---|---|---|---|
cx_Freeze | 易- | 易 | 不支持 | python27.dll:2.34 MB | 安装时 |
cx_Freeze | exe:1.17 MB | ||||
pyinstaller | 易 | 易 | 支持 | exe:4.20 MB | 命令行 |
技术点
–add-data
使用此命令的资源如何取出 ?
def pkg_res(filename):
if hasattr(sys, '_MEIPASS'):
base_path = sys._MEIPASS
else:
base_path = os.path.abspath('.')
return os.path.join(base_path, filename)
def read_f():
f_res = pkg_res("resource/gdb_show_details.cmd")
with open(f_res, 'rt') as fr:
print(fr.read())
进阶1
把 隐藏 或者 没有显示import的python代码 打进包内.
如运行时动态加载的python代码, 在用pyinstaller打包时,是不会打进去的. 解决这个问题的方式比较简单,也算是有些另类.
先用上面的命令行成功打出执行程序, 在打出执行程序后,在 dist
也可能是 build
目录下会生成一个 spec
文件.
下面贴一个spec文件(我的spec文件全名是main.exe.spec)内容示例:
# -*- mode: python ; coding: utf-8 -*-
block_cipher = None
a = Analysis(['main.py', 'package.py'],
pathex=['', '.\\dist\\build'],
binaries=[],
datas=[],
hiddenimports=[],
hookspath=[],
runtime_hooks=[],
excludes=[],
win_no_prefer_redirects=False,
win_private_assemblies=False,
cipher=block_cipher,
noarchive=False)
pyz = PYZ(a.pure, a.zipped_data,
cipher=block_cipher)
exe = EXE(pyz,
a.scripts,
a.binaries,
a.zipfiles,
a.datas,
[],
name='main.exe',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
upx_exclude=[],
runtime_tmpdir=None,
console=True , icon='main.ico')
注意 可以要修改spec文件里的路径.
创建一个新的py文件叫package.py(名字没有要求,自己随意), 把隐式加载的py全部在这个文件里进行import, 这个文件只有import, 测试案例的内容如下:
# -*- mode: python ; coding: utf-8 -*-
# 此文件存在的意义是为了配合pyinstaller工具打包
# 并没有实际意义
from my.lib1.code import *
from my.lib1.instructions import *
from my.lib2.code import *
from my.lib2.instructions import *
我的py的入口代码文件是main.py
, 在spec里找到['main.py']
, 一眼看出这是个数组,在这个数组里添加package.py
, 重新打包. 执行程序里就有隐式的py代码了.
进阶2
电脑里同时安装了py27, py38(py3+) , 环境(命令行)默认是py27, 怎么为py38安装pyinstaller?
进入到py38的安装目录, 执行命令行:
.\Scripts\pip.exe install pyinstaller
进阶3
电脑里同时安装了py27, py38(py3+) 怎么指定py版本进行打包.
参考下面bat文件内容:
echo off
set PYTHON=D:\Python38_32;D:\Python38_32\lib;\D:\Python38_32\DLLs;D:\Python38_32\libs;D:\Python38_32\Scripts
set path=%path%;%PYTHON%
D:\Python38_32\Scripts\pyinstaller main.exe.spec
rem py文件,不要带后缀[.py]
set py_file=main
rem 中间目录
set out_dir=.\dist
rem 复制到当前目录
copy /Y /B %out_dir%\%py_file%.exe .\
rem 删除目录
rd /Q /s %out_dir%
rd /Q /s .\build
echo "finish!"
pause