# Pyinstaller

# 前言

这是一个关于如何利用 pyinstaller 去打包一个完整的项目 (其中包括环境依赖和 exe 可执行文件) 的主要过程。
通过 pyinstaller 打包好后,在其他电脑中运行.exe 可执行程序时不需要配置相关环境依赖即可顺利运行。
此外,还分享了在打包过程和打包完成后以及在其他电脑上运行等出现的一些问题的解决方法。

# 有关 pyinstaller

  • 安装 pyinstaller --> pip install pyinstaller
  • 主要的命令选项
--h,--help模块的帮助信息
-F产生单个可执行文件
-D产生一个目录 (包含多个文件) 作为可执行文件
-wexe 程序运行时是否显示命令符窗口
--hidden-import module-name隐藏不需要的导入模块
多隐藏方法 --hidden-import 'xxx,xxx,xxx'
详细的 pyinstaller 命令介绍

# 正式开始

对于打包一个完整项目,首先需要一个干净的环境 (包括新建的 conda 环境或没有安装第三方库的全局 python 系统环境)。
为了避免打包时出现一些不必要的错误,这里推荐没有安装第三方库的全局 python 系统环境。(仅为推荐,新建一个 conda 环境也行).
另外,请确保路径中不包含中文等特殊字符。

  • 打包的基本指令为 pyinstaller -option xxx.py

基本流程:

  • 1. 同时按 win + R 键,输入 cmd 来调出命令符提示符,通过 pip 安装需要的第三方库 (这里推荐在 pycharm 中进行,pycharm 会提示安装所需要的第三库)

  • 2. 之后再调出命令提示符,按以下例子来输入 (例如项目文件夹路径是 E:\test) --> C:\Users\user-name>E: --> E:\>cd test (这里推荐在文件资源管理器中进入到项目的文件夹中,在路径输入栏中输入 cmd 直接进入)

  • 3. 然后先用 pyinstaller 窗口模式进行调试 (打包大环境推荐用 -D,例如主要的 py 文件为 test.py) --> pyinstaller -D test.py

  • 4. 之后再回顾打包过程中出现的一些问题,修复后在运行行 dist\ 项目名 \ 文件夹中的.exe 文件

  • 5. 将运行时出现的问题解决后,删除项目文件夹中的 (_pycache_,build,dist,xxx.spec) 文件

  • 6. 再通过以下命令隐藏命令符窗口打包项目 pyinstaller -D -w xxx.py

  • 7. 最后将 dist 文件夹下的文件夹压缩成压缩包后迁移到别的电脑中运行。

# 出现的一些问题

# Ⅰ. 提示打包好后,回顾打包过程中出现的一些问题


# 问题 1.No module name "xxx"

这是缺少第三方库,看见这个就可以直接用 pip 安装这个库

# 问题 2.Hidden import not found "xxx"

这个大多数不会影响程序的运行,可以忽略

# 问题 3. 有提示 WARNING

只要程序能正常运行,这个警告可以忽略

# Ⅱ. 打包好后,运行程序出现的一些问题


# 问题 4.Failed to execute script [.exe 文件名]

一般是由于 scikit-image 这个库导致的。由于所使用的设备缺少某些.dll 文件或缺少某些第三方库的依赖文件。如,缺少了 C++ 运行库,可下载 vc_redistx64.exe

# 问题 5. 闪退

主要是因为一些库没有安装到或者打包时 pyinstaller 强行打包了一些不必要的库。
解决方法:这个问题主要在基本流程的第 5 步中常出现。要是按照基本流程来做,只需要打开.exe 文件,通过查看命令符窗口报的错来解决 (通常窗口消失快,可通过 windows 截屏快捷键__win + Shift + S__来截取报错内容))。或者,以有窗口模式下打包项目,之后将.exe 文件托入命令符窗口中来查看错误。(再或者,用手机录制运行时的整个过程)
例如提示 no module name "xxx",先检查是否是需要的库,若是不必要的库可通过以下命令重新打包 (例如库名字为 scipy.xxx.xxx) pyinstaller -D test.py --hidden-import scipy.xxx.xxx

# 问题 6. 深度学习的多线程

主要情况为,使用含有深度学习模型进行预测的.exe 可执行程序时,同时出现多窗口 (例如有包含 qt 时会打开多个窗口)。解决方法:新建一个 py 文件命名为 multiprocessing_win.py, 写入以下代码。

import os
import sys  
import multiprocessing  
  
try:  
    
    if sys.platform.startswith('win'):  
        import multiprocessing.popen_spawn_win32 as forking  
    else:  
        import multiprocessing.popen_fork as forking  
except ImportError:  
    import multiprocessing.forking as forking  
  
if sys.platform.startswith('win'):  
    
    class _Popen(forking.Popen):  
        def __init__(self, *args, **kw):  
            if hasattr(sys, 'frozen'):  
                
                
                os.putenv('_MEIPASS2', sys._MEIPASS)  
            try:  
                super(_Popen, self).__init__(*args, **kw)  
            finally:  
                if hasattr(sys, 'frozen'):  
                    
                    
                    
                    
                    if hasattr(os, 'unsetenv'):  
                        os.unsetenv('_MEIPASS2')  
                    else:  
                        os.putenv('_MEIPASS2', '')  
  
    
    forking.Popen = _Popen

之后将这个文件放到项目的根目录中,并在执行多进程的 py 文件中写入

import multiprocessing_win
import multiprocessing

按以上要求做好后,在实现图形界面的 py 文件中或主 py 文件中按以下例子加入

import multiprocessing_win
import multiprocessing
if __name__ == '__main__':
    multiprocessing.freeze_support()
    app.run()

# Ⅲ. 迁移到其他电脑中,运行时出现的一些问题。


# 问题 7.Failed to execute script pyi_rth__tkinter

在 pythonxx/lib 目录下,找到 pyinstaller/hooks/hook-tkinter.py
通过搜索找到 path_to_tcl,再按以下例子修改 (主要为添加了 and 'Python' not in path_to_tcl 来防止路径丢失报错)

path_to_tcl = bins[0][1]
#if 'Library/Frameworks' in path_to_tcl:
if 'Library/Frameworks' in path_to_tcl and 'Python' not in path_to_tcl:

# 问题 8.Failed to execute script pyi_rth_certifi

主要为 python 的 ssl 证书过期的原因。
解决方法主要有:

  • 1. 安装 OpenSSL 库
  • 2. 缺少了 ssl 证书,因此可以从 Python 官网上下载相应 python 版本的压缩包,解压后将解压包中的_ssl.pyd (复制到 Anaconda 目录下 DLLs 下覆盖原文件),将 libcrypto-1_1.dll、libssl-1_1.dll (复制到 Anaconda 或 python 根目录)。
  • 3. 把 import requests 或者类似 import 了爬虫库的地方直接注释掉或者删掉 (需要利用网络的可忽略)