我试图实现一个简单,轻量级的系统来记录Qt GUI事件并从脚本中播放它们。我认为使用Qt的事件系统的魔力会相当简单,但我遇到了一个我不明白的问题。
以下是我正在做的事情的快速摘要:
RECORDING:
我使用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)
PLAYBACK:
我只是在一个worker(非GUI )线程中执行上面的脚本。 (我无法使用GUI线程,因为我想继续向应用程序发送脚本事件,即使在运行模式对话框事件循环时阻止了主要的事件循环。)
重要的事情发生在我的post_event()
函数中,它需要做两件事:
QApplication.postEvent(obj, recorded_event)
obj
正在运行的同一个事件循环中。QApplication.processEvents()
第二部分完成后,我的期望是第一部分(记录的事件)的所有效果都已完成,因为特殊事件在之后排队记录的事件。
整个系统主要是似乎适用于鼠标事件,关键事件等。但是当我尝试播放事件时,我遇到了QAction
处理程序的问题我的主要QMenuBar
。
无论我尝试什么,似乎我无法通过点击{{{ 1}}项目。据我所知,QAction.triggered
在 QMenu
处理程序完成之前返回。
QApplication.processEvents()
小部件或QAction
信号是否有特殊内容违反QMenu
和/或QAction
的正常规则? 我需要一种方法来阻止我的QApplication.postEvent()
QApplication.processEvents()
处理程序的完成。
[*]并非所有事件都被记录下来。我只记录QMenu
个事件,我还会过滤掉其他一些类型(例如QAction
事件和普通鼠标移动)。
[**]这很重要,因为脚本中的下一个事件可能是指前一个事件创建的小部件。
答案 0 :(得分:1)
我认为使用QFuture和QFutureWatcher可以最好地解决您的问题(也就是说,如果您将QtConcurrent命名空间用于线程,而不是QThreads)。基本上,Qt事件处理系统不一定按照发布的顺序处理事件。如果需要阻塞直到某个操作完成,并且您在单独的线程中执行该操作,则可以使用QtConcurrent :: run()返回的QFuture对象与QFutureWatcher一起阻塞,直到该特定线程完成其处理。
要考虑的是处理事件的方式。使用QApplication.postEvent()时,您创建的事件将添加到接收方的事件队列中,以便稍后处理。在幕后,Qt可以重新排序和压缩这些事件以节省处理器时间。我怀疑这更像是你的问题。
在处理播放的函数中,请考虑使用QCoreApplication :: processEvents(),它将在所有事件处理完毕后才会返回。 QCoreApplication的文档是here.
答案 1 :(得分:1)
QMenu小部件和QAction信号是一种特殊情况。 QMenu有一个exec()函数,通常用于弹出窗口。我怀疑(但我不确定)QMenuBar在打开常规下拉菜单时会使用此机制。文档对此并不清楚,但Menus的行为很像对话框,因为它们阻止了所有其他用户活动 - 除了通过给菜单提供自己的事件循环之外,Qt将如何做到这一点?我无法填写你帖子中信息的所有空白,但我看不到你的播放线程如何应对新的事件循环。