目录
前言
Qt应用程序是源于事件驱动的。比如鼠标点击、释放、移动,这些被叫做鼠标事件;按下或者松开键盘上的一个按键,被称为键盘事件。
一般在 main()函数中创建一个 QApplication 对象,并调用它的 exec(函数,这个函数就是开始 Ot事件循环的函数。通常 Windows 操作系统会把从操作系统得到的消息如鼠标移动、按键等放入操作系统的消息队列中,Qt事件循环会不停地读取这些事件并依次处理。Q中的所有事件类都继承于类 QEvent。在事件对象创建完毕后,Qt将这个事件对象传递给 QObjec的 event()函数。event()函数并不直接处理事件,而是按照事件对象的类型将其分派给特定的事件处理函数(event handler)。
鼠标事件
我们通过一个示例来了解,鼠标事件时怎么操作的。首先创建一个基类为 QMainwindow的项目,添加一个新的类,类名为"MyLabel"基于QWidge继承于类QLabel(在 MyLabel.h中进行定义 )。在QLabel类中定义了很多事件的处理函数,这些函数都是protected virtual的,即可以在派生类中重新实现这些函数。我在MyLabel类中进行重定义。
cpp
#ifndef MYLABEL_H
#define MYLABEL_H
#include <QWidget>
#include<QLabel>
class MyLabel:public QLabel
{
Q_OBJECT
public:
explicit MyLabel(QWidget *parent = 0);
// 鼠标单击事件
void mousePressEvent(QMouseEvent *ev);
// 鼠标释放事件
void mouseReleaseEvent(QMouseEvent *ev);
// 鼠标移动事件
void mouseMoveEvent(QMouseEvent *ev);
//进入窗口区域
void enterEvent(QEvent *);
// 离开窗口区域
void leaveEvent(QEvent *);
};
#endif // MYLABEL_H
在MyLabel.cpp文件中对要进行冲洗定义的事件重定义(由于 QLabel是支持 HTML代码的,所以可以直接使用 HTML 代码来格式化文字。):
cpp
#include "mylabel.h"
#include<QMouseEvent>
#include<QDebug>
MyLabel::MyLabel(QWidget *parent) : QLabel(parent)
{
this->setMouseTracking(true);//追踪鼠标
}
void MyLabel::mousePressEvent(QMouseEvent *ev){
int x=ev->x();
int y=ev->y();
if(ev->button()==Qt::LeftButton){
qDebug()<<"按的是左键";
}else if(ev->button()==Qt::RightButton){
qDebug()<<"按的是右键";
}else if(ev->button()==Qt::MidButton){
qDebug()<<"按的是滚轮键";
}
QString text=QString("<center><h1>Mouse Press:(%1,%2)</h2></center>")
.arg(x).arg(y);
this->setText(text);
}
void MyLabel::mouseReleaseEvent(QMouseEvent *ev){
QString text=QString("<center><h1>Mouse Release:(%1,%2)</h2></center>")
.arg(ev->x()).arg(ev->y());
this->setText(text);
}
void MyLabel::mouseMoveEvent(QMouseEvent *ev){
QString text=QString("<center><h1>Mouse Move:(%1,%2)</h2></center>")
.arg(ev->x()).arg(ev->y());
this->setText(text);
}
void MyLabel::enterEvent(QEvent *e){
QString text=QString("<center><h1>Mouse Enter!</h2></center>");
this->setText(text);
qDebug()<<"你来了";
}
void MyLabel::leaveEvent(QEvent *e){
QString text=QString("<center><h1>Mouse Leave!</h2></center>");
this->setText(text);
qDebug()<<"你走了";
}
在main.cpp文件中:
cpp
#include "mainwindow.h"
#include"mylabel.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MyLabel w;
w.resize(400,200);
w.show();
return a.exec();
}
结果:
之所以要在单击鼠标之后才能在 mouseMoveEvent()函数中显示鼠标坐标值,是因为 QWidget中有一个mouseTracking属性,该属性用于设置是否追踪鼠标。只有鼠标被追踪时,mouseMoveEvent()事件才会被触发。如果mouseTracking是false(默认为false),组件在至少单击一次鼠标之后才能够被追踪,也就是至少需要一次单击才能够触发mouseMoveEvent()事件。如果将mouseTracking 设置为 true,则 mouseMoveEvent()事件可以直接被触发。
cpplabel->setMouseTracking(true);
- 因此需要在 main()函数中添加如上代码。如果不添加以上代码,刚刚运行程序是不会显示任何东西的,要单击鼠标一次。
事件函数
创建事件对象之后,Qt不会直接调用这个事件所对应的函数,而是通过event()函数根据事件的不同将之分发给不同的事件处理函数。event()函数主要用来做事件的分发而不是直接处理事件。如果需要在事件分发之前进行一些操作,可重写这个event()函数。我们有一个继承QLabel的MyLabelWithEvent的一个简单的例子来体现event分发事件的情况。在MyLabelWithEvent.h文件中:
cpp
#ifndef MYLABELWITHEVENT_H
#define MYLABELWITHEVENT_H
#include <QApplication>
#include <QLabel>
#include <QKeyEvent>
#include <QEvent>
class MyLabelWithEvent : public QLabel {
protected:
bool event(QEvent *e);
void keyPressEvent(QKeyEvent * event);
};
#endif // MYLABELWITHEVENT_H
在 MyLabelWithEvent.cpp文件中:
cpp
#include "mylabelwithevent.h"
void MyLabelWithEvent::keyPressEvent(QKeyEvent *event){
this->setText(QString("<center><h1>press: %1</h1></center>")
.arg(QString::number(event->key())));
}
bool MyLabelWithEvent::event(QEvent *e){
if (e->type() == QEvent::KeyPress) {
QKeyEvent * keyEvent = static_cast<QKeyEvent*>(e);
if(keyEvent->key()==Qt::Key_Tab){
this->setText(QString("<center><h1>You press Tab</h1></center>"));
return true;
}
}
return QWidget::event(e);
}
在main.cpp文件中:
cpp
#include "mainwindow.h"
#include"mylabelwithevent.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MyLabelWithEvent w;
w.setWindowTitle("MyLableWithEvent");
w.resize(400,200);
w.show();
return a.exec();
}
cppreturn QWidget::event(e).
- 是因为没有被分发给特殊情况处理然后还是分发给原先的keyPressEvent(QKeyEvent *event) 函数处理。
事件的接受和忽略
在Qt中,所有控件都继承于类QWidget,形成一种层次叠加的结构。在自定义控件的时候,派生类会继承基类的事件,但一般处理事件后,不会将事件继续传递给基类处理。以把 Qt的事件传递看成链式:如果派生类没有处理这个事件,就会继续向基类传递。事实上,Qt的事件对象都有accept()函数和 ignore()函数。正如它们的名字,前者用来告诉 Qt 事件处理函数"接收"了这个事件,不要再传递;后者则告诉 Qt事件处理函数"忽略"了这个事件,需要继续传递,寻找另外的接受者。在事件处理函数中,可使用isAccepted()函数来查询这个事件是不是已经被接收了.
我们用一个小例子来演示事件的接受和忽略的应用:当我们想要关闭一个窗口的时候,如果我们这是弹出一个可交互的对话框,利用其返回的值来决定是否对关闭这个事件的接受还是忽略:
MainWindow.h:
cpp
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
bool To_Close();
void closeEvent(QCloseEvent * event);
private:
Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H
MainWindow.cpp:
cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include<QMessageBox>
#include<QCloseEvent>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
}
MainWindow::~MainWindow()
{
delete ui;
}
bool MainWindow::To_Close(){
QMessageBox::Button res = QMessageBox::question(this,"提示","是否退出",QMessageBox::Yes|QMessageBox::No,QMessageBox::NoButton);
if(res == QMessageBox::Yes){
return true;
}else{
return false;
}
}
void MainWindow::closeEvent(QCloseEvent * event){
if(To_Close()){
event->accept();
}else{
event->ignore();
}
}
事件过滤器
编程时可能需要拦截发送到其他对象的事件,如当打开一个对话框时,不希望其他窗体能接收到鼠标的单击事件。
Qt提供了一种事件过滤的机制。Qt在接收到消息并创建 QEvent事件之后,会调用 event()函数进行分发,而event()函数继承自 QObject类,OObject类中有一个 eventFilter(函数,用于创建事件过滤器。
cpp
virtual bool QObject::eventFilter(QObject * watched, QEvent* event);
所谓事件过滤器,可以理解成一种过滤代码。
EventFilter.h:
cpp
#ifndef EVENTFILTER_H
#define EVENTFILTER_H
#include <QMainWindow>
#include"QTextEdit"
QT_BEGIN_NAMESPACE
namespace Ui { class eventFilter; }
QT_END_NAMESPACE
class EventFilter : public QMainWindow
{
Q_OBJECT
public:
EventFilter(QWidget *parent = nullptr);
~EventFilter();
protected:
bool eventFilter(QObject *obj, QEvent *event);// 事件过滤器
private:
Ui::eventFilter *ui;
QTextEdit * te;
};
#endif // EVENTFILTER_H
EventFilter.cpp:
cpp
#include "eventfilter.h"
#include "ui_eventfilter.h"
#include<QTextEdit>
#include<QDebug>
#include<QKeyEvent>
EventFilter::EventFilter(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::eventFilter)
{
te= new QTextEdit;// 添加一个文本框
setCentralWidget(te);
te->installEventFilter(this);//文本框安装事件过滤器
}
EventFilter::~EventFilter()
{
delete ui;
}
bool EventFilter::eventFilter(QObject *obj, QEvent *event) {
if(obj== te){
// 判断是否为文本框对象
if (event->type() == QEvent::KeyPress){
QKeyEvent * k=(QKeyEvent*)event;
qDebug()<<"key press"<<k->key();
return true;// 注释本行,程序继续运行,文本框有显示
// 不注释,返回 true,不再继续转发,键盘事件被忽略,文本框无显示
return QMainWindow::eventFilter(obj, event);
}
return false;
}
return QMainWindow::eventFilter(obj, event);
}
- 返回true时,代表向文本框输入的事件被过滤出来并且过滤到写入文本框的动作时间上。
- return QMainWindow::eventFilter(obj, event);或者return false;则表示继续过滤但不会写入文本框。
事件在实际开发中的应用不仅如此,在日后的学习中会更加对Qt进行了解的