当前位置: 首页 > news >正文

基于SAM2的眼动数据跟踪3——python转exe

目录

一、前言

二、如何将python转exe

问题:Python→exe 到底要写哪些东西?

问题:为什么onnxruntime_libs = [ (str(site_packages / 'onnxruntime/capi/libonnxruntime_providers_cuda.so'), 'onnxruntime/capi'), (str(site_packages / 'onnxruntime/capi/libonnxruntime_providers_shared.so'), 'onnxruntime/capi') ] 原来是.so,是不行的,改成.dll就行?跟系统有关系?

问题:hiddenimports里面到底要写啥?

问题:strip=False, upx=False, 这什么意思?

问题:为什么datas里面写了路径,后面还要跟一个名字?

问题:为什么打包后生成的是一个_internal文件夹和exe,_internal这个名称是什么时候确定的?(其实不重要)

问题:为什么Windows里需要在.spec中加sys.setrecursionlimit(5000) ?

问题:假设我现在有个python项目,我想把它转为exe,我该怎么做?

一、前言

我下载的X-AnyLabeling是3.1.1版本,如果你用最新版本,可能会遇到一些需要自己解决的问题,我把它放在了D:\zero_track\X-AnyLabeling-3.1.1,如果你跟我保持一致则你可以跟我采用完全一致的步骤方法进行,否则你需要根据你的项目路径进行更改。此外我使用的conda环境是(X_AnyLabeling_win_env),这个环境要基于SAM2的眼动数据跟踪1介绍的那样根据源码Readme去配置,这个环境位置我是放在D:\miniforge3\envs\X_AnyLabeling_win_env,如果你装的是conda而不是miniforge3或者是名字有所不同,则需要根据你的路径进行更改,下面x-anylabeling-linux-gpu.spec的修改内容中不再赘述这些。

建议是跟我保持一致,因为我之所以将X-AnyLabeling-3.1.1放在D:\zero_track是因为在zero_track项目中我打算对SAM算法各种改进项目以及各种目标跟踪算法进行一个性能比较和研究测试或者是与其他研究联动等等,而X-AnyLabeling-3.1.1只是其中一个。不过,这只是计划。

二、如何将python转exe

我们之前都是通过这样一句python语句打开X_AnyLabeling程序的:

(X_AnyLabeling_win_env) D:\zero_track\X-AnyLabeling-3.1.1> python anylabeling/app.py

现在我们要将python转exe。在scripts/build_executable.sh中,可以看到有不同的系统选择,也可以选择cpu还是gpu。

#!/bin/bash system=$1 if [ $system = "win-cpu" ]; then echo "Building Windows CPU version..." export X_ANYLABELING_DEVICE=CPU pyinstaller --noconfirm x-anylabeling-win-cpu.spec elif [ $system = "win-gpu" ];then echo "Building Windows GPU version..." export X_ANYLABELING_DEVICE=GPU pyinstaller --noconfirm x-anylabeling-win-gpu.spec elif [ $system = "linux-cpu" ];then echo "Building Linux CPU version..." export X_ANYLABELING_DEVICE=CPU pyinstaller --noconfirm x-anylabeling-linux-cpu.spec elif [ $system = "linux-gpu" ];then echo "Building Linux GPU version..." export X_ANYLABELING_DEVICE=GPU pyinstaller --noconfirm x-anylabeling-linux-gpu.spec elif [ $system = "macos" ];then echo "Building macOS version..." export X_ANYLABELING_DEVICE=CPU pyinstaller --noconfirm x-anylabeling-macos.spec else echo "System value '$system' is not recognized." fi

这里我们选择的是win-gpu,因为我们是在windows上进行python转exe的,同时我们需要的是gpu版本的,如果你要跑SAM2视频跟踪,不要想着cpu能跑,那将会慢到卡死。

在转exe之前,我们需要先确保依赖已经正确安装,如果是gpu版本:

(X_AnyLabeling_win_env) D:\zero_track\X-AnyLabeling-3.1.1> pip install -r requirements-gpu-dev.txt (如果是cpu版本,则是pip install -r requirements-dev.txt)

然后打开 anylabeling/app_info.py,将__preferred_device__ = "CPU" 改为__preferred_device__ = "GPU"

然后我们只需要打开git bash 终端(这是windows系统下能用的linux终端),然后输入(先不要运行,看完下面)

