使用 Python 和 PySide6 将 Qt 与现有 UE 5.4.4 集成时,看到 QApplication 的奇怪行为。具体通过执行以下操作来处理此问题:
app
的 QApplication,这样它就不会被垃圾收集。现在,应该发生的事情是什么都没有。 QApplication 尚未执行。不应该有事件循环运行,因此不会处理超时信号。作为管道开发人员,我多年来创建了数十个 QApplication。要进行事件处理,您必须: 使用 exec 启动事件循环;或通过某些回调泵送 processEvents。基于 Qt 的应用程序(例如 Maya)会为您启动事件循环,但 UE 不是 Qt 应用程序。它不附带任何 Qt 库。我已经咨询了 Epic 支持人员。他们不知道这是如何或为何发生的。
话虽如此,如果您按照上述步骤操作,您将看到计时器在三秒后启动。
我已经用 GoogleFu 搜索了一大堆来寻找这个问题的答案。唯一的线索指向一个名为 QAbstractEventDispatcher 的类,该类是创建并附加到 QApplication 的,具有其部分设置。我们创建的 QApp 确实有其中之一,类型为 QWindowsGuiEventDispatcher。 Qt 文档非常模糊地指出您可以通过实现此类来与主机事件循环集成。然而,Epic 并没有这样做。因此,证据表明 Qt 正在以某种方式检测到 QApplication 是在具有事件循环的线程中创建的,并且它附加到该事件循环。然而,这种行为没有记录。
所以问题是,任何人都可以解释 QApplication 对象如何检测到它正在具有事件循环的线程中实例化并将其自身附加到它吗?
TLDR:在 UE Editor、OutputLog、Python REPL 中,输入以下内容:
from PySide6 import QtCore, QtWidgets
if QtWidgets.QApplication.instance() is None:
app = QtWidgets.QApplication()
QtCore.QTimer.singleShot(3000, lambda: print('Timer fired')
这应该做的绝对是没什么。应用程序事件循环不应运行。要启动计时器,需要进行
exec_
调用或 processEvents
调用。然而,在输入该代码的三秒钟内,计时器将触发,您将看到该 lambda 的消息。
可能有两个原因。
UE 中使用的“Python 终端”基于 iPython(或类似)终端,或者您使用的是相对较新版本的 PySide(可能是 6.7.3 或更高版本)。
在第一种情况下,此类交互式解释器使用 input hook(参见
PyOS_InputHook
),当提示空闲时自动允许事件处理。
这在许多情况下都非常有用,例如在显示 matplotlib 绘图时仍然能够交互式地更改值,而无需每次都编写完整的程序。
PyQt 多年来一直自行提供此类支持(因此,即使在标准解释器中,它也默认工作),并且也有 正确记录。
显然,PySide 最近才引入这一点,但不幸的是它尚未记录,虽然注册/取消注册的函数(与 PyQt 类似)似乎确实存在,但它们似乎并不公开。
如果你想避免这种情况并且UE解释器是基于iPython的,你可以尝试查看相关文档以了解如何禁用它。