9

我正在尝试实现一个简单、轻量级的系统来记录 Qt GUI 事件并从脚本中回放它们。我认为使用 Qt 的事件系统的魔力这将是相当简单的,但我遇到了一个我不明白的问题。

这是我正在做的事情的快速总结:

记录:

QApplication.instance().eventFilter()用来捕获我感兴趣的所有 GUI 事件*并将它们保存到 Python 脚本中,其中每个步骤看起来像这样:

obj = get_named_object('MainWindow.my_menubar')
recorded_event = QMouseEvent(2, PyQt4.QtCore.QPoint(45, 8), 1, Qt.MouseButtons(0x1), Qt.KeyboardModifiers(0x0))
post_event(obj, recorded_event)

回放:

我只是在工作线程(非 GUI )中执行上面的脚本。(我不能使用 GUI 线程,因为我想继续向应用程序发送脚本事件,即使在模式对话框事件循环运行时阻塞了“主”事件循环。)

重要的事情发生在我的post_event()函数中,它需要做两件事:

  • 首先,打电话QApplication.postEvent(obj, recorded_event)
  • 等待所有事件完成处理:**
    • 将特殊事件发布到obj正在运行的同一事件循环中。
    • 处理特殊事件时:
      • 称呼QApplication.processEvents()
      • 设置一个标志,告诉播放线程可以继续

在第二部分完成后,我的期望是第一部分(录制的事件)的所有效果都已完成,因为特殊事件在录制事件之后排队。

整个系统似乎对鼠标事件、按键事件等都很好。但是QAction当我尝试为我的 main 播放事件时,我遇到了处理程序的问题QMenuBar

无论我尝试什么,似乎我都无法强制我的播放线程阻止QAction.triggered由于单击我的QMenu项目而导致的所有处理程序的完成。据我所知,在处理程序完成之前QApplication.processEvents()返回。QAction

QMenu小部件或信号是否有什么特别之处会破坏和/或QAction的正常规则? 我需要一种方法来阻止我处理程序的完成。QApplication.postEvent()QApplication.processEvents()QMenuQAction

[*] 并非每个事件都被记录下来。我只记录spontaneous()事件,也过滤掉一些其他类型(例如Paint事件和普通的鼠标移动)。

[**] 这很重要,因为脚本中的下一个事件可能引用由上一个事件创建的小部件。

4

2 回答 2

1

我认为您的问题可能最好通过使用 QFuture 和 QFutureWatcher 来解决(也就是说,如果您将 QtConcurrent 命名空间用于线程,而不是 QThreads)。基本上,Qt 事件处理系统不一定按它们发布的顺序处理事件。如果您需要阻塞直到某个操作完成,并且您在单独的线程中执行该操作,您可以使用 QtConcurrent::run() 返回的 QFuture 对象和 QFutureWatcher 来阻塞,直到该特定线程完成其处理.

其他需要考虑的是您处理事件的方式。当您使用 QApplication.postEvent() 时,您创建的事件将被添加到接收者的事件队列中以供稍后处理。在幕后,Qt 可以重新排序和压缩这些事件以节省处理器时间。我怀疑这更多是你的问题。

在处理播放的函数中,考虑使用 QCoreApplication::processEvents(),它在所有事件都完成处理之前不会返回。QCoreApplication 的文档在这里。

于 2013-04-05T17:10:34.677 回答
1

QMenu 小部件和 QAction 信号是一种特殊情况。QMenu 有一个 exec() 函数,通常用于弹出窗口。我怀疑(但我不确定)QMenuBar 在打开常规下拉菜单时会使用这种机制。文档对此并不清楚,但菜单的行为很像对话框,因为它们会阻止所有其他用户活动——除了给菜单提供自己的事件循环之外,Qt 将如何做到这一点?我无法填写您帖子中信息中的所有空白,但我看不出您的播放线程将如何应对新的事件循环。

于 2013-06-01T06:05:49.683 回答