bash scripts/build_executable.sh win-gpu

如果选择win-gpu的话,实际上执行的是下面这句,所以说如果在windows系统下没有装git终端,则可以直接在cmd命令终端下使用这句(先不要运行,看完下面):

pyinstaller --noconfirm x-anylabeling-win-gpu.spec

在此之前,我们需要改一个文件。

在x-anylabeling-win-gpu.spec中,

# -*- mode: python -*- # vim: ft=python import sys import sysconfig from pathlib import Path site_packages = Path(sysconfig.get_path("purelib")) # 找到当前虚拟环境的 site-packages onnxruntime_libs = [ # 准备两张 CUDA 动态库 (str(site_packages / 'onnxruntime/capi/libonnxruntime_providers_cuda.so'), 'onnxruntime/capi'), (str(site_packages / 'onnxruntime/capi/libonnxruntime_providers_shared.so'), 'onnxruntime/capi') ] a = Analysis( ['anylabeling/app.py'], # 入口脚本 pathex=['anylabeling'], # 额外模块搜索路径 binaries=[], # 无额外二进制 datas=[ # 需要拷贝的数据文件 ('anylabeling/configs/auto_labeling/*.yaml', 'anylabeling/configs/auto_labeling'), ('anylabeling/configs/*.yaml', 'anylabeling/configs'), ('anylabeling/views/labeling/widgets/auto_labeling/auto_labeling.ui', 'anylabeling/views/labeling/widgets/auto_labeling'), ('anylabeling/services/auto_labeling/configs/bert/*', 'anylabeling/services/auto_labeling/configs/bert'), ('anylabeling/services/auto_labeling/configs/clip/*', 'anylabeling/services/auto_labeling/configs/clip'), ('anylabeling/services/auto_labeling/configs/ppocr/*', 'anylabeling/services/auto_labeling/configs/ppocr'), ('anylabeling/services/auto_labeling/configs/ram/*', 'anylabeling/services/auto_labeling/configs/ram'), *onnxruntime_libs # 把上面两张 .so 也当数据拷 ], hiddenimports=[], # 无隐藏导入 hookspath=[], runtime_hooks=[], excludes=[], ) pyz = PYZ(a.pure, a.zipped_data) # 把纯 Python 代码压成 zlib exe = EXE( pyz, a.scripts, a.binaries, a.zipfiles, a.datas, # ← 所有东西一起打进一个文件 name='X-Anylabeling-Linux-GPU', debug=False, # 不输出调试 strip=False, upx=False, runtime_tmpdir=None, console=False, # 不弹控制台 icon='anylabeling/resources/images/icon.icns', ) # 包成 .app app = BUNDLE( exe, name='X-AnyLabeling.app', icon='anylabeling/resources/images/icon.icns', bundle_identifier=None, info_plist={'NSHighResolutionCapable': 'True'}, )

上面的结果就是生成单文件 exe(Linux 下无后缀),双击即可运行,所有依赖都在 exe 内部。但是我觉得这样不好,因为我们有要修改的配置文件,全封装进exe不但运行的时候非常慢(每次运行exe要把依赖复制到tmp文件夹很耗时),而且什么都修改不了。

所以我们修改为下面的单文件夹模式,这样会在D:\zero_track\X-AnyLabeling-3.1.1\dist生成:X-AnyLabeling-GPU文件夹,里面是一个_internal文件夹和一个X-AnyLabeling-GPU.exe。可以看到我们下面把参数a.binaries, a.zipfiles, a.datas从EXE那里转移到了BUNDLE,这就是将一些东西从原本的单个exe抽出来放在_internal文件夹

