Qt postEvent() 和事件过滤器
Qt postEvent() and event filter
我想过滤掉 QCoreApplication::postEvent(...)
发送给我的工作人员 QThread
的一些不需要的事件,然后再在事件循环中实际处理。
事件何时真正被事件过滤器过滤:在调用线程中的 postEvent()
或稍后在 QThread
事件循环中?
我认为答案是 "in event loop",但我无法在 qt 文档中找到确切问题的答案。
worker 和它的事件过滤器都需要在同一个线程中。事件由线程的事件循环拾取并在传递给它们的接收器之前通过过滤器传递 QObject
(当过滤器允许这样做时)。下面是演示此行为的示例:
#include <QtCore>
//a thread that can be destroyed at any time
//see
class SafeThread : public QThread{
using QThread::run;
public:
explicit SafeThread(QObject *parent = nullptr):QThread(parent){}
~SafeThread(){ quit(); wait(); }
};
struct MyEvent : public QEvent {
static const QEvent::Type myType = static_cast<QEvent::Type>(2000);
MyEvent():QEvent(myType){}
};
//worker QObject that handles MyEvent by printing a message
class Worker : public QObject {
public:
explicit Worker(QObject *parent = nullptr):QObject(parent){}
bool event(QEvent *event) {
if(event->type() == MyEvent::myType) {
qInfo() << "event in thread: " << QThread::currentThreadId();
return true;
}
return QObject::event(event);
}
};
class EventFilter : public QObject {
public:
explicit EventFilter(QObject *parent = nullptr):QObject(parent){}
bool eventFilter(QObject *watched, QEvent *event) {
if(event->type() == MyEvent::myType) {
qInfo() << "filter in thread: " << QThread::currentThreadId();
return false; //do not filter the event
}
return QObject::eventFilter(watched, event);
}
};
int main(int argc, char *argv[]) {
QCoreApplication a(argc, argv);
//create worker and thread
Worker worker;
EventFilter filter;
SafeThread thread;
filter.moveToThread(&thread);
worker.moveToThread(&thread);
worker.installEventFilter(&filter);
thread.start();
qInfo() << "calling postEvent from thread: " << QThread::currentThreadId();
QCoreApplication::postEvent(&worker, new MyEvent());
QTimer::singleShot(1000, &a, &QCoreApplication::quit);
return a.exec();
}
输出应该类似于:
calling postEvent from thread: 0xAAAA
filter in thread: 0xBBBB
event in thread: 0xBBBB
过滤必须在事件传递时执行,因为过滤器期望目标对象存在并具有可访问状态。只有在传递事件时才能保证这种存在和状态。使用 QObject
的方法是不安全的,保存 select 少数,来自其他线程,因此 通常在发布事件时,不可能安全地访问目标对象!。只有在保证对象的生命周期并且使用 thread-safe 方法访问状态时才可能这样做,并且以不会导致死锁的方式完成。
我想过滤掉 QCoreApplication::postEvent(...)
发送给我的工作人员 QThread
的一些不需要的事件,然后再在事件循环中实际处理。
事件何时真正被事件过滤器过滤:在调用线程中的 postEvent()
或稍后在 QThread
事件循环中?
我认为答案是 "in event loop",但我无法在 qt 文档中找到确切问题的答案。
worker 和它的事件过滤器都需要在同一个线程中。事件由线程的事件循环拾取并在传递给它们的接收器之前通过过滤器传递 QObject
(当过滤器允许这样做时)。下面是演示此行为的示例:
#include <QtCore>
//a thread that can be destroyed at any time
//see
class SafeThread : public QThread{
using QThread::run;
public:
explicit SafeThread(QObject *parent = nullptr):QThread(parent){}
~SafeThread(){ quit(); wait(); }
};
struct MyEvent : public QEvent {
static const QEvent::Type myType = static_cast<QEvent::Type>(2000);
MyEvent():QEvent(myType){}
};
//worker QObject that handles MyEvent by printing a message
class Worker : public QObject {
public:
explicit Worker(QObject *parent = nullptr):QObject(parent){}
bool event(QEvent *event) {
if(event->type() == MyEvent::myType) {
qInfo() << "event in thread: " << QThread::currentThreadId();
return true;
}
return QObject::event(event);
}
};
class EventFilter : public QObject {
public:
explicit EventFilter(QObject *parent = nullptr):QObject(parent){}
bool eventFilter(QObject *watched, QEvent *event) {
if(event->type() == MyEvent::myType) {
qInfo() << "filter in thread: " << QThread::currentThreadId();
return false; //do not filter the event
}
return QObject::eventFilter(watched, event);
}
};
int main(int argc, char *argv[]) {
QCoreApplication a(argc, argv);
//create worker and thread
Worker worker;
EventFilter filter;
SafeThread thread;
filter.moveToThread(&thread);
worker.moveToThread(&thread);
worker.installEventFilter(&filter);
thread.start();
qInfo() << "calling postEvent from thread: " << QThread::currentThreadId();
QCoreApplication::postEvent(&worker, new MyEvent());
QTimer::singleShot(1000, &a, &QCoreApplication::quit);
return a.exec();
}
输出应该类似于:
calling postEvent from thread: 0xAAAA
filter in thread: 0xBBBB
event in thread: 0xBBBB
过滤必须在事件传递时执行,因为过滤器期望目标对象存在并具有可访问状态。只有在传递事件时才能保证这种存在和状态。使用 QObject
的方法是不安全的,保存 select 少数,来自其他线程,因此 通常在发布事件时,不可能安全地访问目标对象!。只有在保证对象的生命周期并且使用 thread-safe 方法访问状态时才可能这样做,并且以不会导致死锁的方式完成。