事件
- 事件(event)是由系统或者Qt本身在不同时刻发出的。当用户按下鼠标、敲下键盘,或者是窗口需要重新绘制的时候,都会发出一个相应的事件。一些事件在对用户操作做出响应时发出,如键盘事件等。另一些事件则由系统自动发出,如定时器事件。
Qt消息循环机制
- Qt程序需要在main函数创建一个QApplication对象,然后调用它的exec函数,这个函数就是开始Qt的事件循环。在执行exec函数之后,程序将进入事件循环来监听应用程序的事件。
- 当应用程序监听到事件触发后,先将请求交给事件过滤器,过滤后再交给事件分发器,由事件分发器交给具体的事件处理器进行处理。
- 事件过滤器,事件分发器和事件处理器,默认都会对事件进行处理,如果我们想要自己处理事件,就重写对应的事件函数。然后在对应的事件函数中,进行相应的处理。
- 一般来说,重写事件处理函数的场景比较多,事件过滤和事件分发,基本上交给系统处理就可以了,我们不用自己处理。为了说明事件过滤器和事件分发器的作用,因此下面也重写了事件过滤函数和事件分发函数。
- 由于重写事件处理器函数比较常用,阅读时可以先查看事件处理,再查看事件过滤和事件分发。
事件过滤
事件过滤函数
- bool eventFilter(QObject *watched, QEvent *event);
- 返回值是bool类型,如果返回true,表示用户要处理这个事件,不向下分发事件。
示例
c
复制代码
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QEvent>
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = 0);
~Widget();
protected:
// 键盘按下事件
void keyPressEvent(QKeyEvent *event);
// 事件分发
bool event(QEvent* ev);
// 重写事件过滤器函数
bool eventFilter(QObject *watched, QEvent *event);
private:
Ui::Widget *ui;
};
#endif // WIDGET_H
c
复制代码
#include "widget.h"
#include "ui_widget.h"
#include <QMessageBox>
#include <QDebug>
#include <QMouseEvent>
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
// 安装事件过滤器
installEventFilter(this);
}
Widget::~Widget()
{
delete ui;
}
void Widget::keyPressEvent(QKeyEvent *event)
{
// 判断键盘按下事件
if(event->type() == QEvent::KeyPress){
QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event);
// 判断按下的是否为tab键
if(keyEvent->key() == Qt::Key_Tab){
QMessageBox::question(this, "提示", "事件处理函数中Tab键按下", QMessageBox::Yes);
}
}
}
bool Widget::event(QEvent *ev)
{
// 判断键盘按下事件
if(ev->type() == QEvent::KeyPress){
QKeyEvent* keyEvent = static_cast<QKeyEvent*>(ev);
// 判断按下的是否为tab键
if(keyEvent->key() == Qt::Key_Tab){
QMessageBox::question(this, "提示", "事件分发函数中Tab键按下", QMessageBox::Yes);
// 返回true, 表示自己处理这个事件,不向下分发了,这样对应的事件处理函数就接收不到该事件了
return true;
}
}
// 其他事件交给父类,默认处理
return QWidget::event(ev);
}
bool Widget::eventFilter(QObject *watched, QEvent *event)
{
// 判断是哪个窗口,这里是当前窗口
if(watched == this){
if(event->type() == QEvent::KeyPress){
QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event);
// 判断按下的是否为tab键
if(keyEvent->key() == Qt::Key_Tab){
QMessageBox::question(this, "提示", "Tab键按下", QMessageBox::Yes);
// 返回true, 表示自己处理这个事件,不向下分发了
// 这样不仅事件处理函数接收不到该事件,事件分发函数也接受不到该事件
return true;
}
}
}
// 其他事件交给系统处理
return QWidget::eventFilter(watched, event);
}
- 如果在事件过滤器中处理了事件并且不往下传递,那么在事件分发器和事件处理器都不会接收到相应的事件。
事件分发
事件分发函数
- bool event(QEvent* ev);
- 返回值是bool类型,如果返回true,表示用户要处理这个事件,不向下分发事件。
示例
- 下来通过一个例子演示下,在事件分发函数中,处理键盘的tab键按下事件,并且处理后不向下传递,这样对应的事件处理函数就接收不到该事件了。
- 头文件
c
复制代码
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QEvent>
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = 0);
~Widget();
protected:
// 键盘按下事件
void keyPressEvent(QKeyEvent *event);
// 事件分发
bool event(QEvent* ev);
private:
Ui::Widget *ui;
};
#endif // WIDGET_H
c
复制代码
#include "widget.h"
#include "ui_widget.h"
#include <QMessageBox>
#include <QDebug>
#include <QMouseEvent>
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
}
Widget::~Widget()
{
delete ui;
}
void Widget::keyPressEvent(QKeyEvent *event)
{
// 判断键盘按下事件
if(event->type() == QEvent::KeyPress){
QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event);
// 判断按下的是否为tab键
if(keyEvent->key() == Qt::Key_Tab){
QMessageBox::question(this, "提示", "事件处理函数中Tab键按下", QMessageBox::Yes);
}
}
}
bool Widget::event(QEvent *ev)
{
// 判断键盘按下事件
if(ev->type() == QEvent::KeyPress){
QKeyEvent* keyEvent = static_cast<QKeyEvent*>(ev);
// 判断按下的是否为tab键
if(keyEvent->key() == Qt::Key_Tab){
QMessageBox::question(this, "提示", "Tab键按下", QMessageBox::Yes);
// 返回true, 表示自己处理这个事件,不向下分发了,这样对应的事件处理函数就接收不到该事件了
return true;
}
}
// 其他事件交给父类,默认处理
return QWidget::event(ev);
}
- 演示
- 通过示例可以看到,虽然在事件处理函数中对tab键按下进行了处理,但由于在事件分发函数中,并未向下传递tab键按下事件,因此事件处理函数并未被触发。
事件处理
- 事件触发后,先经过事件过滤器进行过滤,再经过事件分发器,下发到具体的事件处理器中,默认事件处理器也会自动处理事件,如果想要自己处理,就可以重写事件处理函数。
- QObject类 是所有Qt类的父类,因此所有Qt类都可以继承QObject类的事件处理函数,QObject类的事件函数主要是定时器事件函数,由系统自动触发。
- QWidget类 是所有Qt窗口类的事件函数,因此所有Qt窗口类都可以继承QWidget类的事件处理函数,QWidget类的事件函数比较多,比如键盘按下事件,鼠标移动事件等,QWidget类的事件函数,主要由用户触发。
QObject类的事件处理函数
- virtual void timerEvent(QTimerEvent *event)
- virtual void closeEvent(QCloseEvent *event)
- virtual void hideEvent(QHideEvent *event)
- virtual void showEvent(QShowEvent *event)
- virtual void resizeEvent(QResizeEvent *event)
- virtual void keyPressEvent(QKeyEvent *event)
- virtual void keyReleaseEvent(QKeyEvent *event)
- virtual void mouseDoubleClickEvent(QMouseEvent *event)
- virtual void mouseMoveEvent(QMouseEvent *event)
- virtual void mousePressEvent(QMouseEvent *event)
- virtual void mouseReleaseEvent(QMouseEvent *event)
- virtual void paintEvent(QPaintEvent *event)
示例1:窗口事件处理
- 接下来我们通过一个例子来演示下对窗口关闭 和窗口重设大小的事件如何处理
- 创建一个类,继承于QWidget类
- 头文件
c
复制代码
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = 0);
~Widget();
protected:
// 重写窗口关闭事件函数
void closeEvent(QCloseEvent *event);
// 重写重绘窗口大小事件函数
void resizeEvent(QResizeEvent *event);
private:
Ui::Widget *ui;
};
#endif // WIDGET_H
c
复制代码
#include "widget.h"
#include "ui_widget.h"
#include <QMessageBox>
#include <QCloseEvent>
#include <QResizeEvent>
#include <QDebug>
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
}
Widget::~Widget()
{
delete ui;
}
void Widget::closeEvent(QCloseEvent *event)
{
int retStatus = QMessageBox::question(this, "提示", "确定关闭窗口吗?", QMessageBox::Yes | QMessageBox::No);
if(retStatus == QMessageBox::Yes){
// 处理该事件,也就是关闭窗口
event->accept();
}else{
// 忽略该事件,也就是不关闭窗口
event->ignore();
}
}
void Widget::resizeEvent(QResizeEvent *event)
{
qDebug()<<"old size : "<<event->oldSize();
qDebug()<<"new size : "<<event->size();
}
- 效果展示
示例2:鼠标事件处理
- 再实现一个对鼠标按下 、鼠标松开 和鼠标移动的事件处理流程。
- 头文件
c
复制代码
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = 0);
~Widget();
protected:
// 重写鼠标按下事件
void mousePressEvent(QMouseEvent* event);
// 重写鼠标松开事件
void mouseReleaseEvent(QMouseEvent* event);
// 重写鼠标移动事件
void mouseMoveEvent(QMouseEvent* event);
private:
Ui::Widget *ui;
};
#endif // WIDGET_H
c
复制代码
#include "widget.h"
#include "ui_widget.h"
#include <QMessageBox>
#include <QDebug>
#include <QMouseEvent>
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
// 设置鼠标被追踪
// 不设置该属性,鼠标按下移动时才能触发鼠标移动事件函数
// 设置该属性后,不管鼠标是否按下,鼠标移动事件函数都会被触发
// this->setMouseTracking(true);
ui->label->setText("鼠标正常");
}
Widget::~Widget()
{
delete ui;
}
void Widget::mousePressEvent(QMouseEvent *event)
{
if(event->button() == Qt::LeftButton){
ui->label->setText("鼠标左键按下");
}else if(event->button() == Qt::RightButton){
ui->label->setText("鼠标右键按下");
}else{
ui->label->setText("鼠标按下");
}
}
void Widget::mouseReleaseEvent(QMouseEvent *event)
{
ui->label->setText("鼠标松开");
}
void Widget::mouseMoveEvent(QMouseEvent *event)
{
QString msg;
msg = "鼠标移动(" + QString::number(event->x()) + "," + QString::number(event->y()) + ")";
ui->label->setText(msg);
}
- 效果展示
示例3:绘图事件处理
- 处理绘图事件算是很常用的一个功能,下面通过一个示例进行简单说明
- 头文件
c
复制代码
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QEvent>
#include <QPainter>
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = 0);
~Widget();
protected:
// 绘图事件
void paintEvent(QPaintEvent* e);
private:
Ui::Widget *ui;
};
#endif // WIDGET_H
c
复制代码
#include "widget.h"
#include "ui_widget.h"
#include <QMessageBox>
#include <QDebug>
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
}
Widget::~Widget()
{
delete ui;
}
void Widget::paintEvent(QPaintEvent *e)
{
// 实例化画家对象, this表示绘图设备是当前窗口
QPainter painter(this);
// 画线
painter.drawLine(QPoint(0, 0), QPoint(100, 100));
// 设置画笔(红色)
QPen pen(QColor(255, 0, 0));
// 设置画笔宽度
pen.setWidth(3);
// 设置画笔风格
pen.setStyle(Qt::DotLine);
painter.setPen(pen);
// 画矩形
painter.drawRect(QRect(0, 0, 100, 100));
// 设置画刷
QBrush brush(Qt::green);
// 设置画刷风格
brush.setStyle(Qt::Dense1Pattern);
painter.setBrush(brush);
// 画圆
painter.drawEllipse(QPoint(150, 50), 50, 50);
// 画文字
painter.drawText(QRect(0, 150, 150, 150), "HELLO");
// 画图片
painter.drawPixmap(100, 100, QPixmap(":/gray.jpg"));
}
- 演示
- 绘图事件比较特殊,默认是系统自动触发,但是还可以手动进行触发,那就是在函数中调用 update() 函数,调用之后也会自动触发绘事件,对应的绘图事件处理函数会被执行。比如可以在鼠标点击槽函数中调用 update() 函数,这里就不进行演示了。
示例4:定时器事件处理
- 定时器事件由系统触发,可以重写处理函数,在其中进行相应操作,这也是非常常用的一个功能。
- 头文件
c
复制代码
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QEvent>
#include <QTimerEvent>
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = 0);
~Widget();
protected:
// 定时器事件处理函数
void timerEvent(QTimerEvent *e);
private:
Ui::Widget *ui;
// 定时器id
int timerId;
};
#endif // WIDGET_H
c
复制代码
#include "widget.h"
#include "ui_widget.h"
#include <QMessageBox>
#include <QDebug>
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
// 启动定时器,定时事件为1秒
timerId = startTimer(1000);
}
Widget::~Widget()
{
// 停止定时器
killTimer(timerId);
delete ui;
}
void Widget::timerEvent(QTimerEvent *e)
{
qDebug()<<"定时器事件触发";
}