PyInstaller生成EXE遇qtpy.QtBindingsNotFoundError求解决方案
报错信息
运行生成的EXE文件时持续触发以下错误:
Traceback (most recent call last): File "main.py", line 7, in <module> import qtmodern.styles File "pyimod02_importers.py", line 457, in exec_module File "qtmodern\styles.py", line 3, in <module> File "pyimod02_importers.py", line 457, in exec_module File "qtpy\__init__.py", line 293, in <module> qtpy.QtBindingsNotFoundError: No Qt bindings could be found [PYI-44852:ERROR] Failed to execute script 'main' due to unhandled exception!
打包命令
使用的打包命令:
poetry run pyinstaller main.spec --clean
相关文件内容
main.spec
# -*- mode: python ; coding: utf-8 -*- a = Analysis( ['main.py'], pathex=['fullpathonmypc\.venv\Lib\site-packages'], binaries=[], datas=[], hiddenimports= ["qtmodern", "qtmodern.styles", "qtmodern.windows", "qtpy", "qtpy.QtCore", "qtpy.QtGui", "qtpy.QtWidgets", "PyQt6.QtCore", "PyQt6.QtGui", "PyQt6.QtWidgets", "PyQt6.QtPrintSupport", "PyQt6.QtSvg", "PyQt6.QtOpenGL", "event_connector", "numpy"], hookspath=[], hooksconfig={}, runtime_hooks=[], excludes=['PySide6'], noarchive=False, optimize=0, ) pyz = PYZ(a.pure) exe = EXE( pyz, a.scripts, [], exclude_binaries=True, name='main', debug=False, bootloader_ignore_signals=False, strip=False, upx=True, console=True, disable_windowed_traceback=False, argv_emulation=False, target_arch=None, codesign_identity=None, entitlements_file=None, ) coll = COLLECT( exe, a.binaries, a.datas, strip=False, upx=True, upx_exclude=[], name='main', )
hooks\hook-qtmodern.py
from PyInstaller.utils.hooks import collect_submodules hiddenimports = collect_submodules("qtmodern")
hooks\hook-qtpy.py
from PyInstaller.utils.hooks import collect_submodules hiddenimports = collect_submodules("qtpy")
已尝试的排查措施
- 在main.spec的hiddenimports列表中添加所有相关模块
- 创建hooks\hook-qtpy.py文件
- 在main.py报错行前添加代码
os.environ["QT_API"] = "pyqt6" - 在虚拟环境中执行
poetry run python -c "import qtpy; import PyQt6; print(qtpy.API_NAME)",确认输出为"PyQt6" - 在main.spec的binaries列表中添加
collect_dynamic_libs("PyQt6"),因打包报错ValueError: not enough values to unpack (expected 2, got 0)移除 - 执行
pyi-archive_viewer main.exe确认PyQt6已被打包 - 查阅文档得知Qt 6暂不被PyInstaller完全支持,插件无法复制
- 不带--onefile参数、带--clean参数重新生成main.spec,迁移原有配置
- 向ChatGPT求助并遵循建议
- 在main.spec的datas中添加
collect_data_files("PyQt6"),因打包报错移除 - 在main.spec的hiddenimports中添加
collect_submodules("qtpy") + collect_submodules("PyQt6")的内容
解决建议
修复pathex路径格式:Windows路径反斜杠需转义或改用正斜杠,将pathex中的
'fullpathonmypc\.venv\Lib\site-packages'修改为'fullpathonmypc/.venv/Lib/site-packages'或'fullpathonmypc\\.venv\\Lib\\site-packages',避免路径解析错误导致依赖查找失败。加载自定义钩子:当前hookspath为空,PyInstaller无法识别你创建的钩子。修改Analysis部分的
hookspath=[]为hookspath=['./hooks'],确保钩子被加载。同时完善hook-qtpy.py:
from PyInstaller.utils.hooks import collect_submodules, collect_data_files hiddenimports = collect_submodules("qtpy") datas = collect_data_files("qtpy")
手动复制Qt插件:找到虚拟环境中PyQt6的插件目录(一般为
./.venv/Lib/site-packages/PyQt6/Qt6/plugins),将整个plugins文件夹复制到打包后的main目录下(与main.exe同级)。添加运行时钩子强制配置环境变量:创建
hooks/runtime_hook.py文件,内容如下:
import os os.environ["QT_API"] = "pyqt6" # 手动指定Qt插件路径 os.environ["QT_QPA_PLATFORM_PLUGIN_PATH"] = os.path.join(os.path.dirname(os.path.abspath(__file__)), "../plugins")
然后在main.spec的Analysis部分添加runtime_hooks=['./hooks/runtime_hook.py'],确保程序启动时就加载正确的环境配置。
禁用UPX压缩Qt相关文件:UPX可能破坏Qt动态链接库,将EXE和COLLECT部分的
upx=True改为upx=False,避免压缩导致的依赖损坏。重新生成基础spec再调整配置:先运行
poetry run pyinstaller main.py --noconsole --hidden-import=qtmodern --hidden-import=qtpy --hidden-import=PyQt6生成基础spec文件,再手动添加excludes、hookspath、runtime_hooks等配置,减少手动编写spec的错误。
内容的提问来源于stack exchange,提问作者AGA

