Qt之事件系统
Qt中,事件作为一个对象,继承自QEvent类,常见的有键盘事件QKeyEvent、鼠标事件QMouseEvent和定时器事件QTimerEvent等。常见的处理事件的方法如下:
(1).重新实现部件的paintEvent、mousePressEvent()等事件处理函数,这是最常用的一种方法,不过只能用来出来特定部件的特定事件。
(2).重新实现event()函数,QObject类的event()函数可以在事件到达默认的事件处理函数之前获取该事件。
(3).在对象上安装事件过滤器,使用事件过滤器可以在一个界面类中同时处理不同子部件的不同事件。
测试案例如下:
#ifndef MYLINEEDIT_H #define MYLINEEDIT_H #include <QLineEdit> class MyLineEdit : public QLineEdit { Q_OBJECT public: explicit MyLineEdit(QWidget *parent = nullptr); protected: void keyPressEvent(QKeyEvent *event) override; }; #endif // MYLINEEDIT_H
#include "my_lineedit.h" #include <QKeyEvent> #include <QDebug> MyLineEdit::MyLineEdit(QWidget *parent) : QLineEdit(parent) { } void MyLineEdit::keyPressEvent(QKeyEvent *event) { qDebug() << "MyLineEdit键盘按下事件"; }
#ifndef MAINWINDOW_H #define MAINWINDOW_H #include <QtWidgets> class MyLineEdit; class MainWindow : public QMainWindow { Q_OBJECT public: MainWindow(QMainWindow *parent = nullptr); ~MainWindow(); protected: void keyPressEvent(QKeyEvent *event) override; private: MyLineEdit *m_pLineEdit; }; #endif // MAINWINDOW_H
#include "main_window.h" #include "my_lineedit.h" #include <QKeyEvent> #include <QDebug> MainWindow::MainWindow(QMainWindow *parent) : QMainWindow(parent),m_pLineEdit(new MyLineEdit(this)) { this->setFixedSize(600, 400); m_pLineEdit->move(100, 100); } MainWindow::~MainWindow() { } void MainWindow::keyPressEvent(QKeyEvent *event) { qDebug() << "MainWindow键盘按下事件"; }
这里自定义了一个MyLineEdit类,它继承自QLineEdit类,然后在MainWindow界面中添加一个MyLineEdit部件,这里既实现了MyLineEdit类的键盘按下事件处理函数,也实现了MainWindow类的键盘按键事件的处理函数,随意按下键盘的按键,只会打印"MyLineEdit键盘按下事件",说明这时只执行了MyLineEdit类中的keyPressEvent()函数。
下面在MyLineEdit类中的keyPressEvent()函数最后添加如下一行代码,让其忽略掉这个事件
event->ignore(); //忽略该事件
这时如果在按下按键,则会打印"MyLineEdit键盘按下事件"和"MainWindow键盘按下事件",但是这时却输入框中却无法输入任何字符,为了可以在输入框中正常输入字符,还需要添加下面一行代码:
void MyLineEdit::keyPressEvent(QKeyEvent *event) { qDebug() << "MyLineEdit键盘按下事件"; QLineEdit::keyPressEvent(event); //执行QLineEdit类的默认事件处理 event->ignore(); //忽略该事件 }
从上述例子可以看出,事件是先传递给指定窗口部件的,确切的说应该是先传递给获得焦点的窗口部件,但是如果该部件忽略掉该事件,那么这个事件就会传递给这个部件的父部件。重新实现事件处理函数时,一般要调用父类的相应事件处理函数来实现默认操作。
另外来看看event()函数和eventFilter()函数的使用:
在MyLineEdit类中重写event()函数
bool MyLineEdit::event(QEvent *event) { if (event->type() == QEvent::KeyPress) { qDebug() << "MyLineEdit的event()函数"; } return QLineEdit::event(event); }
在MainWindow类中重写eventFilter()函数
bool MainWindow::eventFilter(QObject *obj, QEvent *event) { if (obj == m_pLineEdit) { if (event->type() == QEvent::KeyPress) { qDebug() << "Widget的事件过滤器"; } } return QMainWindow::eventFilter(obj, event); }
打印结果如下:
可以看出,事件的传递顺序是这样的:先是事件过滤器,然后是焦点部件的event()函数,最后是焦点部件的事件处理函数;如果焦点部件忽略了该事件,那么会执行父部件的事件处理函数,如下图所示:注意,event()函数处理函数是在焦点部件内重新定义的,而事件过滤器却是在焦点部件的父部件中定义的
鼠标事件和滚轮事件
QMouseEvent类用来表示一个鼠标事件,在窗口部件中按下鼠标或者移动鼠标指针时,都会产生鼠标事件,利用QMouseEvent类可以获知鼠标的哪个键被按下了,鼠标指针的当前位置等信息。QWheelEvent类用来表示鼠标滚轮事件,主要用来获取滚轮移动的方向和距离。
下面的例子的实现效果是:可以在界面上按着鼠标左键来拖动窗口,双击鼠标左键来改变窗口标题名称,按着鼠标右键则改变鼠标指针的形状,释放鼠标使鼠标指针恢复原样,使用滚轮可以放大或者缩小编辑器中的内容。
protected: virtual void mousePressEvent(QMouseEvent *event) override; virtual void mouseReleaseEvent(QMouseEvent *event) override; virtual void mouseDoubleClickEvent(QMouseEvent *event) override; virtual void mouseMoveEvent(QMouseEvent *event) override; virtual void wheelEvent(QWheelEvent *event) override; private: QPoint m_offset;
//鼠标按下事件 void MainWindow::mousePressEvent(QMouseEvent *event) { if (event->button() == Qt::LeftButton) { QCursor cursor; cursor.setShape(Qt::ClosedHandCursor); QApplication::setOverrideCursor(cursor); //使鼠标指针暂时改变形状 m_offset = event->globalPos() - pos(); //获取指针位置和窗口位置的差值 } else if (event->button() == Qt::RightButton) { QCursor cursor; cursor.setShape(Qt::OpenHandCursor); QApplication::setOverrideCursor(cursor);//使鼠标指针暂时改变形状 } QMainWindow::mousePressEvent(event); } //鼠标释放事件 void MainWindow::mouseReleaseEvent(QMouseEvent *event) { QApplication::restoreOverrideCursor(); //恢复鼠标指针形状 QMainWindow::mouseReleaseEvent(event); } //鼠标双击事件 void MainWindow::mouseDoubleClickEvent(QMouseEvent *event) { if (event->button() == Qt::LeftButton) { this->setWindowTitle("123"); } } //鼠标移动事件 void MainWindow::mouseMoveEvent(QMouseEvent *event) { if (event->buttons() & Qt::LeftButton) { QPoint temp; temp = event->globalPos() - m_offset; this->move(temp); } QMainWindow::mouseMoveEvent(event); } //鼠标滚轮事件 void MainWindow::wheelEvent(QWheelEvent *event) { if (event->delta() > 0) { m_pTextEdit->zoomIn(); //放大文本 } else { m_pTextEdit->zoomOut(); //缩小文本 } }
键盘事件
QKeyEvent类用来描述一个键盘事件,当键盘按键被按下或者被释放时,键盘事件便会被发送给拥有键盘输入焦点的部件,QKeyEvent的key()函数可以获取具体的按键,对于Qt中给定的所有键,可以在帮助文档中通过Qt::Key关键字查看,另外需要注意的是回车键是Qt::KeyReturn;见爬满上的一些修饰键,比如Ctrl和Shift等,需要使用QKeyEvent的modifiers()函数来获取,可以在帮助文档中使用Qt::Keyboard-Modifier关键字来查看所有的修饰键。
protected: virtual void keyPressEvent(QKeyEvent *event) override; virtual void keyReleaseEvent(QKeyEvent *event) override;
void MainWindow::keyPressEvent(QKeyEvent *event) { if (event->modifiers() == Qt::ControlModifier) { if (event->key() == Qt::Key_M) { this->setWindowState(Qt::WindowMinimized); } } QMainWindow::keyPressEvent(event); } void MainWindow::keyReleaseEvent(QKeyEvent *event) { qDebug() << event->key(); QMainWindow::keyReleaseEvent(event); }
定时器事件
QTimerEvent类用来描述一个定时器事件,对于一个QObject的子类,只需要使用int Qobject::startTimer(int interval)函数就可以开启一个定时器,这个函数需要输入一个以毫秒为单位的整数作为参数来表明设定的时间,函数返回一个整型编号来代表这个定时器。当定时器溢出时可以在timerEvent()函数中进行需要的操作。
protected: virtual void timerEvent(QTimerEvent *event) override;
m_timer_id = startTimer(1000); m_timer_id1 = startTimer(1500); m_timer_id2 = startTimer(2200);
void MainWindow::timerEvent(QTimerEvent *event) { if (event->timerId() == m_timer_id) { qDebug() << "timer"; } else if (event->timerId() == m_timer_id1) { qDebug() << "timer1"; } else if (event->timerId() == m_timer_id2) { qDebug() << "timer2"; } QMainWindow::timerEvent(event); }
随机数的使用
qsrand(QTime(0,0,0).secsTo(QTime::currentTime())); m_timer_id = startTimer(5000);
void MainWindow::timerEvent(QTimerEvent *event) { if (event->timerId() == m_timer_id) { int rand = qrand() % 300; qDebug() << rand; //产生300以内的正整数 } QMainWindow::timerEvent(event); }
事件过滤器与事件的发送
Qt中提供了事件过滤器来实现在一个部件中监控其他多个部件的事件,事件过滤器与其他部件不同,它不是一个类,只是由两个函数组成的一种操作,用来完成一个部件对其他部件的事件的监视,这两个函数分别是installEventFilter()和eventFilter(),都是QObject类中的函数。
在构造函数中添加事件过滤器
m_pTextEdit->installEventFilter(this); m_pSpinBox->installEventFilter(this);
要对一个部件使用事件过滤器,那么就要先使用installEventFilter()函数为其安装事件过滤器,这个函数的参数表明了监视对象,这里就为textEdit部件和spin-Box部件安装了事件过滤器,其参数this表明要在本窗口中监视textEdit和spinBox的事件,这样就需要重新实现Widget类的eventFilter()函数,在其中截获并处理两个子部件的事件。
bool MainWindow::eventFilter(QObject *obj, QEvent *event) { if (obj == m_pTextEdit) { if (event->type() == QEvent::Wheel) { //将event强制转换为发生的事件的类型 QWheelEvent *wheelEvent = static_cast<QWheelEvent*>(event); if (wheelEvent->delta() > 0) { m_pTextEdit->zoomIn(); } else { m_pTextEdit->zoomOut(); } return true; } else { return false; } } else if (obj == m_pSpinBox) { if (event->type() == QEvent::KeyPress) { QKeyEvent *keyEvent = static_cast<QKeyEvent*>(event); if (keyEvent->key() == Qt::Key_Space) { m_pSpinBox->setValue(0); return true; } else { return false; } } } return QMainWindow::eventFilter(obj, event); }
如果要对一个特定的事件进行处理,而且不希望它在后面的传递过程中再被处理,那么就返回true,否则返回false。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了