引言
个人认为,事件机制是Qt最难以理解且最为精妙的一部分。事件主要分为两种:
- 在与用户交互时发生。比如按下鼠标(mousePressEvent),敲击键盘(keyPressEvent)等。
- 系统自动发生,比如计时器事件(timerEvent)等。
在发生事件时(比如说上面说的按下鼠标),就会产生一个QEvent对象(这里是QMouseEvent,为QEvent的子类),这个QEvent对象会传给当前组件的event函数。如果当前组件没有安装事件过滤器(这个后面会提到),则会被event函数发放到相应的xxxEvent函数中(这里是mousePressEvent函数)。
需要区分的是:事件与信号并不相同。
比如:鼠标单击按钮,鼠标事件(QMouseEvent),而按钮本身发射clicked()信号。一般而言我们只需要关注单击信号,不用考虑鼠标事件。但是当我们要对该按钮做额外操作,不想通过信号处理,此时事件就是一个很好的选择。关闭事件(QCloseEvent)是一个常用的事件。
一,事件
处理事件有5种常用的方法:
- 重新实现部件的paintEvent()、mousePressEvent()等事件处理函数。这是最常用的一种方法,不过只能用来处理特定部件的特定事件(即需要新建类去实现)
- 重新实现notify()函数。这个函数的功能强大,提供了完全的控制,可以再事件过滤器得到事件之间就获得他们。但是,它一次只能处理一个事件。
- 向QApplication对象上安装事件过滤器。因为一个程序只有一个QApplication对象,实现的功能和notify()函数相同,优点是可以同时处理多个事件。
- 重新实现event()函数。QObject类的event()函数可以在事件达到默认事件处理函数之前获得该事件。
- 在对象上安装事件过滤器。使用事件过滤器可以再一个界面类中同时处理不同子部件的事件(在本类中实现)
实际编程中最常用的是方法(1),其次是方法(5)。方法2要继承QApplication类,方法3需要全局的事件过滤器,减缓事件的传递。
鼠标事件:
常用的鼠标事件:(本篇处理事件用的是方法一:重写鼠标事件)
- void mousePressEvent(QMouseEvent *event); //单击
- void mouseReleaseEvent(QMouseEvent *event); //释放
- void mouseDoubleClickEvent(QMouseEvent *event); //双击
- void mouseMoveEvent(QMouseEvent *event); //移动
- void wheelEvent(QWheelEvent *event); //滑轮
鼠标事件使用的时候,加头文件: #include <QMouseEvent>
重写事件框架:
1️⃣鼠标按下事件
void Widget::mousePressEvent(QMouseEvent *event) { // 如果是鼠标左键按下 if(event->button() == Qt::LeftButton){ ··· } // 如果是鼠标右键按下 else if(event->button() == Qt::RightButton){ ··· } }
2️⃣鼠标移动事件
void Widget::mouseMoveEvent(QMouseEvent *event) { // 这里必须使用buttons() if(event->buttons() & Qt::LeftButton){ //进行的按位与 ··· } }
默认情况下,触发事件需要点击一下,才能触发。可设置为自动触发:setMouseTracking(true);
3️⃣鼠标释放事件
void Widget::mouseReleaseEvent(QMouseEvent *event) { ··· }
4️⃣鼠标双击事件
void Widget::mouseDoubleClickEvent(QMouseEvent *event) { // 如果是鼠标左键按下 if(event->button() == Qt::LeftButton){ ··· } }
5️⃣滚轮事件
void Widget::wheelEvent(QWheelEvent *event) { // 当滚轮远离使用者时 if(event->delta() > 0){ ··· }else{//当滚轮向使用者方向旋转时 ··· } }
实例演示(在label控件中,移动鼠标获取实时位置,并显示在界面上)
- 创建mylabel类,基类设置为QLabel
这里用了类似自定义控件的方法,对Mylabel类进行封装。设置基类QLabel 是为了在ui界面中提升label控件(即将label控件和Mylabel关联,提升时候必须二者基类相同)
- 在mylabel.h中声明鼠标事件
#pragma once #include <qlabel.h> class mylabel : public QLabel { public: mylabel(QWidget* parent = 0); ~mylabel(); public: //鼠标移动事件 void mouseMoveEvent(QMouseEvent* event); //鼠标按下事件 void mousePressEvent(QMouseEvent* event); //鼠标释放事件 void mouseReleaseEvent(QMouseEvent* event); };
- 在mylabel.cpp中重写事件
#include "mylabel.h" #include"QMouseEvent" mylabel::mylabel(QWidget* parent) :QLabel(parent) { } mylabel::~mylabel() { } //鼠标移动显示坐标 void mylabel::mouseMoveEvent(QMouseEvent* event) { if (event->buttons() & Qt::LeftButton) //进行的按位与(只有左键点击移动才满足) { QString str = QString("Move:(X:%1,Y:%2)").arg(event->x()).arg(event->y()); this->setText(str); } } //鼠标按下显示“ok,mouse is press” void mylabel::mousePressEvent(QMouseEvent* event) { setText("Ok, mouse is press"); } //鼠标释放清除显示 void mylabel::mouseReleaseEvent(QMouseEvent* event) { setText(" "); }
- 在主函数(QTest.cpp)中声明mylabel的类对象(即声明一个mylabel类的label控件)
#include "qtest.h" QTest::QTest(QWidget *parent) : QWidget(parent) { ui.setupUi(this);
//声明mylabel类的控件 mylabel* label1 = new mylabel(this); label1->setGeometry(QRect(130, 100, 271, 161));
//设置边框 label1->setFrameShape(QFrame::Panel); }
另外,当调用setMouseTracking(true);时(即设置鼠标状态为自动触发),需要将鼠标移动事件的if语句去掉(因为不需要点击触发了)
修改maylabel.cpp事件:
#include "mylabel.h" #include"QMouseEvent" mylabel::mylabel(QWidget* parent) :QLabel(parent) { //设置鼠标状态(自动触发) setMouseTracking(true); } mylabel::~mylabel() { } //鼠标移动显示坐标 void mylabel::mouseMoveEvent(QMouseEvent* event) { QString str = QString("Move:(X:%1,Y:%2)").arg(event->x()).arg(event->y()); this->setText(str); } //鼠标按下显示“ok,mouse is press” void mylabel::mousePressEvent(QMouseEvent* event) { setText("Ok, mouse is press"); } //鼠标释放清除显示 void mylabel::mouseReleaseEvent(QMouseEvent* event) { setText(" "); }
效果展示: