pyinstaller打包报错及解决

发布时间 2023-08-17 21:41:57作者: 豆瓣酱瓣豆

#使用方法

-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 的名字

打包命令

pyinstaller -F thired.py
pyinstaller -F -i ajai4-i7gr6-001.ico db.py
pyinstaller -F -i jykj.ico -w acx-14.spec 没有命令行
pyinstaller -F -w -i "res/jykj.ico" --key "chenziqing-data" text.py --add-data="res/*.*;./res"

pyinstaller打包报错

1、Error: geos_c.dll

Error: geos_c.dll not found, required by hook-shapely.py.
Please check your installation or provide a pull request to PyInstaller to update hook-shapely.py

去下载geos_c.dll https://www.dll-files.com/geos_c.dll.html
放在C:\Windows\System32 下

2、RecursionError: maximum recursion depth exceeded

import sys
sys.setrecursionlimit(5000)

3、IMPORTERROR: NO MODULE NAMED ‘MLARRAY‘解决方案 mlarray

https://www.freesion.com/article/46391125160/
pyinstaller test.py --additional-hooks-dir=hooks
pyinstaller -F -i jykj.ico -w asphalt-计算PCISCI-5.spec --additional-hooks-dir=hooks

会遇到的打包问题
https://www.cnblogs.com/alex-13/p/12849264.html
打包资源维问题
https://blog.csdn.net/kobeyu652453/article/details/108732747



4、WARNING: file already exists but should not: torch_C.cp36-win_amd64.pyd

https://blog.csdn.net/weixin_43218120/article/details/108596405

.spec
添加下面行
for d in a.datas:
	if '_C.cp36-win_amd64.pyd' in d[0]:
		a.datas.remove(d)
		break




a = Analysis(['MyTcpServer.py'],
             pathex=['G:\\yolo'],
             binaries=[],
             datas=[],
             hiddenimports=[],
             hookspath=[],
             runtime_hooks=[],
             excludes=[],
             win_no_prefer_redirects=False,
             win_private_assemblies=False,
             cipher=block_cipher,
             noarchive=False)
for d in a.datas:
	if '_C.cp37-win_amd64.pyd' in d[0]:
		a.datas.remove(d)
		break



5、ctypes 找不到依赖pypiwin32 或pywin32-ctypes

# ctypes 找不到依赖pypiwin32 或pywin32-ctypes
# Traceback (most recent call last):
#   File "lib\site-packages\PyInstaller\loader\pyiboot01_bootstrap.py", line 151, in __init__
#   File "ctypes\__init__.py", line 348, in __init__
# OSError: [WinError 126] 找不到指定的模块。
"""
在python安装路径下找到Lib/site-packages/Pyinstaller目录下有个compat.py文件定位到212行

源码如下:

复制代码
if is_win:
    try:
        from win32ctypes.pywin32 import pywintypes  # noqa: F401
        from win32ctypes.pywin32 import win32api
    except ImportError:
        xxxx
        xxxx
点击并拖拽以移动
复制代码
做如下修改:将两个from改为import

复制代码
if is_win:
    try:
        # from win32ctypes.pywin32 import pywintypes  # noqa: F401
        # from win32ctypes.pywin32 import win32api
        import pywintypes
        import win32api
    except ImportError:
        xxxx
        xxxx
复制代码
 

6、PyInstaller解决 ImportError: cannot import name ‘PackagePath‘ from ‘importlib_metadata‘

'''
PyInstaller解决 ImportError: cannot import name ‘PackagePath‘ from ‘importlib_metadata‘
'''
# cannot import name ‘PackagePath’ from ‘importlib_metadata’: Python 3.7
# 问题和解决方案:
# importlib_metadata版本太老了,升级一下
# pip install importlib-metadata --upgrade

'''
第一步:卸载,重装(在anaconda prompt里)或者虚拟环境下
pip uninstall matplotlib

pip install matplotlib==3.1.1

第二步:打包
pyinstaller -F XXX.py

第三步:修改spec文件
原hiddenimports=[],

修改后

hiddenimports=[‘matplotlib’],
'''

7、Path in environment MATPLOTLIBDATA not a directory

# Path in environment MATPLOTLIBDATA not a directory
"""
在spec文件中的excludes行加上excludes=['matplotlib'],
hiddenimports=['numpy.random.common'],然后pyinstaller xx.spec重新封装一遍。

