【问题标题】:PyInstaller packaged application works fine in Console mode, crashes in Window modePyInstaller 打包的应用程序在控制台模式下工作正常,在窗口模式下崩溃
【发布时间】:2012-11-05 23:10:20
【问题描述】:

我正在使用 Python 和 PySide 构建一个相当复杂的应用程序。终于发布的日子快到了,所以我想将此应用程序构建为 exe。

但是,我手上有一个奇怪的问题。我过去使用过 PyInstaller(顺便说一下,使用的是第 2 版),但我从来没有遇到过这种情况。

基本上,当我使用 --console 标志构建应用程序时,它可以正常工作 - 但它会打开控制台窗口。当我使用窗口标志 (-w) 构建应用程序时,它无法正常工作。它开始了一切,但有所有这些奇怪的故障。例如,加载文本文件通常会引发BadFileDescriptor 错误(在控制台模式下不会发生),并且应用程序在执行特定任务后会崩溃。更糟糕的是任务是一个循环,第一次执行还不错,但再次开始执行时就崩溃了。

当我查看 minidump 文件时,有一些关于 QtGui4.dll 文件的内存访问冲突的错误。同样,这不会在控制台模式下发生。

有人有什么想法吗?

【问题讨论】:

  • 您的程序使用print 语句还是sys.stdout.write?读取here(不幸的是您是意大利语)错误的文件描述符错误可能是由此引起的,您可以通过删除/注释掉prints 或重定向sys.stdout 或改用logging 来解决它。似乎当您在窗口模式下启动应用程序时,stdout 是一个固定大小的缓冲区,这会导致内存访问冲突。
  • 它确实使用了打印语句。你能告诉我更多关于如何重定向 sys.stdout 的信息吗?我的意思是,是否可以用一行代码轻松地一次性替换软件中的所有打印语句?
  • 您可以在程序执行的最开始放置类似import sys; import tempfile; sys.stdout = tempfile.TemporaryFile(); sys.stderr = tempfile.TemporaryFile()(希望格式更好)的内容,然后所有prints 将被重定向到一个临时文件,该文件将当你的程序退出时被删除。对于更长期的解决方案,我会考虑使用logging
  • 非常感谢!我会试试这个。我已经启用了日志记录,但我的大部分语句都是printlogging。再次感谢。
  • @Bakuriu,嗯,出于某种原因,它对我不起作用:我现在有这个:sys.stdout = tempfile.TemporaryFile() sys.stderr = tempfile.TemporaryFile() 但由于某种原因,我仍然可以在我的 PyCharm 控制台中看到打印,并且应用程序仍然可以不行。 --- 编辑,没关系,我的代码的一部分中有reload(sys)。现在可以了!

标签: python qt pyqt pyside pyinstaller


【解决方案1】:

BadFileDescriptor 错误和由此导致的内存访问冲突是由窗口模式下的应用程序的stdout 是一个固定大小的缓冲区这一事实引起的。 因此,如果您正在写信给stdout,无论是直接使用print 还是sys.stdout,一段时间后您就会看到这些错误。

您可以通过以下方式解决此问题:

  1. 删除/注释掉stdout 上的文字
  2. 使用logging 而不是打印到标准输出
  3. 在应用程序执行开始时重定向stdout。这是需要更改较少代码的解决方案,尽管我认为将调试语句移至 logging 会是更好的选择。

要重定向stdout,您可以使用这种代码:

import sys
import tempfile
sys.stdout = tempfile.TemporaryFile()
sys.stderr = tempfile.TemporaryFile()

就在执行程序之前。您还可以使用一些自定义对象将输出放入“日志”文件或其他文件中,重要的是输出不应填充固定大小的缓冲区。

例如,您可以执行以下操作,以便能够利用 logging 模块而无需更改太多代码:

import sys
import logging

debug_logger = logging.getLogger('debug')
debug_logger.write = debug_logger.debug    #consider all prints as debug information
debug_logger.flush = lambda: None   # this may be called when printing
#debug_logger.setLevel(logging.DEBUG)      #activate debug logger output
sys.stdout = debug_logger

这种方法的缺点是print 对每行执行更多的stdout.write 调用:

>>> print 'test'
DEBUG:debug:test
DEBUG:debug:

如果您愿意,您可以编写一个真正的 write 函数来避免这种行为,该函数仅使用“全行”调用 the_logger.debug

无论如何,我认为这种解决方案应该只是暂时的,并且只能在将prints 移植到对logging.debug 的调用之前使用。

(显然,记录器应该写入文件而不是 stdout 以避免错误。)

【讨论】:

  • 在多处理中使用了第二种方法,它抱怨 flush() 丢失 - 我还必须向 debug_logger 添加一个虚拟(仅包含“pass”)刷新例程。
  • @Andris 编辑以包含它,只是为了安全。
  • 在现代 Windows 上,有时 Windows 只会“暂停”进程,等待 stdout 被清空,从而使您的程序静默失败。叹息。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-03-22
  • 2019-09-11
  • 2019-12-05
  • 2019-12-20
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多