通常,每个线程最多有一个事件队列。
可以有额外的事件队列,有两种不同的实现策略,它们是否与QCoreApplication::postEvent保持兼容。
如果您不关心 QCoreApplication::postEvent 与您的队列一起工作,那么一切都取决于您。您可以以任何您希望的方式实现队列。请注意,Qt 没有实现将QObject 标记为属于特定事件队列的方法。当您准备好从队列中传递事件时,您只需调用QCoreApplication::notify,将目标对象指针和事件指针传递给它。十分简单。当然,您无法控制从默认队列传递给任何和所有对象的事件。
另一种方法是与QCoreApplication::postEvent 保持兼容。即,您以某种方式“标记”一个对象,使其事件由您的队列处理。您拦截即将传递给目标的事件,将其排入队列,并根据需要自行处理。
这是QStateMachine-like hack。这很好。而且不好。以及介于两者之间的事情。值得知道它是如何完成的以及为什么。
分层状态机通常需要控制事件的传递并将它们自己的事件注入队列中,在其他事件之前。这是为了使状态转换事件按照与导致转换的事件相关的正确顺序传递;此外,有时可能需要多次传递导致转换的事件,保留以供以后传递等。
这完全是在默认事件调度系统强制执行的严格事件生命周期面前。为了解决这个问题,QStateMachine 实现了自己的事件队列。每当您对将传递给某个对象的事件声明转换时,状态机都会将自己安装为该对象上的事件过滤器。
然后,每当原始事件到达目标对象时,过滤器就会拦截该事件并阻止其传递到目标对象。现在它必须制作事件的副本,并将其插入到自己的队列中。必须进行复制,因为一旦控件离开事件过滤器和/或目标对象的event() 方法,事件调度程序就会删除该事件。
不幸的是,在 Qt 6 之前,QEvents 无法克隆 - 至少不能公开。有一些clone functionality hidden in Qt's innards, usable by user code,但它是一个私有API,而不是基于虚拟复制构造函数。
Qt 6 添加了QEvent::clone 方法,并且应该假定事件是可克隆的。未移植到 Qt 6 的遗留代码中的自定义事件将不支持这一点,如果它们携带数据,它们的克隆将无法完全发挥作用。
使用基于过滤器的惯用语/hack,您可以实现属于某个事件队列的QObject 的概念。当您的队列过滤目标对象上的事件、克隆它们并将它们排入队列以单独传递时,该对象在功能上驻留在您的事件队列中。如果没有进行此类过滤,则该对象驻留在默认的每线程队列中。您也可以只拦截您的队列感兴趣的类型的事件,就像 QStateMachine 所做的那样。