Qt事件的详细介绍和原理

86 篇文章 1 订阅
6 篇文章 0 订阅

介绍

事件类型:
1、窗口系统事件spontaneous。
2、异步事件PostEvent。
3、同步事件SendEvent

窗体系统事件spontaneous
代表事件有:各类外设事件,如鼠标单双击、滚轮、移动,键盘按键等事件。
使用场景:该类事件都是窗体系统进行捕获的,我们一般不需要进行操作,除非我们需要模拟鼠标、键盘的事件。

异步事件PostEvent
代表事件有:绘图时的update事件。
使用场景:不需要马上回应的异步情况下,模拟各种事件。
例子:
//模拟按键点击

    QTextEdit* text = new QTextEdit(this);
    QPushButton* btn = new QPushButton("模拟",this);
    text->setGeometry(100,0,400,300);
    connect(btn,&QPushButton::clicked,this,[=]()
    {
        QKeyEvent *key = new QKeyEvent(QEvent::KeyPress, ' ', Qt::NoModifier, "A");
        QApplication::postEvent(text, key); //_currentLineEdit

//        QKeyEvent *key = new QKeyEvent(QEvent::KeyPress, Qt::Key_Backspace, Qt::NoModifier);
//        QApplication::postEvent(text, key); //_currentLineEdit
    });

同步事件PostEvent
代表事件有:绘图时的repaint事件。
使用场景:需要发送后立刻执行的情况下,模拟各种事件。
例子:
//模拟按键点击

    QTextEdit* text = new QTextEdit(this);
    QPushButton* btn = new QPushButton("模拟",this);
    text->setGeometry(100,0,400,300);
    connect(btn,&QPushButton::clicked,this,[=]()
    {
        QKeyEvent *key = new QKeyEvent(QEvent::KeyPress, ' ', Qt::NoModifier, "A");
        QApplication::sendEvent(text, key); //_currentLineEdit

//        QKeyEvent *key = new QKeyEvent(QEvent::KeyPress, Qt::Key_Backspace, Qt::NoModifier);
//        QApplication::sendEvent(text, key); //_currentLineEdit
    });

sendEvent、PostEvent、自定义事件:详细实例。

原理

sendEvent的原理需要查看它的源代码就能发现它同步执行的方式了。
1、

bool QCoreApplication::sendEvent(QObject *receiver, QEvent *event)
{
    Q_TRACE(QCoreApplication_sendEvent, receiver, event, event->type());
    // 如果事件不为空,则将 spont 标志设置为 false
    if (event)
        event->spont = false;
    // 调用内部函数 notifyInternal2 发送事件,并返回结果
    return notifyInternal2(receiver, event);
}

直接调用notifyInternal2接口.
2、

bool QCoreApplication::notifyInternal2(QObject *receiver, QEvent *event)
{
    bool selfRequired = QCoreApplicationPrivate::threadRequiresCoreApplication();
    if (!self && selfRequired)
        return false;

    bool result = false;
    void *cbdata[] = { receiver, event, &result };
    if (QInternal::activateCallbacks(QInternal::EventNotifyCallback, cbdata)) {
        return result;
    }

    QObjectPrivate *d = receiver->d_func();
    QThreadData *threadData = d->threadData;
    QScopedScopeLevelCounter scopeLevelCounter(threadData);
    if (!selfRequired)
        return doNotify(receiver, event);
    return self->notify(receiver, event);
}

如果selfRequired的请求不存在则调用doNotify,如果存在则调用到notify。
3、

bool QCoreApplication::notify(QObject *receiver, QEvent *event)
{
    // ~QCoreApplication()析构调用后不会传递任何事件
    if (QCoreApplicationPrivate::is_app_closing)
        return true;
    return doNotify(receiver, event);
}

判断程序打开时才调用doNotify。
4、

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);
}

调用notify_helper。
5、

