python3采用pyinstaller进行项目工程打包一些探索

文章目录

  • 一、Pyinstaller的安装
    • 1.Pyinstaller简介
    • 2.Pyinstaller安装及参数
    • 3.Pyinstaller基础用法
      • 3.1.1 PyInstaller使用
      • 3.1.2 单目录模式
      • 3.1.3 单文件模式
  • 二、Pyinstaller项目打包
    • 1. 执行命令打包单个文件
    • 2.打包多个文件
      • 2.1 start_agent.spec配置文件详解
  • 三、打包出现的问题

pyinstaller打包项目

一、Pyinstaller的安装

1.Pyinstaller简介

  PyInstaller是一个跨平台的Python应用打包工具,支持 Windows/Linux/MacOS三大主流平台,能够把 Python 脚本及其所在的 Python 解释器打包成可执行文件,从而允许最终用户在无需安装 Python 的情况下执行应用程序。
  PyInstaller 制作出来的执行文件并不是跨平台的,如果需要为不同平台打包,就要在相应平台上运行PyInstaller进行打包。
  PyInstaller打包的流程:读取编写好的Python项目–>分析其中条用的模块和库,并收集其文件副本(包括Python的解释器)–>将副本和Python项目文件(放在一个文件夹//封装在一个可执行文件)中。

2.Pyinstaller安装及参数

pip install PyInstaller

  PyInstaller包的安装可以在Anaconda环境下以conda install pyinstaller进行安装,在PyCharm中可以通过pip install pyinstaller进行安装。安装成功后就可以着手进行打包了。当然打包需要用到以下一些相关命令了。
python3采用pyinstaller进行项目工程打包一些探索_第1张图片

  常用到的命令为-F、-D、-i、-p、-w等,其中-i用于指定生成项目的图标,需要使用绝对路径。对于打包结果较大的项目,选用-d生成目录相比单可执行文件的打包方式,执行速度更快,但包含更加多的文件。本文的例子选中-D方式打包。

3.Pyinstaller基础用法

3.1.1 PyInstaller使用

pyinstaller main.py

  PyInstaller 最简单使用只需要指定作为程序入口的脚本文件。PyInstaller 执行打包程序后会在当前目录下创建下列文件和目录:
 main.spec 文件,其前缀和脚本名相同,指定了打包时所需的各种参数;
 build 子目录,其中存放打包过程中生成的临时文件。warnxxxx.txt文件记录了生成过程中的警告/错误信息。如果 PyInstaller 运行有问题,需要检查warnxxxx.txt文件来获取错误的详细内容。
 xref-xxxx.html文件输出 PyInstaller 分析脚本得到的模块依赖关系图。
 dist子目录,存放生成的最终文件。如果使用单文件模式将只有单个执行文件;如果使用目录模式的话,会有一个和脚本同名的子目录,其内才是真正的可执行文件以及附属文件。

3.1.2 单目录模式

  单目录模式是 PyInstaller 将 Python 程序编译为同一个目录下的多个文件,其中 xxxx.exe 是程序入口点(xxxx 是脚本文件名称,可以通过命令行修改)。单目录模式是 PyInstaller 的默认模式,可以自己加上 -D 或者 --onedir 开关显式开启。
  单目录模式打包生成的目录除可执行文件外,还包括 Python 解释器(PythonXX.dll)、系统运行库(ucrtbase.dll 以及其它 apixx.dll),以及一些编译后的 Python 模块(.pyd 文件)。

3.1.3 单文件模式

  文件模式是将整个程序编译为单一的可执行文件。需要在命令行添加 -F 或者 --onefile 开关开启。
Python脚本是解释型程序,而不是 原生的编译程序,并不能产生出真正单一的可执行文件。如果使用单文件模式,PyInstaller打包生成的是自动解压程序,需要先把所有文件解压到一个临时目录(通常名为_MEIxxxx,xxxx是随机数字),再从临时目录加载解释器和附属文件。程序运行完毕后,如果一切正常,会将临时目录再删除。
  PyInstaller会对运行时的Python解释器修改。如果直接运行 Python 脚本,那么sys.frozen 变量不存在,如果通过 PyInstaller 生成的可执行文件运行,PyInstaller 会设置sys.frozen 变量为 True;如果使用单文件模式,sys._MEIPASS 变量包含了PyInstaller 自动创建的临时目录名。
  单文件模式因为有临时目录和解压文件过程,所以程序启动速度会比较慢。如果程序运行到一半崩溃,则临时目录将没有机会被删除。

二、Pyinstaller项目打包

  PyInstaller在Windows/Linux/Mac环境下的使用:执行命令相同,只需要在不同环境下执行即可

python3采用pyinstaller进行项目工程打包一些探索_第2张图片

1. 执行命令打包单个文件

# 1.执行命令
pyinstaller -F xxx.py

# 2.去生成的dist文件夹找xxx.exe运行

# 3.运行成功,xxx.exe则为可执行文件,删除其它文件

2.打包多个文件

# 1.执行命令,start_agent.py为程序入口文件
pyinstall -D ×××.py 

# 2.删除生成的bulid和dist文件夹,仅保留start_agent.spec文件

# 3.修改start_agent.spec文件,详见下

# 4.执行命令
pyinstaller -F ×××.spec

# 5.去dist文件夹下找start_agent文件

# 6.运行成功,删除临时文件目录build;dist目录为打包的结果,可执行文件和其它程序运行的关联文件都在这个目录下

2.1 start_agent.spec配置文件详解

# -*- mode: python ; coding: utf-8 -*-
import sys
sys.setrecursionlimit(5000)
block_cipher = None
# 以py文件为输入,分析py文件的依赖模块,并生成相应的信息
a = Analysis(['×××.py'],# 要打包.py文件名列表,和xxx.py同级可以不同添加
             pathex=['../workspaces/...'], # 项目路径
             binaries=[],# 程序调用外部pyd、dll文件(二进制文件路径)以数组形式传入;例:('D:\\pro\\text.dll', 'pro'),将'pdftotext.dll'pro,与原项目结构一致即可
             datas=[],# 存放的资源文件(图片、文本等静态文件)以数组形成传入;例:('D:\\static\\c.ioc','static'),将'cc.ioc'打包之后放在static目录,与原项目结构一致即可
             hiddenimports=[],# pyinstaller解析模块时可能会遗漏某些模块(not visible to the analysis phase),造成打包后执行程序时出现类似No Module named xxx;这时就需要在hiddenimports中加入遗漏的模块
             hookspath=[],
             runtime_hooks=[],
             excludes=[],# 去除不必要的模块import,写在excludes中添加此模块
             win_no_prefer_redirects=False,
             win_private_assemblies=False,
             cipher=block_cipher,
             noarchive=False)


# .pyz的压缩包,包含程序运行需要的所有依赖
pyz = PYZ(a.pure, a.zipped_data,
             cipher=block_cipher)
# 根据Analysis和PYZ生成单个exe程序所需要的属性及其配置
exe = EXE(pyz,
          a.scripts,
          [],
          exclude_binaries=True,
          name='×××',# 生成exe文件的名字
          debug=False,# debug模式
          bootloader_ignore_signals=False,
          strip=False,
          upx=True,
          console=True ) # 是否在打开exe文件时打开cmd命令框
# 收集前三个部分的内容进行整合,生成程序所需要的依赖包,及资源文件和配置
coll = COLLECT(exe,
               a.binaries,
               a.zipfiles,
               a.datas,
               strip=False,
               upx=True,
               upx_exclude=[],
               name='×××')

  对配置文件进行修改如下:

# -*- mode: python ; coding: utf-8 -*-
import sys
sys.setrecursionlimit(5000)
block_cipher = None
# 以py文件为输入,分析py文件的依赖模块,并生成相应的信息
a = Analysis(['×××.py'],# 要打包.py文件名列表,和xxx.py同级可以不同添加
             pathex=['../workspaces/...'], # 项目路径
             binaries=[],# 程序调用外部pyd、dll文件(二进制文件路径)以数组形式传入;例:('D:\\pro\\text.dll', 'pro'),将'pdftotext.dll'pro,与原项目结构一致即可
             datas=[],# 存放的资源文件(图片、文本等静态文件)以数组形成传入;例:('D:\\static\\c.ioc','static'),将'cc.ioc'打包之后放在static目录,与原项目结构一致即可
             hiddenimports=[],# pyinstaller解析模块时可能会遗漏某些模块(not visible to the analysis phase),造成打包后执行程序时出现类似No Module named xxx;这时就需要在hiddenimports中加入遗漏的模块
             hookspath=[],
             runtime_hooks=[],
             excludes=[],# 去除不必要的模块import,写在excludes中添加此模块
             win_no_prefer_redirects=False,
             win_private_assemblies=False,
             cipher=block_cipher,
             noarchive=False)

dict_database = Tree('../workspaces/.../common',prefix='common')
a.datas +=dict_database
dict_conf = Tree('../workspaces/.../conf',prefix='conf')
a.datas +=dict_conf
dict_logs = Tree('../workspaces/.../logs',prefix='logs')
a.datas +=dict_logs

# .pyz的压缩包,包含程序运行需要的所有依赖
pyz = PYZ(a.pure, a.zipped_data,
             cipher=block_cipher)
# 根据Analysis和PYZ生成单个exe程序所需要的属性及其配置
exe = EXE(pyz,
          a.scripts,
          [],
          exclude_binaries=True,
          name='×××',# 生成exe文件的名字
          debug=False,# debug模式
          bootloader_ignore_signals=False,
          strip=False,
          upx=True,
          console=True ) # 是否在打开exe文件时打开cmd命令框
# 收集前三个部分的内容进行整合,生成程序所需要的依赖包,及资源文件和配置
coll = COLLECT(exe,
               a.binaries,
               a.zipfiles,
               a.datas,
               strip=False,
               upx=True,
               upx_exclude=[],
               name='×××')

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

  • Analysis以py文件为输入,它会分析py文件的依赖模块,并生成相应的信息
  • PYZ是一个.pyz的压缩包,包含程序运行需要的所有依赖
  • EXE根据上面两项生成
  • COLLECT生成其他部分的输出文件夹,COLLECT也可以没有

中间部分添加了缺失的依赖
中间添加了依赖文件夹的绝对路径,是和这个路径相对应的
pathex=[’…/workspaces/...’],
这个路径是之前工具自己生成的,所以我们添加的两个目录的绝对路径也和它保持一致,这个修改完成后久可以了

三、打包出现的问题

  • 编辑.spec文件路径相关
    1.windows尽量使用绝对路径,用双斜杠\ \
    2.linux路径/home/my_project/web
    3.路径避免使用中文
    
- 打包.spec文件报错:RecursionError: maximum recursion depth exceeded
```python
    1.在spec文件上添加递归深度的设置
    import sys
    sys.setrecursionlimit(5000)
  • no modules named xxxx
    出现该问题是打包后的根目录中没有所需要的模块,因此可以将对应安装库中的文件复制到根目录中,此错误就不再出现。或者可以在hiddenimports中加入缺失的包。或者在datas中作同样的处理。
  • 路径冻结
    增加一个py文件,例如叫frozenPath.py
import sys
import os
def app_path():
    """Returns the base application path."""
    if hasattr(sys, 'frozen'):
        # Handles PyInstaller
        return os.path.dirname(sys.executable)
    return os.path.dirname(__file__)

其中的app_path()函数返回一个程序的执行路径,为了方便我们将此文件放在项目文件的根目录,通过这种方式建立了相对路径的关系。源代码中使用路径时,以app_path()的返回值作为基准路径,其它路径都是其相对路径。以本文中使用的python项目打包为例,如下所示:

import frozenPath
# 根目录路径
appPath = frozenPath.app_path()
background = QtGui.QPixmap(appPath+"/img/1.png")

你可能感兴趣的:(软件各种操作快捷)