# -*- mode: python -*- # vim: ft=python import sys import sysconfig from pathlib import Path sys.setrecursionlimit(5000) # required on Windows # site_packages = Path(sysconfig.get_path("purelib")) a = Analysis( ['anylabeling/app.py'], # [新增] pathex=['anylabeling', r'D:\zero_track\X-AnyLabeling-3.1.1\segment-anything-2-camera','D:/zero_track/X-AnyLabeling-3.1.1/xanylabeling_data'], binaries=[], datas=[ ('anylabeling/configs/auto_labeling/*.yaml', 'anylabeling/configs/auto_labeling'), ('anylabeling/configs/*.yaml', 'anylabeling/configs'), ('anylabeling/views/labeling/widgets/auto_labeling/auto_labeling.ui', 'anylabeling/views/labeling/widgets/auto_labeling'), ('anylabeling/services/auto_labeling/configs/bert/*', 'anylabeling/services/auto_labeling/configs/bert'), ('anylabeling/services/auto_labeling/configs/clip/*', 'anylabeling/services/auto_labeling/configs/clip'), ('anylabeling/services/auto_labeling/configs/ppocr/*', 'anylabeling/services/auto_labeling/configs/ppocr'), ('anylabeling/services/auto_labeling/configs/ram/*', 'anylabeling/services/auto_labeling/configs/ram'), # [新增] ('D:/miniforge3/envs/X_AnyLabeling_win_env/Lib/site-packages/onnxruntime/capi/onnxruntime_providers_cuda.dll', 'onnxruntime/capi'), ('D:/miniforge3/envs/X_AnyLabeling_win_env/Lib/site-packages/onnxruntime/capi/onnxruntime_providers_shared.dll', 'onnxruntime/capi'), # sam2配置文件地址 ('D:/zero_track/X-AnyLabeling-3.1.1/segment-anything-2-camera/sam2_configs', 'sam2_configs'), # 模型地址 ('D:/zero_track/X-AnyLabeling-3.1.1/xanylabeling_data/models/*', 'xanylabeling_data/models'), # 模型路径配置文件地址 ('D:/zero_track/X-AnyLabeling-3.1.1/xanylabeling_data/models.json', 'xanylabeling_data/models.json') ], # [新增] hiddenimports=[ 'sam2', 'sam2.build_sam', 'sam2.sam2_image_predictor', 'sam2_configs', ], hookspath=[], runtime_hooks=[], excludes=[], ) pyz = PYZ(a.pure, a.zipped_data) # [修改] exe = EXE( pyz, a.scripts, # a.binaries, # a.zipfiles, # a.datas, exclude_binaries=True, # 关键:不打包二进制,留给 COLLECT name='X-Anylabeling-Linux-GPU', debug=True, # 打开调试信息 strip=False, upx=False, # runtime_tmpdir=None, console=True, # 是否带控制台 icon='anylabeling/resources/images/icon.icns', ) # [修改] coll = BUNDLE( exe, a.binaries, a.zipfiles, a.datas, strip=False, upx=False, name='X-AnyLabeling.app', )

问题:Python→exe 到底要写哪些东西?

