事件(event)是由系统或者 Qt 本身在不同的时刻发出的。当用户按下鼠标、敲下键盘,或者是窗口需要重新绘制的时候,都会发出一个相应的事件
一些事件在对用户操作做出响应时发出,如键盘事件等;另一些事件则是由系统自动发出,如计时器事件
回顾
Qt 程序需要在main()函数创建一个QApplication对象,然后调用它的exec()函数,这个函数就是开始 Qt 的事件循环,在执行exec()函数之后,程序将进入事件循环来监听应用程序的事件。
当事件发生时,Qt 将创建一个事件对象。Qt 中所有事件类都继承于QEvent,在事件对象创建完毕后,Qt 将这个事件对象传递给QObject的event()函数。event()函数并不直接处理事件,而是按照事件对象的类型分派给特定的事件处理函数
event()函数主要用于事件的分发,所以如果希望在事件分发之前做一些操作,就可以重写这个event()函数
event()函数中实际是通过事件处理器来响应一个具体的事件。这相当于event()函数将具体事件的处理“委托”给具体的事件处理器。而这些事件处理器是 protected virtual 的,因此,我们重写了某一个事件处理器,即可让 Qt 调用我们自己实现的版本。
在程序将事件分发到事件分发器前,可以利用过滤器做拦截
QObject有一个eventFilter()函数,用于建立事件过滤器
virtual bool QObject::eventFilter ( QObject * watched, QEvent * event )
事件过滤器会检查接收到的事件。如果这个事件是我们感兴趣的类型,就进行我们自己的处理;如果不是,就继续转发。这个函数返回一个 bool 类型,如果不想让它继续转发,就返回 true,否则返回 false
安装过滤器需要调用QObject::installEventFilter()函数
void QObject::installEventFilter ( QObject * filterObj )
注意事项
1.事件过滤器和被安装过滤器的组件必须在同一线程,否则,过滤器将不起作用。另外,如果在安装过滤器之后,这两个组件到了不同的线程,那么,只有等到二者重新回到同一线程的时候过滤器才会有效
2.事件过滤器在目标对象接收到事件之前进行处理,如果我们将事件过滤掉,目标对象根本不会见到这个事件
前置工作
1.项目 => Add New =>C++ class
2.在mianWindow.ui文件当中创建一个Label控件 => 提升为
3.可以更改控件的格式 看的更明显
注意:在第二步当中可以发现:基类名称为 Q L a b e l QLabel QLabel,所以生成的myLabel.h和myLabel.cpp文件要改动:
//.h文件
#include
class myLabel : public QLabel//改为继承QLabel
//.cpp文件
myLabel::myLabel(QWidget *parent) : QLabel(parent) //父类对象从QWidget(parent) =》 QLabel(parent)
myLabel.h
//鼠标进入事件
void enterEvent(QEvent *event); //从父类继承的函数
//鼠标离开事件
void leaveEvent(QEvent *);
//鼠标按下
virtual void mousePressEvent(QMouseEvent *ev);
//鼠标释放
virtual void mouseReleaseEvent(QMouseEvent *ev);
//鼠标移动
virtual void mouseMoveEvent(QMouseEvent *ev);
//通过event事件分发器 拦截 鼠标按下事件
bool event(QEvent *e);
myLabel.cpp
//鼠标进入事件
void myLabel::enterEvent(QEvent *event)
{
qDebug() << "鼠标进入了";
}
//鼠标离开事件
void myLabel::leaveEvent(QEvent *)
{
qDebug() << "鼠标离开了";
}
//鼠标按下
void myLabel::mousePressEvent(QMouseEvent *ev)
{
//当鼠标左键按下 提示信息
if( ev->button() == Qt::LeftButton)
{
//arg:参数 global的含义:基于窗口的距离 普通的x,y:基于控件的距离
QString str = QString( "鼠标按下了 x = %1 y = %2 globalX = %3 globalY = %4 " ).arg(ev->x()).arg(ev->y()).arg(ev->globalX()).arg(ev->globalY());
qDebug() << str;
}
}
//鼠标释放
void myLabel::mouseReleaseEvent(QMouseEvent *ev)
{
if( ev->button() == Qt::LeftButton)
{
QString str = QString( "鼠标释放了 x = %1 y = %2 globalX = %3 globalY " \
"= %4 " ).arg(ev->x()).arg(ev->y()).arg(ev->globalX()).arg(ev->globalY());
qDebug() << str;
}
}
//鼠标移动
void myLabel::mouseMoveEvent(QMouseEvent *ev)
{
// 当鼠标左键按下 提示信息 buttons:返回按键类型 ev->buttons() & Qt::LeftButton:如果是左键才为真
if( ev->buttons() & Qt::LeftButton ) //移动是持续的过程!
{
QString str = QString( "鼠标移动了 x = %1 y = %2 globalX = %3 globalY " \
"= %4 " ).arg(ev->x()).arg(ev->y()).arg(ev->globalX()).arg(ev->globalY());
qDebug() << str;
}
}
bool myLabel::event(QEvent *e)
{
//如果是鼠标按下 ,在event事件分发中做拦截操作
if(e->type() == QEvent::MouseButtonPress)
{
QMouseEvent * ev = static_cast<QMouseEvent *>(e); //QEvent是QMouseEvent的父类
QString str = QString( "Event函数中::鼠标按下了 x = %1 y = %2 globalX = %3 globalY = %4 " ).arg(ev->x()).arg(ev->y()).arg(ev->globalX()).arg(ev->globalY());
qDebug() << str;
return true; //true代表用户自己处理这个事件,不向下分发 => 不会触发mousePressEvent函数
}
//其他事件 交给父类处理 =>默认处理
return QLabel::event(e);
}
注意:
1.QString的arg()函数可以自动替换掉QString中出现的占位符。其占位符以 % 开始,后面是占位符的位置,例如 %1,%2 这种
QString("[%1, %2]").arg(x).arg(y); =>x替换 %1,y替换 %2 QString为[x, y]
2.要点击鼠标之后才能在 m o u s e M o v e E v e n t mouseMoveEvent mouseMoveEvent函数中显示鼠标坐标值,原因如下:
如果想不点击鼠标也能在 m o u s e M o v e E v e n t mouseMoveEvent mouseMoveEvent函数中显示鼠标坐标值,在构造函数当中:
myLabel::myLabel(QWidget *parent) : QLabel(parent)
{
//设置鼠标追踪状态 默认为false
setMouseTracking(true);
}
3.$ev->button() $可以判断所有按键 Q t : : L e f t B u t t o n Qt::LeftButton Qt::LeftButton Q t : : R i g h t B u t t o n Qt::RightButton Qt::RightButton
e v − > b u t t o n s ( ) ev->buttons() ev−>buttons()判断组合按键 判断move时候的左右键 结合 & 操作符
创建方式1:
利用事件 void timerEvent ( QTimerEvent * ev)
,启动定时器: s t a r t T i m e r ( 1000 ) startTimer(1000) startTimer(1000) 单位是毫秒,$timerEvent 的返回值是定时器的唯一标示可以和 的返回值是定时器的唯一标示 可以和 的返回值是定时器的唯一标示可以和ev->timerId $做比较
创建方式2:
1.利用定时器类 QTimer => 创建定时器对象 QTimer * timer = new QTimer(this)
2.启动定时器 timer->start(毫秒)
3.每隔一定毫秒发送信号 timeout
,进行监听
4.暂停 : timer->stop
前置内容
1.先预先创建4个 L a b e l Label Label:
创建定时器方法1:需要重写定时器的事件
//widget.h
//重写定时器的事件
void timerEvent(QTimerEvent *);
int id1; //定时器1的唯一标示
int id2; //定时器2的唯一标示
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
//启动定时器 => 会返回定时器的标识符
id1 = startTimer(1000); //参数:时间间隔,单位是毫秒
id2 = startTimer(2000);
}
void MainWindow::timerEvent(QTimerEvent* ev)
{
if(ev->timerId() == id1)//label1 每隔1秒+1
{
static int num = 1;
ui->label_1->setText( QString::number(num++));
}
if(ev->timerId() == id2) //label2 每隔2秒 +1
{
static int num2 = 1;
ui->label_2->setText( QString::number(num2++));
}
}
第二种方式创建定时器
在ui界面当中多增加两个按钮用于停止和恢复定时器:
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
//定时器第二种方式
QTimer * timer = new QTimer(this);
//启动定时器
timer->start(500);
connect(timer,&QTimer::timeout,[=](){ //label3 每隔0.5秒+1
static int num = 1;
ui->label_3->setText(QString::number(num++));
});
//点击暂停按钮 实现停止定时器
connect(ui->stop_btn,&QPushButton::clicked,[=](){
timer->stop();
qDebug() <<"定时器已暂停" ;
});
//点击恢复按钮 重写启动定时器
connect(ui->start_btn,&QPushButton::clicked,[=](){
timer->start(500);
qDebug() <<"定时器已恢复" ;
});
}
定时器的事件过滤器
重写 bool eventFilter(QObject *, QEvent *);
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow)
{
//步骤1:给控件label_4 安装事件过滤器
ui->label_4->installEventFilter(this);
}
// 步骤2 重写 eventfilter事件
bool MainWindow::eventFilter(QObject * obj , QEvent * e)
{
if(obj == ui->label_4)//控件判断 因为可能很多控件都安装了事件过滤器
{
if(e->type() == QEvent::MouseButtonPress)//如果是label的鼠标按下
{
QMouseEvent * ev = (QMouseEvent*)e;
QString str = QString( "事件过滤器中::鼠标按下了 x = %1 y = %2 globalX = %3 globalY = %4 " ).arg(ev->x()).arg(ev->y()).arg(ev->globalX()).arg(ev->globalY());
qDebug() << str;
qDebug() <<"事件过滤器拦截成功";
return true; //true代表用户自己处理这个事件,不向下分发
}
}
//其他默认处理
return QWidget::eventFilter(obj,e);
}