QT程序是事件驱动的, 程序的每一个动做都是由内部某个事件所触发。QT事件的发生和处理成为程序运行的主线,存在于程序整个生命周期。windows
常见的QT事件类型以下:app
键盘事件: 按键按下和松开异步
鼠标事件: 鼠标移动,鼠标按键的按下和松开socket
拖放事件: 用鼠标进行拖放ide
滚轮事件: 鼠标滚轮滚动函数
绘屏事件: 重绘屏幕的某些部分oop
定时事件: 定时器到时源码分析
焦点事件: 键盘焦点移动post
进入和离开事件: 鼠标移入widget以内,或是移出优化
移动事件: widget的位置改变
大小改变事件: widget的大小改变
显示和隐藏事件: widget显示和隐藏
窗口事件: 窗口是否为当前窗口
QT将系统产生的消息转化为QT事件,QT事件被封装为对象,全部的QT事件均继承抽象类QEvent,用于描述程序内部或外部发生的动做,任意的QObject对象都具有处理QT事件的能力。
操做系统将获取的事件,好比鼠标按键,键盘按键等keyPressEvent,keyReleaseEvent,mousePressEvent,mouseReleaseEvent事件, 放入系统的消息队列中,Qt事件循环的时候读取消息队列中的消息,转化为QEvent并被分发到相应的QWidget对象,相应的QWidget中的event(QEvent *)进行事件处理会对事件进行处理,event(QEvent *)会根据事件类型调用不一样的事件处理函数,在事件处理函数中发送QT预约义的信号,最终调用信号关联的槽函数。
GUI应用程序的事件处理:
A、QT事件产生后会被当即发送到相应的QWidget对象
B、相应的QWidget中的event(QEvent *)进行事件处理
C、event(QEvent *)根据事件类型调用不一样的事件处理函数
D、在事件处理函数中发送QT预约义的信号
E、调用信号关联的槽函数
程序产生事件有两种方式, 一种是调用QApplication::postEvent(), 例如QWidget::update()函数,当须要从新绘制屏幕时,程序调用update()函数,new出来一个paintEvent,调用 QApplication::postEvent(),将其放入Qt的消息队列中,等待依次被处理;另外一种方式是调用sendEvent()函数,事件不会放入队列, 而是直接被派发和处理, QWidget::repaint()函数用的就是阻塞型的。
sendEvent()中事件对象的生命期由Qt程序管理,支持分配在栈上和堆上的事件对象;postEvent()中事件对象的生命期由Qt平台管理,只支持分配在堆上的事件对象,事件被处理后由Qt平台销毁。
A、事件由具体的QWidget(子)对象进行处理,信号则由具体的QWidget(子)对象生成。
B、改写事件处理函数可能会致使程序行为发生改变,信号是否存在对应的槽函数不会改变程序的行为。
C、一般,信号在具体的事件处理函数中产生。
事件有两种调度方式,同步和异步。
Qt的事件循环是异步的,当调用QApplication::exec()时,就进入了事件循环,先处理Qt事件队列中的事件, 直至为空,再处理系统消息队列中的消息, 直至为空, 处理系统消息的时候会产生新的Qt事件, 须要对其再次进行处理。
调用QApplication::sendEvent的时候, 消息会当即被处理,是同步的。实际上QApplication::sendEvent()是经过调用QApplication::notify(), 直接进入了事件的派发和处理环节。
事件过滤器是Qt中一个独特的事件处理机制, 功能强大并且使用起来灵活方便。经过事件过滤器, 可让一个对象侦听拦截另一个对象的事件。事件过滤器实现以下: 在全部Qt对象的基类QObject中有一个类型为QObjectList的成员变量,名字为eventFilters,当某个QObject(A)给另外一个QObject(B)安装了事件过滤器后, B会把A的指针保存在eventFilters中。在B处理事件前,会先去检查eventFilters列表, 若是非空, 就先调用列表中对象的eventFilter()函数。一个对象能够给多个对象安装过滤器,一个对象能同时被安装多个过滤器, 在事件到达以后, 事件过滤器以安装次序的反序被调用。事件过滤器函数( eventFilter() ) 返回值是bool型, 若是返回true, 则表示事件已经被处理完毕, Qt将直接返回, 进行下一事件的处理。若是返回false, 事件将接着被送往剩下的事件过滤器或是目标对象进行处理。
QT中,事件的派发是从 QApplication::notify()开始的, 由于QAppliction也是继承自QObject, 因此先检查QAppliation对象, 若是有事件过滤器安装在qApp上, 先调用事件过滤器,接下来QApplication::notify() 会过滤或合并一些事件(好比失效widget的鼠标事件会被过滤掉, 而同一区域重复的绘图事件会被合并),事件被送到reciver::event()处理。
在reciver::event()中, 先检查有无事件过滤器安装在reciever上。如有, 则调用之。而后根据QEvent的类型, 调用相应的特定事件处理函数。常见的事件都有特定事件处理函数, 好比:mousePressEvent(), focusOutEvent(), resizeEvent(), paintEvent(), resizeEvent()等等。在实际应用中, 常常须要重载特定事件处理函数处理事件。对于不常见的事件, 没有相对应的特定事件处理函数,若是要处理这些事件, 就须要使用别的办法, 好比重载event() 函数, 或是安装事件过滤器。
对于某些类别的事件,若是在整个事件的派发过程结束后尚未被处理, 那么这个事件将会向上转发给它的父widget, 直到最顶层窗口。Qt中和事件相关的函数经过两种方式相互通讯,一种是QApplication::notify(), QObject::eventFilter(), QObject::event()经过返回bool值来表示是否已处理;另外一种是调用QEvent::ignore() 或 QEvent::accept() 对事件进行标识,只用于event()函数和特定事件处理函数之间的沟通,并且只有用在某些类别事件上是有意义的, 这些事件就是上面提到的那些会被转发的事件, 包括: 鼠标, 滚轮, 按键等事件。
QT提供了五种不一样级别的事件处理和过滤:
A、重写特定事件处理函数.
最多见的事件处理办法就是重写mousePressEvent(), keyPressEvent(), paintEvent() 等特定事件处理函数。
B、重写event()函数.
重写event()函数时, 须要调用父类的event()函数来处理不须要处理或是不清楚如何处理的事件。
return QWidget::event(event);
C、在Qt对象上安装事件过滤器
安装事件过滤器有两个步骤: (假设要用A来监视过滤B的事件)
首先调用B的installEventFilter( const QOject *obj ), 以A的指针做为参数,全部发往B的事件都将先由A的eventFilter()处理。而后, A要重写QObject::eventFilter()函数, 在eventFilter() 中对事件进行处理。
D、给QAppliction对象安装事件过滤器
若是给QApplication对象装上过滤器,那么全部的事件在发往任何其余的过滤器时,都要先通过当前eventFilter()。在QApplication::notify() 中, 是先调用qApp的过滤器, 再对事件进行分析, 以决定是否合并或丢弃。
E、继承QApplication类,并重载notify()函数
Qt是用QApplication::notify()函数来分发事件的,要在任何事件过滤器查看任何事件以前先获得这些事件,重写notify()函数是惟一的办法。一般来讲事件过滤器更好用一些, 由于不须要去继承QApplication类,并且能够给QApplication对象安装任意个数的事件过滤器。
GUI应用程序的事件处理:
A、QT事件产生后会被当即发送到QWidget对象
B、QWwidget中的event(QEvent *)进行事件处理
C、event(QEvent *)根据事件类型调用不一样的事件处理函数
D、在事件处理函数中发送QT预约义的信号
E、调用信号关联的槽函数
QT源码中事件梳理过程当中调用函数以下:
QApplication::exec() QCoreApplication::exec() QEventLoop::exec(ProcessEventsFlags ) QEventLoop::processEvents(ProcessEventsFlags ) QEventDispatcherWin32::processEvents(QEventLoop::ProcessEventsFlags) QT_WIN_CALLBACK QtWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) bool QETWidget::translateMouseEvent(const MSG &msg) bool QApplicationPrivate::sendMouseEvent(...) inline bool QCoreApplication::sendSpontaneousEvent(QObject *receiver, QEvent *event) bool QCoreApplication::notifyInternal(QObject *receiver, QEvent *event) bool QApplication::notify(QObject *receiver, QEvent *e) bool QApplicationPrivate::notify_helper(QObject *receiver, QEvent * e) bool QWidget::event(QEvent *event)
int main(int argc, char *argv[]) { QApplication app(argc, argv); Widget window; // Widget 继承自QWidget window.show(); return app.exec(); // 进入Qpplication事件循环 }
int QApplication::exec() { #ifndef QT_NO_ACCESSIBILITY QAccessible::setRootObject(qApp); #endif //简单的交给QCoreApplication来处理事件循环 return QCoreApplication::exec(); }
int QCoreApplication::exec() { if (!QCoreApplicationPrivate::checkInstance("exec")) return -1; //获得当前Thread数据 QThreadData *threadData = self->d_func()->threadData; if (threadData != QThreadData::current()) { qWarning("%s::exec: Must be called from the main thread", self->metaObject()->className()); return -1; } //检查event loop是否已经建立 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; //委任QEventLoop 处理事件队列循环 int returnCode = eventLoop.exec(); threadData->quitNow = false; if (self) { self->d_func()->in_exec = false; if (!self->d_func()->aboutToQuitEmitted) emit self->aboutToQuit(); self->d_func()->aboutToQuitEmitted = true; sendPostedEvents(0, QEvent::DeferredDelete); } return returnCode; }
int QEventLoop::exec(ProcessEventsFlags flags) { Q_D(QEventLoop); //访问QEventloop私有类实例d //we need to protect from race condition with QThread::exit QMutexLocker locker(&static_cast<QThreadPrivate *>(QObjectPrivate::get(d->threadData->thread))->mutex); if (d->threadData->quitNow) return -1; if (d->inExec) { qWarning("QEventLoop::exec: instance %p has already called exec()", this); return -1; } d->inExec = true; d->exit = false; ++d->threadData->loopLevel; d->threadData->eventLoops.push(this); locker.unlock(); // remove posted quit events when entering a new event loop QCoreApplication *app = QCoreApplication::instance(); if (app && app->thread() == thread()) QCoreApplication::removePostedEvents(app, QEvent::Quit); //这里的实现代码很多,最为重要的是如下几行 #if defined(QT_NO_EXCEPTIONS) while (!d->exit) processEvents(flags | WaitForMoreEvents | EventLoopExec);#else try { while (!d->exit) //只要没有碰见exit,循环派发事件 processEvents(flags | WaitForMoreEvents | EventLoopExec); } catch (...) { qWarning("Qt has caught an exception thrown from an event handler. Throwing\n" "exceptions from an event handler is not supported in Qt. You must\n" "reimplement QApplication::notify() and catch all exceptions there.\n"); // copied from below locker.relock(); QEventLoop *eventLoop = d->threadData->eventLoops.pop(); Q_ASSERT_X(eventLoop == this, "QEventLoop::exec()", "internal error"); Q_UNUSED(eventLoop); // --release warning d->inExec = false; --d->threadData->loopLevel; throw; }#endif // copied above locker.relock(); QEventLoop *eventLoop = d->threadData->eventLoops.pop(); Q_ASSERT_X(eventLoop == this, "QEventLoop::exec()", "internal error"); Q_UNUSED(eventLoop); // --release warning d->inExec = false; --d->threadData->loopLevel; return d->returnCode; }
bool QEventLoop::processEvents(ProcessEventsFlags flags) { Q_D(QEventLoop); if (!d->threadData->eventDispatcher) return false; if (flags & DeferredDeletion) QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); //将事件派发给与平台相关的QAbstractEventDispatcher子类 return d->threadData->eventDispatcher->processEvents(flags); }
AbstractEventDispatcher的子类QEventDispatcherWin32得到用户的输入事件,并将其打包成message后,经过标准的Windows API传递给Windows OS
bool QEventDispatcherWin32::processEvents(QEventLoop::ProcessEventsFlags flags) { Q_D(QEventDispatcherWin32); if (!d->internalHwnd) createInternalHwnd(); d->interrupt = false; emit awake(); bool canWait; bool retVal = false; bool seenWM_QT_SENDPOSTEDEVENTS = false; bool needWM_QT_SENDPOSTEDEVENTS = false; do { DWORD waitRet = 0; HANDLE pHandles[MAXIMUM_WAIT_OBJECTS - 1]; QVarLengthArray<MSG> processedTimers; while (!d->interrupt) { DWORD nCount = d->winEventNotifierList.count(); Q_ASSERT(nCount < MAXIMUM_WAIT_OBJECTS - 1); MSG msg; bool haveMessage; if (!(flags & QEventLoop::ExcludeUserInputEvents) && !d->queuedUserInputEvents.isEmpty()) { // process queued user input events haveMessage = true; msg = d->queuedUserInputEvents.takeFirst(); //从处理用户输入队列中取出一条事件,处理队列里面的用户输入事件 } else if(!(flags & QEventLoop::ExcludeSocketNotifiers) && !d->queuedSocketEvents.isEmpty()) { // process queued socket events haveMessage = true; msg = d->queuedSocketEvents.takeFirst(); // 从处理socket队列中取出一条事件,处理队列里面的socket事件 } else { haveMessage = PeekMessage(&msg, 0, 0, 0, PM_REMOVE); if (haveMessage && (flags & QEventLoop::ExcludeUserInputEvents) && ((msg.message >= WM_KEYFIRST && msg.message <= WM_KEYLAST) || (msg.message >= WM_MOUSEFIRST && msg.message <= WM_MOUSELAST) || msg.message == WM_MOUSEWHEEL || msg.message == WM_MOUSEHWHEEL || msg.message == WM_TOUCH #ifndef QT_NO_GESTURES || msg.message == WM_GESTURE || msg.message == WM_GESTURENOTIFY#endif || msg.message == WM_CLOSE)) { // queue user input events for later processing haveMessage = false; d->queuedUserInputEvents.append(msg); // 用户输入事件入队列,待之后处理 } if (haveMessage && (flags & QEventLoop::ExcludeSocketNotifiers) && (msg.message == WM_QT_SOCKETNOTIFIER && msg.hwnd == d->internalHwnd)) { // queue socket events for later processing haveMessage = false; d->queuedSocketEvents.append(msg); // socket 事件入队列,待之后处理 } } if (!haveMessage) { // no message - check for signalled objects for (int i=0; i<(int)nCount; i++) pHandles[i] = d->winEventNotifierList.at(i)->handle(); waitRet = MsgWaitForMultipleObjectsEx(nCount, pHandles, 0, QS_ALLINPUT, MWMO_ALERTABLE); if ((haveMessage = (waitRet == WAIT_OBJECT_0 + nCount))) { // a new message has arrived, process it continue; } } if (haveMessage) { #ifdef Q_OS_WINCE // WinCE doesn't support hooks at all, so we have to call this by hand :( (void) qt_GetMessageHook(0, PM_REMOVE, (LPARAM) &msg);#endif if (d->internalHwnd == msg.hwnd && msg.message == WM_QT_SENDPOSTEDEVENTS) { if (seenWM_QT_SENDPOSTEDEVENTS) { // when calling processEvents() "manually", we only want to send posted // events once needWM_QT_SENDPOSTEDEVENTS = true; continue; } seenWM_QT_SENDPOSTEDEVENTS = true; } else if (msg.message == WM_TIMER) { // avoid live-lock by keeping track of the timers we've already sent bool found = false; for (int i = 0; !found && i < processedTimers.count(); ++i) { const MSG processed = processedTimers.constData()[i]; found = (processed.wParam == msg.wParam && processed.hwnd == msg.hwnd && processed.lParam == msg.lParam); } if (found) continue; processedTimers.append(msg); } else if (msg.message == WM_QUIT) { if (QCoreApplication::instance()) QCoreApplication::instance()->quit(); return false; } if (!filterEvent(&msg)) { TranslateMessage(&msg); //将事件打包成message调用Windows API派发出去 DispatchMessage(&msg); //分发一个消息给窗口程序。消息被分发到回调函数,将消息传递给windows系统,windows处理完毕,会调用回调函数 => section 7 } } else if (waitRet < WAIT_OBJECT_0 + nCount) { d->activateEventNotifier(d->winEventNotifierList.at(waitRet - WAIT_OBJECT_0)); } else { // nothing todo so break break; } retVal = true; } // still nothing - wait for message or signalled objects canWait = (!retVal && !d->interrupt && (flags & QEventLoop::WaitForMoreEvents)); if (canWait) { DWORD nCount = d->winEventNotifierList.count(); Q_ASSERT(nCount < MAXIMUM_WAIT_OBJECTS - 1); for (int i=0; i<(int)nCount; i++) pHandles[i] = d->winEventNotifierList.at(i)->handle(); emit aboutToBlock(); waitRet = MsgWaitForMultipleObjectsEx(nCount, pHandles, INFINITE, QS_ALLINPUT, MWMO_ALERTABLE | MWMO_INPUTAVAILABLE); emit awake(); if (waitRet < WAIT_OBJECT_0 + nCount) { d->activateEventNotifier(d->winEventNotifierList.at(waitRet - WAIT_OBJECT_0)); retVal = true; } } } while (canWait); if (!seenWM_QT_SENDPOSTEDEVENTS && (flags & QEventLoop::EventLoopExec) == 0) { // when called "manually", always send posted events QCoreApplicationPrivate::sendPostedEvents(0, 0, d->threadData); } if (needWM_QT_SENDPOSTEDEVENTS) PostMessage(d->internalHwnd, WM_QT_SENDPOSTEDEVENTS, 0, 0); return retVal; }
extern "C" LRESULT QT_WIN_CALLBACK QtWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { ... //将消息从新封装成QEvent的子类QMouseEvent ==> Section 8 result = widget->translateMouseEvent(msg); ... }
bool QETWidget::translateMouseEvent(const MSG &msg) { ...... res = QApplicationPrivate::sendMouseEvent(target, &e, alienWidget, this, &qt_button_down, qt_last_mouse_receiver); }
bool QApplicationPrivate::sendMouseEvent(QWidget *receiver, QMouseEvent *event, QWidget *alienWidget, QWidget *nativeWidget,QWidget **buttonDown, QPointer<QWidget> &lastMouseReceiver,bool spontaneous) { ... if (spontaneous) result = QApplication::sendSpontaneousEvent(receiver, event); else result = QApplication::sendEvent(receiver, event); ... return result; }
inline bool QCoreApplication::sendSpontaneousEvent(QObject *receiver, QEvent *event) { //将event标记为自发事件 //进一步调用 2-5 QCoreApplication::notifyInternal if (event) event->spont = true; return self ? self->notifyInternal(receiver, event) : false; }
bool QCoreApplication::notifyInternal(QObject *receiver, QEvent *event) { ... // 如下代码主要意图为Qt强制事件只可以发送给当前线程里的对象,也就是说receiver->d_func()->threadData应该等于QThreadData::current()。 //注意,跨线程的事件须要借助Event Loop来派发 QObjectPrivate *d = receiver->d_func(); QThreadData *threadData = d->threadData; ++threadData->loopLevel; QT_TRY { returnValue = notify(receiver, event); } QT_CATCH (...) { --threadData->loopLevel; QT_RETHROW; } ... return returnValue; }
任何线程的任何对象的全部事件在发送时都会调用notify函数。
bool QCoreApplication::notify(QObject *receiver, QEvent *event) { Q_D(QCoreApplication); // no events are delivered after ~QCoreApplication() has started if (QCoreApplicationPrivate::is_app_closing) return true; if (receiver == 0) { // serious error qWarning("QCoreApplication::notify: Unexpected null receiver"); return true; } #ifndef QT_NO_DEBUG d->checkReceiverThread(receiver);#endif return receiver->isWidgetType() ? false : d->notify_helper(receiver, event); }
bool QCoreApplicationPrivate::notify_helper(QObject *receiver, QEvent * event) { // send to all application event filters if (sendThroughApplicationEventFilters(receiver, event)) return true; // 向事件过滤器发送该事件,这里介绍一下Event Filters. 事件过滤器是一个接受即将发送给目标对象全部事件的对象。 //如代码所示它开始处理事件在目标对象行动以前。过滤器的QObject::eventFilter()实现被调用,能接受或者丢弃过滤 //容许或者拒绝事件的更进一步的处理。若是全部的事件过滤器容许更进一步的事件处理,事件将被发送到目标对象自己。 //若是他们中的一个中止处理,目标和任何后来的事件过滤器不能看到任何事件。 if (sendThroughObjectEventFilters(receiver, event)) return true; return receiver->event(event); }
bool QWidget::event(QEvent *event) { ... switch (event->type()) { case QEvent::MouseMove: mouseMoveEvent((QMouseEvent*)event); break; case QEvent::MouseButtonPress: #if 0 resetInputContext(); #endif mousePressEvent((QMouseEvent*)event); break; ... }
A、构造事件对象
QEvent *event = new QEvent(QEvent::Close);
B、发送事件给指定对象
QApplication::sendEvent(this, event);
A、肯定要对组件的哪些事件进行处理, 如close、key、keyboard 事件
B、重写对象的 event() 函数
QEvent中的主要成员函数
void ignore();接收者忽略当前事件,事件可能传递给父组件
void accept();接收者指望处理当前事件
bool isAccepted();判断当前事件是否被处理
A、肯定须要过滤处理哪些对象的哪些事件
B、构造本身的事件过滤类: 重写类的eventFilter函数
C、在主程序中实例化一个过滤类对象
D、调用过滤类对象的installEventFilter(receiver, QEvent *event)函数,
在目标对象上安装过滤器。
事件过滤器能够对其余组件接收到的事件进行监控,任意的QObject对象均可以做为事件过滤器使用,事件过滤器对象须要重写eventFilter函数。
组件经过installEventFilter函数安装事件过滤器,事件过滤器在组件以前接收到事件,可以决定是否将事件转发到组件对象。
事件过滤器的通常实现:
//返回true,表示事件已经被处理,无须传递给obj对象
//返回false,事件正常传递都给obj对象
bool Widget::eventFilter(QObject* obj, QEvent* event)
{
if( (根据obj判断)
{
if(根据event判断)
{
//事件处理
}
}
//调用父类的eventFilter函数
return QWidget::eventFilter(obj, event);
}
若是一个组件安装了多个过滤器,则最后一个安装的会最早调用。
若是在事件过滤器中delete了某个接收组件,务必将返回值设为true。不然,Qt仍是会将事件分发给已经删除的接收组件,从而致使程序崩溃。
事件过滤器和被安装的组件必须在同一线程,不然,过滤器不起做用。另外,若是在install以后,两个组件到了不一样的线程,那么,只有等到两者从新回到同一线程的时候过滤器才会有效。
事件的调用最终都会调用QCoreApplication的notify()函数,所以,最大的控制权其实是重写QCoreApplication的notify()函数。由此能够看出,Qt的事件处理其实是分层五个层次:重定义事件处理函数,重定义event()函数,为单个组件安装事件过滤器,为QApplication安装事件过滤器,重定义QCoreApplication的notify()函数。这几个层次的控制权是逐层增大的。
Qt能够在程序中自主发送事件,发送事件的方式分为两种:
A、阻塞型事件发送
事件发送后须要等待事件处理完成。
bool QCoreApplication::sendEvent ( QObject * receiver, QEvent * event ) [static]
B、非阻塞型事件发送
事件发送后当即完成。
事件被发送到事件队列中等待处理。
void QCoreApplication::postEvent ( QObject * receiver, QEvent * event ) [static]
void QCoreApplication::postEvent ( QObject * receiver, QEvent * event, int priority ) [static]
sendEvent()中事件对象的生命期由Qt程序管理,支持分配在栈上和堆上的事件对象;postEvent()中事件对象的生命期由Qt平台管理,只支持分配在堆上的事件对象,事件被处理后由Qt平台销毁。
sendEvent()当即同步处理要发送的event。当sendEvent()返回的时候, 表示相关的事件过滤器或目标对象处理完了event。对于多数的event类, 有一个成员函数 isAccepted() 能够用来判别event事件是已被接受处理或被拒绝处理。
postEvent()将event提交到一个事件队列中等待调度。在下一次 Qt 的主 event loop 运行的时候,主 event loop 就会调度全部提交到队列中的 event, 以某种优化的方式. 例如, 若是有几个 resize event, 他们就会被压缩成一个事件. 一样适用于 paint events: QWidget::update() 调用postEvent(), 以免屡次重画来避免闪烁以及提升速度.
postEvent()也被用于对象的初始化过程, 由于提交过的 event 一般在相应对象初始化完毕后极短的 时间内就会被调度. 在实现一个控件的时候, 在自定义控件的 constructor 中尽早支持事件机制是很是重要的, 在可能接受到任何事件以前,确保尽早初始化成员变量。
事件和信号不一样,事件由具体对象进行处理,信号由具体对象产生,改写事件处理函数可能致使程序行为发生改变。
void Widget::testSendEvent() { QMouseEvent dbcEvt(QEvent::MouseButtonDblClick, QPoint(0, 0), Qt::LeftButton, Qt::NoButton, Qt::NoModifier); qDebug() << "Before sendEvent()"; //同步执行event事件处理函数 QApplication::sendEvent(this, &dbcEvt); qDebug() << "After sendEvent()"; } void Widget::testPostEvent() { //必须分配在堆空间 QMouseEvent* dbcEvt = new QMouseEvent(QEvent::MouseButtonDblClick, QPoint(0, 0), Qt::LeftButton, Qt::NoButton, Qt::NoModifier); qDebug() << "Before postEvent()"; //放入事件队列,一部执行event事件处理函数 QApplication::postEvent(this, dbcEvt); qDebug() << "After postEvent()"; } bool Widget::event(QEvent* evt) { if( evt->type() == QEvent::MouseButtonDblClick ) { qDebug() << "event(): " << evt; } return QWidget::event(evt); }
发送事件后,事件会被在事件处理函数被处理。
Qt自定义事件的要点:
A、自定义事件必须继承自QEvent。
B、自定义事件类必须拥有全局惟一的Type值(自定义事件可使用QEvent::User以后的值,并保证全局惟一)。
C、程序必须提供处理自定义事件对象的方法
#ifndef STRINGEVENT_H #define STRINGEVENT_H #include <QEvent> #include <QString> class StringEvent : public QEvent { QString m_data; public: const static Type TYPE = static_cast<Type>(QEvent::User + 0xFF); explicit StringEvent(QString data = ""): QEvent(TYPE) { m_data = data; } QString data() { return m_data; } }; #endif // STRINGEVENT_H
自定义事件对象的处理方法:
A、将事件过滤器安装到目标对象
在eventFilter函数中实现自定义事件的处理逻辑
#ifndef WIDGET_H #define WIDGET_H #include <QWidget> #include <QLineEdit> #include "StringEvent.h" #include <QMouseEvent> #include <QDebug> #include <QApplication> class Widget : public QWidget { Q_OBJECT QLineEdit m_edit; public: Widget(QWidget *parent = 0): QWidget(parent), m_edit(this) { m_edit.installEventFilter(this); } bool event(QEvent* evt) { if( evt->type() == QMouseEvent::MouseButtonDblClick ) { qDebug() << "event: Before sentEvent"; StringEvent e("D.T.Software"); QApplication::sendEvent(&m_edit, &e); qDebug() << "event: After sentEvent"; } return QWidget::event(evt); } bool eventFilter(QObject* obj, QEvent* evt) { if( (obj == &m_edit) && (evt->type() == StringEvent::TYPE) ) { StringEvent* se = dynamic_cast<StringEvent*>(evt); qDebug() << "Receive: " << se->data(); m_edit.insert(se->data()); return true; } return QWidget::eventFilter(obj, evt); } ~Widget() { } }; #endif // WIDGET_H
B、在目标对象的类中重写事件处理函数
在event函数中编写自定义事件的处理逻辑
#ifndef WIDGET_H #define WIDGET_H #include <QWidget> #include <QPushButton> #include "StringEvent.h" #include <QMouseEvent> #include <QDebug> #include <QApplication> class Widget : public QWidget { Q_OBJECT QPushButton m_button; private slots: void onButton() { //槽函数内发送自定义事件 StringEvent e("D.T.Software"); QApplication::sendEvent(this, &e); qDebug() << "clicked button "; } public: Widget(QWidget *parent = 0): QWidget(parent), m_button(this) { m_button.setText("Button"); connect(&m_button, SIGNAL(clicked()), this, SLOT(onButton())); } bool event(QEvent* evt) { //若是事件类型为自定义事件 if( evt->type() == StringEvent::TYPE ) { qDebug() << "event"; //执行自定义事件的处理函数 return stringEvent(evt); } return QWidget::event(evt); } bool stringEvent(QEvent* evt) { StringEvent* se = dynamic_cast<StringEvent*>(evt); qDebug() << "stringEvent: " << se->data(); return true; } ~Widget() { } }; #endif // WIDGET_H
自定义事件类的适用场合:
A、扩展一个已有组件类的功能
B、开发一个全新功能的组件
C、向第三方的组件类发送消息
拖放一个文件进入窗口时将触发拖放事件,每个QWidget对象均可以处理拖放事件。
拖放事件的处理函数
void dragEnterEvent(QDragEnterEvent *e)
void dragEvent(QDropEvent *)
拖放事件中的QMimeData,QMimeData是QT中的多媒体数据类,拖放事件经过QMimeData对象传递数据,QMimeData支持多种不一样类型的多媒体数据。
经常使用QMimeData数据处理函数
自定义拖放事件的流程:
A、对接收拖放事件的对象调用setAcceptDrops成员函数
B、重写dragEnterEvent函数并判断MIME类型
指望类型数据:e->acceptProposedAction();
其余数据类型:e->ignore();
C、重写dragEvent函数并判断MIME类型
指望数据类型:从事件对象中获取MIME数据并处理
其余数据类型:e->ignore();
//在窗口类构造函数中设置拖放事件生效 setAcceptDrops(true); //重写窗口类的dropEvent函数 void MainWindow::dropEvent(QDropEvent* e) { if( e->mimeData()->hasUrls() ) { QList<QUrl> list = e->mimeData()->urls(); QString path = list[0].toLocalFile(); QFileInfo fi(path); if( fi.isFile() ) { saveOldFile(); if( !m_isTextChanged ) { showFile(path); } } else { showErrorMessage("Cannot open a folder!"); } } else { e->ignore(); } } //重写窗口类的dragEnterEvent函数 void MainWindow::dragEnterEvent(QDragEnterEvent* e) { if( e->mimeData()->hasUrls() ) { e->acceptProposedAction(); } else { e->ignore(); } }