PyInstaller 的核心工作只有 3 步:

  1. Analysis
    把入口脚本(app.py)里import到的所有模块、动态库、数据文件、隐藏导入……统统“抓”出来。
    需要告诉它:

    • 入口脚本是谁

    • 额外二进制(.dll/.so)在哪

    • 额外数据文件(.yaml/.ui/.json)在哪

    • 哪些包是“动态 import”必须手工加(hiddenimports)

    • 哪些包不要打进去(excludes)

  2. EXE
    把 Analysis 的结果变成“可执行文件”。
    需要决定:

    • 单文件模式(one-fileexclude_binaries=False)还是单文件夹模式(one-folderexclude_binaries=True

    • 是否带控制台(console=True/False

    • 是否开调试信息(debug=True/False

    • 图标、名字、UPX 压缩等

  3. COLLECT / BUNDLE
    单文件夹模式下,再把“剩下的二进制 + 数据”收集到一个目录;macOS 下用 BUNDLE 生成.app包。

写完这三段,保存成.spec,然后
pyinstaller --noconfirm xxx.spec
就出来 exe(或 app)。

问题:为什么onnxruntime_libs = [ (str(site_packages / 'onnxruntime/capi/libonnxruntime_providers_cuda.so'), 'onnxruntime/capi'), (str(site_packages / 'onnxruntime/capi/libonnxruntime_providers_shared.so'), 'onnxruntime/capi') ] 原来是.so,是不行的,改成.dll就行?跟系统有关系?

是的,.so.dll的区别纯粹是“操作系统约定”,跟 Python 本身无关:

表格

复制

操作系统动态库后缀环境变量/搜索路径
Linux.soLD_LIBRARY_PATH
Windows.dllPATH
macOS.dylibDYLD_LIBRARY_PATH

ONNX Runtime 的 GPU 版在安装时会根据当前平台复制同名不同后缀的文件:

  • Linux 下只有
    libonnxruntime_providers_cuda.so
    libonnxruntime_providers_shared.so

  • Windows 下只有
    onnxruntime_providers_cuda.dll
    onnxruntime_providers_shared.dll

因此

site_packages / 'onnxruntime/capi/libonnxruntime_providers_cuda.so'

在 Windows 机器上根本找不到同名文件,PyInstaller 会报“文件不存在”或运行时报

Could not locate onnxruntime_providers_cuda.dll

把后缀改成.dll只是让路径字符串与真实文件一致,没有别的魔法。
跨平台写 spec 时可以用一个小技巧:

import sys, sysconfig, os from pathlib import Path site_packages = Path(sysconfig.get_path("purelib")) ext = {'win32': '.dll', 'linux': '.so', 'darwin': '.dylib'}[sys.platform] onnxruntime_libs = [ (str(site_packages / f'onnxruntime/capi/onnxruntime_providers_cuda{ext}'), 'onnxruntime/capi'), (str(site_packages / f'onnxruntime/capi/onnxruntime_providers_shared{ext}'), 'onnxruntime/capi') ]

这样同一份.spec在 Linux/Windows/macOS 都能自动拿到正确文件。

问题:hiddenimports里面到底要写啥?

hiddenimports里只写**“PyInstaller 静态分析抓不到,但运行时一定会 import”的模块名。
一句话:
“让 exe 在运行时不报 ModuleNotFoundError” 的最小集合。**


一、先搞清为什么需要它

PyInstaller 靠文本扫描import xxx语句来收集依赖,但遇到下面 4 种情况就瞎了:

  1. 动态__import__()/importlib.import_module()

  2. 插件式加载(字符串拼接名字)

  3. 二进制扩展(.pyd/.so)被其它扩展调用

  4. 运行时按条件才 import(if flag: import y

这些模块不会出现在分析结果里,可执行文件里也就没打包进去,于是双击 exe 直接:

ModuleNotFoundError: No module named 'xxx'

二、到底写什么?——“运行时一定出现”的顶级包名或完整子模块名

写法就是纯字符串,一行一个,不要路径、不要文件后缀、不要写.py

场景

正确写法

错误写法

顶级包

'sam2'

'sam2.py''D:\\sam2'

子模块

'sam2.build_sam'

'sam2/build_sam.py'

包内动态 import

'mmcv.ops'

'mmcv/ops'

扩展模块

'onnxruntime.capi._pybind_state'

'_pybind_state.pyd'


三、快速定位该写谁

  1. 跑一遍 exe,看 traceback
    缺谁就把谁名字加进去 → 最靠谱。

  2. 全局搜索代码里的字符串拼接

    for name in ['sam2', 'yolov5', 'ppocr']: importlib.import_module(f'models.{name}')

    那么models.sam2models.yolov5models.ppocr都要写。

  3. 用社区钩子
    很多库已有官方钩子(hook-xxx.py),装了钩子就不用手写;
    没有钩子的库才手动补。


四、常见真实例子

hiddenimports = [ # SAM2 动态 import 'sam2', 'sam2.build_sam', 'sam2.sam2_image_predictor', # 插件式 OCR 'ppocr.data', 'ppocr.modeling', # 二进制扩展 'onnxruntime.capi._pybind_state', # CUDA 后端 'onnxruntime.providers.cuda', # 第三方库延迟加载 'sklearn.utils._typedefs', 'sklearn.neighbors._partition_nodes', ]

五、一句话总结

hiddenimports就是**“PyInstaller 漏掉但程序实际会 import 的模块名清单”**,
缺啥写啥不要多写不要写路径写完重新打包 → 运行 → 再缺再加,直到 exe 不再报ModuleNotFoundError为止。

问题:strip=False, upx=False, 这什么意思?

这两行是PyInstaller 在打包阶段对二进制文件做的“瘦身”与“压缩”开关——
默认都是False(不动原文件),一旦设为True就可能让体积变小,但也可能把程序弄崩


  1. strip=False


  • 只影响 Linux / macOS 下的.so/.dylib
    对应命令行工具strip:把动态库里的调试符号表、重定位信息等全部剃掉。

  • 体积能再小 5 %–30 %,但
    – 崩溃时gdb/lldb看不到函数名,堆栈全是???
    – 极少数库(CUDA、PyTorch)被strip后会直接加载失败。

  • Windows 的.pyd/.dll不受该选项影响(PE 文件格式不同,没有strip概念)。


  1. upx=False


  • UPX = “Ultimate Packer for eXecutables”,把可执行文件/动态库压成自解压格式。

  • 体积可再小 20 %–60 %,但
    – 启动时会先解压到内存,冷启动变慢
    – Windows Defender / 杀毒软件常把 UPX 压过的文件当成可疑加壳,容易误报;
    – 部分库(opencv_world*.dllonnxruntime_providers_cuda.dlltorch*.dll)被 UPX 后加载失败(内部自校验不通过)。


  1. 实战建议

场景推荐值理由
本地调试False出问题能看完整符号,不会被压缩干扰
正式发布,且充分测试过strip=TrueLinux/macOS 体积再小一圈
想发到全网下载保持upx=False杀毒误报、加载失败代价更高;真在乎体积用 7z 分发即可

一句话总结
strip=False=不剥符号upx=False=不用 UPX 压缩
两者都是“求稳”做法,体积换兼容性;
只要出现过“打包后 exe 打不开 / 杀毒报毒 / CUDA 初始化失败”,第一时间把这两个开关关掉再试。

问题:为什么datas里面写了路径,后面还要跟一个名字?

datas 里每一项是二元组

('源路径', '打包后在 exe 内部的目录名')
  • 第 1 个元素告诉 PyInstaller去哪里找文件;

  • 第 2 个元素告诉 PyInstaller在生成的 bundle 里把文件放到哪个目录(也就是运行时sys._MEIPASS下的相对路径)。


例子 1:把单个文件放进去

datas=[ ('conf/settings.yaml', 'conf') ]

打包后目录结构(运行时临时目录):

sys._MEIPASS/ ├─ conf/ │ └─ settings.yaml

代码里就可以:

import os, sys yaml_path = os.path.join(sys._MEIPASS, 'conf', 'settings.yaml')

例子 2:把整个文件夹映射进去

datas=[ ('models/*.pt', 'models') ]

所有匹配的.pt文件会被复制到:

sys._MEIPASS/models/xxx.pt

例子 3:换内部名字

datas=[ ('assets/icon.png', 'res/img') ]

运行时:

sys._MEIPASS/res/img/icon.png

与原始目录结构无关,内部路径完全由第 2 个字符串决定


一句话记忆

第 1 段是“硬盘上去哪儿拿”
第 2 段是“拿进来以后放哪儿”
不写第 2 段,PyInstaller 就不知道在 bundle 里建什么目录,程序就找不到文件。

问题:为什么打包后生成的是一个_internal文件夹和exe,_internal这个名称是什么时候确定的?(其实不重要)

_internal这个名字不是.spec文件里写的,而是PyInstaller 在单文件夹模式(one-folder)下的硬编码默认子目录名
只要满足下面两个条件,它就会自动出现:

  1. 打包时用了单文件夹模式(即EXE(..., exclude_binaries=True)然后COLLECT(...)BUNDLE(...)把二进制再收集回来);

  2. 没有通过命令行--nameCOLLECT(name=...)显式指定目录名。


出现时机

  • PyInstaller/building/api.pyCOLLECT类里写死:

# PyInstaller 5.x 源码摘录 class COLLECT(Target): def __init__(self, *args, **kws): ... # 如果没给 name,就用脚本名 + '_internal' if self.name is None: self.name = os.path.splitext(os.path.basename(self.scripts[0]))[0] + '_internal'

因此当你用默认.spec(或由pyinstaller your.py自动生成)时,PyInstaller 会:

  1. 生成dist\your.exe

  2. 再生成dist\your_internal\并把所有.dll/.pyd和数据塞进去;

  3. 启动时your.exe先把_internal解压到临时目录,再把该路径加入sys.path/sys.dllhandle,最后调真正的入口。


想改名怎么办

  • 命令行

    pyinstaller your.spec --name MyApp

    目录会变成dist\MyApp\dist\MyApp_internal\

  • 手动改.spec
    COLLECTBUNDLEname参数写死

    coll = COLLECT(exe, a.binaries, a.zipfiles, a.datas, name='runtime') # 会生成 dist\runtime\

一句话总结
_internal是 PyInstaller单文件夹模式默认的后缀目录名,源码里写死;
不出现它的办法只有:用单文件模式或者自己指定目录名

问题:为什么Windows里需要在.spec中加sys.setrecursionlimit(5000) ?

在 Windows 下用 PyInstaller 打包超大项目(或深目录+长路径)时,经常会在.spec开头看到:

import sys sys.setrecursionlimit(5000)

这不是 PyInstaller 的“官方要求”,而是规避 Python 默认递归深度限制临时 workaround


  1. 触发场景


PyInstaller 在Analysis 阶段会递归地:

  • 扫描import

  • 解析hook文件

  • 处理datas/binaries路径

如果项目很大、目录层级深(尤其放在D:\very\long\path\to\project\...),Python 的路径解析+模块遍历就会深递归,最终抛出:

RecursionError: maximum recursion depth exceeded
  1. 默认限制


CPython 的默认递归深度:

>>> sys.getrecursionlimit() 1000

对普通代码够用,但对 PyInstaller 的“巨型 AST + 长路径”可能不够。


  1. 为什么 Windows 更常见


  • Windows 路径长度上限 260 字符,PyInstaller 内部会把长路径拆成多级拼接,递归层数比 Linux/macOS 更高

  • 很多 Windows 用户习惯把项目放在桌面/下载文件夹,路径本身就很深。


  1. 解决方式


.spec文件最顶部(任何函数调用前)把限制临时调高即可:

import sys sys.setrecursionlimit(5000) # 或 10000,够用就行

这只是运行打包脚本时生效,不会影响最终 exe 的运行性能,也不改变 Python 解释器本身。


  1. 官方态度


PyInstaller 文档把这条写进 FAQ:

If you encounterRecursionErrorduring analysis, increase the limit in your.specfile.


一句话总结
Windows 长路径 + 大项目 → PyInstaller 递归深度爆表 →.spec里手动sys.setrecursionlimit(5000)只是让打包过程不崩溃,对最终程序无任何副作用。

问题:假设我现在有个python项目,我想把它转为exe,我该怎么做?

下面给你一条**“从 0 到拿到可运行 exe”**的最短路线,分三步:
(先能跑,再优化;全程在虚拟环境里做,避免把系统 Python 搅乱)


  1. 准备阶段:在虚拟环境里安装依赖

# 1. 创建并激活虚拟环境(Python 3.8~3.11 最稳) python -m venv venv venv\Scripts\activate # Linux/Mac 用 source venv/bin/activate # 如果是conda环境 # 指定 Python 版本,环境名随意 conda create -n exepack python=3.10 -y conda activate exepack # 2. 安装你的项目 + PyInstaller pip install -r requirements.txt # 你的依赖 pip install pyinstaller

  1. 先“裸跑”PyInstaller,看能不能启动


进入项目入口脚本所在目录,假设入口是myapp\main.py

# 最简单命令,生成单文件夹模式 pyinstaller myapp\main.py --name MyApp --clean -y

解释:

  • --name MyApp让 exe 叫MyApp.exe,目录叫MyApp(而不是默认的main.exe/main_internal)。

  • --clean强制清空缓存。

  • -y不询问直接覆盖。

跑完会出来:

dist\ ├─ MyApp\ │ ├─ MyApp.exe # 启动器 │ └─ …一堆 dll/pyd 文件

双击MyApp.exe

  • 如果能正常打开 → 恭喜,直接跳到第 3 步微调。

  • 如果闪退/报错 → 看弹出的控制台日志,缺啥补啥(见下方“常见补缺”)。


  1. 微调:写一份最小.spec,把缺的东西加进去


第 2 步只是“快速验证”。真正交付要写.spec文件,否则每次命令行都得敲一长串。

  1. 让 PyInstaller 先生成模板:

pyi-makespec myapp\main.py --name MyApp --onefile

得到MyApp.spec

  1. 用文本编辑器打开MyApp.spec,按需要补 3 处:

# ① 数据文件(ui、yaml、图标等) datas=[ ('myapp/config/*.yaml', 'myapp/config'), ('myapp/resources/icon.png', 'myapp/resources'), ], # ② 隐藏导入(运行时报 ModuleNotFoundError 就加) hiddenimports=[ 'sklearn.utils._typedefs', 'onnxruntime.capi._pybind_state', ], # ③ 单文件还是单文件夹(示例保持单文件) exe = EXE( ... console=False, # 正式发布用 False;调试阶段可 True icon='myapp/resources/icon.ico', )
  1. 重新打包:

pyinstaller MyApp.spec --clean -y

产物:

dist\MyApp.exe # 单文件,双击即可运行

常见补缺速查表

报错现象解决手段
ModuleNotFoundError: xxxxxx加到hiddenimports
FileNotFoundError: config\a.yaml把该 yaml 加到datas;代码里用sys._MEIPASS拼接路径(见下)
双击无窗口闪退--console=True看日志;解决后再改回False
杀毒软件报毒关掉UPX(默认就关),用数字签名工具签一下 exe

读取打包后资源的标准代码

import sys, os def resource_path(rel): """获取打包后资源绝对路径""" base = getattr(sys, '_MEIPASS', os.path.abspath('.')) return os.path.join(base, rel) config = resource_path('myapp/config/settings.yaml')

一句话总结

  1. venv或者conda + 装依赖

  2. 先裸跑pyinstaller main.py --name MyApp验证能否启动

  3. pyi-makespec生成.spec,把数据/隐藏导入加进去,再pyinstaller MyApp.spec出最终 exe

按这个流程,10 分钟就能拿到可运行的 exe;后续再慢慢优化体积、图标、自动更新等高级需求。

http://www.jsqmd.com/news/497050/

相关文章:

  • 比迪丽角色生成实战案例:从‘a beautiful girl’到龙珠经典造型复刻
  • 如何将genact假活动生成器集成到自动化脚本:完整指南
  • FireRed-OCR Studio入门指南:OCR结果置信度阈值设定与人工复核策略
  • 嵌入式C开发三大核心架构:从能运行到高可用的实战指南
  • Android开发的定心丸-Android从底层到上层开发技巧经验汇总_上卷_助您不走弯路_快速前行!
  • 比迪丽AI绘画教程:如何用Inpainting修复生成中的局部瑕疵
  • Qwen3-ASR-0.6B内容审核应用:敏感词实时检测与高亮标记
  • FireRed-OCR Studio开源镜像部署:GPU显存优化与量化配置详解
  • OpenClaw官方下载替代:nanobot开源镜像+Qwen3-4B全栈部署教程(含日志排查)
  • 通义千问1.5-1.8B-GPTQ-Int4效果展示:中文逻辑推理、多轮对话与代码生成真实案例
  • Qwen2.5-7B-Instruct法律应用:合同审查要点+修改建议+法条引用
  • IndexTTS-2-LLM真实项目案例:电子书语音转换系统教程
  • Qwen3-Reranker-0.6B应用解析:如何用rerank结果指导LLM生成更精准答案
  • SSTI 刷题记录
  • LiuJuan Z-ImageGPU算力方案:单卡4090支撑多任务并发生成实测
  • 浦语灵笔2.5-7B金融场景:K线图+新闻截图→行情解读→投资建议初稿
  • lite-avatar形象库惊艳案例:客服数字人7×24小时处理300+并发咨询无卡顿
  • Qwen2-VL-2B-Instruct实操手册:Streamlit界面调试信息与Device维度解析
  • [特殊字符] VSCode Copilot 里的大模型,到底是不是“真的”?一篇讲透它背后的控制权
  • DeOldify上色服务灾备方案:模型文件异地备份+服务配置Git版本管理
  • 实时口罩检测-通用模型标注规范说明:COCO格式转换实操
  • YOLO X Layout实战教程:结合PaddleOCR构建端到端文档理解Pipeline
  • AIGlasses_for_navigation代码实例:curl调用/api/config接口完成API Key动态更新
  • RabbitMQ交换机类型全解析:direct/fanout/topic/headers应用场景与代码实现
  • RMBG-2.0镜像免配置优势:预装PyTorch+OpenCV+Gradio,开箱即用不踩坑
  • Gemma-3-12b-it高性能推理部署:12B模型在RTX 4090×2环境下的实测表现
  • 2026年上海食品加工生产线哪家好?番茄酱、芒果浆、苹果汁、蘑菇酱、芒果汁、菠萝汁、枸杞、沙棘生产线厂家选择指南,加派机械深耕五十载的区域产业定制化伙伴 - 海棠依旧大
  • Chord视频理解工具实战案例:广告视频产品露出时段与位置热力图
  • 2026年荆州沙市区罗湖牌丸子:五家百年老店口碑与选购全指南 - 2026年企业推荐榜
  • 通义千问3-Reranker-0.6B实战教程:结合Embedding模型的两级检索架构