打包 (仅 --onefile)

2 投票
4 回答
2592 浏览
提问于 2025-04-18 05:14

请大家仔细阅读这篇帖子并帮我一下。

我想用pyinstaller创建一个--onefile的可执行文件。

我在开发环境中有这些:

  • windows 8.1 x64
  • Qt 5.2.1
  • Python 27
  • PyQt5.2.1(用Visual Studio 2012构建的)
  • Visual Studio 2012
  • PyInstaller 2.1
  • pyWin32

在部署环境(虚拟机)中有这些(作为一个干净的虚拟机):

  • windows 8 x64
  • vcredist_x64 2012

这是我想要构建的简单Python程序:

#main.py
import sys
from PyQt5.QtWidgets import QApplication, QPushButton

app = QApplication(sys.argv)

win = QPushButton("Hello World!")
win.show()

sys.exit(app.exec_())
#------------------------------------------------

好的,当我用--onedirpyinstaller main.py)构建时,它在开发环境和部署环境中都能正常工作。

但是当我用--onefilepyinstaller -F main.py)构建时,它在开发环境中可以正常工作,但在部署环境中却不行。

并且出现了这个错误:

这个应用程序启动失败,因为找不到或无法加载 Qt平台插件“windows”。

可用的平台插件有:minimal, offscreen, windows。

重新安装应用程序可能会解决此问题。

我哪里出错了?或者这个构建有什么问题?

根据这个错误,它找不到在临时文件夹中_MEIxxxxx文件夹里的qt5_plugins文件夹。

或者,你认为问题出在sys模块上吗?如果是的话,我该怎么做?

谢谢你的提前回复。

更新:

我应该说在构建时我收到了这些警告和错误:

1024 WARNING: 找不到文件 C:\Python27\msvcp90.dll 1024 WARNING: 程序集不完整 1026 ERROR: 找不到 amd64_Microsoft.VC90.CRT_1fc8b3b9a1e18e3b_9.0.21022.8_none

更新2:

我手动将msvcp90.dllmsvcm90.dll添加到c:\Python27中,这些警告和错误就解决了。

更新3:

qt.conf:

[Paths]
Plugins = qt5_plugins

main.spec:

# -*- mode: python -*-
a = Analysis(['main.py'],
             pathex=['D:\\hello2'],
             hiddenimports=['sip', "PyQt5.QtCore", "PyQt5.QtGui", "PyQt5.QtWidgets"],
             hookspath=None,
             runtime_hooks=None)
pyz = PYZ(a.pure)
plugins = [("qt5_plugins/platforms/qwindows.dll",
             "C:\\Qt\\Qt5.2.1\\5.2.1\\msvc2012_64\\plugins\\platforms\\qwindows.dll", "BINARY")]
data = [
  ("qt.conf", "qt.conf", "DATA")
]
exe = EXE(
    pyz,
    a.scripts,
    a.binaries + plugins,
    a.zipfiles,
    a.datas + data,
    name='main.exe',
    debug=False,
    strip=None,
    upx=True,
    console=True
)

pyinstaller自动生成的qt5_plugins包含平台插件,我认为不需要手动添加作为额外库。

4 个回答

0

出现 application failed to start because ... platform plugin "windows" 这个错误,可能是因为使用了 UPX 压缩。

如果你在用 PyInstaller 并且设置了 UPX 来打包,那么你需要手动把压缩过的 qwindows.dll 文件替换成未压缩的版本。

未压缩的版本可以在这里找到:

C:\Python34\Lib\site-packages\PyQt5\plugins\platforms\qwindows.dll

把上面的文件粘贴到 PyInstaller 的临时文件夹里,这个文件夹的路径大概是:

C:\Users\JohnSmith\AppData\Roaming\pyinstaller\bincache01_py34_64bit\qt5_plugins\platforms\

然后重新运行你的 PyInstaller 命令。

0

我找到了解决办法,完成了工作:

在干净的虚拟机上运行exe时出现了一些错误,导致程序无法正常运行:

  • 使用 if __name__ == "__main__": 这行代码
  • 把我们的目录添加到 PATH 环境变量中
  • 手动添加我们需要的文件夹或文件

    比如说:

    • 我们的 .qml 文件或图片文件夹
    • 用于使用 qml 文件的 qml 目录等等

这是一个示例:

main.py

import sys
import os

