引言
- 在VC編程中,需要往一個(gè)窗口發(fā)送消息時(shí),常常用到SendMessage和PostMessage。其中SendMessage是阻塞式的,發(fā)送消息后,立即響應(yīng)消息,響應(yīng)完成后才繼續(xù)往下執(zhí)行。而PostMessage是非阻塞式的,發(fā)送的消息會(huì)放到消息隊(duì)列后,立即返回繼續(xù)往下執(zhí)行。SendMessage和PostMessage都無(wú)需保證線程一致性。即,可以無(wú)論在工作線程中還是主線程中,都能向指定的窗口發(fā)送消息。
- 在Qt編程中,很自然的會(huì)想到sendEvent和postEvent來(lái)實(shí)現(xiàn)VC這種消息發(fā)送機(jī)制。sendEvent是阻塞式的,發(fā)送事件后,立即對(duì)事件做出響應(yīng)。而postEvent則是非阻塞式的,發(fā)送的事件會(huì)放到事件隊(duì)列后,立即返回繼續(xù)往下執(zhí)行。需要注意的是,sendEvent必須滿(mǎn)足線程一致性,即必須保證事件接收對(duì)象所駐留的線程與sendEvent的調(diào)用線程保持一致。而postEvent無(wú)需保證這一點(diǎn)。
- 因此,可以直接使用postEvent實(shí)現(xiàn)PostMessage機(jī)制。而在事件接收對(duì)象所駐留的線程與sendEvent的調(diào)用線程是同一個(gè)線程時(shí),可以使用sendEvent來(lái)實(shí)現(xiàn)SendMessage機(jī)制。但是當(dāng)sendEvent的調(diào)用線程與事件接收對(duì)象所駐留的線程不一致時(shí),這里需要借助postEvent和QEventLoop共同完成。
- QEventLoop類(lèi)為我們提供了一種進(jìn)入和退出一個(gè)事件循環(huán)的方法。在任何時(shí)候,你都可以創(chuàng)建一個(gè)QEventLoop實(shí)例,然后調(diào)用exec()來(lái)啟動(dòng)一個(gè)事件循環(huán)實(shí)現(xiàn)阻塞效果。在這個(gè)循環(huán)期間,可以調(diào)用exit()來(lái)強(qiáng)制使exct()返回。
實(shí)現(xiàn)方案
#pragma once
#include <QObject>
#include <QEvent>
#include <QVariant>
void QPostMessage(QObject* obj, int msg, QVariant params);
long QSendMessage(QObject* obj, int msg, QVariant params);
static long s_rst = 0;
class LEvent : public QObject, public QEvent
{
public:
LEvent(int msg, const QVariant params = QVariant(), long& rst = s_rst);
~LEvent();
public:
static Type m_eventType;
int m_message = 0;
const QVariant& m_params;
long& m_rst;
};
#include "LEvent.h"
QEvent::Type LEvent::m_eventType = (QEvent::Type)QEvent::registerEventType();
LEvent::LEvent(int msg, const QVariant params/* = QVariant()*/, long& rst/* = s_rst*/)
: QEvent(m_eventType), m_message(msg), m_params(params), m_rst(rst)
{
}
LEvent::~LEvent()
{
}
void QPostMessage(QObject* obj, int msg, QVariant params)
{
QCoreApplication::postEvent(obj, new LEvent(msg, params));
}
long QSendMessage(QObject* obj, int msg, QVariant params)
{
long lResult = 0;
if (obj->thread() == QThread::currentThread())
{
//事件隊(duì)列不會(huì)刪除事件。通常的方法是在棧上創(chuàng)建事件
LEvent e(msg, params, lResult);
QCoreApplication::sendEvent(obj, &e);
}
else
{
//事件隊(duì)列將獲得事件的所有權(quán)并在事件發(fā)送后刪除它。必須在堆上創(chuàng)建事件
LEvent* e = new LEvent(msg, params, lResult);
QCoreApplication::postEvent(obj, e, Qt::HighEventPriority);
QEventLoop loop;
// 當(dāng)事件處理完畢后,事件實(shí)例會(huì)被銷(xiāo)毀,通知loop退出事件循環(huán)
QObject::connect(e, SIGNAL(destroyed(QObject*)), &loop, SLOT(quit()));
loop.exec(QEventLoop::ExcludeUserInputEvents);
}
return lResult;
}
參考文章:
https://note.youdao.com/ynoteshare1/index.html?id=79b2bd18897960d901608f1d219e56f0&type=note