bool QCoreApplicationPrivate::notify_helper(QObject *receiver, QEvent * event)
{
    Q_TRACE(QCoreApplication_notify_entry, receiver, event, event->type());

	// send to all application event filters (only does anything in the main thread)
    if (QCoreApplication::self
            && receiver->d_func()->threadData->thread == mainThread()
            && QCoreApplication::self->d_func()->sendThroughApplicationEventFilters(receiver, event)) {
        Q_TRACE(QCoreApplication_notify_event_filtered, receiver, event, event->type());
        return true;
    }
    // send to all receiver event filters
    if (sendThroughObjectEventFilters(receiver, event)) {
        Q_TRACE(QCoreApplication_notify_event_filtered, receiver, event, event->type());
        return true;
    }

    Q_TRACE(QCoreApplication_notify_before_delivery, receiver, event, event->type());

    // deliver the event
    const bool consumed = receiver->event(event);

    Q_TRACE(QCoreApplication_notify_after_delivery, receiver, event, event->type(), consumed);

    return consumed;
}
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 == 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;
}

在这里可以看到事件是如何处理的:
先送入Application的事件过滤器,看看是否在事件过滤器中处理,再查看receiver是否有此事件的过滤器,最后将事件送入receiver的event接口。从整个过程来看,可以认为sendEvent直接调用了receiver的event接口。因此,可以认为处理方式为同步处理方式。

说到spontaneous事件和postevent事件的原理,就需要了解main函数中“a.exec()”,因为他们的执行都是在exec这个函数中执行的。
1、spontaneous窗体系统事件一般都是通过系统层面的中断来捕获事件的。
postEvent源码:

void QCoreApplication::postEvent(QObject *receiver, QEvent *event, int priority)
{
    Q_TRACE(QCoreApplication_postEvent_entry, receiver, event, event->type());

    if (receiver == 0) {
        qWarning("QCoreApplication::postEvent: Unexpected null receiver");
        delete event;
        return;
    }

    QThreadData * volatile * pdata = &receiver->d_func()->threadData;
    QThreadData *data = *pdata;
    if (!data) {
        // posting during destruction? just delete the event to prevent a leak
        delete event;
        return;
    }

    // lock the post event mutex
    data->postEventList.mutex.lock();

    // 判断事件是否在另外一个线程
    while (data != *pdata) {
        data->postEventList.mutex.unlock();

        data = *pdata;
        if (!data) {
            // 发送期间如果事件已经不存在了,则销毁它,防止内存泄漏。
            delete event;
            return;
        }

        data->postEventList.mutex.lock();
    }

    QMutexUnlocker locker(&data->postEventList.mutex);

    // 如果这是可一个压缩事件,请执行压缩,例如短时间多次触发update就会触发压缩事件。
    if (receiver->d_func()->postedEvents
        && self && self->compressEvent(event, receiver, &data->postEventList)) {
        Q_TRACE(QCoreApplication_postEvent_event_compressed, receiver, event);
        return;
    }

    if (event->type() == QEvent::DeferredDelete)
        receiver->d_ptr->deleteLaterCalled = true;

    if (event->type() == QEvent::DeferredDelete && data == QThreadData::current()) {
        // remember the current running eventloop for DeferredDelete
        // events posted in the receiver's thread.

        // Events sent by non-Qt event handlers (such as glib) may not
        // have the scopeLevel set correctly. The scope level makes sure that
        // code like this:
        //     foo->deleteLater();
        //     qApp->processEvents(); // without passing QEvent::DeferredDelete
        // will not cause "foo" to be deleted before returning to the event loop.

        // If the scope level is 0 while loopLevel != 0, we are called from a
        // non-conformant code path, and our best guess is that the scope level
        // should be 1. (Loop level 0 is special: it means that no event loops
        // are running.)
        int loopLevel = data->loopLevel;
        int scopeLevel = data->scopeLevel;
        if (scopeLevel == 0 && loopLevel != 0)
            scopeLevel = 1;
        static_cast<QDeferredDeleteEvent *>(event)->level = loopLevel + scopeLevel;
    }

    // 删除异常上的事件以防止内存泄漏,直到该事件在postEventList中正确拥有为止
    QScopedPointer<QEvent> eventDeleter(event);
    Q_TRACE(QCoreApplication_postEvent_event_posted, receiver, event, event->type());
    //将事件添加到postEventList中,注意这里的优先级第一个最高,最后一个优先级最低
    data->postEventList.addEvent(QPostEvent(receiver, event, priority));
    eventDeleter.take();
    event->posted = true;
    ++receiver->d_func()->postedEvents;
    data->canWait = false;
    locker.unlock();

    QAbstractEventDispatcher* dispatcher = data->eventDispatcher.loadAcquire();
    if (dispatcher)
        dispatcher->wakeUp();
}

