• Qt 事件机制


    1.1 Qt中的事件

    1.1.1 事件的处理

    Qt中处理事件的五种方法:
    方法一:重新实现部件的paintEvent()、mousePressEvent()等事件处理函数。
    方法二:重新实现notify函数。但它一次只能处理一个事件。这种方法需要继承QApplication类。
    方法三:向QApplication对象上安装事件过滤器。每个程序只有一个QApplication对象,功能与notify类似,但可同时处理多个事件。需要使用全局的事件过滤器。
    方法四:重新实现event()函数。其可以在事件到达默认的事件处理函数之前获得该事件。
    方案五:在对象上安装事件过滤器。使用事件过滤器可以在界面类中同时处理不同子部件的不同事件。
    最常见的是方法一方法五

    1.1.2 事件的传递

    每个程序main()函数的最后都会调用QApplication类的exec()函数,它会使Qt应用程序进入事件循环,然后应用程序在运行时会接收发生的各种事件。一旦有事件发生,Qt会构建相应的QEvent子类的对象来表示它,然后将它传递给相对应的Qobject对象或者其子对象。

    具体的例子可以参考霍亚飞的Qt快速入门第三版6-2源码。

    结论是,事件的传递顺序是这样的:先是事件过滤器,然后是焦点部件的event()函数,最后是焦点部件的事件处理函数;如果焦点部件忽略了该事件,就执行父部件的事件处理函数。注意,event()函数和事件处理函数是在焦点部件内部定义的,而事件过滤器却是在焦点部件的父部件中定义的。

    2.1 定时器事件与随机数

    2.1.1 定时器

    QTimerEvent类用来描述一个定时器事件。对于一个QObject的子类来说,只需要int QObject::startTimer(int interval)函数可以直接开启定时器,时间为毫秒级。函数返回整形数来代表这个定时器,当定时器溢出时可以在timerEvent()函数在进行操作。
    QTimer是一个计时器类,它的使用分三步,创建对象,连接signal和slot函数,start()。

    QTimer *timer = new QTimer(this);
    connect(timer, SIGNAL(timeout()), this, SLOT(update()));
    timer->start(1000);
    

    其中,SIGNAL(timeout())表示:每当计时结束,计时器归零并重新计时,并发送一个信号激活slot函数。如果我们想让这个计时器只计时一次,那么必须使用void setSingleShot(bool singleShot)函数。

    QTimer *timer = new QTimer(this);
    connect(timer, SIGNAL(timeout()), this, SLOT(update()));
    timer->setsetSingleShot(true)
    timer->start(60000);
    

    这样计时器只会倒计时1分钟,然后结束。

    2.1.2 随机数

    Qt中,生成随机数的函数为qrand()。该函数是标准C++函数rand的线程安全版本。但是,如果我们仅仅只是调用该函数来生成随机数,那么每次得到的随机数都将是相同的,这是因为Qt生成的随机数严格来说是一个“伪随机”,它的产生是根据随机数种子计算得到的,如果种子相同,那么所得到的随机数也是相同的。因此,为了使我们的随机数看起来更为“随机”一些,我们在每次获取随机数之前都需要初始化一个不同的随机数种子,这就需要用到qsrand()函数,该函数可以为qrand()函数设置一个不同的初值。

    qsrand(QTime(0, 0, 0).secsTo(QTime::currentTime()));
    int rand = qrand() % 30; //产生30以内的正整数
    

    这里使用QTime类的secsTo()函数,它表示两个时间点之间所包含的秒数,比如代码中就是指从零点整到当前时间所经历的秒数。当使用qrand()要获得一个范围内的数据时,一般是让它与一个整数取余,这里是30,所有生成的数值在0~29之间(包含0和29)。

    3.1 事件过滤器与事件的发送

    Qt中提供了事件过滤器来实现在一个部件中监控其他多个部件的事件。事件过滤器与其他部件不同,它不是一个类,只是由两个函数组成的一种操作,用来完成一个部件对其他部件的事件的监控。这两个函数分别是installEvenFilter()和eventFilter(),都是QObject类中的函数。

    新建Qt Widget应用,将项目名称改成myeventfilter,基类选择QWidget,类名保存Widget不变。完成后向设计模式中向界面上拖入一个Text Edit和一个Spin Box。在widget.h文件中添加public函数声明:

    bool eventFilter(QObject *obj, QEvent *event);
    

    然后再widget.cpp文件中添加头文件:

    #include <QKeyEvent>
    #include <QWheelEvent>
    

    在构造函数中添加代码:

    ui->textEdit->installEventFilter(this);//为编辑部件在本窗口安装事件过滤器
    ui->spinBox->installEventFilter(this);
    

    要对一个部件使用事件过滤器,那么就要先使用其的installEventFilter()函数为其安装事件过滤器,其参数this表明要在本部件(即Widget)中监视textEdit和spinBox的事件。这样就要重新实现Widget类的eventFilter()函数,在其中截获并处理两个子部件的事件。

    bool Widget::eventFilter(QObject *obj, QEvent *event)
    {
        if (obj == ui->textEdit) //判断部件
        {
            if (event->type() == QEvent::Wheel) //判断事件
            {
                //将event强制转化为发送的事件的类型
                QWheelEvent *wheelEvent = static_cast<QWheelEvent*>(event);
                //QWheelEvent是指用户滚动鼠标滚轮或类似输入设备时触发的事件。
                if (wheelEvent->delta() > 0)
                {
                    //zoom-in是将视距缩小,相当于离开事物近一点距离去观察事物,那么看到的图片就会放大
                    ui->textEdit->zoomIn();
                }
                else
                {
                    //zoom-out就是视距缩大,看到的图片就会缩小
                    ui->textEdit->zoomOut();
                }
                return true; //事件已经处理
            }
            else
            {
                return false; //如果是其他事件,可以进一步进行进一步的处理
            }
        }
        else if (obj == ui->spinBox)
        {
            if (event->type() == QEvent::KeyPress)
            {
                QKeyEvent *keyevent = static_cast<QKeyEvent*>(event);
                if (keyevent->key() == Qt::Key_Space)
                {
                    ui->spinBox->setValue(0);
                    return true;
                }
                else 
                {
                    return false;
                }
            }
            else
            {
                return false;
            }
        }
        else
        {
            return QWidget::eventFilter(obj, event);
        }
    }
    

    在这个事件过滤器中先判断部件的类型,然后再判断事件的类型,如果是需要的事件,那么就将其进行强制类型转换,然后进行相应的处理。如果要对一个特定的事件进行处理,而且不希望它在后面的传递过程中再被处理,那么就返回true,否则返回false。这个函数实现了textEdit部件中使用滚轮进行内容的放大或缩小。

    Qt中提供了发送一个事件的功能,它由QCoreApplication类的sendEvent函数和postEvent函数来实现

    bool QCoreApplication::sendEvent(QObject *receiver, QEvent *event)
    void QCoreApplication::postEvent(QObject *receiver, QEvent *event, int priority = Qt::NormalEventPriority)
    

    这两函数的区别是:
    sendEvent()会立刻去处理指定的事件,而postEvent()则会将事件放到等待调度队列中,下一次Qt的主事件循环运行时才会处理它。除此之外,sendEvent对象参数在发送完成后无法自动删除,所以需要在栈上创建QEvent对象;而postEvent()中的QEvent对象参数必须在堆上进行创建(例如用new),当事件被发送后事件队列会自动删除它。

  • 相关阅读:
    如何去重一个Oracle表
    配置Eclipse来开发Java 程序
    在windows上使用opera mini
    Oracle OLAP 介绍
    一个Batch作业调度系统构思
    how to Use Subversion with TortoiseSVN
    java official Design Pattern
    how to install ubuntu OS combined with Windows
    确保DWBI项目成功的几个关键点
    spinner 读取sqlite
  • 原文地址:https://www.cnblogs.com/wsl540/p/14482048.html
Copyright © 2020-2023  润新知