QT基础第四天(4)qt事件机制:事件基础概念,常见事件机制,事件处理以及事件的重写

        依然先来一段引入,大家都知道当我们使用手机点击屏幕上的qq图标,我们就进入了qq这个软件,这到底是怎么实现的呢?难道真的是我们点击到了qq这个图标我们才进入这个软件的吗?        

        Now,现在我们一起来理解其中原理,依然拿我们的手机举例,手机的的屏幕大家都知道是触摸屏,触摸屏顾名思义就是可以触摸的屏幕,其实这是两个东西,一个是触摸板另一个是显示屏,触摸屏上是有,x,y坐标用来连接cpu进行定位,恰好x,y这个交点就是我们屏幕上显示的qq图标,所以我们点击qq图标进入了qq这个软件,而我们点击屏幕再到松开,又引发了种种机制。。。。

        现在正式进入我们今天的内容,qt的事件机制。

 一.事件(event)

qt的事件主要分为两种:

1.操作系统产生

2.qt应用程序产生(人机交互过程)

        首先,我们要知道qt事件均继承抽象类Qevent,其描述程序内部或外部发生的动作。任意的Qobject对象都具备处理Qt事件的能力。

        在发生事件时,会产生一个Qevent对象,这个对象回传递给当前组件的event()函数。如果当前组件没有安装事件过滤器,则会将Qevent对象发放当相应的xxxEvent()函数中。

        不同的Qevent对象会有各种各样的属性,比如:

        (1)鼠标移动:mouseMoveEvent()

        (2)鼠标单击:mousePressEvent()

        (3)鼠标双击:mouseDoubleClickEvent()

        (4)鼠标弹起:mouseReleaseEvent()

        (5)鼠标滚轮:wheelEvent()

        (6)键盘按下:keyPressEvent()

        (7)键盘弹起:keyReleaseEvent() 

     tips:这是都是从基类中继承过来的虚函数,所以我们可以在派生类中直接进行重写,来达到我们想要的功能。

虽然事件很多,但是我们可以归纳一个总体流程:

Qt的main函数中创建一个App对象,然后调用exec()函数,此时的app会进入消息循环,可以监听app的事件,如果发生事件就会产生一个qevent对象:

回到开始的地方,我们说qt的事件主要分为两种。

(1)操作系统自己产生的事件:

        操作系统获取到一个鼠标按压的事件,会将该事件放入到系统的消息队列中,Qt消息循环的时候读取消息队列中的消息,转化为QEvent对象并被分发到相应的QWidget对象,event(QEvent *)函数会对事件进行处理(根据事件类型调用不同的事件处理函数),在事件处理函数中发送Qt预定义的信号,最终调用信号关联的槽函数。

(2)qt应用程序自己产生的事件:

        程序产生事件有两种方式:
一种是调用Application::postEvent( )函数:例如QWidget::update( )函数,它new一个paintEvent( ),然后调用Application::postEvent( )将其放入Qt的消息队列中,等待依次被处理;
另一种是调用sendEvent( )函数:事件不会放入消息队列,而是直接被派发和处理。Qwidget::repaint( )函数就是会被立即处理,也就是阻塞型的。

二.Qt事件的处理 

1.调度方式

事件有两种调度方式:同步和异步

Qt的事件循环是异步的,当调用exec()时,就进入了消息循环。先处理Qt事件队列中的事件,直到为空,再处理系统消息队列中的消息,直至为空。吹系统消息队列的时候会产生新的Qt事件,需要对其再次进行处理。
调用sendEvent的时候,消息会立即被处理,是同步的(阻塞的)。实际上sendEvent( )是通过调用QApplication::notify( ), 直接进入了事件的派发和处理。

2.事件的派发和处理

事件过滤器是Qt中一个独特的事件处理机制。通过事件过滤器,可以让一个对象侦听拦截另一个对象的事件。
(1)事件过滤器实现如下:
在基类QObject中有一个类行为QObjetList的成员变量,名为eventFilters,当某个QObject(A)给另一个QObject(B)安装了事件过滤器后,B会把A的指针保存在eventFilters中,在B处理事件前,会先去检查eventFilters列表,如果非空,就先调用列表中对象的eventFilter( )函数。一个对象可以给多个对象安装过滤器,一个对象能同时被安装多个过滤器,在事件到达之后,事件过滤器以安装次序的反序被调用。

事件过滤器的返回值如果为true,则表示事件已经被处理完毕,Qt将直接返回,进行下一事件的处理。如果返回false,事件则将接着被送往剩下的事件过滤器或目标对象进行处理。

(2)Qt中事件的派发是从QApplication::notify( )开始的,因为QApplication也是继承自QObject,所以先检查QApplication对象,如果有事件过滤器安装在它身上,先调用事件过滤器,接下来QApplication::notify( )会过滤或合并一些事件(比如失效widget的鼠标事件会被过滤掉,而同一区域重复的绘图事件会被合并),事件被送到reciver::event( )处理。
 

 3.事件的转发

对于某些类别的事件,如果在整个事件的派发过程结束后还没被处理,那么这个事件将会向上转发给他的父widget,直到顶层窗口。

三.事件的重写 

         当我们的需求用基础部件无法实现的时候,通过事件的机制来进行重写,也就是自定义控件,接下来直接上例子。

