• QT事件及处理


    目录

    事件介绍

     事件过滤器

    事件处理器

    介绍

     事件处理函数

    鼠标单击事件

    鼠标释放事件

    其他

     重写事件处理函数

    自定义按钮


    事件介绍

    当事件产生被发送到对应的窗口之后,窗口并不会直接处理这个事件,而是对这些事件进行细分,然后根据事件的类型再次进行分发,对应的事件处理器函数得到这个分发的事件之后就开始处理这个事件。

    关于窗口事件的分发,对应一个事件分发器,叫做event

    [override virtual protected] bool QWidget::event(QEvent *event);

    这个类中常用的一些API函数:

    比如说 :

    void QEvent::accept();该函数的作用是让窗口接受传递过来的事件,事件不会向上层窗口(父窗口)传递

    void QEvent::ignore():该函数的作用是让窗口忽略传递过来的事件,事件被传递给父窗口(向上传递)

    QEvent::Type QEvent::type() const;得到传递的窗口的事件的类型,返回值是一个枚举类型

     不需要人为干预的情况下,事件分发器会自主的完成相关事件的分发。

    以下是这个函数的部分源码展示:

    1. bool QWidget::event(QEvent *ev)
    2. {
    3. switch(ev->type())
    4. {
    5. // 鼠标移动
    6. case QEvent::MouseMove:
    7. mouseMoveEvent((QMouseEvent*)event);
    8. break;
    9. // 鼠标按下
    10. case QEvent::MouseButtonPress:
    11. mousePressEvent((QMouseEvent*)event);
    12. break;
    13. // 鼠标释放
    14. case QEvent::MouseButtonRelease:
    15. mouseReleaseEvent((QMouseEvent*)event);
    16. break;
    17. // 鼠标双击
    18. case QEvent::MouseButtonDblClick:
    19. mouseDoubleClickEvent((QMouseEvent*)event);
    20. break;
    21. // 键盘按键被按下事件
    22. case QEvent::KeyPress:
    23. break;
    24. ...
    25. ...
    26. ...
    27. default:
    28. break;
    29. }
    30. }

     我们不想让某些触发的事件进入到当前窗口中,可以在事件分发器中进行拦截。

    事件分发器函数返回值:

    • 如果传入的事件已被识别并且处理,则需要返回 true,否则返回 false。如果返回值是 true,那么 Qt 会认为这个事件已经处理完毕,不会再将这个事件发送给其它对象,而是会继续处理事件队列中的下一事件。
    • 在event()函数中,调用事件对象的 accept() 和 ignore() 函数是没有作用的,不会影响到事件的传播。

    过滤某个事件,只需要在判断出这个事件之后直接返回 true,例如:在一个自定义的Label控件中拦截鼠标按下事件过滤鼠标按下

    1. //事件分发器
    2. bool MyLabel::event(QEvent *e)
    3. {
    4. if (e->type() == QEvent::MouseButtonPress)
    5. {
    6. qDebug() << "event 拦截鼠标按下事件";
    7. return true;
    8. }
    9. return QLabel::event(e);
    10. }

     事件过滤器

    除了使用事件分发器来过滤Qt窗口中产生的事件,还可以通过事件过滤器过滤相关的事件。当Qt的事件通过应用程序对象发送给相关窗口之后,窗口接收到数据之前这个期间可对事件进行过滤,过滤掉的事件就不能被继续处理了。QObject有一个eventFilter()函数,用于建立事件过滤器。

    函数原型:

    [virtual] bool QObject::eventFilter(QObject *watched, QEvent *event);

    参数解释:

    • watched:要过滤的事件的所有者对象
    • event:要过滤的具体的事件
    • 返回值:如果想过滤掉这个事件,停止它被进一步处理,返回true,否则返回 false

    基本过程:

    1. 给要被过滤事件的类对象安装事件过滤器:void QObject::installEventFilter(QObject *filterObj);
    2. 在要进行事件过滤的类中(filterObj 参数对应的类)重写从QObject类继承的虚函数eventFilter();

    举个例子:在一个窗口中有一个多行文本输入框QTextEdit,需要让我们屏蔽掉键盘上的回车键

    方法:

    • 自定义一个新的类让其继承QTextEdit,在这个子类中重写键盘事件keyPressEvent,在这个函数里边屏蔽掉回车键
    • 自定义一个新的类让其继承QTextEdit,在这个子类中重写事件分发器event,在这个函数里边屏蔽掉回车键(上面介绍了)
    • 给QTextEdit安装事件过滤器,基于QTextEdit的父窗口对这个控件的事件进行过滤

    用第三种方法,举例子:

    1. #include "mainwindow.h"
    2. #include "ui_mainwindow.h"
    3. #include
    4. #include
    5. #include
    6. #include
    7. MainWindow::MainWindow(QWidget *parent)
    8. : QMainWindow(parent)
    9. , ui(new Ui::MainWindow)
    10. {
    11. ui->setupUi(this);
    12. ui->textEdit->installEventFilter(this);//给类对象安装事件过滤器
    13. }
    14. MainWindow::~MainWindow()
    15. {
    16. delete ui;
    17. }
    18. bool MainWindow::eventFilter(QObject *watched, QEvent *event)//重写从QObject类继承的虚函数eventFilter()
    19. {
    20. // 判断对象和事件
    21. if(watched == ui->textEdit && event->type() == QEvent::KeyPress)
    22. {
    23. QKeyEvent* keyEv = (QKeyEvent*)event;
    24. if(keyEv->key() == Qt::Key_Enter || // 小键盘确认
    25. keyEv->key() == Qt::Key_Return) // 大键盘回车
    26. {
    27. QMessageBox::information(this,"information","回车键被按下了");
    28. return true;
    29. }
    30. }
    31. return false;
    32. }

    多层嵌套窗口中如果想要过滤掉QTextEdit的某些事件,可以交给其中一个窗口处理,也可以给QTextEdit同时安装多个过滤器:

    ui->textEdit->installEventFilter(窗口A对象);
    ui->textEdit->installEventFilter(窗口B对象);
    ui->textEdit->installEventFilter(窗口C对象);

    如果一个对象存在多个事件过滤器,那么,最后一个安装的会第一个执行,也就是说窗口C先进行事件过滤,然后窗口B,最后窗口A。

    事件过滤器和被安装过滤器的组件必须在同一线程,否则,过滤器将不起作用。另外,如果在安装过滤器之后,这两个组件到了不同的线程,那么,只有等到二者重新回到同一线程的时候过滤器才会有效。

    事件处理器

    介绍

    窗口事件产生之后,事件会经过:事件派发 -> 事件过滤->事件分发->事件处理几个阶段。Qt窗口中对于产生的一系列事件都有默认的处理动作,如果我们有特殊需求就需要在合适的阶段重写事件的处理动作。

    事件在QT中的流程:

    1. 当事件产生之后,Qt使用用应用程序对象调用notify()函数将事件发送到指定的窗口:
    2. 事件在发送过程中可以通过事件过滤器进行过滤,默认不对任何产生的事件进行过滤
    3. 当事件发送到指定窗口之后,窗口的事件分发器会对收到的事件进行分类:
    4. 事件分发器会将分类之后的事件(鼠标事件、键盘事件、绘图事件。。。)分发给对应的事件处理器函数进行处理,每个事件处理器函数都有默认的处理动作(我们也可以重写这些事件处理器函数)

    比如:可对这些事件处理函数进行重写

    // 鼠标按下
    [virtual protected] void QWidget::mousePressEvent(QMouseEvent *event);
    // 鼠标释放
    [virtual protected] void QWidget::mouseReleaseEvent(QMouseEvent *event);
    // 鼠标移动
    [virtual protected] void QWidget::mouseMoveEvent(QMouseEvent *event);

     事件处理函数

    鼠标单击事件

    当鼠标左键、鼠标右键、鼠标中键被按下,该函数被自动调用,通过参数可以得到当前按下的是哪个鼠标键

    [virtual protected] void QWidget::mousePressEvent(QMouseEvent *event);

    1. Qt::LeftButton 左
    2. Qt::RightButton 右
    3. Qt::MidButton 中
    4. void Widget::mousePressEvent(QMouseEvent *event){
    5. if(event->button()==Qt::LeftButton) {
    6. qDebug()<<"左键按下";
    7. }

    鼠标释放事件

    [virtual protected] void QWidget::mousePressEvent(QMouseEvent *event);

    当鼠标左键、鼠标右键、鼠标中键被释放,该函数被自动调用,通过参数可以得到当前释放的是哪个鼠标键

    1. void Widget::mouseReleaseEvent(QMouseEvent *event){
    2. if(event->button()==Qt::LeftButton) {
    3. qDebug()<<"左键释放";
    4. }
    5. }

    其他

    鼠标双击事件:[virtual protected] void QWidget::mouseDoubleClickEvent(QMouseEvent *event);

    鼠标进入事件:[virtual protected] void QWidget::enterEvent(QEvent *event);

    键盘按下事件:[virtual protected] void QWidget::keyPressEvent(QKeyEvent *event);

    窗口重绘事件:[virtual protected] void QWidget::paintEvent(QPaintEvent *event);

    窗口关闭事件:[virtual protected] void QWidget::closeEvent(QCloseEvent *event);

     重写事件处理函数

    事件处理器函数都是虚函数,因此我们就可以添加一个标准窗口类的派生类,这样不仅使子类继承了父类的属性,还可以在这个子类中重写父类的虚函数。

    代码:

    1. #include "mainwindow.h"
    2. #include "ui_mainwindow.h"
    3. #include
    4. #include
    5. MainWindow::MainWindow(QWidget *parent)
    6. : QMainWindow(parent)
    7. , ui(new Ui::MainWindow)
    8. {
    9. ui->setupUi(this);
    10. }
    11. MainWindow::~MainWindow()
    12. {
    13. delete ui;
    14. }
    15. void MainWindow::closeEvent(QCloseEvent *ev)
    16. {
    17. QMessageBox::Button btn = QMessageBox::question(this, "关闭窗口", "您确定要关闭窗口吗?");
    18. if(btn == QMessageBox::Yes)
    19. {
    20. // 接收并处理这个事件
    21. ev->accept();
    22. }
    23. else
    24. {
    25. // 忽略这个事件
    26. ev->ignore();
    27. }
    28. }
    29. void MainWindow::resizeEvent(QResizeEvent *ev)
    30. {
    31. qDebug() << "oldSize: " << ev->oldSize()
    32. << "currentSize: " << ev->size();
    33. }

    自定义按钮

    与上面的思路是一样的。新添加的按钮类可以让它继承 QPushButton,也可以让它继承其他的窗口类(代价是当鼠标点击事件触发之后需要自己发射自定义信号),这里让添加的子类从QWidget类派生。

    1. #ifndef MYBUTTON_H
    2. #define MYBUTTON_H
    3. #include
    4. class MyButton : public QWidget
    5. {
    6. Q_OBJECT
    7. public:
    8. explicit MyButton(QWidget *parent = nullptr);
    9. void setImage(QString normal, QString hover, QString pressed);
    10. protected:
    11. void mousePressEvent(QMouseEvent* ev);
    12. void mouseReleaseEvent(QMouseEvent* ev);
    13. void enterEvent(QEvent* ev);
    14. void leaveEvent(QEvent* ev);
    15. void paintEvent(QPaintEvent* ev);
    16. signals:
    17. void clicked();
    18. private:
    19. QPixmap m_normal;
    20. QPixmap m_press;
    21. QPixmap m_hover;
    22. QPixmap m_current;
    23. };
    24. #endif // MYBUTTON_H
    1. #include "mybutton.h"
    2. #include
    3. MyButton::MyButton(QWidget *parent) : QWidget(parent)
    4. {
    5. }
    6. void MyButton::setImage(QString normal, QString hover, QString pressed)
    7. {
    8. // 加载图片
    9. m_normal.load(normal);
    10. m_hover.load(hover);
    11. m_press.load(pressed);
    12. m_current = m_normal;
    13. // 设置按钮和图片大小一致
    14. setFixedSize(m_normal.size());
    15. }
    16. void MyButton::mousePressEvent(QMouseEvent *ev)
    17. {
    18. // 鼠标被按下, 发射这个自定义信号
    19. emit clicked();
    20. m_current = m_press;
    21. update();
    22. }
    23. void MyButton::mouseReleaseEvent(QMouseEvent *ev)
    24. {
    25. m_current = m_normal;
    26. update();
    27. }
    28. void MyButton::enterEvent(QEvent *ev)
    29. {
    30. m_current = m_hover;
    31. update();
    32. }
    33. void MyButton::leaveEvent(QEvent *ev)
    34. {
    35. m_current = m_normal;
    36. update();
    37. }
    38. void MyButton::paintEvent(QPaintEvent *ev)
    39. {
    40. QPainter p(this);
    41. p.drawPixmap(rect(), m_current);
    42. }

    对QWidget窗口进行提升为MyButton类型(该类型自己写了clicked信号)

    1. MainWindow::MainWindow(QWidget *parent)
    2. : QMainWindow(parent)
    3. , ui(new Ui::MainWindow)
    4. {
    5. ui->setupUi(this);
    6. // 给自定义按钮设置图片
    7. ui->button->setImage(":/ghost-1.png", ":/ghost-2.png", ":/ghost-3.png");
    8. // 处理自定义按钮的鼠标点击事件
    9. connect(ui->button, &MyButton::clicked, this, [=]()
    10. {
    11. QMessageBox::information(this, "按钮", "莫要调戏我...");
    12. });
    13. }
    14. MainWindow::~MainWindow()
    15. {
    16. delete ui;
    17. }

  • 相关阅读:
    MongoDB的聚合笔记
    【Jupyter】远程连接Jupyter服务器
    【面经】讲一下mysql的b+树
    STM32G0-BSP板级支持包
    Springboot+Vue项目-基于Java+MySQL的房屋租赁系统(附源码+演示视频+LW)
    GBase 8c V3.0.0数据类型——JSON/JSONB函数和操作符
    对卡巴斯基发现的一个将shellcode写入evenlog的植入物的复现
    OpenCV使用教程-读取图像imread使用说明
    狂神。SpringBoot学习(3)
    链表高阶面试题及二叉树的遍历【前、中、后、层】
  • 原文地址:https://blog.csdn.net/m0_64397669/article/details/133011651