最近项目需要对Qt事件耗时进行统计分析,并进行过滤,基于此设计一个事件监控器来进行事件分析。
基本原理
1、Qt事件由QAppliction::exec()
进入循环,当发生键盘、鼠标、Tab等事件时,QAppliction
会通过QAppliction::notify(QObject * obj, QEvent * event)
函数分发到界面中,界面再分发到子类中,最后由某一类完成事件处理后,从当前类的QObject::event(QEvent *event)
中像父类进行返回,最后完成一次事件循环。
2、Qt事件循环是在主线程中执行的,不存在多线程,若事件成功返回,必定上一次分发的事件相邻,可以通过记录之间的时间戳来计算事件耗时情况。
3、Qt支持重写QAppliction::notify(QObject * obj, QEvent * event)
事件和QObject::event(QEvent *event)
。
代码
在QAppliction
中重新实现notify()
bool Myapplication::notify(QObject *obj, QEvent *event)
{
EventMonitor::instance()->start(obj,event->type());
return QApplication::notify(obj,event);
}
重新实现界面类中的event()
,并添加几个事件做延时进行测试
bool MainWindow::event(QEvent *event){
bool result = QApplication::event(event);
EventMonitor::instance()->end(this,event->type());
return result;
}
void MainWindow::resizeEvent(QResizeEvent *event)
{
QThread::msleep(500);
MainWindow::resizeEvent(event);
}
void MainWindow::paintEvent(QPaintEvent *event)
{
QThread::msleep(300);
MainWindow::paintEvent(event);
}
void MainWindow::contextMenuEvent(QContextMenuEvent *event)
{
QThread::msleep(200);
MainWindow::contextMenuEvent(event);
}
主函数中添加监控事件列表
int main(int argc, char *argv[])
{
Myapplication a(argc, argv);
MainWindow w;
w.show();
EventMonitor::instance()->addMonitorEventType({ QEvent::Paint ,QEvent::Close /*,QEvent::Resize*/,QEvent::ContextMenu });
EventMonitor::instance()->setThreshold(250);
qDebug() << EventMonitor::instance()->MonitorEventType()
<< EventMonitor::instance()->threshold();
return a.exec();
}
** eventMonitor.h
**
class EventMonitor
{
public:
static EventMonitor* instance(){
static EventMonitor monitor;
return &monitor;
}
~EventMonitor();
void start(QObject *obj,int type);
void end(QObject *obj,int type);
//!
//! \brief 监控的时间阈值
//! \param us
//!
void setThreshold(qreal us);
qreal threshold() const;
//!
//! \brief 监控的事件列表
//!
typedef QSet<QEvent::Type> EventTypeSet;
void addMonitorEventType(QEvent::Type type);
void addMonitorEventType(EventTypeSet set);
void addAllMonitorEventType();
void removeMonitorEventType(QEvent::Type type);
void removeMonitorEventType(EventTypeSet set);
void removeAllMonitorEventType();
EventTypeSet MonitorEventType() const;
private:
explicit EventMonitor();
void init();
void trace();
timeval m_start,m_end;
qreal m_threshold;
EventTypeSet m_set;
struct EventInfo{
Qt::HANDLE thread_id;
QEvent::Type type;
QString func_name;
QString obj_name;
qreal elapsed;
}info;
friend inline QDebug operator<<(QDebug debug, const EventInfo &info)
{
debug <<info.type
<< " obj[" +info.obj_name + "]"
<< " func[" +info.func_name + "]"
<< QString::number(info.elapsed) + "ms";
return debug;
}
};
** eventMonitor.cpp
**
EventMonitor::~EventMonitor()
{
}
void EventMonitor::start(QObject *obj, int type)
{
gettimeofday(&m_start,NULL);
info.thread_id = QThread::currentThreadId();
info.type = (QEvent::Type)type;
info.obj_name = obj->objectName();
info.func_name = QString::fromUtf8(typeid (*obj).name());
info.elapsed = 0;
}
void EventMonitor::end(QObject *obj, int type)
{
gettimeofday(&m_end,NULL);
if(info.thread_id == QThread::currentThreadId()
&& info.type == (QEvent::Type)type
&& strcmp(typeid (*obj).name(),info.func_name.toStdString().data()) ==0 ){
QStringList tmp =QString::fromUtf8(typeid (*obj).name()).split(QRegExp("[0-9]+"));
QString func_name;
for(int i =0; i < tmp.size() -1;i++){
if(!tmp.at(i).isEmpty())
func_name += tmp.at(i) +"::";
}
info.func_name = func_name + tmp.last();
info.elapsed = ((m_end.tv_sec-m_start.tv_sec)*1000000+(m_end.tv_usec-m_start.tv_usec)) /1000;
if(info.elapsed >= m_threshold){
trace();
}
}
}
void EventMonitor::setThreshold(qreal us)
{
m_threshold = us;
}
qreal EventMonitor::threshold() const
{
return m_threshold;
}
void EventMonitor::addMonitorEventType(QEvent::Type type)
{
EventTypeSet set;
set.insert(type);
addMonitorEventType(set);
}
void EventMonitor::addMonitorEventType(EventMonitor::EventTypeSet set)
{
m_set += set;
}
void EventMonitor::addAllMonitorEventType()
{
EventTypeSet set;
for(int i =0; i <(int)QEvent::MaxUser;i++){
if(const char *value = QMetaEnum::fromType<QEvent::Type>().valueToKey(i)){
set.insert((QEvent::Type)QMetaEnum::fromType<QEvent::Type>().keyToValue(value));
}
}
addMonitorEventType(set);
}
void EventMonitor::removeMonitorEventType(QEvent::Type type)
{
EventTypeSet set;
set.insert(type);
removeMonitorEventType(set);
}
void EventMonitor::removeMonitorEventType(EventMonitor::EventTypeSet set)
{
m_set -= set;
}
void EventMonitor::removeAllMonitorEventType()
{
EventTypeSet set;
for(int i =0; i <(int)QEvent::MaxUser;i++){
if(const char *value = QMetaEnum::fromType<QEvent::Type>().valueToKey(i)){
set.insert((QEvent::Type)QMetaEnum::fromType<QEvent::Type>().keyToValue(value));
}
}
removeMonitorEventType(set);
}
EventMonitor::EventTypeSet EventMonitor::MonitorEventType() const
{
return m_set;
}
EventMonitor::EventMonitor()
{
init();
}
void EventMonitor::init()
{
m_threshold =10.0;
}
void EventMonitor::trace()
{
if(m_set.contains(info.type)){
qDebug() << info;
}
}