从上面可以看出,postEvent实际上是将事件添加到receiver所在线程中的一个队列中,至于这个队列所在的线程什么时候处理这个事件,postEvent是无法控制的。
2、再来看下main函数中“a.exec()”。

int QApplication::exec()
{
    return QGuiApplication::exec();
}
int QCoreApplication::exec()
{
    if (!QCoreApplicationPrivate::checkInstance("exec"))
        return -1;

    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;
    }
    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();
    threadData->quitNow = false;

    if (self)
        self->d_func()->execCleanup();

    return returnCode;
}
int QEventLoop::exec(ProcessEventsFlags flags)
{
    Q_D(QEventLoop);
    //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;
    }

    struct LoopReference {
        QEventLoopPrivate *d;
        QMutexLocker &locker;

        bool exceptionCaught;
        LoopReference(QEventLoopPrivate *d, QMutexLocker &locker) : d(d), locker(locker), exceptionCaught(true)
        {
            d->inExec = true;
            d->exit.storeRelease(false);
            ++d->threadData->loopLevel;
            d->threadData->eventLoops.push(d->q_func());
            locker.unlock();
        }

        ~LoopReference()
        {
            if (exceptionCaught) {
                qWarning("Qt has caught an exception thrown from an event handler. Throwing\n"
                         "exceptions from an event handler is not supported in Qt.\n"
                         "You must not let any exception whatsoever propagate through Qt code.\n"
                         "If that is not possible, in Qt 5 you must at least reimplement\n"
                         "QCoreApplication::notify() and catch all exceptions there.\n");
            }
            locker.relock();
            QEventLoop *eventLoop = d->threadData->eventLoops.pop();
            Q_ASSERT_X(eventLoop == d->q_func(), "QEventLoop::exec()", "internal error");
            Q_UNUSED(eventLoop); // --release warning
            d->inExec = false;
            --d->threadData->loopLevel;
        }
    };
    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);

#ifdef Q_OS_WASM
    // Partial support for nested event loops: Make the runtime throw a JavaSrcript
    // exception, which returns control to the browser while preserving the C++ stack.
    // Event processing then continues as normal. The sleep call below never returns.
    // QTBUG-70185
    if (d->threadData->loopLevel > 1)
        emscripten_sleep(1);
#endif

    while (!d->exit.loadAcquire())
        processEvents(flags | WaitForMoreEvents | EventLoopExec);

    ref.exceptionCaught = false;
    return d->returnCo
}
bool QEventLoop::processEvents(ProcessEventsFlags flags)
{
    Q_D(QEventLoop);
    if (!d->threadData->hasEventDispatcher())
        return false;
    return d->threadData->eventDispatcher.load()->processEvents(flags);
}    

调用到了这个位置时,就需要区分系统了,processEvents这个函数是来源于QAbstractEventDispatcher的一个纯虚函数,在Windows中是调用继承QAbstractEventDispatcher的QEventDispatcherWin32的processEvents;在linux系统中是继承QAbstractEventDispatcher的QEventDIspatcherUNIX的processEvents。

