【Qt】消息机制和事件

文章目录

  • 事件
  • event()
  • 事件过滤器
  • 案例:检测鼠标事件
  • 案例:定时器

事件

事件(event)是由系统或者 Qt 本身在不同的时刻发出的。当用户按下鼠标、敲下键盘,或者是窗口需要重新绘制的时候,都会发出一个相应的事件

一些事件在对用户操作做出响应时发出,如键盘事件等;另一些事件则是由系统自动发出,如计时器事件


回顾

Qt 程序需要在main()函数创建一个QApplication对象,然后调用它的exec()函数,这个函数就是开始 Qt 的事件循环,在执行exec()函数之后,程序将进入事件循环来监听应用程序的事件。

当事件发生时,Qt 将创建一个事件对象。Qt 中所有事件类都继承于QEvent,在事件对象创建完毕后,Qt 将这个事件对象传递给QObject的event()函数。event()函数并不直接处理事件,而是按照事件对象的类型分派给特定的事件处理函数


event()

event()函数主要用于事件的分发,所以如果希望在事件分发之前做一些操作,就可以重写这个event()函数

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

event()函数中实际是通过事件处理器来响应一个具体的事件。这相当于event()函数将具体事件的处理“委托”给具体的事件处理器。而这些事件处理器是 protected virtual 的,因此,我们重写了某一个事件处理器,即可让 Qt 调用我们自己实现的版本。

  • 由此可见,event()是一个集中处理不同类型的事件的地方

事件过滤器

在程序将事件分发到事件分发器前,可以利用过滤器做拦截

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

image-2023100520492631

2.在mianWindow.ui文件当中创建一个Label控件 => 提升为

image-20231005205126983

3.可以更改控件的格式 看的更明显

image-20231005205210158


注意:在第二步当中可以发现:基类名称为 Q L a b e l QLabel QLabel,所以生成的myLabel.h和myLabel.cpp文件要改动:

//.h文件
#include <QLabel>
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函数中显示鼠标坐标值,原因如下:

  • QWidget中有一个mouseTracking属性,该属性用于设置是否追踪鼠标只有鼠标被追踪时,mouseMoveEvent()才会发出。
  • 如果mouseTracking是 false(默认即是),组件在至少一次鼠标点击之后,才能够被追踪,也就是能够发出mouseMoveEvent()事件如果mouseTracking为 true,则mouseMoveEvent()直接可以被发出。

如果想不点击鼠标也能在 m o u s e M o v e E v e n t mouseMoveEvent mouseMoveEvent函数中显示鼠标坐标值,在构造函数当中:

myLabel::myLabel(QWidget *parent) : QLabel(parent)
{//设置鼠标追踪状态   默认为falsesetMouseTracking(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

image-20231006102712863

创建定时器方法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界面当中多增加两个按钮用于停止和恢复定时器:

image-20231006104029330

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秒+1static 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);
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/114814.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

序列解包和生成器表达式

序列解包 可以使用序列解包功能对多个变量同时赋值 (1) x, y, z 1, 2, 3 print(x, y, z)必须一一对应 x, y, z 1, 2 会抛出异常 (2) 括号可加可不加 v_tuple (False, 3.5, abc) (x, y, z) v_tuple # 等价于x, y, z v_tuple print(x, y, z)可以对range对象进行解包 …

联邦学习的梯度重构

梯度泄露的攻击方法&#xff1a;深度泄露梯度&#xff08;DLG&#xff09;——>在高度压缩的场景下是失效的 原因&#xff1a;梯度压缩&#xff08;可减小通信开销&#xff09;——>存在信息损失<——从而DLG方法效果有限 但是这本身存在的信息损失怎么解决呢&#x…

深入解析docker内核网桥

今天做虚拟桌面&#xff0c;朋友问我&#xff0c;为什么vnc 连接另一个docker 容器一直超时&#xff0c;原因是在docker 启动的时候没有组网&#xff0c;那么接下来我就要解析下docker的内核网络。 我们思考几个问题&#xff0c;带你了解linux 中docker 网络实现的基本原理。 文…

【Java基础面试四十六】、 List<? super T>和List<? extends T>有什么区别?

文章底部有个人公众号&#xff1a;热爱技术的小郑。主要分享开发知识、学习资料、毕业设计指导等。有兴趣的可以关注一下。为何分享&#xff1f; 踩过的坑没必要让别人在再踩&#xff0c;自己复盘也能加深记忆。利己利人、所谓双赢。 面试官&#xff1a;问题 参考答案&#x…

计算机算法分析与设计(15)---贪心算法(虚拟汽车加油问题和最优分解问题)

文章目录 一、虚拟汽车加油问题1.1 问题描述1.2 思路分析1.3 代码编写 二、最优分解问题2.1 问题描述2.2 思路分析2.3 代码编写 一、虚拟汽车加油问题 1.1 问题描述 一辆虚拟汽车加满油后可行驶 n n n km。旅途中有若干加油站。设计一个有效算法&#xff0c;指出应在哪些加油…

MyBatisPlus实现连表操作、批量处理

