当前位置:网站首页>QT基础第四天(4)qt事件机制:事件基础概念,常见事件机制,事件处理以及事件的重写
QT基础第四天(4)qt事件机制:事件基础概念,常见事件机制,事件处理以及事件的重写
2022-08-02 14:37:00 【飞赴】
依然先来一段引入,大家都知道当我们使用手机点击屏幕上的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()
{
}
效果图:
拖动后:
边栏推荐
猜你喜欢
随机推荐
什么是hashCode?
马甲包接入过程记录
PAT Grade A 1143 Lowest Common Ancestor
2022-07-19 第五小组 瞒春 学习笔记
MySQL 视图(详解)
PAT甲级 1130 中缀表达式
【Frequency Domain Analysis】Spectral leakage, frequency resolution, picket fence effect
如何正确且快速的清楚C盘!!释放C盘空间内存保姆级教程
PAT甲级 1078 哈希
事件对象,事件流(事件冒泡和事件捕获)、事件委托、L0和L2注册等相关概念及用法
codeforces k-Tree (dp仍然不会耶)
第六章-6.1-堆-6.2-维护堆的性质-6.3-建堆
BOM(Browser Object Model)浏览器对象模型相关概念
MySQL 的几种碎片整理方案总结(解决delete大量数据后空间不释放的问题)
遍历堆 PAT甲级 1155 堆路径
2022 Low Voltage Electrician Exam Questions and Online Mock Exam
How to check the WeChat applet server domain name and modify it
lambda表达式、Stream接口及Optional类
2022-0801 第六小组 瞒春 学习笔记
Reading is the cheapest and noblest