bool QEventDispatcherWin32::processEvents(QEventLoop::ProcessEventsFlags flags)
{
    Q_D(QEventDispatcherWin32);

    if (!d->internalHwnd) {
        createInternalHwnd();
        wakeUp(); // trigger a call to sendPostedEvents()
    }

    d->interrupt.store(false);
    emit awake();

    bool canWait;
    bool retVal = false;
    bool seenWM_QT_SENDPOSTEDEVENTS = false;
    bool needWM_QT_SENDPOSTEDEVENTS = false;
    do {
        DWORD waitRet = 0;
        DWORD nCount = 0;
        HANDLE *pHandles = nullptr;
        if (d->winEventNotifierActivatedEvent) {
            nCount = 1;
            pHandles = &d->winEventNotifierActivatedEvent;
        }
        QVarLengthArray<MSG> processedTimers;
        while (!d->interrupt.load()) {
            MSG msg;
            bool haveMessage;

            if (!(flags & QEventLoop::ExcludeUserInputEvents) && !d->queuedUserInputEvents.isEmpty()) {
                // 处理排队的用户输入事件。
                haveMessage = true;
                msg = d->queuedUserInputEvents.takeFirst();
            } else if(!(flags & QEventLoop::ExcludeSocketNotifiers) && !d->queuedSocketEvents.isEmpty()) {
                // 处理排队的套接字事件
                haveMessage = true;
                msg = d->queuedSocketEvents.takeFirst();
            } else {
                haveMessage = PeekMessage(&msg, 0, 0, 0, PM_REMOVE);
                if (haveMessage) {
                    if (flags.testFlag(QEventLoop::ExcludeUserInputEvents)
                        && isUserInputMessage(msg.message)) {
                        // 将用户输入事件排队以供稍后处理
                        d->queuedUserInputEvents.append(msg);
                        continue;
                    }
                    if ((flags & QEventLoop::ExcludeSocketNotifiers)
                        && (msg.message == WM_QT_SOCKETNOTIFIER && msg.hwnd == d->internalHwnd)) {
                        // 将套接字事件排队以供稍后处理
                        d->queuedSocketEvents.append(msg);
                        continue;
                    }
                }
            }
            if (!haveMessage) {
                // no message - check for signalled objects
                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) {
                // WinCE doesn't support hooks at all, so we have to call this by hand :(
                if (!d->getMessageHook)
                    (void) qt_GetMessageHook(0, PM_REMOVE, reinterpret_cast<LPARAM>(&msg));

                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 (!filterNativeEvent(QByteArrayLiteral("windows_generic_MSG"), &msg, 0)) {
                    // 将事件打包成message调用Windows API派发出去
                    TranslateMessage(&msg);
                    // 分发一个消息给窗口程序。消息被分发到回调函数,将消息传递给windows系统,windows处理完毕,会调用回调函数。
                    DispatchMessage(&msg);
                }
            } else if (waitRet - WAIT_OBJECT_0 < nCount) {
                activateEventNotifiers();
            } else {
                // nothing todo so break
                break;
            }
            retVal = true;
        }

        // still nothing - wait for message or signalled objects
        canWait = (!retVal
                   && !d->interrupt.load()
                   && (flags & QEventLoop::WaitForMoreEvents));
        if (canWait) {
            emit aboutToBlock();
            waitRet = MsgWaitForMultipleObjectsEx(nCount, pHandles, INFINITE, QS_ALLINPUT, MWMO_ALERTABLE | MWMO_INPUTAVAILABLE);
            emit awake();
            if (waitRet - WAIT_OBJECT_0 < nCount) {
                activateEventNotifiers();
                retVal = true;
            }
        }
    } while (canWait);

    if (!seenWM_QT_SENDPOSTEDEVENTS && (flags & QEventLoop::EventLoopExec) == 0) {
        // when called "manually", always send posted events
        sendPostedEvents();
    }

    if (needWM_QT_SENDPOSTEDEVENTS)
        PostMessage(d->internalHwnd, WM_QT_SENDPOSTEDEVENTS, 0, 0);

    return retVal;
}

