从源码看 QT 的事件系统及自定义事件
事件是程序内部或外部触发的动作或状态变化的信号。在 Qt 中,所有事件都是 QEvent 派生类的对象,事件由 QObject 派生类的对象接收和处理。每一个事件都有对应的 QEvent 派生类,当事件发生时,QT 会创建相应事件的对象。然后调用接收者(QObject 派生类对象)的 event() 方法,将事件交给其进行处理。QT 中事件与窗口(widget)密切相关,常见的事件有 QResizeEvent、QPaintEvent、QMouseEvent、QKeyEvent、QTimerEvent等。每个事件都与一个指定类型( QEvent::Type)关联。注:以下引用的源码版本为 QT 7.6.3
事件类型
QT 中的事件都是预定义事件,都与指定的类型关联。枚举类 QEvent::Type 定义系统所支持的事件类型,所有的 QEvent 子类需要重载 QEvent::Type QEvent::type() const
方法指明事件类型。事件类型的定义及含义详见: https://doc.qt.io/qt-6/qevent.html#type
QT 中的事件根据其发送者和用途,可以分为三类:
- 系统事件
与操作系统相关,由 QT 封装后再交给应用程序处理。如:QMouseEvent、QKeyEvent、QInputEvent。方法bool QEvent::spontaneous() const
用于判断是否为系统事件。 - QT 框架事件
用于 QT 对象间的通信。如:QActionEvent、QDynamicPropertyChangeEvent。 - 自定义事件
用户处理应用程序时定义的事件。事件类型必须位于 QEvent::User 与 QEvent::MaxUser 之间。
事件循环
应用程序在 main 函数中执行 QCoreApplication::exec()
后,QT 的事件循环系统开始运行。从源码中可以看出,QCoreApplication::exec()
主要是启动了事件循环系统。
// qcoreapplication.cpp
int QCoreApplication::exec()
{
if (!QCoreApplicationPrivate::checkInstance("exec"))
return -1;
QThreadData *threadData = self->d_func()->threadData.loadAcquire();
if (threadData != QThreadData::current()) {
qWarning("%s::exec: Must be called from the main thread", self->metaObject()->className());
return -1;
}
if (!threadData->eventLoops.isEmpty()) {
qWarning("QCoreApplication::exec: The event loop is already running");
return -1;
}
threadData->quitNow = false;
QEventLoop eventLoop;
self->d_func()->in_exec = true;
self->d_func()->aboutToQuitEmitted = false;
// 事件循环系统开始执行
int returnCode = eventLoop.exec(QEventLoop::ApplicationExec);
threadData->quitNow = false;
if (self)
self->d_func()->execCleanup();
return returnCode;
}
// qguiapplication.cpp
int QGuiApplication::exec()
{
#if QT_CONFIG(accessibility)
QAccessible::setRootObject(qApp);
#endif
return QCoreApplication::exec();
}
// qapplication.cpp
int QApplication::exec()
{
return QGuiApplication::exec();
}
实际上,事件循环也不是由 QEventLoop 执行的,它只是对 QAbstractEventDispatcher 进行了包装。QAbstractEventDispatcher 是一个抽象类,定义了处理事件的虚函数。事件的具体执行跟平台相关。如 QEventDispatcherWin32 处理 windows 平台上的事件,QEventDispatcherUNIX 处理 unix 平台上的事件。QAbstractEventDispatcher 的子类在 processEvents() 方法中处理事件,对于 QEventDispatcherWin32 类 processEvents() 方法执行内容如下:首先,执行 sendPostedEvents() 处理事件队列中的事件,即发送 postEvent 事件队列中的事件(注意,QEventDispatcherWin32 子类中的 sendPostedEvents() 还有其它操作);然后从系统消息队列中获取消息,处理 WM_TIMER、WM_QUIT 消息,并继续分发消息。
// qeventloop.cpp
int QEventLoop::exec(ProcessEventsFlags flags)
{
Q_D(QEventLoop);
auto threadData = d->threadData.loadRelaxed();
//we need to protect from race condition with QThread::exit
QMutexLocker locker(&static_cast<QThreadPrivate *>(QObjectPrivate::get(threadData->thread.loadAcquire()))->mutex);
if (threadData->quitNow)
return -1;
if (d->inExec) {
qWarning("QEventLoop::exec: instance %p has already called exec()", this);
return -1;
}
struct LoopReference {
...
};
LoopReference ref(d, locker);
// remove posted quit events when entering a new event loop
QCoreApplication *app = QCoreApplication::instance();
if (app && app->thread() == thread())
QCoreApplication::removePostedEvents(app, QEvent::Quit);
while (!d->exit.loadAcquire())
processEvents(flags | WaitForMoreEvents | EventLoopExec);
ref.exceptionCaught = false;
return d->returnCode.loadRelaxed();
}
bool QEventLoop::processEvents(ProcessEventsFlags flags)
{
Q_D(QEventLoop);
auto threadData = d->threadData.loadRelaxed();
if (!threadData->hasEventDispatcher())
return false;
return threadData->eventDispatcher.loadRelaxed()->processEvents(flags);
}
处理系统事件
系统事件(windows 下的 WM_KEYUP、WM_MOUSEFIRST 等消息)一般与用户界面密切相关,QT 中就是与 QWidget 相关。应用程序中要使用 QWidget 就要配置 QT += gui。此时,在 main 函数中执行的是 QApplication::exec()
。QApplication 继承自 QGuiApplication,QGuiApplication 继承自 QCoreApplication。QGuiApplication 类实现了系统消息的处理。
QGuiApplication 的构造函数中调用了 QGuiApplicationPrivate::init() 方法。该方法调用了 QCoreApplicationPrivate::init(), 执行了 SESSIONMANAGER、PLUGINS、 animation、TESTABILITY 等初始化,并调用 QGuiApplicationPrivate::createPlatformIntegration() 识别应用程序所在的操作系统平台,通过 static void init_platform() 方法初始化平台环境。init_platform() 方法通过集成平台工厂(QPlatformIntegrationFactory::create())创建了对应平台的集成对象(QPlatformIntegration 的子类)。
QWindowsIntegration 类是对应 windows 平台的集成类(目录 qtbase\src\plugins\platforms\windows 存放 windows 平台相关类)。再看 QCoreApplicationPrivate::init() 方法,它通过 createEventDispatcher() 方法创建了 QAbstractEventDispatcher 对象。QGuiApplicationPrivate 类(继承自 QCoreApplicationPrivate 类)对 createEventDispatcher() 进行了重载。重载后的方法通过QWindowsIntegration::createEventDispatcher() 方法创建 QWindowsGuiEventDispatcher(继承自 QAbstractEventDispatcher 类) 对象。QWindowsGuiEventDispatcher 类对 processEvents()、sendPostedEvents() 方法进行了重载。重载后的代码如下:
bool QWindowsGuiEventDispatcher::processEvents(QEventLoop::ProcessEventsFlags flags)
{
const QEventLoop::ProcessEventsFlags oldFlags = m_flags;
m_flags = flags;
const bool rc = QEventDispatcherWin32::processEvents(flags);
m_flags = oldFlags;
return rc;
}
void QWindowsGuiEventDispatcher::sendPostedEvents()
{
QEventDispatcherWin32::sendPostedEvents();
QWindowSystemInterface::sendWindowSystemEvents(m_flags);
}
再看 QApplication::exec() 的实现,最终使用的是 QCoreApplication::exec()。所以事件循环由 QAbstractEventDispatcher 的子类(即 QWindowsGuiEventDispatcher 类)通过 processEvents() 方法进行处理。
// qapplication.cpp
int QApplication::exec()
{
return QGuiApplication::exec();
}
// qguiapplication.cpp
int QGuiApplication::exec()
{
#if QT_CONFIG(accessibility)
QAccessible::setRootObject(qApp);
#endif
return QCoreApplication::exec();
}
QEventDispatcherWin32::processEvents() 方法中执行了 sendPostedEvents() 方法。QWindowsGuiEventDispatcher 类中的 sendPostedEvents() 方法在调用 QEventDispatcherWin32::sendPostedEvents() 后调用了 QWindowSystemInterface::sendWindowSystemEvents(m_flags)。QWindowSystemInterface 的私有类 QWindowSystemInterfacePrivate 类中定义了 MouseEvent、KeyEvent、PaintEvent 等事件类,并定义了一个用于存放 windows 消息的队列(windowSystemEventQueue)。QT 转换后的事件存放在 windowSystemEventQueue 队列中。sendWindowSystemEvents() 方法遍历 windowSystemEventQueue 队列,并将事件交给 QGuiApplicationPrivate::processWindowSystemEvent(event) 进行处理。processWindowSystemEvent 会根据事件类型进行分类处理。具体的事件处理方法(如processPaintEvent)会将系统消息包装为 QT 事件(如QPaintEvent),并通过 QCoreApplication::sendSpontaneousEvent 方法执行具体的事件处理(事件过滤和receiver->event()操作)。
void QGuiApplicationPrivate::processPaintEvent(QWindowSystemInterfacePrivate::PaintEvent *e)
{
Q_ASSERT_X(platformIntegration()->hasCapability(QPlatformIntegration::PaintEvents), "QGuiApplication",
"The platform sent paint events without claiming support for it in QPlatformIntegration::capabilities()");
if (!e->window)
return;
QPaintEvent paintEvent(e->region);
QCoreApplication::sendSpontaneousEvent(e->window, &paintEvent);
// We report back the accepted state to the platform, so that it can
// decide when the best time to send the fallback expose event is.
e->eventAccepted = paintEvent.isAccepted();
}
还有一个问题就是操作系统发送的 WM_KEYUP 消息是如何与 QT 应用程序交互的呢?回到 QWindowsIntegration 类,它的私有类 QWindowsIntegrationPrivate 中有一个成员变量 QWindowsContext m_context。QWindowsContext 提供了一个 registerWindowClass() 方法,该方法中使用了 windows 的系统函数 RegisterClassEx() 进行了类注册。在注册过程中,绑定了windows 消息的处理函数(qWindowsWndProc())。qWindowsWndProc() 对系统消息进行了分类,并调用 QWindowsContext::windowsProc() 处理系统消息。windowsProc() 调用对应的消息处理方法(QWindowsMouseHandler、QWindowsKeyMapper 等)对消息内容进行解析,然后交给 QWindowSystemInterface 对应的 handle 方法(如 handleMouseEvent、handlePaintEvent 等)进行处理。这些 handle 会将解析后的消息存放到 windowSystemEventQueue 队列,并唤醒 QAbstractEventDispatcher 对象进行消息处理。
QWidget 对象创建后并没有立即创建窗口,而是在 show(true) 或 setVisible(true) 时创建窗口。创建的窗口为一个 QWidgetWindow 对象,QWidgetWindow 是 QWindow 的子类。QWindow 的 create() 方法使用 QPlatformIntegration 对象(windows 下为 QWindowsIntegration对象)的 createPlatformWindow()方法(或createForeignWindow()方法)创建对应平台上的窗口。该方法调用了 QWindowsWindowData::create() 方法(和 QWindowsWindow 一起在qwindowswindow.cpp 中实现),构建并返回 QWindowsWindow 对象。QWindowsWindowData::create() 方法中调用了 QWindowsContext 的 registerWindowClass() 方法进行了类的注册,并调用 windows 的系统函数 CreateWindowEx() 创建了窗口。
至此,windows 系统的消息接收、包装、传递过程完整结束。涉及到了类及其调用关系(仅用作示意)如下图所示:
发送事件
- QT 应用内部发送事件
QT 中事件的发送涉及以下3个方法,但是事件的发送最终由 notify() 完成,notify() 会直接调用接收者的 event() 方法。
bool QCoreApplication::notify(QObject *receiver, QEvent *event)
void QCoreApplication::postEvent(QObject *receiver, QEvent *event, int priority = Qt::NormalEventPriority)
bool QCoreApplication::sendEvent(QObject *receiver, QEvent *event)
void QCoreApplication::postEvent(QObject *receiver, QEvent *event, int priority = Qt::NormalEventPriority)
将事件和接收者信息保存到事件队列并立即返回。事件循环处理方法 QAbstractEventDispatcher::processEvents() 会调用 QCoreApplication::sendPostedEvents(QObject *receiver, int event_type) 将队列中的事件全部发送出去(使用 QCoreApplication::sendEvent() 发送事件)。sendPostedEvents() 处理事件时会从事件队列中删除事件,所以该事件必须分配在堆上。因此使用 postEvent 发布事件后,再访问该事件并不安全。注意:该方法不是线程安全的。bool QCoreApplication::sendEvent(QObject *receiver, QEvent *event)
调用 QCoreApplication::notify() 方法将事件发送出去,并返回事件处理程序的返回值。发送事件后,该事件不会被删除。一般是在栈上创建该事件。bool QCoreApplication::notify(QObject *receiver, QEvent *event)
调用接收方(需继承 QObject)的 event() 方法,并返回 event() 的返回值。不管接收方是否在当前线程中,都会调用其 event() 方法。注意:如果重写了该函数,必须确保所有处理事件的线程(包括由其他库启动的线程)都已经停止工作,然后才能开始销毁应用程序对象。QGuiApplication、QApplication 都重写了该方法,也就是说在事件的发送操作上有不同的实现s。
- QT 向操作系统发送事件
若需要向操作系统发送消息/事件,就需要调用系统 API,如:SendMessage/PostMessage、XSendEvent。或者借助系统命令行工具,使用 QProcess 调用系统命令。
- 进程间发送事件
QT 并不支持进程间的事件,可通过进程间通信方法(共享内存、Socket通信等)来解决。
事件优先级
QCoreApplication::postEvent() 发送的事件可以设置优先级。事件按优先级降序排序,即优先级高的事件在优先级较低的事件之前。优先级可以是任何整数值,即介于 INT_MAX 和 INT_MIN 之间,包含INT_MAX 和 INT_MIN。具有相同优先级的事件将按发布的顺序进行处理。为了便于使用 Qt::EventPriority 定义了 3 个优先级。
枚举值 | 值 | 描述 |
---|---|---|
Qt::HighEventPriority | 1 | 高优先级 |
Qt::NormalEventPriority | 0 | 优先级介于 HighEventPriority 和 LowEventPriority之间 |
Qt::LowEventPriority | -1 | 低优先级 |
也可以自定义优先级: |
enum CustomEventPriority
{
// An important event
ImportantEventPriority = Qt::HighEventPriority,
// A more important event
MoreImportantEventPriority = ImportantEventPriority + 1,
// A critical event
CriticalEventPriority = 100 * MoreImportantEventPriority,
// Not that important
StatusEventPriority = Qt::LowEventPriority,
// These are less important than Status events
IdleProcessingDoneEventPriority = StatusEventPriority - 1
};
事件的传递
为了确定接收者是否被处理发送的事件,事件系统提供了 accept() 和 ignore() 方法。当接收者处理该事件并期望事件系统知道该事件已被处理时,使用 accept() 方法设置 "accept" 标记;如果接收者不处理该事件,使用 ignore() 方法设置 "accept" 标记。
对于一些特殊的系统事件,需要事件系统进行逐层传递。例如,操作系统发送的 WM_KEYUP、WM_MOUSEFIRST 等消息,一般先由顶层子窗口进行处理,然后冒泡到父窗口(SendMessage()、PostMessage()发送的消息除外)。在 windows 环境下,创建窗口时会调用操作系统函数 RegisterClass 注册窗口类,并指定消息处理函数。顶层的消息处理函数如果接收该消息则返回true,如果不接收该消息则会调用 DefWindowProc() 方法继续传递处理消息。按照设计原则(单一职责),这些事件的传递应该由 QWidget 来负责实现。但是在 QWidget 的事件处理方法里运行的是 event->ignore();
并没做其它事,这种设计看起来很奇怪。
QT 处理这些事件传递的方式是通过 QApplication 重写 notify() 方法来实现。也就是说使用 QCoreApplication、QGUIApplication 时,QMouseEvent 这类需要传递的事件不会自动传递给父窗口。QApplication 重写的 notify() 方法部分代码摘录如下:
bool QApplication::notify(QObject *receiver, QEvent *e)
{
Q_D(QApplication);
...
const bool isWindowType = receiver->isWindowType();
const bool isWidgetType = receiver->isWidgetType();
...
if (isWidgetType) {
QWidget * w = static_cast<QWidget *>(receiver);
switch (e->type()) {
case QEvent::ShortcutOverride:
case QEvent::KeyPress:
case QEvent::KeyRelease: {
QKeyEvent* key = static_cast<QKeyEvent*>(e);
bool def = key->isAccepted();
/*
QLineEdit will emit a signal on Key_Return, but
ignore the event, and sometimes the connected
slot deletes the QLineEdit (common in itemview
delegates), so we have to check if the widget
was destroyed even if the event was ignored (to
prevent a crash)
Note that we don't have to reset pr while
propagating (because the original receiver will
be destroyed if one of its ancestors is)
*/
QPointer<QObject> pr = receiver;
while (w) {
if (def)
key->accept();
else
key->ignore();
res = d->notify_helper(w, e);
if (res && key->isAccepted())
break;
if (!pr || w->isWindow())
break;
w = w->parentWidget();
}
qt_in_tab_key_event = false;
break;
}
case QEvent::MouseButtonPress:
case QEvent::MouseButtonRelease:
case QEvent::MouseButtonDblClick:
case QEvent::MouseMove: {
...
}
} else {
res = d->notify_helper(receiver, e);
}
return res;
}
从源码中可以看出,以下事件类型在 QT 中实现了自动传递: QEvent::ShortcutOverride、QEvent::KeyPress、QEvent::KeyRelease、QEvent::MouseButtonPress、QEvent::MouseButtonRelease、QEvent::MouseButtonDblClick、QEvent::MouseMove、QEvent::Wheel、QEvent::ContextMenu、QEvent::TabletMove、QEvent::TabletPress、QEvent::TabletRelease、QEvent::ToolTip、QEvent::WhatsThis、QEvent::QueryWhatsThis、QEvent::StatusTip、QEvent::WhatsThisClicked、QEvent::DragEnter、QEvent::DragMove、QEvent::Drop、QEvent::DragLeave、QEvent::TouchBegin、QEvent::NativeGesture、QEvent::Gesture、QEvent::GestureOverride。
对于其它事件,如果要实现事件的传递可以通过重写接收者的 event() 方法来实现。通过事件的 accept 标记来确定是否继续传递事件。
拦截事件
从 QT 对事件的处理流程来看,事件都由 notify() 方法进行分发,事件先经过 ApplicationEventFilters、ObjectEventFilters 处理,最后由接收者的 event() 方法进行处理。QGuiApplication 和 QApplication 虽然对 notify() 方法进行了重写,但是对事件的处理流程没有改变。
bool QCoreApplication::notify(QObject *receiver, QEvent *event)
{
Q_ASSERT(receiver);
Q_ASSERT(event);
#if QT_VERSION >= QT_VERSION_CHECK(7, 0, 0)
Q_ASSERT(receiver->d_func()->threadData.loadAcquire()->thread.loadRelaxed()
== QCoreApplicationPrivate::mainThread());
#endif
// no events are delivered after ~QCoreApplication() has started
if (QCoreApplicationPrivate::is_app_closing)
return true;
return doNotify(receiver, event);
}
static bool doNotify(QObject *receiver, QEvent *event)
{
Q_ASSERT(event);
// ### Qt 7: turn into an assert
if (receiver == nullptr) { // 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);
}
/*!
\internal
Helper function called by QCoreApplicationPrivate::notify() and qapplication.cpp
*/
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);
// send to all application event filters (only does anything in the main thread)
if (receiver->d_func()->threadData.loadRelaxed()->thread.loadAcquire() == mainThread()
&& QCoreApplication::self
&& QCoreApplication::self->d_func()->sendThroughApplicationEventFilters(receiver, event)) {
filtered = true;
return filtered;
}
// send to all receiver event filters
if (sendThroughObjectEventFilters(receiver, event)) {
filtered = true;
return filtered;
}
// deliver the event
consumed = receiver->event(event);
return consumed;
}
/*! \reimp
*/
bool QGuiApplication::notify(QObject *object, QEvent *event)
{
if (object->isWindowType()) {
if (QGuiApplicationPrivate::sendQWindowEventToQPlatformWindow(static_cast<QWindow *>(object), event))
return true; // Platform plugin ate the event
}
QGuiApplicationPrivate::captureGlobalModifierState(event);
return QCoreApplication::notify(object, event);
}
bool QApplicationPrivate::notify_helper(QObject *receiver, QEvent * e)
{
// These tracepoints (and the whole function, actually) are very similar
// to the ones in QCoreApplicationPrivate::notify_helper; the reason for their
// duplication is because tracepoint symbols are not exported by QtCore.
// If you adjust the tracepoints here, consider adjusting QCoreApplicationPrivate too.
Q_TRACE(QApplication_notify_entry, receiver, e, e->type());
bool consumed = false;
bool filtered = false;
Q_TRACE_EXIT(QApplication_notify_exit, consumed, filtered);
// send to all application event filters
if (threadRequiresCoreApplication()
&& receiver->d_func()->threadData.loadRelaxed()->thread.loadAcquire() == mainThread()
&& sendThroughApplicationEventFilters(receiver, e)) {
filtered = true;
return filtered;
}
if (receiver->isWidgetType()) {
QWidget *widget = static_cast<QWidget *>(receiver);
#if !defined(QT_NO_CURSOR)
// toggle HasMouse widget state on enter and leave
if ((e->type() == QEvent::Enter || e->type() == QEvent::DragEnter) &&
(!QApplication::activePopupWidget() || QApplication::activePopupWidget() == widget->window()))
widget->setAttribute(Qt::WA_UnderMouse, true);
else if (e->type() == QEvent::Leave || e->type() == QEvent::DragLeave)
widget->setAttribute(Qt::WA_UnderMouse, false);
#endif
if (QLayout *layout=widget->d_func()->layout) {
layout->widgetEvent(e);
}
}
// send to all receiver event filters
if (sendThroughObjectEventFilters(receiver, e)) {
filtered = true;
return filtered;
}
// deliver the event
consumed = receiver->event(e);
QCoreApplicationPrivate::setEventSpontaneous(e, false);
return consumed;
}
QT 对事件的拦截有以下 5 种方法:
重载 notify() 方法
此方法的权限最高,可以完全控制事件。但是该方法需要继承QCoreApplication,一个应用中只能有一个 Application 对象,也只能有一个 notify() 方法。全局事件过滤器
在 QCoreApplication::instance() 的实例上可以通过 QObject::installEventFilter() 安装事件过滤器(可以安装多个)。这些过滤器是 notify() 方法中第一个调用的方法,是一个全局事件过滤器,可以拦截所有事件。在全局事件过滤器中可以处理设置为 disable 的窗口的鼠标事件。注意:过滤器所在对象必须在主线程内,否则全局事件处理器不会调用。局部事件过滤器
对于需要拦截事件的对象(必须继承自 QObject ),可以在该对象上调用 QObject::installEventFilter() 安装事件过滤器(可以安装多个),来拦截该对象接收到的所有事件。该过滤器是 notify() 方法中第二个调用的方法,事件可以在被 event() 方法接收前进行处理。重载 event() 方法
notify() 方法是通过调用对象的 event() 方法来处理事件的,所以重载该方法可以处理所有发送到对象的事件。重载特定事件的方法
重载 paintEvent()、 mousePressEvent() 等事件的处理方法也可以拦截事件。
自定义事件
枚举类型 QEvent::Type 定义了 Qt 中的有效事件类型,用户事件的值应介于 User 和 MaxUser 之间:
枚举值 | 值 | 描述 |
---|---|---|
QEvent::User | 1000 | 用户自定义事件 |
QEvent::MaxUser | 65535 | 用户自定义事件的最大值 |
自定义事件需要使用 int QEvent::registerEventType(int hint = -1)
方法注册和保留自定义事件类型,以确保自定义事件的唯一性。
// 定义自定义事件
class MyEvent : public QEvent {
public:
static const QEvent::Type Type = static_cast<QEvent::Type>(1000);
MyEvent() : QEvent(Type) {}
};
// 投递事件
QCoreApplication::postEvent(target, new MyEvent());
// 处理事件
bool MyObject::event(QEvent *event) {
if (event->type() == MyEvent::Type) {
qDebug() << "Custom event received";
return true;
}
return QObject::event(event);
}
参考:
QEvent Class
QCoreApplication class
Another Look at Events
【Qt源码笔记】Qt事件与Windows消息循环的联系
从源码看 QT 的事件系统及自定义事件的更多相关文章
- QT源码之Qt信号槽机制与事件机制的联系
QT源码之Qt信号槽机制与事件机制的联系是本文要介绍的内容,通过解决一个问题,从中分析出的理论,先来看内容. 本文就是来解决一个问题,就是当signal和slot的连接为Qt::QueuedConne ...
- 从源码看Azkaban作业流下发过程
上一篇零散地罗列了看源码时记录的一些类的信息,这篇完整介绍一个作业流在Azkaban中的执行过程,希望可以帮助刚刚接手Azkaban相关工作的开发.测试. 一.Azkaban简介 Azkaban作为开 ...
- 解密随机数生成器(二)——从java源码看线性同余算法
Random Java中的Random类生成的是伪随机数,使用的是48-bit的种子,然后调用一个linear congruential formula线性同余方程(Donald Knuth的编程艺术 ...
- 从源码看Android中sqlite是怎么通过cursorwindow读DB的
更多内容在这里查看 https://ahangchen.gitbooks.io/windy-afternoon/content/ 执行query 执行SQLiteDatabase类中query系列函数 ...
- 从源码看Android中sqlite是怎么读DB的(转)
执行query 执行SQLiteDatabase类中query系列函数时,只会构造查询信息,不会执行查询. (query的源码追踪路径) 执行move(里面的fillwindow是真正打开文件句柄并分 ...
- 从Chrome源码看浏览器的事件机制
.aligncenter { clear: both; display: block; margin-left: auto; margin-right: auto } .crayon-line spa ...
- 从Chrome源码看JS Array的实现
.aligncenter { clear: both; display: block; margin-left: auto; margin-right: auto } .crayon-line spa ...
- 从源码看JDK提供的线程池(ThreadPoolExecutor)
一丶什么是线程池 (1)博主在听到线程池三个字的时候第一个想法就是数据库连接池,回忆一下,我们在学JavaWeb的时候怎么理解数据库连接池的,数据库创建连接和关闭连接是一个比较耗费资源的事情,对于那些 ...
- 详解 QT 源码之 Qt 事件机制原理
QT 源码之 Qt 事件机制原理是本文要介绍的内容,在用Qt写Gui程序的时候,在main函数里面最后依据都是app.exec();很多书上对这句的解释是,使 Qt 程序进入消息循环.下面我们就到ex ...
- CentOS源码安装QT
在VirtualBox上的CentOS下安装qt-everywhere-opensource-src-4.8.4 ,执行 ./confiure时失败,失败信息为:Basic XLib function ...
随机推荐
- delphi cxgrid保存正在编辑的行
procedure SaveGridViewEditing(AView: TcxGridDBTableView); overload; var vDst: TDataSet; begin // 应用未 ...
- .NET周刊【1月第4期 2025-01-26】
国内文章 低成本高可用方案!Linux系统下SQL Server数据库镜像配置全流程详解 https://www.cnblogs.com/lyhabc/p/18660810/linux-sql-ser ...
- 2025苹果春季发布会前瞻:新品迭出,Apple Intelligence国行版即将上线!
随着2025年的到来,苹果公司的春季发布会也渐行渐近.作为科技行业的领军企业,苹果每一次的新品发布都备受瞩目.本次春季发布会,苹果预计会带来一系列令人期待的新品,同时,国行Mac用户也将迎来一个重大更 ...
- @所有Mac用户 刺客信条系列登陆Mac平台!
[历史性的跨越] 在无数Mac游戏爱好者的热切期盼中,终于将风靡全球的3A级巨作--刺客信条系列,成功移植至MacOS系统!这意味着,无论是穿梭于中世纪欧洲的隐秘巷弄,还是翱翔于维多利亚时代的伦敦天际 ...
- Luogu P9180 [COCI2022-2023#5] Slastičarnica 题解 [ 蓝 ] [ 区间 dp ] [ dp 状态优化 ] [ 前缀和优化 ]
Slastičarnica:非常好的区间 dp 题. 暴力 不难设计出暴力状态:\(dp_{q,i,j}\) 表示进行到第 \(q\) 次操作,剩下区间 \([i,j]\) 是否可行. 直到全部状态都 ...
- min-max 容斥(最值反演)学习笔记
min-max 容斥,又名最值反演(我其实更喜欢后面这个名字),是一种常用的反演思想. 引入 在皇后游戏一题中,我们曾经证明过 \(\max(a,b)-a-b=-\min(a,b)\). 我们尝试推广 ...
- RCE_STUDY
概念 RCE(Remote code execution)远程代码执行漏洞,RCE又分命令执行和代码执行. RCE-远程代码执行:远程执行PHP代码 RCE-远程命令执行:远程执行Linux或者Win ...
- Java的数据类型详解
java的为强类型语言,所以要求变量的使用要严格符合规定,所有的变量都必须先定义后在使用: 什么是变量? 变量顾名思义,就是可变的量:是程序中最基本的存储单元,其要素要包括:变量名.变量类型和作用域: ...
- rust学习笔记(4)
流程控制 if if n < 0 { print!("{} is negative", n); } else if n > 0 { print!("{} is ...
- Django实战项目-学习任务系统-查询列表分页显示
接着上期代码框架,6个主要功能基本实现,剩下的就是细节点的完善优化了. 接着优化查询列表分页显示功能,有很多菜单功能都有查询列表显示页面情况,如果数据量多,不分页显示的话,页面展示效果就不太好. 本次 ...