接上一篇文章https://blog.csdn.net/Master_Cui/article/details/109182406,本文继续解析QCoreApplication::sendEvent和QCoreApplication::sendPostedEvents的代码
先看下QCoreApplication::sendEvent
bool QCoreApplication::sendEvent(QObject *receiver, QEvent *event)
{
Q_TRACE(QCoreApplication_sendEvent, receiver, event, event->type());
if (event)
event->spont = false;
return notifyInternal2(receiver, event);
}//sendEvent直接调用了notifyInternal2
bool QCoreApplication::notifyInternal2(QObject *receiver, QEvent *event)
{
bool selfRequired = QCoreApplicationPrivate::threadRequiresCoreApplication();
if (!self && selfRequired)
return false;
// Make it possible for Qt Script to hook into events even
// though QApplication is subclassed...
bool result = false;
void *cbdata[] = { receiver, event, &result };
if (QInternal::activateCallbacks(QInternal::EventNotifyCallback, cbdata)) {//将事件通知的回调函数放到一个qlist中,永远返回false
return result;
}
// Qt强制执行以下规则:事件只能发送到当前线程中的对象
//所以receiver->d_func()->threadData和等效QThreadData::current()
// 只是为了减少函数调用的开销
QObjectPrivate *d = receiver->d_func();
QThreadData *threadData = d->threadData;
QScopedScopeLevelCounter scopeLevelCounter(threadData);
if (!selfRequired)
return doNotify(receiver, event);//分别调用doNotify和notify
return self->notify(receiver, event);
}
bool QCoreApplication::notify(QObject *receiver, QEvent *event)
{
if (QCoreApplicationPrivate::is_app_closing)// no events are delivered after ~QCoreApplication() has started
return true;
return doNotify(receiver, event);
}//即使调用notify,最终还是要调用doNotify
上述代码的时序就是sendEvent-》notifyInternal2-》notify-》doNotify
接着看下doNotify的实现
static bool doNotify(QObject *receiver, QEvent *event)
{
if (receiver == 0) { // serious error
qWarning("QCoreApplication::notify: Unexpected null receiver");
return true;
}
#ifndef QT_NO_DEBUG
QCoreApplicationPrivate::checkReceiverThread(receiver);//检查接收者的线程是否和当前线程一致
#endif
return receiver->isWidgetType() ? false : QCoreApplicationPrivate::notify_helper(receiver, event);//接收者是否是窗口,如果是,返回false,不处理,否则调用notify_helper
}
bool QCoreApplicationPrivate::notify_helper(QObject *receiver, QEvent * event)
{
// Note: when adjusting the tracepoints in here
// consider adjusting QApplicationPrivate::notify_helper too.
Q_TRACE(QCoreApplication_notify_entry, receiver, event, event->type());
bool consumed = false;
bool filtered = false;
Q_TRACE_EXIT(QCoreApplication_notify_exit, consumed, filtered);
// 发送事件到所有应用程序的事件过滤器(仅在主线程中执行任何操作)
if (QCoreApplication::self
&& receiver->d_func()->threadData->thread.loadAcquire() == mainThread()
&& QCoreApplication::self->d_func()->sendThroughApplicationEventFilters(receiver, event)) {
filtered = true;
return filtered;
}
// 发送事件到所有接收者的事件过滤器
if (sendThroughObjectEventFilters(receiver, event)) {
filtered = true;
return filtered;
}
// deliver the event
consumed = receiver->event(event);//调用接受者的event函数处理事件,可由用户重写或者是父类的event函数
return consumed;
}
通过上面的分析,代码的时序就是doNotify-》notify_helper-》sendThroughApplicationEventFilters-》sendThroughObjectEventFilters-》event,而且可以得知。doNotify不处理窗口事件,并且事件过滤器接收事件比event函数早,和文章https://blog.csdn.net/Master_Cui/article/details/109093845与https://blog.csdn.net/Master_Cui/article/details/109109972中的说明一致
sendThroughApplicationEventFilters和sendThroughObjectEventFilters的实现就是调用应用程序或者接收者的eventFilter,具体实现如下
bool QCoreApplicationPrivate::sendThroughApplicationEventFilters(QObject *receiver, QEvent *event)
{
// We can't access the application event filters outside of the main thread (race conditions)
Q_ASSERT(receiver->d_func()->threadData->thread.loadAcquire() == mainThread());
if (extraData) {//
// application event filters are only called for objects in the GUI thread
for (int i = 0; i < extraData->eventFilters.size(); ++i) {
QObject *obj = extraData->eventFilters.at(i);
if (!obj)
continue;
if (obj->d_func()->threadData != threadData) {
qWarning("QCoreApplication: Application event filter cannot be in a different thread.");
continue;
}
if (obj->eventFilter(receiver, event))
return true;
}
}
return false;
}
bool QCoreApplicationPrivate::sendThroughObjectEventFilters(QObject *receiver, QEvent *event)
{
if (receiver != QCoreApplication::instance() && receiver->d_func()->extraData) {
for (int i = 0; i < receiver->d_func()->extraData->eventFilters.size(); ++i) {
QObject *obj = receiver->d_func()->extraData->eventFilters.at(i);
if (!obj)
continue;
if (obj->d_func()->threadData != receiver->d_func()->threadData) {
qWarning("QCoreApplication: Object event filter cannot be in a different thread.");
continue;
}
if (obj->eventFilter(receiver, event))
return true;
}
}
return false;
}
通过上述代码也可以知道,事件过滤器是在元对象的extraData中,而且,如果eventFilter返回true,notify_helper函数直接返回,接受事件的函数就无法再收到并处理事件,和博客https://blog.csdn.net/Master_Cui/article/details/109093845中的描述符合
sendPostedEvents的实现稍微复杂,代码如下
void QCoreApplication::sendPostedEvents(QObject *receiver, int event_type)
{
QThreadData *data = QThreadData::current();
QCoreApplicationPrivate::sendPostedEvents(receiver, event_type, data);
}//调用了QCoreApplicationPrivate::sendPostedEvents(receiver, event_type, data);
void QCoreApplicationPrivate::sendPostedEvents(QObject *receiver, int event_type,
QThreadData *data)
{
if (event_type == -1) {
event_type = 0;
}
if (receiver && receiver->d_func()->threadData != data) {
qWarning("QCoreApplication::sendPostedEvents: Cannot send posted events for objects in another thread");
return;
}
++data->postEventList.recursion;
auto locker = qt_unique_lock(data->postEventList.mutex);
// by default, we assume that the event dispatcher can go to sleep after
// processing all events. if any new events are posted while we send
// events, canWait will be set to false.
data->canWait = (data->postEventList.size() == 0);//查看事件队列中是否有事件,没有,则设置等待标志位
if (data->postEventList.size() == 0 || (receiver && !receiver->d_func()->postedEvents)) {//如果队列中没有事件或者接收者没有要发送的事件,退出
--data->postEventList.recursion;
return;
}
data->canWait = true;
int startOffset = data->postEventList.startOffset;//设置开始发送的事件下标
int &i = (!event_type && !receiver) ? data->postEventList.startOffset : startOffset;//设置事件的索引
data->postEventList.insertionOffset = data->postEventList.size();//如果有新的事件进入队列,设置插入队列的位置
// Exception-safe cleaning up without the need for a try/catch block
struct CleanUp {
QObject *receiver;
int event_type;
QThreadData *data;
bool exceptionCaught;
inline CleanUp(QObject *receiver, int event_type, QThreadData *data) :
receiver(receiver), event_type(event_type), data(data), exceptionCaught(true)
{}
inline ~CleanUp()
{
if (exceptionCaught) {
// since we were interrupted, we need another pass to make sure we clean everything up
data->canWait = false;
}
--data->postEventList.recursion;
if (!data->postEventList.recursion && !data->canWait && data->hasEventDispatcher())
data->eventDispatcher.loadRelaxed()->wakeUp();
// clear the global list, i.e. remove everything that was
// delivered.
if (!event_type && !receiver && data->postEventList.startOffset >= 0) {
const QPostEventList::iterator it = data->postEventList.begin();
data->postEventList.erase(it, it + data->postEventList.startOffset);
data->postEventList.insertionOffset -= data->postEventList.startOffset;
Q_ASSERT(data->postEventList.insertionOffset >= 0);
data->postEventList.startOffset = 0;
}
}
};
CleanUp cleanup(receiver, event_type, data);//上述类主要就是为了当事件循环退出时,清除事件队列中的所有事件
while (i < data->postEventList.size()) {//遍历事件队列
// avoid live-lock
if (i >= data->postEventList.insertionOffset)
break;
const QPostEvent &pe = data->postEventList.at(i);
++i;
if (!pe.event)
continue;
if ((receiver && receiver != pe.receiver) || (event_type && event_type != pe.event->type())) {//一致性检测
data->canWait = false;
continue;
}
if (pe.event->type() == QEvent::DeferredDelete) {//对DeferredDelete的事件进行特殊处理,不用太关注
// DeferredDelete events are sent either
// 1) when the event loop that posted the event has returned; or
// 2) if explicitly requested (with QEvent::DeferredDelete) for
// events posted by the current event loop; or
// 3) if the event was posted before the outermost event loop.
int eventLevel = static_cast<QDeferredDeleteEvent *>(pe.event)->loopLevel();
int loopLevel = data->loopLevel + data->scopeLevel;
const bool allowDeferredDelete =
(eventLevel > loopLevel
|| (!eventLevel && loopLevel > 0)
|| (event_type == QEvent::DeferredDelete
&& eventLevel == loopLevel));
if (!allowDeferredDelete) {
// cannot send deferred delete
if (!event_type && !receiver) {
// we must copy it first; we want to re-post the event
// with the event pointer intact, but we can't delay
// nulling the event ptr until after re-posting, as
// addEvent may invalidate pe.
QPostEvent pe_copy = pe;
// null out the event so if sendPostedEvents recurses, it
// will ignore this one, as it's been re-posted.
const_cast<QPostEvent &>(pe).event = 0;
// re-post the copied event so it isn't lost
data->postEventList.addEvent(pe_copy);
}
continue;
}
}
// first, we diddle the event so that we can deliver
// it, and that no one will try to touch it later.
pe.event->posted = false;
QEvent *e = pe.event;//设置事件和接收者
QObject * r = pe.receiver;
--r->d_func()->postedEvents;//要发送事件的计数-1
Q_ASSERT(r->d_func()->postedEvents >= 0);
// next, update the data structure so that we're ready
// for the next event.
const_cast<QPostEvent &>(pe).event = 0;
locker.unlock();
const auto relocker = qScopeGuard([&locker] { locker.lock(); });
QScopedPointer<QEvent> event_deleter(e); // will delete the event (with the mutex unlocked)
// after all that work, it's time to deliver the event.
QCoreApplication::sendEvent(r, e);//依然是调用QCoreApplication::sendEvent
// careful when adding anything below this point - the
// sendEvent() call might invalidate any invariants this
// function depends on.
}//循环结束
cleanup.exceptionCaught = false;
}
上述代码显示,sendPostedEvents就是多了一步对事件队列的遍历,然后将队列中的事件一个一个取出来,最后按顺序调用QCoreApplication::sendEvent(r, e)
上述代码中涉及的数据结构有QThreadData,使用该数据结构主要是为了取出当前线程的事件队列,代码如下
class QThreadData
{
public:
QThreadData(int initialRefCount = 1);
~QThreadData();
//......省略
QStack<QEventLoop *> eventLoops;
QPostEventList postEventList;
QAtomicPointer<QThread> thread;
QAtomicPointer<QAbstractEventDispatcher> eventDispatcher;
bool quitNow;
bool canWait;
}
而事件队列的存储使用的是如下数据结构
class QPostEventList : public QVector<QPostEvent>
{
public:
// recursion == recursion count for sendPostedEvents()
int recursion;
// sendOffset == the current event to start sending
int startOffset;
// insertionOffset == set by sendPostedEvents to tell postEvent() where to start insertions
int insertionOffset;
QMutex mutex;
inline QPostEventList()
: QVector<QPostEvent>(), recursion(0), startOffset(0), insertionOffset(0)
{ }
void addEvent(const QPostEvent &ev) {
int priority = ev.priority;
if (isEmpty() ||
constLast().priority >= priority ||
insertionOffset >= size()) {
// optimization: we can simply append if the last event in
// the queue has higher or equal priority
append(ev);
} else {
// insert event in descending priority order, using upper
// bound for a given priority (to ensure proper ordering
// of events with the same priority)
QPostEventList::iterator at = std::upper_bound(begin() + insertionOffset, end(), ev);
insert(at, ev);
}
}
private:
//hides because they do not keep that list sorted. addEvent must be used
using QVector<QPostEvent>::append;
using QVector<QPostEvent>::insert;
};
class QPostEvent
{
public:
QObject *receiver;
QEvent *event;
int priority;
inline QPostEvent()
: receiver(nullptr), event(nullptr), priority(0){ }
inline QPostEvent(QObject *r, QEvent *e, int p)
: receiver(r), event(e), priority(p){ }
};
可见,事件队列本质是一个QPostEvent的vector
所以经过分析可知,事件机制的时序就是通过exec先进入事件循环,然后通过平台生成对应的子类来分发事件,在Ubuntu18.04下,使用的是类QEventDispatcherGlib中的processEvents来处理时间,之后,通过glib中的主事件循环机制将定时器事件source,网络事件source和一般事件source添加到程序的主上下文中并对主上下文进行迭代(上述过程实际是通过IO复用接口poll来完成的),接着对不同的事件进行分发,并分别调用 QCoreApplication::sendEvent和 QCoreApplication::sendPostedEvents,最后,通过这两个函数将事件交给事件过滤器函数eventfilter和事件函数event处理
参考
Qt5.14源码
欢迎大家评论交流,作者水平有限,如有错误,欢迎指出