TranslateMessage(&msg)和DispatchMessage(&msg)这两个函数将事件打包成msg分发给windows的窗口回调函数。

 QT_WIN_CALLBACK QtWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
 {
     // ...
     // 检查message是否属于Qt可转义的鼠标事件
     if (qt_is_translatable_mouse_event(message))
     {
         if (QApplication::activePopupWidget() != 0)
         { // in popup mode
             POINT curPos = msg.pt;
             // 取得鼠标点击坐标所在的QWidget指针,它指向我们在main创建的widget实例
             QWidget* w = QApplication::widgetAt(curPos.x, curPos.y);
             if (w)
             {
                 widget = (QETWidget*)w;
             }
         }
 
         if (!qt_tabletChokeMouse)
         {
             // 对,就在这里。Windows的回调函数将鼠标事件分发回给了Qt Widget
             // => Section 2-2
             result = widget->translateMouseEvent(msg);  // mouse event
         }
     }
     // ...
 }

以上就是windows窗口回调函数了。通过translateMouseEvent来将事件分发出去。

bool QETWidget::translateMouseEvent(const MSG &msg)
{
    // ...这里有很长的一段代码可以忽略
    // 让我们看一下sendMouseEvent的声明
    // widget是事件的接受者;e是封装好的QMouseEvent
    // ==> Section 2-3
    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, bool onlyDispatchEnterLeave)
{
    Q_ASSERT(receiver);
    Q_ASSERT(event);
    Q_ASSERT(nativeWidget);
    Q_ASSERT(buttonDown);

    if (alienWidget && !isAlien(alienWidget))
        alienWidget = 0;

    QPointer<QWidget> receiverGuard = receiver;
    QPointer<QWidget> nativeGuard = nativeWidget;
    QPointer<QWidget> alienGuard = alienWidget;
    QPointer<QWidget> activePopupWidget = QApplication::activePopupWidget();

    const bool graphicsWidget = nativeWidget->testAttribute(Qt::WA_DontShowOnScreen);

    bool widgetUnderMouse = QRectF(receiver->rect()).contains(event->localPos());

    // Clear the obsolete leaveAfterRelease value, if mouse button has been released but
    // leaveAfterRelease has not been updated.
    // This happens e.g. when modal dialog or popup is shown as a response to button click.
    if (leaveAfterRelease && !*buttonDown && !event->buttons())
        leaveAfterRelease = 0;

    if (*buttonDown) {
        if (!graphicsWidget) {
            // Register the widget that shall receive a leave event
            // after the last button is released.
            if ((alienWidget || !receiver->internalWinId()) && !leaveAfterRelease && !QWidget::mouseGrabber())
                leaveAfterRelease = *buttonDown;
            if (event->type() == QEvent::MouseButtonRelease && !event->buttons())
                *buttonDown = 0;
        }
    } else if (lastMouseReceiver && widgetUnderMouse) {
        // Dispatch enter/leave if we move:
        // 1) from an alien widget to another alien widget or
        //    from a native widget to an alien widget (first OR case)
        // 2) from an alien widget to a native widget (second OR case)
        if ((alienWidget && alienWidget != lastMouseReceiver)
            || (isAlien(lastMouseReceiver) && !alienWidget)) {
            if (activePopupWidget) {
                if (!QWidget::mouseGrabber())
                    dispatchEnterLeave(alienWidget ? alienWidget : nativeWidget, lastMouseReceiver, event->screenPos());
            } else {
                dispatchEnterLeave(receiver, lastMouseReceiver, event->screenPos());
            }

        }
    }

#ifdef ALIEN_DEBUG
    qDebug() << "QApplicationPrivate::sendMouseEvent: receiver:" << receiver
             << "pos:" << event->pos() << "alien" << alienWidget << "button down"
             << *buttonDown << "last" << lastMouseReceiver << "leave after release"
             << leaveAfterRelease;
#endif

    // We need this quard in case someone opens a modal dialog / popup. If that's the case
    // leaveAfterRelease is set to null, but we shall not update lastMouseReceiver.
    const bool wasLeaveAfterRelease = leaveAfterRelease != 0;
    bool result = true;
    // This code is used for sending the synthetic enter/leave events for cases where it is needed
    // due to other events causing the widget under the mouse to change. However in those cases
    // we do not want to send the mouse event associated with this call, so this enables us to
    // not send the unneeded mouse event
    if (!onlyDispatchEnterLeave) {
        if (spontaneous)
            result = QApplication::sendSpontaneousEvent(receiver, event);
        else
            result = QApplication::sendEvent(receiver, event);
    }

    if (!graphicsWidget && leaveAfterRelease && event->type() == QEvent::MouseButtonRelease
        && !event->buttons() && QWidget::mouseGrabber() != leaveAfterRelease) {
        // Dispatch enter/leave if:
        // 1) the mouse grabber is an alien widget
        // 2) the button is released on an alien widget
        QWidget *enter = 0;
        if (nativeGuard)
            enter = alienGuard ? alienWidget : nativeWidget;
        else // The receiver is typically deleted on mouse release with drag'n'drop.
            enter = QApplication::widgetAt(event->globalPos());
        dispatchEnterLeave(enter, leaveAfterRelease, event->screenPos());
        leaveAfterRelease = 0;
        lastMouseReceiver = enter;
    } else if (!wasLeaveAfterRelease) {
        if (activePopupWidget) {
            if (!QWidget::mouseGrabber())
                lastMouseReceiver = alienGuard ? alienWidget : (nativeGuard ? nativeWidget : 0);
        } else {
            lastMouseReceiver = receiverGuard ? receiver : QApplication::widgetAt(event->globalPos());
        }
    }

    return result;
}
bool QCoreApplication::sendSpontaneousEvent(QObject *receiver, QEvent *event)
{
    Q_TRACE(QCoreApplication_sendSpontaneousEvent, receiver, event, event->type());

    if (event)
        event->spont = true;
    return notifyInternal2(receiver, event);
}