1、实现连表查询 正常来说单靠mybatisplus无法实现连表查询&#xff0c;只能靠单表sql然后进行拼接形成连表查询&#xff0c;或者使用xml文件去编写sql语句来实现连表查询。但他又给我们提供了一个插件MyBatis-Plus-Join&#xff0c;用来弥补mybatisplus再连表上的不足&#…

Apache Jmeter测压工具快速入门

Jmeter测压工具快速入门 一、Jmeter介绍二、Jmeter On Mac2.1 下载2.2 安装2.2.1 环境配置2.2.2 初始化设置 2.3 测试2.3.1 创建JDBC Connection Configuration2.3.2 创建线程组2.3.3 创建JDBC Request2.3.4 创建结果监控2.3.5 运行结果 2.4 问题记录2.4.1 VM option UseG1GC异…

【C语言】每日一题(旋转数组)

旋转数组&#xff0c;链接奉上 目录 方法:创建额外的数组&#xff1a;整体思路&#xff1a;代码实现&#xff1a; 数组反转&#xff1a;整体思路&#xff1a;代码实现&#xff1a;小插曲&#xff1a; 方法: 创建额外的数组&#xff1a; 整体思路&#xff1a; 创建一个额外的…

oracle实现搜索不区分大小写

<if test"code ! null and code ! ">and upper(code) like upper(%${code}%) </if>关键字upper

51单片机的时钟系统

1.简介 51内置的时钟系统可以用来计时&#xff0c;与主程序分割开来&#xff0c;在计时过程中不会终端主程序&#xff0c;还可以通过开启时钟中断来执行相应的操作。 2.单片机工作方式 单片机内部有两个十六位的定时器T0和T1。每个定时器有两种工作方式选择&#xff0c;分别…

Redis-Sentinel高可用架构学习

Redis-Sentinel高可用架构 Redis主从复制过程&#xff1a; 主从同步原理 Redis Sentinel&#xff08;哨兵&#xff09;高可用集群方案&#xff1a;Redis-Sentinel是Redis官方推荐的高可用性(HA)解决方案。 当用Redis做Master-slave的高可用方案时&#xff0c;假如master宕机了…

STM32F4_照相机

目录 前言 1. BMP编码 2. JPEG编码 前言 我们所要实现的照相机&#xff0c;支持BMP图片格式的照片和JPEG图片格式的照片。 1. BMP编码 BMP文件是由文件头、位图信息头、颜色信息和图形数据四部分构成。 1. BMP文件头&#xff08;14个字节&#xff09;&#xff1a;BMP文件…

numpy矩阵画框框

在n>5(n是奇数)的nn数组中&#xff0c;用*画外方框和内接菱形。 (本笔记适合熟悉numpy的coder翻阅) 【学习的细节是欢悦的历程】 Python 官网&#xff1a;https://www.python.org/ Free&#xff1a;大咖免费“圣经”教程《 python 完全自学教程》&#xff0c;不仅仅是基础那…

c++中的继承

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、继承的概念及定义1、继承的概念2、继承的定义2.1 定义格式2.2 继承关系和访问限定符2.3 继承基类成员访问方式的变化 二、基类和派生类对象赋值转换三、继承…

【27】c++设计模式——>迭代器模式(遍历双向链表)(2)

//实现双向链表 #pragma once #include<iostream> #include<string> #include<vector> using namespace std;class Iterator; class ForwardIterator; class ReverseIterator;//链表的最小组成部分是一个节点&#xff0c;先实现一个节点 struct Node //c中st…

在Espressif-IDE中使用Wokwi仿真ESP32

陈拓 2023/10/17-2023/10/19 1. 概述 在Espressif-IDE v2.9.0版本之后可直接在IDE中使用Wokwi模拟器。 1.1 什么是 Wokwi 模拟器&#xff1f; Wokwi 是一款在线电子模拟器&#xff0c;支持模拟各种开发板、元器件和传感器&#xff0c;例如乐鑫产品 ESP32。 Wokwi 提供基于浏…

html表格标签

<!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>Title</title> </head> <body><!--表格table 行 tr 列 td --> <table border"1px"><tr> <!--colsp…

逻辑漏洞详解

原理&#xff1a; 没有固定的概念&#xff0c;一般都是不符合常识的情况。比如任意用户注册&#xff0c;短信炸弹&#xff0c;占用资源&#xff0c;交易支付、密码修改、密码找回、越权修改、越权查询、突破限制。 根据实际业务逻辑进行比对&#xff0c;购物的可以根据数量&a…

gulp打包vue3+jsx+less插件

最终转换结果如下 在根目录下添加gulpfile.js文件&#xff0c;package.json添加命令npm run gulp var gulp require(gulp) var babel require(gulp-babel) var less require(gulp-less) var del require(del); var spawn require(child_process).spawn;const outDir &…

【FPGA零基础学习之旅#16】嵌入式块RAM-双口ram的使用

&#x1f389;欢迎来到FPGA专栏~双口ram的使用 ☆* o(≧▽≦)o *☆嗨~我是小夏与酒&#x1f379; ✨博客主页&#xff1a;小夏与酒的博客 &#x1f388;该系列文章专栏&#xff1a;FPGA学习之旅 文章作者技术和水平有限&#xff0c;如果文中出现错误&#xff0c;希望大家能指正…