Qt中postEvent和sendEvent函数

本文深入探讨了Qt中postEvent和sendEvent函数的工作原理,详细解释了这两种方式如何将事件发送到事件队列以及它们之间的主要区别。通过了解postEvent如何将事件添加到队列以及sendEvent如何绕过队列直接发送事件,读者可以更好地掌握Qt事件处理机制。

Qt中postEvent和sendEvent函数

部分内容参考http://blog.youkuaiyun.com/lvmengzou/article/details/65450908

qt事件循环需要维护一个事件队列,在Qt的main函数中最后一般调用QApplication::exec()成员函数来保持程序对事件队列的处理,exec()的实质是不停调用processEvent()函数从队列中获取事件,并处理,然后删除,postEvent的作用就是发送一个事件到此队列中,由于删除队列中事件调用delete运算符,所以,postEvent()传递的事件一定要是动态分配的。

sendEvent()函数直接跳过事件循环队列,直接调用notify()函数发送事件到目标对象,并等待事件处理结果,所以其传递的事件直接在栈上分配即可,(postEvent()函数只负责添加事件到队列,不等待事件处理结果)。

sendEvent调用关系如下:

inline bool QCoreApplication::sendEvent(QObject *receiver, QEvent *event)  
{  if (event) event->spont = false; return self ? self->notifyInternal(receiver, event) : false; }

QCoreApplication::notifyInternal(QObject * receiver, QEvent * event)

QCoreApplication::notify(QObject * receiver, QEvent *event)

QCoreApplicationPrivate::notify_helper(QObject * receiver, QEvent * event)

最终调用receiver->event(event);

PostEvent()函数只是单纯添加事件到postEventList

void QCoreApplication::postEvent(QObject *receiver, QEvent *event)  
{  
    postEvent(receiver, event, Qt::NormalEventPriority);  
}

void QCoreApplication::postEvent(QObject *receiver, QEvent *event, int priority)  
{  
    ...  
    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();  
  
    // if object has moved to another thread, follow it  
    while (data != *pdata) {                        <span style="font-family: Arial, Helvetica, sans-serif;">//在这里判断receiver线程信息是否发生变化。(有可能是另外一个线程调用用receiver->moveToThread)</span>  
        data->postEventList.mutex.unlock();  
  
        data = *pdata;  
        if (!data) {  
            // posting during destruction? just delete the event to prevent a leak  
            delete event;  
            return;  
        }  
  
        data->postEventList.mutex.lock();  
    }  
//这里postEventList还是被锁着的。  
    // if this is one of the compressible events, do compression  
    if (receiver->d_func()->postedEvents  
        && self && self->compressEvent(event, receiver, &data->postEventList)) {  
        data->postEventList.mutex.unlock();//这个事件有可能被压缩(实际上是发现队列中有这个事件还没有被处理,且这个事件是可以被压缩的,例如paintevent)  
        return;  
    }  
  
    event->posted = true;  
    ++receiver->d_func()->postedEvents;  
    if (event->type() == QEvent::DeferredDelete && data == QThreadData::current()) {  
        // remember the current running eventloop for DeferredDelete  
        // events posted in the receiver's thread  
        event->d = reinterpret_cast<QEventPrivate *>(quintptr(data->loopLevel)); //receiver即将被析构?  
    }  
//将事件添加到postEventList中,注意这里的优先级第一个最高,最后一个优先级最低  
    if (data->postEventList.isEmpty() || data->postEventList.last().priority >= priority) {  
        // optimization: we can simply append if the last event in  
        // the queue has higher or equal priority  
        data->postEventList.append(QPostEvent(receiver, event, priority));  
    } 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 begin = data->postEventList.begin()  
                                         + data->postEventList.insertionOffset,  
                                   end = data->postEventList.end();  
        QPostEventList::iterator at = qUpperBound(begin, end, priority);  
        data->postEventList.insert(at, QPostEvent(receiver, event, priority));  
    }  
    data->canWait = false;  
    data->postEventList.mutex.unlock();//在这里解除锁  
//receiver所在的线程调用eventDispatcher处理postEventList  
    if (data->eventDispatcher)  
        data->eventDispatcher->wakeUp();  
}  

如果想查看Qt中QApplication::exec()函数是如何传递事件到目标对象,并根据notify返回值和事件isAccept()的返回值决定是否传递事件到目标对象的父对象,可以重载目标对象的事件处理函数,并在事件处理函数中加入断点,开始调试,然后在可执行程序界面上触发事件处理函数,程序就会在事件处理函数的断点处停止,然后逐级返回,查看整个调用情况,从processEvent()函数到最终我们重载的事件处理函数,至少有20层函数调用。(调试之前要安装Qt源码组件)