从上面两个函数就可以知道translateMouseEvent–>sendMouseEvent–>sendSpontaneousEvent或者sendEvent。到了sendEvent我们知道后面的调用流程了。其中sendSpontaneousEvent函数里面其实也是和sendEvent一样来进行后面的调用的。
postEvent和spontaneous调用流程较复杂,总结一下:
1、postEvent和spontaneous事件加入到postEventList的链表中。
2、在main函数的exec里面存在一个eventloop系统循环函数。
3、系统循环函数区分windous和linux系统后从processEvents中依次取出各种事件,其中就包括postEvent和spontaneous事件,将它们打包成MSG事件分发给窗口回调函数。
4、窗口回调函数收到MSG事件后调用translateMouseEvent–>sendMouseEvent–>sendSpontaneousEvent–>sendEvent。

  • 24
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
QTextEdit是一个常见的用于输入和编辑文本的Qt组件。根据您的问题,我可以简要地解释一下QTextEdit的拖拽功能。 QTextEdit支持两种类型的拖拽操作:拖拽进QTextEdit和从QTextEdit拖拽出去。 首先,让我们来看看拖拽进QTextEdit的情况。如果您想将一些文本或者其他可拖拽的数据拖拽到QTextEdit中,您需要先将数据设置为可拖拽的项。您可以使用QDrag类来实现这个功能。具体来说,您可以在源组件上调用QDrag开始拖拽操作,并设置要拖拽的数据。在QTextEdit中,您可以重写dragEnterEvent()和dropEvent()方法来处理拖拽操作。在dragEnterEvent()中,您可以检查拖拽的数据类型,并决定是否接受这个拖拽操作。在dropEvent()中,您可以将拖拽的数据插入到QTextEdit中。 其次,如果您想从QTextEdit拖拽出数据,您可以使用setDragEnabled()方法来启用此功能。然后您可以选择要拖拽的文本并拖动鼠标来开始拖拽操作。通过重写dragEnterEvent()和dragMoveEvent()方法,您可以确定拖拽操作是否允许,并设置拖拽的数据类型等信息。如果您希望在拖拽操作完成后执行某些操作,您可以重写dropEvent()方法。 总体而言,QTextEdit的拖拽功能提供了一种方便的方法来与其他组件或应用程序之间交换数据。通过了解QDrag和相关的事件处理方法,您可以自由地控制拖拽操作的行为和数据的传输。希望这个回答对您有所帮助!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

东方忘忧

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值