参考博客 博客1  博客2

首先是安装pyinstaller模块,这个很简单,直接pip install pyinstaller就行了。我们就重点讲讲怎么用吧。先从最简单的开始。。。。

  写个简单的脚本holle.py

pyinstaller 加密打包 python脚本总结_python hello.py

这里简单介绍一下pyinstaller的参数使用:

-h,--help 查看该模块的帮助信息
-F,-onefile 产生单个的可执行文件
-D,--onedir 产生一个目录(包含多个文件)作为可执行程序
-a,--ascii 不包含 Unicode 字符集支持
-d,--debug 产生 debug 版本的可执行文件
-w,--windowed,--noconsolc 指定程序运行时不显示命令行窗口(仅对 Windows 有效)
-c,--nowindowed,--console 指定使用命令行窗口运行程序(仅对 Windows 有效)
-o DIR,--out=DIR 指定 spec 文件的生成目录。如果没有指定,则默认使用当前目录来生成 spec 文件
-p DIR,--path=DIR 设置 Python 导入模块的路径(和设置 PYTHONPATH 环境变量的作用相似)。也可使用路径分隔符(Windows 使用分号,Linux 使用冒号)来分隔多个路径
-n NAME,--name=NAME 指定项目(产生的 spec)名字。如果省略该选项,那么第一个脚本的主文件名将作为 spec 的名字

在表 1 中列出的只是 PyInstaller 模块所支持的常用选项,如果需要了解 PyInstaller 选项的详细信息,则可通过 pyinstaller -h 来查看。

打开终端,进入到hello.py的所在目录,执行 pyinstaller -F hello.py。执行完会自动生成build、dist目录和hello.spec文件。

pyinstaller 加密打包 python脚本总结_相对路径_02

咱要的exe文件在dist目录下,其他的就没有用了。直接把dist目录下的 hello.exe文件拷贝到其他地方,比如桌面,然后在桌面创建一个123.txt文件,随便写点什么。主要测试一下exe能否正常执行,有没有打印预期的内容。试了一下,不错。是预期的效果。那接下来就可以开始我们的正题了。打包多个目录下的多个py文件。

代码目录结构如下:

pyinstaller 加密打包 python脚本总结_python_03

1、先生成spec文件

 打开Terminal,进入到bin目录下,执行pyinstaller -D run.py,还是一样执行完会自动生成build、dist目录和run.spec文件。让我们来看看spec文件长啥样。

pyinstaller 加密打包 python脚本总结_python_04

 

 

 2、配置spec文件

因为项目里写了好多自定义的模块,以及应用的图片和模板文件。所以需要配置一下spec文件,才能正确的生成exe文件。

主要配置以下4个地方即可:

pyinstaller 加密打包 python脚本总结_ide_05

 

pyinstaller 加密打包 python脚本总结_python run.spec

 

(1) py文件打包配置

  针对多目录多文件的python项目,打包时候需要将所有相关的py文件输入到Analysis类里。Analysis类中的pathex定义了打包的主目录,对于在此目录下的py文件可以只写文件名不写路径。如上的spec脚本,将所有项目中的py文件路径以列表形式写入Analysis,这里为了说明混合使用了绝对路径和相对路径。

(2) 资源文件打包配置

  资源文件包括打包的python项目使用的相关文件,如图标文件,文本文件等。对于此类资源文件的打包需要设置Analysis的datas

比如:datas=[(SETUP_DIR + '\\db\\input','db\\input'),(SETUP_DIR + '\\doc','doc')],只需要添加依赖的输入目录以及放图片、模板的目录即可。
注意:SETUP_DIR + '\\db\\input'为源码中的绝对路径,Windows路径需要用\\来转义,后面的db\\input为打包后生成的目录,写相对路径即可。这样我们就不需要再手动把依赖的文件拷贝到run目录了

(3)Hidden import配置

  pyinstaller在进行打包时,会解析打包的python文件,自动寻找py源文件的依赖模块。但是pyinstaller解析模块时可能会遗漏某些模块(not visible to the analysis phase),造成打包后执行程序时出现类似No Module named xxx。这时我们就需要在Analysis下hiddenimports中加入遗漏的模块。
如:

from core.barcode_handler import *
from conf import settings as ss
from core.log import Log as log
只需要写入自定义的模块即可
hiddenimports=['core','core.main','conf.settings','core.log','core.barcode_handler']

 

3、配置好spec文件后,重新生成exe文件。

可以把之前生成的build和dist目录删除掉,只留刚配置好的run.spec文件就行。

最后打开Terminal,进入到bin目录下,执行pyinstaller -D run.spec,一样会生成build和dist目录,我们只要dist目录就行了。

pyinstaller 加密打包 python脚本总结_python_07

 

 可以把dist目录拷贝到桌面测试一下。为了方便操作,可以创建一个run.exe和输入、出入目录的快捷方式放到根目录下,dist目录可以任意命名。

pyinstaller 加密打包 python脚本总结_相对路径_08

 

 执行一下看看效果:

pyinstaller 加密打包 python脚本总结_相对路径_09

 

 pyinstaller 加密打包 python脚本总结_相对路径_10

 

 不错,程序运行正常。

 最后总结起来就三步:

1、先执行pyinstaller xxx.py生成xxx.spec文件

2、配置xxx.spec文件

3、执行pyinstaller -D xxx.spec

最后:如果生成了exe文件,但是双击xx.exe执行没有效果,又看不到报错信息的话,可以在Terminal或者cmd里执行

pyinstaller 加密打包 python脚本总结_资源文件_11

 

 

冻结打包路径

执行打包后的程序,经常会出现程序使用的图标无法显示,程序使用的关联文件无法关联。或者,在打包的本机上运行正常,但是将打包后的程序放到其它机器上就有问题。这些现象都很有可能是由程序使用的文件路径发生改变产生的,因此在打包时候我们需要根据执行路径进行路径“冻结”。

1.使用绝对路径

在python代码中使用绝对路径调用外部文件可以保证打包时候路径可追溯,因此在本机上运行打包后程序基本没问题。但是当本机上对应路径的资源文件被改变,或者将打包程序应用到别的机器,都会出现搜索不到资源文件的问题。这种方式不是合适的打包发布python软件的方式。

2.使用冻结路径

增加一个py文件,例如叫frozen_dir.py

# -*- coding: utf-8 -*-
"""
Created on Sat Aug 25 22:41:09 2018
frozen dir
@author: yanhua
"""
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 frozen_dir
SETUP_DIR = frozen_dir.app_path()
 
FONT_MSYH = matplotlib.font_manager.FontProperties(
                fname = SETUP_DIR + r'\data\fonts\msyh.ttf',
                size = 8)
 
DIR_HELP_DOC = SETUP_DIR + r'\data\docs'
DIR_HELP_VIDEO = SETUP_DIR + r'\data\videos'

通过冻结路径,使用了基准目录下的data目录下的fonts, docs, videos。

主程序中也做了类似的调整,改变其设置路径方法

import frozen_dir
 
SETUP_DIR = frozen_dir.app_path()+r'\lib'
sys.path.append(SETUP_DIR)

如果冻结路径是文件、图片等资源,不仅在主程序上像上面那样写,还要在.spec里这么写:

datas=[('/home/hsj/collect_data/config.ini', '.')]

这里研究了有1小时才研究出来