09 Qt消息机制及事件处理

在前面我们也曾经简单提到,Qt 程序需要在main()函数创建一个QApplication对象,然后调用它的exec()函数。这个函数就是开始 Qt 的事件循环。在执行exec()函数之后,程序将进入事件循环来监听应用程序的事件。当事件发生时,Qt 将创建一个事件对象。Qt 中所有事件类都继承于QEvent。在事件对象创建完毕后,Qt 将这个事件对象传递给QObject的event()函数。event()函数并不直接处理事件,而是按照事件对象的类型分派给特定的事件处理函数。

1、重写事件的回调函数

在所有组件的父类QWidget中,定义了很多事件处理的回调函数,如:keyReleaseEvent()、keyPressEvent()、mouseDoubleClickEvent()、mouseMoveEvent()、mousePressEvent()、mouseReleaseEvent() 等。这些函数都是 protected virtual 的,也就是说,我们可以在子类中重新实现这些函数。例如:

class EventLabel : public QLabel
{
protected:
    void mousePressEvent(QMouseEvent *event);//重写QLabel的鼠标按下处理函数
};



void EventLabel::mousePressEvent(QMouseEvent *event)
{
    this->setText(QString("

Press:(%1, %2)

").arg(QString::number(event->x()), QString::number(event->y()))); } int main(int argc, char *argv[]) { QApplication a(argc, argv); EventLabel *label = new EventLabel; label->setWindowTitle("MouseEvent Demo"); label->resize(300, 200); label->show(); return a.exec(); }

2、定时器

在Qt中使用定时器有两种方法,一种是使用QObiect类的定时器;一种是使用QTimer类。

1、QObject类的定时器

int QObject::startTimer(int interval);

/* 开始一个定时器并返回定时器ID,函数调用失败,将返回0。定时器开始后,每隔interval毫秒间隔将触发一次事件 */

void QObject::killTimer(int id);
/* 删除一个指定的id的定时器 */


virtual void QObject::timerEvent(QTimerEvent *event);
/* 虚函数timerEvent()被重载来实现用户的超时事件处理函数 ,当存在多个定时器时,可通过event的成员函数timerId()进行判断*/

2、QTimer类的定时器

//1.产生一个定时器
QTimer *time_clock=new QTimer(parent);

//2.连接这个定时器的信号和槽,利用定时器的timeout(),即定时时间到就会发送timeout()信号,从而触发slottimedone()槽去完成某项事情
connect(time_clock,SIGNAL(timeout()),this,SLOT(slottimedone()));

//3.开始定时器,并设定定时周期,定时器定时有两种:
start(int time)和setSingleShot(true)
//其中start(int time)是表示每隔time秒就会重启定时器,重复触发
//而setSingleShot(true)则是仅仅启动定时器一次


//4、停止运行定时器
if ( time_clock->isActive() )
    time_clock->stop();

3、事件的接受和忽略

https://www.devbean.net/2012/09/qt-study-road-2-events-accept-reject/

4、event()函数

event()函数主要用于事件的分发。所以,如果你希望在事件分发之前做一些操作,就可以重写这个event()函数了。例如:

bool CustomWidget::event(QEvent *e)
{
    //如果事件类型时键盘按下的话,则进行特殊处理
    if (e->type() == QEvent::KeyPress)
    {
        //需要进行强制类型转换
        QKeyEvent *keyEvent = static_cast(e);
        if (keyEvent->key() == Qt::Key_Tab) 
        {
            qDebug() << "You press tab.";
            //表示该事件已经被处理,不需要往下传送了
            return true;
        }
    }
    //其他事件进行默认处理
    return QWidget::event(e);
}
  1. 如果传入的事件已被识别并且处理,则需要返回 true,否则返回 false。如果返回值是 true,那么 Qt 会认为这个事件已经处理完毕,不会再将这个事件发送给其它对象,而是会继续处理事件队列中的下一事件。
  2. 在event()函数中,调用事件对象的accept()和ignore()函数是没有作用的,不会影响到事件的传播

如果你不想重写一大堆事件处理器,就可以重写这个event()函数,通过QEvent::type()判断不同的事件。

5、事件过滤器

QObject有一个eventFilter()函数,用于建立事件过滤器。函数原型如下:

//安装过滤器
void QObject::installEventFilter ( QObject * filterObj );

virtual bool QObject::eventFilter ( QObject * watched, QEvent * event );

所谓事件过滤器,可以理解成一种过滤代码。事件过滤器会检查接收到的事件。如果这个事件是我们感兴趣的类型,就进行我们自己的处理;如果不是,就继续转发。这个函数返回一个 bool 类型,如果你想将参数 event 过滤出来,比如,不想让它继续转发,就返回 true,否则返回 false。事件过滤器的调用时间是目标对象(也就是参数里面的watched对象)接收到事件对象之前。也就是说,如果你在事件过滤器中停止了某个事件,那么,watched对象以及以后所有的事件过滤器根本不会知道这么一个事件。已经存在的过滤器则可以通过QObject::removeEventFilter()函数移除。事件过滤器的强大之处在于,我们可以为整个应用程序添加一个事件过滤器。

示例代码:

MyWidget::MyWidget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::MyWidget)
{
    ui->setupUi(this);
    //安装事件过滤器
    ui->textEdit->installEventFilter(this);
}

bool MyWidget::eventFilter(QObject *obj, QEvent *event)
{
    //判断该对象是不是自己感兴趣的控件
    if (obj == ui->textEdit)
    {
        //判断该事件类型是不是自己感兴趣的事件
        if (event->type() == QEvent::KeyPress)
        {
            //强制类型转换
            QKeyEvent *keyEvent = static_cast(event);
            qDebug() << "Ate key press" << keyEvent->key();
            return true;
        }
        return false;
    }
    else
    {
        //其他对象进行默认处理
        return MyWidget::eventFilter(obj, event);
    }
}

注意:

事件过滤器和被安装过滤器的组件必须在同一线程,否则,过滤器将不起作用。另外,如果在安装过滤器之后,这两个组件到了不同的线程,那么,只有等到二者重新回到同一线程的时候过滤器才会有效。

 

6、总结

现在我们可以总结一下 Qt 的事件处理,实际上是有五个层次:

  1. 重写paintEvent()、mousePressEvent()等事件处理函数。这是最普通、最简单的形式,同时功能也最简单。
  2. 重写event()函数。event()函数是所有对象的事件入口,QObject和QWidget中的实现,默认是把事件传递给特定的事件处理函数。
  3. 在特定对象上面安装事件过滤器。该过滤器仅过滤该对象接收到的事件。
  4. 在QCoreApplication::instance()上面安装事件过滤器。该过滤器将过滤所有对象的所有事件,因此和notify()函数一样强大,但是它更灵活,因为可以安装多个过滤器。全局的事件过滤器可以看到 disabled 组件上面发出的鼠标事件。全局过滤器有一个问题:只能用在主线程。
  5. 重写QCoreApplication::notify()函数。这是最强大的,和全局事件过滤器一样提供完全控制,并且不受线程的限制。但是全局范围内只能有一个被使用(因为QCoreApplication是单例的)。

你可能感兴趣的:(QT入门教程)