使用pyInstaller/UPX制作的可执行文件出现QtCore4.dll错误。

15
一个Python程序,我使用pyInstaller编译后,大小超过了400MB。该程序的GUI基于htmlPY,它是“PySide's QtWebKit库”的封装。程序的巨大大小部分归因于它使用了numpy、scipy和nltk,并且还有图形库。
为了最小化程序的大小,我安装了UPX。这将程序的大小减小到略大于100MB,这是可以接受的。
第一个问题是pyInstaller没有检测到htmlPy,并且没有将其包含在编译后的程序中。这可以通过将htmlPy模块从我的Python安装中复制到pyInstaller创建的“dist”目录中来解决。这样做后,没有使用UPX编译的版本的程序工作正常。
在将htmlPy添加到“dist”目录后,运行可执行文件时,在创建GUI时程序崩溃。我不确定这是否是由于UPX和QT之间的问题交互作用,还是由于UPX、QT和htmlPy之间的问题交互作用。Windows的“问题签名”如下:
Problem signature:
  Problem Event Name:   APPCRASH
  Application Name: main.exe
  Application Version:  0.0.0.0
  Application Timestamp:    00000000
  Fault Module Name:    QtCore4.dll
  Fault Module Version: 4.8.7.0
  Fault Module Timestamp:   561e435a
  Exception Code:   c0000005
  Exception Offset: 000000000010883a

任何想法关于这里发生了什么,以及如何修复它?
编辑:
这是我的.spec文件的内容:
# -*- mode: python -*-

block_cipher = None

added_files = [
     ( 'htmlPy/binder.js', 'htmlPy' ),
     ( 'templates/*', 'templates' ),
   ]
a = Analysis(['main.py'],
             pathex=['C:\\..\\My_App'],
             binaries=None,
             datas=added_files,
             hiddenimports=[],
             hookspath=[],
             runtime_hooks=['rthook_pyqt4.py'],
             excludes=[],
             win_no_prefer_redirects=False,
             win_private_assemblies=False,
             cipher=block_cipher)
pyz = PYZ(a.pure, a.zipped_data,
             cipher=block_cipher)
exe = EXE(pyz,
          a.scripts,
          exclude_binaries=True,
          name='My_App',
          debug=False,
          strip=False,
          upx=True,
          console=True )
coll = COLLECT(exe,
               a.binaries,
               a.zipfiles,
               a.datas,
               strip=False,
               upx=True,
               name='My_App')

这是rthook_pyqt4.py的内容:

import sip

sip.setapi(u'QDate', 2)
sip.setapi(u'QDateTime', 2)
sip.setapi(u'QString', 2)
sip.setapi(u'QTextStream', 2)
sip.setapi(u'QTime', 2)
sip.setapi(u'QUrl', 2)
sip.setapi(u'QVariant', 2)

编辑2:

这是一些初始化代码(标准htmlPy表现):

app.static_path = path.join(BASE_DIR, "static/")
print "Step 1"
app.template_path = path.join(BASE_DIR, "templates/")
print "Step 2"
app.template = ("index.html", {"username": "htmlPy_user"})
print "Step 3"
...

程序在到达第三步之前崩溃。

1
尽管有时可以起作用,但复制模块到pyinstaller文件夹中并不是一个好主意。尝试使用“--hidden-import”选项添加缺失的模块,看看是否仍然存在问题。 - Repiklis
我假设“没有setup.py”意味着没有.spec文件。恐怕在这种情况下,你需要一个来添加binder.js作为数据文件。我将在下面添加详细的答案。 - Repiklis
1
谢谢大家的点赞。我想授予悬赏积分(还有23个小时),但我们还没有任何答案提出可能的诊断措施,也没有找到解决问题的可能途径。我想我会试着联系UPX和pyInstaller的人,看看他们能否提供任何建议。 - Boa
你是否在使用 virtualenv?另外,你是否尝试将 QtCore4.dll 的路径添加到 "已添加的文件" 中——例如,添加 ('C:\\Python35-32\\Lib\\site-packages\\....\\Qt4Core.dll', '.') - sytech
我也遇到了一些与pyinstaller相关的问题,其中一些与你的类似。这些问题总结在https://stackoverflow.com/questions/46818993/compiling-python-with-pyinstaller中。 - Stéphane
显示剩余11条评论
1个回答

2
您的两个主要问题涉及以下内容:
  1. 正确性 - 使用UPX的应用程序无法运行
  2. 性能 - 400 MiB太大了,而100 MiB可以让您面向更多用户
如果应用程序太大,可能会使其对更多人有用,但如果它无法运行,它对任何人都没有用。您怀疑UPX可以改善第二个问题,但它的相互作用会影响第一个问题。
有趣的是,构建一个简单的HelloWorld应用程序,将其与pyInstaller + UPX打包,并使用其他依赖项(如Qt)进行装饰,直到看到它以类似于当前故障的方式中断。
可能更有成效的做法是放弃使用UPX,转而采用其他方法,包括NSIS。您可以使用像strace()这样的工具来监视在系统测试运行期间实际使用的分发文件,并在打包过程中删除未使用的文件。通过FUSE代理请求将产生类似的信息。您可以列出已发布应用程序的依赖项,并依靠pip或conda并行下载依赖项,如果“安装时间”确实是驱动您将400缩小到100 MiB的愿望的原因。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接