8、Mac Osx/Windos 使用pyinstalle 打包后图标不生效

Mac Osx/Windos 使用pyinstalle 打包后图标不生效
我尝试了很久才发现,Mac下图标文件为icns格式Win为ico

# Mac 使用pyinstalle打包配置图标
pyinstaller test.py --noconsole --icon=logo.icns
# Windos 使用pyinstalle打包配置图标
pyinstaller test.py --noconsole --icon=logo.ico

.spec用法

# -*- mode: python ; coding: utf-8 -*-
block_cipher = pyi_crypto.PyiBlockCipher(key='chenziqing-data')
a = Analysis(['应变计-裂缝计-倾角计-数据接入.py'],
             pathex=[],
             binaries=[],
             datas=[('res/*.*', './res')],
             hiddenimports=[],
             hookspath=[],
             hooksconfig={},
             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='应变计-裂缝计-倾角计-数据接入',
          debug=False,
          bootloader_ignore_signals=False,
          strip=False,
          upx=True,
          upx_exclude=[],
          runtime_tmpdir=None,
          console=False,
          disable_windowed_traceback=False,
          target_arch=None,
          codesign_identity=None,
          entitlements_file=None , 
          icon='res\\jykj.ico')

block_cipher

上面还有个变量block_cipher,主要是防止exe被反编译。
block_cipher = pyi_crypto.PyiBlockCipher(key='chenziqing-data')

Analysis

datas用法

# 第一个写文件路径,第二个写位置 如 . /res/ data 等
datas = [('res/三极子偕行楷.ttf', 'res'), ('res/851手写杂书体.ttf', 'res'), ('res/千图笔锋手写体.ttf', 'res'), ('res/千图纤墨体.ttf', 'res'),
         ('res/王强手写体.ttf', 'res'), ]
         
如果在exe中进行引用的话需要使用方法如下
ttf = processPath("res/三极子偕行楷.ttf") 对ttf进行引用
    
#https://blog.csdn.net/u012917013/article/details/125860885
def processPath(path):
    '''
    针对pyInstaller打包程序而设计。这是官方给出的方案。
    在pyInstaller打包时,会给sys设置属性frozen,
    并且会将打包程序的绝对路径存储在sys._MEIPASS。
    所以当不打包程序时,不需要使用该方案拼接路径
    :param path: 相对于根目录的路径
    :return: 拼接好的路径
    '''
    # if getattr(sys, 'frozen', False):  # 判断是否存在属性frozen,以此判断是打包的程序还是源代码。false为默认值,即没有frozen属性时返回false
    #     base_path = sys._MEIPASS  # 该属性也是打包程序才会有,源代码尝试获取该属性会报错
    # else:
    #     base_path = os.path.abspath(".")  # 当源代码运行时使用该路径
    # return os.path.join(base_path, path)
    """ 获得资源的绝对路径 """
    try:
        # PyInstaller 创建临时文件夹时会将路径存入 sys._MEIPASS
        base_path = sys._MEIPASS
    except Exception:
        # 不是 exe 执行环境,未找到 sys._MEIPASS,使用当前路径
        base_path = os.path.abspath(".")
    return os.path.join(base_path, path)

PYZ

pyz = PYZ(a.pure, a.zipped_data,
             cipher=block_cipher)

exe

exe = EXE(pyz,
          a.scripts,
          a.binaries,
          a.zipfiles,
          a.datas,  
          [],
          name='应变计-裂缝计-倾角计-数据接入',
          debug=False,
          bootloader_ignore_signals=False,
          strip=False,
          upx=True,
          upx_exclude=[],
          runtime_tmpdir=None,
          console=False,
          disable_windowed_traceback=False,
          target_arch=None,
          codesign_identity=None,
          entitlements_file=None , 
          icon='res\\jykj.ico'
          )