18黑马QT笔记之事件(包括事件上下)
1 事件:
事件(event)是由系统或者 Qt 本身在不同的时刻发出的。当用户按下鼠标、敲下键盘,或者是窗口需要重新绘制的时候,都会发出一个相应的事件。一些事件在对用户操作做出响应时发出,如键盘事件等;另一些事件则是由系统自动发出,如计时器事件。
在前面我们也曾经简单提到,Qt 程序需要在main()函数创建一个QApplication对象,然后调用它的exec()函数。这个函数就是开始 Qt 的事件循环。在执行exec()函数之后,程序将进入事件循环来监听应用程序的事件。当事件发生时,Qt 将创建一个事件对象。Qt 中所有事件类都继承于QEvent。在事件对象创建完毕后,Qt 将这个事件对象传递给QObject的event()函数。event()函数并不直接处理事件,而是按照事件对象的类型分派给特定的事件处理函数(event handler),关于这一点,会在后边详细说明。
即上面的意思是,根据主函数的代码,主窗口显示后,exec()被调用代表进入事件循环,等待用户操作。 当用户触发某一个事件,Qt会创建一个具体事件的对象,产生该对象的具体类继承于基类QEvent。然后对象传递给QObject的event()函数,event()函数并不直接处理事件,而是按照事件对象的类型分派给特定的事件处理函数。
在所有组件的父类QWidget中,定义了很多事件处理函数,这些函数都是 protected virtual 的,也就是说,我们可以在子类中重新实现这些函数,进而当事件触发时,它的处理结果可以按照我们想要的方式实现。
1)keyPressEvent(); //键盘按下 (标签内重写按下键盘无反应因为标签用于显示不能再接收键盘修改)
2)keyReleaseEvent(); //键盘/释放 键盘事件处理函数一般在窗口中重写 即上面的也是
3)mouseDoubleClickEvent(); //鼠标双击 鼠标事件重写函数一般在自定义控件中重写
4)mouseMoveEvent(); //鼠标移动
5)mousePressEvent(); //鼠标按下
6)mouseReleaseEvent() 。 //鼠标释放
7)void timerEvent(); //计时器重写事件处理函数 一般也在窗口显示
看图理解上面的内容:
下面我们例举一个例子来重写这些事件处理函数:
例子1:用一个标签,重写它的鼠标按下,释放、移动,进入、离开五个事件处理函数。
1)首先,我们创建完带ui的项目后,由于我们要重写标签的事件处理函数,所以我们需要再添加一个自定义控件类MyLabel。
创建自定义控件方法:右击项目添加新文件,继承QWidget先,然后将构造函数和主函数的父类改成继承于QLabel,最后在ui界面右击点击"提升为"即可,这样你选择提升的控件就是你自己定义的新控件了。自定义控件的创建基本都是这样。注意你的具体控件名别与自定义类的名字重名,否则出错,即ui右上角的对象与类。
2)代码(在自定义控件重写这五个事件处理函数):
这里只给出自定义控件的头文件和实现文件:
1)头文件.h:
#ifndef MYLABEL_H
#define MYLABEL_H
#include <QWidget>
#include<QLabel>
class MyLabel : public QLabel
{
Q_OBJECT
public:
explicit MyLabel(QWidget *parent = 0);
//重写自定义控件的数据处理函数 与重写event()一样 都是在类中写
//注意:在虚函数按fn+f1查看帮助文档是没有内容的 必须到基类才有 例如QWidget
void mouseMoveEvent(QMouseEvent *ev);
void mousePressEvent(QMouseEvent *ev);
void mouseReleaseEvent(QMouseEvent *ev);
void enterEvent(QEvent *event);
void leaveEvent(QEvent *event);
//这里重写键盘按下事件处理函数 没有反应以后需要注意
signals:
public slots:
};
#endif // MYLABEL_H
2)实现文件.cpp(包含文档内容和视频内容):
#include "mylabel.h"
#include<QMouseEvent>
#include<QDebug>
MyLabel::MyLabel(QWidget *parent) : QLabel(parent)
{
//设置追踪鼠标 即一开始不用按下就可以追踪显示 这句话写在哪里区别不大
this->setMouseTracking(true);
}
//重写事件处理函数之鼠标按下
void MyLabel::mousePressEvent(QMouseEvent *event)
{
/*文档内容
this->setText(QString("<center><h1>Press:(%1, %2)</h1></center>").arg(QString::number(event->x()),
QString::number(event->y())));
*/
//视频内容
//获取鼠标按下时 触发的对象的坐标
int i = event->x();
int j = event->y();
/* QString的使用方法:用惯这种形式(上面不是很熟悉 上面的arg直接使用两个参数)
* QString str = QString("abc %1 ^_^ %2").arg("123").arg("mike");
* str = abc 123 ^_^ mike
*/
//如果是左键按下
if(event->button() == Qt::LeftButton)
{
qDebug() << "left";
}
else if(event->button() == Qt::RightButton)
{
qDebug() << "right";
}
else if(event->button() == Qt::MidButton)
{
qDebug() << "mid";
}
QString text = QString("<center><h1>Mouse Press: (%1, %2)</h1></center>") .arg(i).arg(j);
this->setText(text);
}
//重写事件处理函数之鼠标释放
void MyLabel::mouseReleaseEvent(QMouseEvent *event)
{
/*文档内容
QString msg;
//使用Qt的sprintf拼接字符串 上面使用的是QString(QString("%1,%2")).arg(1).arg(2)的形式
msg.sprintf("<center><h1>Release: (%d, %d)</h1></center>",event->x(), event->y());
this->setText(msg);
*/
//视频内容
QString text = QString("<center><h1>Mouse Release: (%1, %2)</h1></center>").arg(event->x()).arg(event->y());
this->setText(text);
}
//重写事件处理函数之鼠标移动
void MyLabel::mouseMoveEvent(QMouseEvent *event)
{
/*文档内容
//this代表MyLabel控件了 不是窗口
this->setText(QString("<center><h1>Move: (%1, %2)</h1></center>").arg(QString::number(event->x()),
QString::number(event->y())));
*/
//视频内容
QString text = QString("<center><h1>Mouse move: (%1, %2)</h1></center>").arg(event->x()).arg(event->y());
//this->setText(text);
}
//重写鼠标进入标签事件函数
void MyLabel::enterEvent(QEvent *event)
{
//注:进入事件会被移动事件覆盖 所以想要显示需要注释移动的显示先
QString text = QString("<center><h1>Mouse Enter</h1></center>");
this->setText(text);
}
//重写鼠标离开标签事件函数
void MyLabel::leaveEvent(QEvent *event)
{
QString text = QString("<center><h1>Mouse Leave</h1></center>");
this->setText(text);
}
为什么要点击鼠标之后才能在mouseMoveEvent()函数中显示鼠标坐标值?
这是因为QWidget中有一个mouseTracking属性,该属性用于设置是否追踪鼠标。只有鼠标被追踪时,mouseMoveEvent()才会发出。如果mouseTracking是 false,组件在至少一次鼠标点击之后,才能够被追踪,也就是能够发出mouseMoveEvent()事件。如果mouseTracking为 true,则mouseMoveEvent()直接可以被发出。所以我们在MyLabel构造函数写下this->setMouseTracking(true);即可,位置没啥要求,写在主函数也行,但this需要改改。
例子2(在窗口中重写):
1)重写按键按下处理函数和定时器事件处理函数的头文件(窗口.h):
#ifndef MYWIDGET_H
#define MYWIDGET_H
#include <QWidget>
#include"mylabel.h"
#include<QTimerEvent>
namespace Ui {
class MyWidget;
}
class MyWidget : public QWidget
{
Q_OBJECT
public:
explicit MyWidget(QWidget *parent = 0);
~MyWidget();
protected:
//重写键盘按下事件处理函数 --需要在窗口写 以后就可以避免错误
void keyPressEvent(QKeyEvent *ev);
//重写计时器事件处理函数 --注意 我在标签显示秒数 所以第一个标签所写的处理函数的显示会被覆盖 和进入被移动覆盖一样
void timerEvent(QTimerEvent *event);
private:
Ui::MyWidget *ui;
int timerId1; //定时器1、2的序号 相当于文件描述符
int timerId2;
};
#endif // MYWIDGET_H
2)实现.cpp文件:
#include "mywidget.h"
#include "ui_mywidget.h"
#include<QDebug>
#include<QKeyEvent>
#include<QTimerEvent>
MyWidget::MyWidget(QWidget *parent) :
QWidget(parent),
ui(new Ui::MyWidget)
{
ui->setupUi(this);
//构造窗口时就让它开启定时器 开启内部就会自动计数 按规定时间触发事件 单位为毫秒 返回值为文件描述符(认为他是)
timerId1=this->startTimer(1000);
timerId2=this->startTimer(1000);
}
MyWidget::~MyWidget()
{
delete ui;
}
//重写按键按下事件处理函数 --写在自定义标签不行 具体原因我也不知道 知道不行即可
void MyWidget::keyPressEvent(QKeyEvent *ev)
{
qDebug()<<(char)ev->key(); //加不加char都只能显示大写或其ASCII码值
if(ev->key()==Qt::Key_A)
{
qDebug()<<Qt::Key_A;
}
}
//重写定时器事件处理函数
void MyWidget::timerEvent(QTimerEvent *event)
{
//如果是定时器1 则让它在标签1每一秒显示一次 一共显示五次
if(event->timerId()==timerId1)
{
//注意是static 否则每次都是显示0 这里的sec与下面的不一样 他们会开辟不同的内存
static int sec=0;
ui->MyLabel1->setText(QString("<center><h1>Timer:%1</h1></center>").arg(sec++));
//显示五次后 停止定时器 利用文件描述符
if(sec==5)
{
killTimer(timerId1);
}
}
//如果是定时器2 让它一直显示
if(event->timerId()==timerId2)
{
static int sec=0;
ui->MyLabel2->setText(QString("<center><h1>Timer:%1</h1></center>").arg(sec++));
}
}