QT---事件系统

1         QT事件系统

1.1  事件的定义

QT中事件是有专门的类QEvent,常见的有键盘事件QKeyEvent、鼠标事件QMouseEvent和定时器事件QTimerEvent。例如用鼠标单击下按钮,就会产生一个鼠标事件,按钮会产生一个单击信号。事件可以理解为发出信号的来源,信号的生产者,有了这个鼠标事件产生了这个单击信号。

1.2  事件和信号的区别

仔细来看,事件与信号其实并无多大差别,都是通过触发和响应函数来处理用户的请求。主要区别如下:

(1)    接收者不同

信号的接收者是槽,事件的接收者是一个队列,事件发生时,会产生一个事件对象被插入队列的尾部,系统会循环从队列的头部取事件对象进行处理。事件处理可以是异步的,信号的处理则是同步的。

(2)    单线和多线性

一个信号触发后,可以关联多个槽,多个槽会被执行,当然一个信号也可以触发另外一个信号。而事件则是与窗口相关的,所以事件回调时都是从当前窗口开始,一级一级向上派发,直到有一个窗口返回true,截断了事件的处理为止。

(3)    返回值作用

事件处理函数如果返回true,则这个事件处理已完成,QApplication会接着处理下一个事件,而如果返回false,那么事件分派函数会继续向上寻找下一个可以处理该事件的注册方法。信号处理函数的返回值对信号分派器来说是无意义的。

1.3  事件的接收和处理方法

1.3.1            事件传递逻辑

QT中事件的接收流程如下图所示,postEvent会将事件插入事件队列,异步处理事件,sendEvent直接将事件发出进行处理。如果返回TRUE或者调用accept(),说明事件已经处理完,不会在想父组件传播,如果返回FALSE或者调用ignore()表明这个事件没有被处理完,QT会传递给其父组件继续处理。

                       

1.3.2            自定义事件处理函数

要实现自定义的事件处理函数,可以按照上图的流程,从流程中的某个点着手,将事件接收过来处理。

(1)   重写事件响应函数

例如widget类继承于QWidget,QWidget有keyPressEvent函数,但是widget类重写了keyPressEvent函数,会覆盖基类的函数。按钮按下时,widget的keyPressEvent函数先处理事件。这种方法的局限性是只能处理特定的事件。

void AlarmCenter::keyPressEvent(QKeyEvent *event)

{

    if (event->key() == 0x01000000)

    {

        int i = 1;

    }

    else {

        //将除了Esc的按键返回给父控件QWidget处理 

        QWidget::keyPressEvent(event);

    }

}

(2)重写QObject类的event函数

bool QWidget::event(QEvent *event) 

  { 

     if (e->type() == QEvent::KeyPress)

 { 

        QKeyPressEvent *keyEvent = (QKeyEvent *)event; 

  return false; 

return true; 


(3)安装过滤器Filter过滤消息,

可以在QObject或者QApplication上安装过滤器。AlarmCenter的构造函数中安装过滤器installEventFilter(this);然后在类中实现eventFilter函数即可。

bool AlarmCenter:: eventFilter (QObject *target, QEvent *event)

{

   

    if (event->type() == QEvent::KeyPress)

    {

        int i = 1;

        return true;

    }

    else {

        //将除了Esc的按键返回给父控件QWidget处理 

        return QWidget::eventFilter(target, event);

    }

}

   

(4)   重写QApplication的notify函数

先要自定义自己的MyApplication类,继承于QApplication,然后重写QApplication的notify函数,在notify函数内部建立过滤消息处理,如果要传递事件给其他部件,可以通过postEvent或sendEvent发送给其他部件。

自定义的Application类头文件如下:

#ifndef MYAPPLICATION_H

#define MYAPPLICATION_H

#include <QApplication>

#include <QEvent>

class MyApplication : public QApplication

{

public:

    MyApplication(int & argc, char ** argv);

public:

    bool notify(QObject *receiver, QEvent *e);//重写notify函数

};

#endif

自定义的Application类源文件如下:

#include "myapplication.h"

#include <QMouseEvent>

MyApplication::MyApplication(int & argc, char ** argv) : QApplication(argc, argv)

{

}

 bool MyApplication::notify(QObject *receiver, QEvent *e)

 {

     //屏蔽MouseButtonPress、MouseButtonRelease和MouseMove事件

     if (e->type() == QEvent::MouseButtonPress || e->type() == QEvent::MouseButtonRelease || e->type() == QEvent::MouseMove)

     {

              //此处添加事件处理

        return true;

     }

     return QApplication::notify(receiver, e);

 }

1.4  定时器事件

1.4.1            QTimerEvent类实现定时器

(1)   用int id1 = startTimer(2000);函数启动定时器。

(2)   重写timerEvent函数,接收处理定时器事件

void  AlarmCenter::timerEvent(QTimerEvent *event)

{

    if (event->timerId() == 1)

    {

        qDebug() << "1";

    }

    else

    {

        qDebug() << "not 1";

    }

}

1.4.2            QTimer类实现定时器

(1)在源文件中添加头文件

#include<QTimer>

#include<QTime>

(2)在构造函数中添加代码新建QTimer对象,将QTimer::timeout信号关联到槽函数AlarmCenter::UpdateTimer,调用函数start启动定时器。

m_timer = NULL;

    m_timer = new QTimer(this);

    QObject::connect(m_timer, &QTimer::timeout, this, &AlarmCenter::UpdateTimer);

    m_timer->start(1000);

(3)    实现定时器处理函数

void  AlarmCenter::UpdateTimer()

{

    QTime time = QTime::currentTime();

    QString str = time.toString("hh:mm");

}

1.4.3            只运行一次的定时器

采用QTimer::singleShot(10000,this,&AlarmCenter::UpdateTimer);10秒钟后会执行一次定时器。

1.5  事件过滤器

事件过滤器的作用是实现在一个部件中监控其他多个部件的事件,比如在一个QMainWindow中监控QMainWindow上面多个按钮、输入框、表格等多个部件的事件,进行逻辑分析处理。具体实现方式是通过Object的两个函数installEventFilter和重写函数eventFilter(QObject *target, QEvent *event)函数来接收事件,通过target参数来获取控件的对象,通过event参数来获取事件类型。这样在textEdit文本输入框中输入值的时候,只要键盘按钮按下,就会进入eventFilter中的keypress事件处理。

(1)  在构造函数中installEventFilter函数传入this指针,使得AlarmCenter作为textEdit的事件的接收者。

ui.textEdit->installEventFilter(this);

 

(2)  重写Qobject的eventFilter函数接收个控件的事件。

bool AlarmCenter::eventFilter(QObject *target, QEvent *event)

{

    if (target==ui.textEdit)

    {

        if (event->type() == QEvent::KeyPress)

        {

            int i = 1;

            return true;

        }

        else {

            //将除了Esc的按键返回给父控件QWidget处理 

            return QMainWindow::eventFilter(target, event);

        }

    }

    else

    {

        return QMainWindow::eventFilter(target, event);

    }

}

1.6  SendEvent和postEvent的异同

1.6.1            函数的继承关系和属性

QApplication 继承于QGuiApplication,而QGuiApplication继承于 QCoreApplication,实际上这两个函数是QCoreApplication的静态函数。可以使用QApplication::sendEvent直接调用。

static bool sendEvent(QObject *receiver, QEvent *event);

static void postEvent(QObject *receiver, QEvent *event, int priority = Qt::NormalEventPriority);

1.6.2            两个函数的异同

Sendevent会立即处理事件,在栈上创建临时事件变量,传入指针,调用结束后自动销毁。PostEvent函数则是先把事件放到事件队列中,需要在堆上创建,需要new。等到事件被处理完后,事件队列自动清除内存。例如我们创建一个spinBox,即带上下按钮的输入框。将QTdesigner中将取值范围设置为正负10000。然后通过自定义的按钮按下信号去控制spinBox的值发生变化。

控制spinbox向上按钮,使数字加1,如果放在定时器中,则会定时增加数值。

QKeyEvent myEvent(QEvent::KeyPress, Qt::Key_Up, Qt::NoModifier);

QApplication::sendEvent(ui.spinBox, &myEvent);

采用postEvent控制spinbox向下按钮,使数字减1,如果放在定时器中,则会定时减少数值。

QKeyEvent* myEventde=new QKeyEvent(QEvent::KeyPress, Qt::Key_Down, Qt::NoModifier);

QApplication::postEvent(ui.spinBox, myEventde);

自己编了一个股票监控软件,有如下功能,有兴趣的朋友可以下载;

(1)   个股监测。监测个股实时变化,可以监测个股大单交易、急速拉升和下降、主力入场和出场、股票最高点和最低点提醒。检测到最高点、最低点、主力进场点、主力退场点、急速拉升点、急速下跌点,给出语音或者声音提醒,不用再时刻看着大盘了,给你更多自由的时间;

(2)   大盘监测。监测大盘的走势,采用上证、深证、创业三大指数的综合指数作为大盘走势。并实时监测大盘的最高点和最低点、中间的转折点。

(3)   股票推荐。还能根据历史数据长期或短期走势进行分析,对股市3千多个股票进行分析对比,选出涨势良好的股票,按照增长速度从大到小排序,推荐给你涨势良好的股票;

下载地址:

1.0.3版本(修复大盘指数崩溃缺陷)下载地址:

链接:https://pan.baidu.com/s/1BJcTp-kdniM7VE9K5Kd3vg 提取码:003h

更新链接:

https://www.cnblogs.com/bclshuai/p/10621613.html

原文地址:https://www.cnblogs.com/bclshuai/p/8665804.html