from PyQt5.QtWidgets import QApplication, QPushButton
# from PyQt4.QtGui import QPushButton, QApplication

if getattr(sys, 'frozen', False):
    FullDirectory = os.path.join(os.environ.get("_MEIPASS", sys._MEIPASS))
    os.environ['PATH'] = FullDirectory
    # QApplication.addLibraryPath(FullDirectory)
else:
    FullDirectory = os.path.dirname(os.path.abspath(__file__))

if __name__ == "__main__":
    app = QApplication(sys.argv)
    win = QPushButton("Hello World!")
    win.show()
    sys.exit(app.exec_())

这是一个示例的规格文件:

main.spec

# -*- mode: python -*-
import os
import platform
import sys
sys.path.append("")

a = Analysis(['main.py'])
pyz = PYZ(a.pure)
exename = 'main.exe'
if "64" in platform.architecture()[0]:
    exename = 'main_x64.exe'

exe = EXE(
    pyz,
    a.scripts,
    a.binaries,
    a.zipfiles,
    a.datas,
    name=os.path.join('dist', exename),
    debug=False,
    strip=None,
    upx=True,
    console=True
)

希望这些对你有帮助。

感谢大家。

0

这可能是和其他人尝试在OSX上构建的内容重复的问题吗?

每次我在Linux上用pyinstaller构建PyQt4的时候,都不能直接用pyinstaller命令和参数来构建源代码文件。我必须先生成一个spec文件,然后对它进行修改,最后再用这个修改过的spec文件来构建。

看起来你在构建过程中没有把需要的Windows系统文件和插件文件一起打包。这些文件需要手动添加到你的spec文件中,以告诉它们要作为额外的库被收集,同时还需要一个修改过的qt.conf文件,设置插件的位置为相对路径([Paths]\nPlugins = qt5_plugins")。

我手边没有Windows系统来给你展示一个具体的spec文件,但我可以给你一个在我Mac上接近的例子:

a = Analysis(
    ['/path/to/my/script.py'],
    # Path to extra pythonpath locations
    pathex=['/path/to/my/script'],
    # Extra imports that might not have been auto-detected
    hiddenimports=["PySide", "PySide.QtCore", "PySide.QtGui"],
    hookspath=None,
    runtime_hooks=None
)

pyz = PYZ(a.pure)

# Binary files you need to include in the form of:
# (<destination>, <source>, "<TYPE>")
plugins = [
  (
    "qt4_plugins/imageformats/libqjpeg.dylib", 
    "/usr/local/Cellar/qt/4.8.1/plugins/imageformats/libqjpeg.dylib", 
    "BINARY"
  ),
  (
    "qt4_plugins/phonon_backend/libphonon_qt7.dylib", 
    "/usr/local/Cellar/qt/4.8.1/plugins/phonon_backend/libphonon_qt7.dylib", 
    "BINARY"
  )
]

# Data files you want to include, in the form of:
# (<destination>, <source>, "<TYPE>")
data = [
  ("qt.conf", "qt.conf", "DATA")
]

exe = EXE(
    pyz,
    a.scripts,
    a.binaries + plugins,
    a.zipfiles,
    a.datas + data,
    name='script',
    debug=False,
    strip=None,
    upx=True,
    console=False, 
    resources=['qt.conf']
)

# This BUNDLE part is only relevant on a MAC
app = BUNDLE(
    exe,
    name='script',
    icon=None
)

我首先使用utils/makespec.py生成一个“骨架”spec文件,这个文件包含了一些初始选项,然后我会对它进行修改。接着,我用pyinstaller来处理这个spec文件。

你也可以在这里查看一些其他的参考资料,虽然还是在OSX上:https://github.com/hvdwolf/pyExifToolGUI/blob/master/MacOSX/pyexiftoolgui.spec

1

我第一次尝试使用pyinstaller的时候,收到了缺少“windows”的提示。

我查看了很多“解决方案”,尝试了各种方法,最后通过把C:\Python34\Lib\site-packages\PyQt4\plugins\platforms里的qwindows.dll文件放到应用程序的目录(dist)和qt4_plugins\platforms文件夹里解决了这个问题。(我手动创建了platforms文件夹,而pyinstaller创建了qt4_plugins文件夹)

虽然这不是创建qt.conf文件那样优雅,但至少让应用程序能正常工作了。

我还要补充一下,我使用的是Windows 7,python 3.4和PyQt4。

撰写回答