<think>我们正在处理一个关于Qt框架的问题:如何通过postEvent向MainWindow发送事件,并使用回调函数处理。根据用户提供的引用问题,我们需要注意以下几点:1.引用[1]展示了C++代码中一个函数调用的例子,涉及到了函数指针或成员函数作为回调。2.引用[2]展示了PyQt中信号与槽的连接,即按钮点击触发一个函数(回调)。3.引用[3]展示了一个连接JavaScript信号到槽的示例。4.引用[4]说明了在QMainWindow中使用mouseMoveEvent需要设置鼠标追踪。用户当前的需求是:在Qt中通过postEvent向MainWindow发送事件并使用回调函数处理。分析:在Qt中,事件处理通常通过重写事件处理函数(如keyPressEvent,mousePressEvent等)或安装事件过滤器来实现。但是,用户要求使用postEvent并配合回调函数。这需要理解Qt的事件系统。步骤:1.创建一个自定义事件类(继承自QEvent),用于携带所需的数据。2.在需要的时候,使用QCoreApplication::postEvent()将自定义事件对象投递到主窗口的事件队列中。3.在MainWindow中重写customEvent()函数或重写event()函数来处理自定义事件。4.在事件处理函数中,我们可以调用一个回调函数(比如一个槽函数)来处理事件。但是,这里有一个关键点:在事件处理函数中,我们无法直接传递一个回调函数(像信号槽连接那样)?我们需要定义好处理的方式。另一种思路:我们可以在自定义事件中存储一个回调函数(比如使用std::function),然后在处理事件时调用这个回调。但需要注意的是,事件对象被postEvent后会在事件处理后自动删除,因此我们需要小心管理回调的生命周期。不过,在C++中,回调函数的使用需要谨慎,特别是在跨线程时(postEvent是线程安全的,因此可以跨线程使用)。但是,在同一个线程内,我们可以使用这个机制。然而,Qt的事件处理机制更倾向于使用信号槽或者直接处理事件,而不是在事件对象中携带回调函数。不过我们依然可以实现。由于用户提到的是回调函数,我们可以有两种方式:方式一:在自定义事件中保存回调函数,在事件处理时调用该回调。方式二:事件处理函数直接调用MainWindow的成员函数(相当于固定回调),或者通过信号槽机制触发一个槽(相当于回调)。考虑到跨线程跨模块的复杂性,我们以单线程为例。示例(C++):第一步:定义自定义事件类```cpp#include<QEvent>#include<functional>classCustomEvent:publicQEvent{public://自定义事件类型staticconstQEvent::TypeEventType=static_cast<QEvent::Type>(QEvent::User+1);CustomEvent(std::function<void()>callback):QEvent(EventType),callback_(callback){}voidcallCallback()const{if(callback_)callback_();}private:std::function<void()>callback_;};```第二步:在MainWindow中重写event处理函数```cppboolMainWindow::event(QEvent*event){if(event->type()==CustomEvent::EventType){constCustomEvent*customEvent=static_cast<CustomEvent*>(event);customEvent->callCallback();//调用回调returntrue;}returnQMainWindow::event(event);}```第三步:在某个地方投递事件```cpp//在某个函数中std::function<void()>myCallback=[](){//这里写回调逻辑qDebug()<< "Customeventprocessed!";};//向MainWindow发送自定义事件QApplication::postEvent(mainWindow,newCustomEvent(myCallback));```注意:这里使用lambda表达式创建了回调函数。需要注意的是,如果回调中访问了对象的成员,要确保对象的生命周期。但是,上述方式有一个问题:自定义事件携带std::function,而std::function要求回调的类型必须与定义匹配。在跨线程时,因为事件是在主线程的事件循环中处理的,所以回调会在主线程执行。然而,在PyQt中,我们同样可以实现类似的功能。由于Python中函数是一等公民,因此我们可以将函数直接作为参数传递给自定义事件类。但是,在PyQt中,我们通常不推荐使用postEvent来传递回调,因为Qt的事件系统在Python中可能不如信号槽方便。而且,PyQt的QEvent子类化在Python中同样可行。考虑到用户提供的引用中包括了PyQt的示例(引用[2]),用户可能正在使用PyQt。因此,我们也提供PyQt版本的示例。PyQt自定义事件并回调的步骤:1.定义自定义事件类(继承自QEvent)。2.在事件类中存储回调函数(Python中的函数)。3.在MainWindow中重写event方法,处理自定义事件,并调用回调。4.使用QApplication.postEvent发送事件。示例(PyQt):```pythonfromPyQt6.QtCoreimportQEvent,QObjectfromPyQt6.QtWidgetsimportQMainWindow,QApplication#自定义事件类型CustomEventType=QEvent.Type(QEvent.registerEventType())classCustomEvent(QEvent):def__init__(self,callback):super().__init__(CustomEventType)self.callback=callbackdefcallCallback(self):ifself.callback:self.callback()classMainWindow(QMainWindow):def__init__(self):super().__init__()defevent(self,event):ifevent.type()==CustomEventType:event.callCallback()#调用回调函数returnTruereturnsuper().event(event)#测试if__name__=='__main__':importsysapp=QApplication(sys.argv)window=MainWindow()window.show()#创建一个回调函数defmy_callback():print("Customeventreceivedandcallbackcalled!")#创建自定义事件并发送event=CustomEvent(my_callback)QApplication.postEvent(window,event)sys.exit(app.exec())```但是请注意:在PyQt中,事件的删除由Qt的事件系统管理,我们不需要手动删除。回调函数在事件对象中保存,当事件被处理后,事件对象会被删除。另外,在发送事件时,使用QApplication.postEvent(),事件会在主线程中被处理,因此回调函数也会在主线程中执行。但是,我们也可以使用其他线程发送事件,由于postEvent是线程安全的,所以没问题。但是要注意回调函数中不能操作其他线程的GUI对象,但这里因为是在主线程回调,所以可以操作GUI(因为事件处理在主线程)。问题:为什么不用信号槽?如果是在同一线程内,信号槽也可以实现类似回调的功能,而且更加简单。但是,如果用户有特殊需求(比如需要自定义事件类型,或者需要跨线程发送事件,并且不想在类中显式连接信号槽),使用事件机制是合适的。因此,我们提供了两种方式的示例,供用户参考。生成的相关问题:1.在Qt中使用自定义事件有哪些注意事项?2.QtpostEventsendEvent有什么区别?3.如何在Qt中实现跨线程的事件处理?4.在PyQt中,如何安全地使用回调函数?</think> 在Qt中通过`QCoreApplication::postEvent()`向`QMainWindow`发送自定义事件并使用回调函数处理,可参考以下实现方案: --- ### 完整实现步骤(C++) #### 1. 定义自定义事件类 ```cpp #include <QEvent> #include <functional> class CustomEvent : public QEvent { public: using CallbackFunc = std::function<void()>; static const QEvent::Type CustomType = static_cast<QEvent::Type>(QEvent::User + 1); explicit CustomEvent(CallbackFunc callback) : QEvent(CustomType), m_callback(std::move(callback)) {} void executeCallback() const { if (m_callback) m_callback(); } private: CallbackFunc m_callback; }; ``` #### 2. 在MainWindow中重写事件处理函数 ```cpp #include <QMainWindow> #include <QDebug> class MainWindow : public QMainWindow { Q_OBJECT public: explicit MainWindow(QWidget *parent = nullptr) : QMainWindow(parent) { // 确保能接收自定义事件 qApp->installEventFilter(this); } protected: // 重写事件处理函数 bool event(QEvent *event) override { if (event->type() == CustomEvent::CustomType) { auto customEvent = static_cast<CustomEvent*>(event); customEvent->executeCallback(); // 执行回调 return true; } return QMainWindow::event(event); } }; ``` #### 3. 发送事件并触发回调 ```cpp // 在任意位置发送事件 QCoreApplication::postEvent(mainWindow, new CustomEvent([]() { qDebug() << "自定义事件处理回调已执行!"; // 在此添加业务逻辑 }) ); ``` --- ### PyQt等效实现(Python) #### 1. 定义自定义事件 ```python from PyQt6.QtCore import QEvent, pyqtSignal from PyQt6.QtWidgets import QMainWindow CustomEventType = QEvent.Type(QEvent.registerEventType()) class CustomEvent(QEvent): def __init__(self, callback): super().__init__(CustomEventType) self.callback = callback def execute_callback(self): self.callback() ``` #### 2. 在MainWindow中处理事件 ```python class MainWindow(QMainWindow): def __init__(self): super().__init__() def event(self, event): if event.type() == CustomEventType: custom_event = event # QEvent对象转型 custom_event.execute_callback() # 执行回调 return True return super().event(event) ``` #### 3. 发送事件并触发回调 ```python from PyQt6.QtWidgets import QApplication def my_callback(): print("自定义事件处理回调已执行!") # 发送事件 QApplication.postEvent(main_window, CustomEvent(my_callback)) ``` --- ### 关键知识点说明 1. **自定义事件**:通过继承`QEvent`定义专属事件类型,`QEvent::User + N`确保类型唯一性 2. **事件投递**:`postEvent()`是线程安全的异步操作,事件会被加入接收者线程的事件队列[^1] 3. **回调封装**:事件类存储`std::function`或Python可调用对象实现回调绑定 4. **事件处理**:重写`event()`或`customEvent()`处理自定义事件[^4] 5. **生命周期**:通过`postEvent()`发送的事件会被Qt自动释放,无需手动删除 > 完整示例代码参考:[GitHub - Qt Event Handling Examples](https://github.com/qt/qtbase/tree/dev/examples/widgets/mainwindows) ---
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值