案例一:自定义按钮,让按钮按下去有一个动态的反馈

        首先我们需要重写按钮,当它按下去,我们将按钮进行一个图案填充,那么很明显我们需要对鼠标点击事件和绘图事件进行重写,当点击鼠标后,我们执行绘画事件,将按钮填充图案,鼠标弹起后将信号重置,再执行绘图事件,但由于我们设置了条件判断,弹起后不会进行填充图案。

mypushbutton.h

#ifndef MYPUSHBUTTON_H
#define MYPUSHBUTTON_H

#include <QWidget>
#include <QPaintEvent>

class MYPushButton : public QWidget
{
    Q_OBJECT

//申明自己的信号
signals:
    void clicked(bool);

public:
    explicit MYPushButton(QString str,  QWidget *parent = nullptr);

    void paintEvent(QPaintEvent *event);

    void mousePressEvent(QMouseEvent *event);
    void mouseReleaseEvent(QMouseEvent *event);

private:
    QString text;
    bool isreleased;
};

#endif // MYPUSHBUTTON_H

       widget.h

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QLCDNumber>
#include "mypushbutton.h"

class Widget : public QWidget
{
    Q_OBJECT
public slots:
    void add_num()
    {
        lcd->display(++num);
    }

public:
    Widget(QWidget *parent = 0);
    ~Widget();

private:
    MYPushButton *bt;
    QLCDNumber *lcd;
    int num;

};

#endif // WIDGET_H

mypushbutton.cpp

#include "mypushbutton.h"
#include <QPainter>
#include <QDebug>

MYPushButton::MYPushButton(QString str, QWidget *parent) : QWidget(parent)
{
    setMinimumSize(40, 20);
    text = str;
    bool isreleased = true;
    qDebug() << text;
}

void MYPushButton::mousePressEvent(QMouseEvent *event)
{
    isreleased = false;
    update();

    //发射信号
    emit clicked(true);
}
void MYPushButton::mouseReleaseEvent(QMouseEvent *event)
{
    isreleased = true;
    update();
}

void MYPushButton::paintEvent(QPaintEvent *event)
{
    QPainter p(this);
    if(!isreleased)
    {
        QBrush brh;
        brh.setStyle(Qt::DiagCrossPattern);
        p.setBrush(brh);
    }

    p.drawRect(0, 0, width()-3, height()-3);
    p.drawText(width()/2, height()/2, text);
}

widget.cpp

#include "widget.h"
#include <QVBoxLayout>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
{
    num = 0;
    bt = new MYPushButton("加一下");
    lcd = new QLCDNumber;
    lcd->setMinimumHeight(50);

    QVBoxLayout *vbox = new QVBoxLayout;
    vbox->addWidget(lcd);
    vbox->addWidget(bt);
    setLayout(vbox);

    connect(bt, SIGNAL(clicked(bool)), this, SLOT(add_num() ));
}

Widget::~Widget()
{

}

点之前:

点之后:

案例2:自定义滑杆部件,实现滑动过程,数码管数字随着增加的效果

既然要实现滑动效果我们首先要重写绘图事件,给我们画一条线,和一个滑动按钮,并且我们需要捕获鼠标移动的事件和鼠标点击的事件进行重写

myslider.h

#ifndef MYSLIDER_H
#define MYSLIDER_H

#include <QWidget>

class MYSlider : public QWidget
{
    Q_OBJECT
public:
    explicit MYSlider(QWidget *parent = nullptr);

    void mouseMoveEvent(QMouseEvent *event);
    void mousePressEvent(QMouseEvent *event);
    void paintEvent(QPaintEvent *event);

signals:
    void valueChanged(int);

private:
    QPoint pos;

};

#endif // MYSLIDER_H

 widget.h

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QSlider>
#include <QLCDNumber>
#include "myslider.h"

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = 0);
    ~Widget();

private:
    MYSlider *sdr;
    QLCDNumber *lcd;
};

#endif // WIDGET_H

myslider.cpp

#include "myslider.h"
#include <QPainter>
#include <QMouseEvent>

MYSlider::MYSlider(QWidget *parent) :
    QWidget(parent)
{
    setMinimumSize(100, 50);
}

void MYSlider::mouseMoveEvent(QMouseEvent *event)
{
    pos = event->pos();
    update();
}

void MYSlider::mousePressEvent(QMouseEvent *event)
{
    pos = event->pos();
    update();
}

void MYSlider::paintEvent(QPaintEvent *event)
{
    //画杆儿
    QPainter p(this);
    p.drawLine(QPoint(0, height()/2), QPoint(width(), height()/2));

    //画游标
    QPainter p1(this);
    QBrush brsh;
    brsh.setStyle(Qt::SolidPattern);
    p1.setBrush(brsh);
    p1.drawEllipse(QPoint(pos.x(), height()/2), 30, 30);
    emit valueChanged(pos.x()*100.0/width());
}

widget.cpp

#include "widget.h"
#include <QVBoxLayout>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
{
    lcd = new QLCDNumber;
    lcd->setMinimumHeight(50);
    sdr = new MYSlider;
//    sdr->setOrientation(Qt::Horizontal);

    QVBoxLayout *vbox = new QVBoxLayout;
    vbox->addWidget(lcd);
    vbox->addWidget(sdr);
    setLayout(vbox);

    connect(sdr, SIGNAL(valueChanged(int)), lcd, SLOT(display(int)));
}

Widget::~Widget()
{

}

效果图:

                                

拖动后:

  • 27
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 25
    评论
评论 25
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

飞赴

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值