【QT开发笔记-基础篇】| 第四章 事件QEvent | 4.4 鼠标按下、移动、释放事件
本章要实现的整体效果如下:

QEvent::MouseButtonPress
鼠标按下时,触发该事件,它对应的子类是 QMouseEvent
QEvent::MouseMove
鼠标移动时,触发该事件,它对应的子类是 QMouseEvent
QEvent::MouseButtonRelease
鼠标释放时,触发该事件,它对应的子类是 QMouseEvent
本节通过两个案例来讲解这 3 个事件:
- 按下、移动、释放事件的基本使用
- 拖动一个标签,使之移动位置
1. 按下、移动、释放事件的基本使用
同样使用上一节自定义的标签 LabelX,来进行讲解
1.1 鼠标按下、释放事件
首先,来到 labelx.h,声明这 3 个函数:
class LabelX : public QLabel
{
protected:void mousePressEvent(QMouseEvent* ev);void mouseReleaseEvent(QMouseEvent* ev);void mouseMoveEvent(QMouseEvent* ev);
};
然后,来到 labelx.cpp 实现这 3 个函数:
void LabelX::mousePressEvent(QMouseEvent* ev)
{// qDebug() << "mousePressEvent: " << ev->button() << ev->pos() << ev->globalPos();if ( ev->button() == Qt::LeftButton ) {qDebug() << "左键按下: " << "x=" << ev->x() << ", y=" << ev->y();}
}void LabelX::mouseReleaseEvent(QMouseEvent* ev)
{// qDebug() << "mouseReleaseEvent: " << ev->button() << ev->pos() << ev->globalPos();if ( ev->button() == Qt::LeftButton ) {qDebug() << "左键释放: " << "x=" << ev->x() << ", y=" << ev->y();}
}void LabelX::mouseMoveEvent(QMouseEvent* ev)
{
}
最后,来到 press_move_release_widget.cpp,在构造函数中添加 LabelX 控件,如下:
#include "labelx.h"PressMoveReleaseWidget::PressMoveReleaseWidget(QWidget* parent) : QWidget{parent}
{QVBoxLayout* verticalLayout = new QVBoxLayout(this);verticalLayout->setSpacing(0);verticalLayout->setContentsMargins(0, 0, 0, 0);// 1. 添加一个自定义的标签 LabelXLabelX* lblX = new LabelX(this);lblX->setText("");lblX->setFrameShape(QFrame::Box);lblX->setFixedHeight(50);lblX->setAlignment(Qt::AlignCenter);lblX->setStyleSheet("background-color: blue;color: white;font-size: 25px");verticalLayout->addWidget(lblX);
}
此时运行程序,在标签上点击时,就会在控制台打印按下还是释放,并显示点击的位置:

1.2 鼠标移动事件
鼠标移动,与鼠标按下和释放,在判断按键时有些许不同
如果 mouseMoveEvent 实现如下:
void LabelX::mouseMoveEvent(QMouseEvent* ev)
{qDebug() << "mouseMoveEvent: " << ev->button() << ev->pos() << ev->globalPos();
}
运行结果如下:

我明明按下的是左键,但是打印的却是没有按键按下
因为,此时不能使用 ev->button(),而是要使用 ev->buttons(),如下:
void LabelX::mouseMoveEvent(QMouseEvent* ev)
{// 而是要用buttons()方法qDebug() << "mouseMoveEvent: " << ev->buttons() << ev->pos() << ev->globalPos();
}
此时,就可以正确打印了,如下:

可见,刚开始移动只按左键,移动过程中又按下了右键,也是可以识别到的。
在移动过程中,判断有左键按下的代码,如下:
void LabelX::mouseMoveEvent(QMouseEvent* ev)
{if ( ev->buttons() & Qt::LeftButton ) {qDebug() << "左键移动中: " << "x=" << ev->x() << ", y=" << ev->y();}
}
这样,鼠标按下、移动、释放的整体效果,如下:

1.3 鼠标跟踪
以上,需要鼠标保持按下的状态下,系统才会调用 mouseMoveEvent,实际工作中往往有这么一种需求:
鼠标悬浮在控件上,而不是按下,就触发 mouseMoveEvent 事件,这怎么实现呢?
答案:设置鼠标跟踪,默认情况下鼠标跟踪是关闭的,需要开启
首先,来到 labelx.cpp 中,设置标签使能鼠标跟踪,如下:
LabelX::LabelX(QWidget* parent) : QLabel{parent}
{this->setMouseTracking(true);
}
然后,在 mouseMoveEvent 中添加打印,如下:
void LabelX::mouseMoveEvent(QMouseEvent* ev)
{qDebug() << "mouseMoveEvent: " << ev->buttons() << ev->pos() << ev->globalPos();if ( ev->buttons() & Qt::LeftButton ) {qDebug() << "左键移动中: "<< "x=" << ev->x() << ", y=" << ev->y();}
}
此时,在标签上悬浮移动时,也可以跟踪到鼠标,如下:

2. 鼠标事件移动标签
接下来,实现一个小案例:拖动标签来移动标签的位置
2.1 界面上添加标签
首先,在 press_move_release_widget.h 中添加成员变量:
#include <QLabel>class PressMoveReleaseWidget : public QWidget
{
private:QLabel* lbl;QWidget* widget;
};
在 QLable 外边套一层 QWidget,是为了让标签在这个 widget 范围内移动
然后,在 press_move_release_widget.cpp 的构造中添加一个标签:
PressMoveReleaseWidget::PressMoveReleaseWidget(QWidget* parent) : QWidget{parent}
{// ...// 2. 添加一个 QLabelwidget = new QWidget(this);lbl = new QLabel(widget);lbl->setText("");lbl->setFrameShape(QFrame::Box);lbl->setFixedSize(100, 100);lbl->setStyleSheet("background-color: red;");verticalLayout->addWidget(widget);
}
此时,运行效果如下:

2.2 为 QLabel 安装事件过滤器
PressMoveReleaseWidget::PressMoveReleaseWidget(QWidget* parent) : QWidget{parent}
{// ...lbl->installEventFilter(this);
}
2.3 重写 eventFilter() 函数
重写当前窗口的 eventFilter() 函数
首先,在 press_move_release_widget.h 文件中声明该函数,
同时声明记录窗口位置和鼠标按下位置的变量,如下:
class PressMoveReleaseWidget : public QWidget
{
protected:bool eventFilter(QObject* watched, QEvent* event);private:QPoint pressPos;QPoint wndPos;
};
然后,在 press_move_release_widget.cpp 文件中实现该函数,如下:
#include <QEvent>
#include <QMouseEvent>
#include <QDebug>
bool PressMoveReleaseWidget::eventFilter(QObject* watched, QEvent* event)
{if ( watched != lbl ) {return QWidget::eventFilter(watched, event);}if ( event->type() == QEvent::MouseButtonPress ) {qDebug() << "MouseButtonPress";QMouseEvent* mouseEvent = static_cast<QMouseEvent*>(event);pressPos = mouseEvent->globalPos();wndPos = lbl->pos();qDebug() << wndPos;} else if ( event->type() == QEvent::MouseMove ) {qDebug() << "MouseMove";QMouseEvent* mouseEvent = static_cast<QMouseEvent*>(event);QPoint dstPos = wndPos + (mouseEvent->globalPos() - pressPos);lbl->move(dstPos);// 超出了最左边if ( lbl->pos().x() < 0 ) {lbl->move(0, dstPos.y());}// 超出了最右边if ( lbl->pos().x() > widget->width() - lbl->width() ) {lbl->move(widget->width() - lbl->width(), dstPos.y());}// 超出了最上边if ( lbl->pos().y() < 0 ) {lbl->move(dstPos.x(), 0);}// 超出了最下边if ( lbl->pos().y() > widget->height() - lbl->height() ) {lbl->move(dstPos.x(), widget->height() - lbl->height());}} else if ( event->type() == QEvent::MouseButtonRelease ) {qDebug() << "MouseButtonRelease";}
}
这里有些实现细节,说明如下:
- 如果不是 lbl 的事件,直接调用父类处理
return QWidget::eventFilter(watched, event) - 在鼠标按下时,记录
lbl的位置和鼠标按下位置,作为窗口移动时的参考 - 当
lbl超出 widget 边界时,让它等于边界值
此时,就可以通过鼠标拖动标签,在 widget 范围内移动了,如下:

相关文章:
【QT开发笔记-基础篇】| 第四章 事件QEvent | 4.4 鼠标按下、移动、释放事件
本章要实现的整体效果如下: QEvent::MouseButtonPress 鼠标按下时,触发该事件,它对应的子类是 QMouseEvent QEvent::MouseMove 鼠标移动时,触发该事件,它对应的子类是 QMouseEvent QEvent::MouseButtonRel…...
vue3父子通信+ref,toRef,toRefs使用实例
ref是什么? 生成值类型的响应式数据可用于模板和reactive通过.value修改值可以获取DOM元素 <p ref”elemRef”>{{nameRef}} -- {{state.name}}</p> // 获取dom元素 onMounted(()>{ console.log(elemRef.value); }); toRef是什么? 针对一个响应式对象(rea…...
输入电压转化为电流性 5~20mA方案
输入电压转化为电流性 5~20mA方案 方案一方案二方案三 方案一 XTR111是一款精密的电压-电流转换器是最广泛应用之一。原因有二:一是线性度非常好、二是价格便宜。总结成一点,就是性价比高。 典型电路 最终电路 Z1二极管处输出电流表达式:…...
SpringBoot自带模板引擎Thymeleaf使用详解①
目录 前言 一、SpringBoot静态资源相关目录 二、变量输出 2.1 在templates目录下创建视图index.html 2.2 创建对应的Controller 2.3 在视图展示model中的值 三、操作字符串和时间 3.1 操作字符串 3.2 操作时间 前言 Thymeleaf是一款用于渲染XML/HTML5内容的模板引擎&am…...
推荐算法——Apriori算法原理
0、前言: 首先名字别读错:an pu ruo ao rui 【拼音发音】Apriori是一种推荐算法推荐系统:从海量数据中,帮助用户进行信息的过滤和选择。主要推荐方法有:基于内容的推荐、协同过滤推荐、基于关联规则的推荐、基于知识的…...
vue ant 隐藏 列
vue ant 隐藏 列 如果你使用的是Vue和Ant Design Vue组件库,你可以使用v-if指令来实现条件渲染来隐藏列。以下是一个示例代码: <template><a-table :columns"columns" :data-source"data"><template v-slot:custom…...
java基础之初始化顺序
初始化顺序 在类中变量定义的顺序决定了它们初始化的顺序。在创建任何java对象时,都是依次调用父类非静态初始化块、父类构造器执行初始化、本类的非静态初始化块、本类构造器执行初始化 public class House { // 构造器之前 Window w1 new Window(1); Ho…...
FFmpeg 命令:从入门到精通 | ffmpeg filter(过滤器 / 滤镜)
FFmpeg 命令:从入门到精通 | ffmpeg filter(过滤器 / 滤镜) FFmpeg 命令:从入门到精通 | ffmpeg filter(过滤器 / 滤镜)ffmpeg fliter 基本内置变量视频裁剪文字水印图片水印画中画视频多宫格处理 FFmpeg 命…...
【C语言】23-结构体类型
目录 1. 如何建立结构体类型2. 如何使用结构体2.1 定义结构体变量2.2 结构体变量的初始化和引用2.3 结构体数组2.4 结构体指针2.4.1 指向结构体变量的指针2.4.2 指向结构体数组的指针C 语言提供了一些由系统已定义好的数据类型,如: int、 float、 char 等,用户可以在程序…...
Python小技巧:快速合并字典dict()
文章目录 前言知识点字典合并1. dict.update()基础合并2. 字典推导式 update() 后话 前言 这里是Python小技巧的系列文章。这是第四篇,快速合并字典。 在Python的使用中,有时候需要将两个 dict(字典) 进行合并。 通常我们会借助 dict(字典) 的内置方法 …...
如何使用 React 和 Docusaurus 编写的一些自定义钩子(Hook)
import useRouteContext from @docusaurus/useRouteContext; import {DependencyList, useEffect, useRef, useState, useMemo } from react; import {dequal } from dequal; /* eslint-disable global-require */ // @ts-ignore/*** 用于深度检测依赖的useMemo钩子* @param fa…...
【初识Linux】Linux环境配置、Linux的基本指令 一
Linux基本指令一 一、学习前提(环境配置)①安装Xshell和云服务器推荐②Xshell用途如下图③打开Xshell 二、 Linux基本指令①whoami和who指令②pwd、ls、ls -l三个指令ls指令扩充 ③cd指令前提了解有了上面的认识,我们就可以开始cd指令的学习了 ④tree指令…...
conda常用命令参数,指定版本,依赖库文件,创建虚拟环境,删除,激活,退出,内部安装包,pip通过代理安装包
以下是conda的常用命令和参数: 1. 创建虚拟环境: - 创建一个新的虚拟环境:conda create -n 环境名 pythonx.x - 使用指定的依赖文件创建虚拟环境:conda create -n 环境名 --file requirements.txt 2. 激活虚拟环境&#x…...
【锁的区别】C++线程库和POSIX线程库锁的区别
C线程库和POSIX线程库锁的区别 C线程库代码段的互斥:mutex、recursive_mutex、timed_mutex、recursive_timed_mutex互斥量mutex:直接进行lock()或者unlock()递归互斥锁recursive_mutex:可以多次加锁,意味着加几次锁就需要解几次锁…...
网络层·IP协议
承接前文TCP协议-CSDN博客 简介 协议头格式 网段划分(重要) 划分方法 IP地址的数量限制(背景介绍) 私有IP地址和公网IP地址(提出解决思路) NAT技术(解决方法) 路由 网络层 在复杂的网络环境中确定一个合适的路径 IP协议 主机: 配有IP地址, 可以认为就是你的电脑; 路由器:…...
RabbitMQ学习笔记(下):延迟队列,发布确认高级,备份交换机
十、延迟队列 延迟队列 概念: 延迟队列使用场景: 流程图: 延迟队列整合Springboot 导入依赖: <dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot…...
Python 无废话-基础知识面向对象编程详解
类定义 如何理解万物皆对象? 生活中一些事物,动物(可爱的小狗、调皮的小猫)、交通工具(比亚迪U8汽车、飞机)、人(学生、教师)…… 这些对象都有着独特或共性的属性和方法来描述其…...
凉鞋的 Unity 笔记 106. 第二轮循环场景视图Sprite Renderer
106. 第二轮循环&场景视图&Sprite Renderer 从这一篇开始,我们开始进行第二轮循环。 这次我们至少能够在游戏运行窗口看到一些东西。 首先还是在场景层次窗口进行编辑,先创建一个 Sprite,操作如下: 创建后,会在 Scene …...
Vue中如何进行分布式路由配置与管理
Vue中的分布式路由配置与管理 随着现代Web应用程序的复杂性不断增加,分布式路由配置和管理成为了一个重要的主题。Vue.js作为一种流行的前端框架,提供了多种方法来管理Vue应用程序的路由。本文将深入探讨在Vue中如何进行分布式路由配置与管理࿰…...
Solidity 合约漏洞,价值 38BNB 漏洞分析
Solidity 合约漏洞,价值 38BNB 漏洞分析 1. 漏洞简介 https://twitter.com/NumenAlert/status/1626447469361102850 https://twitter.com/bbbb/status/1626392605264351235 2. 相关地址或交易 攻击交易: https://bscscan.com/tx/0x146586f05a451313…...
SpringBoot-17-MyBatis动态SQL标签之常用标签
文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…...
(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)
题目:3442. 奇偶频次间的最大差值 I 思路 :哈希,时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况,哈希表这里用数组即可实现。 C版本: class Solution { public:int maxDifference(string s) {int a[26]…...
椭圆曲线密码学(ECC)
一、ECC算法概述 椭圆曲线密码学(Elliptic Curve Cryptography)是基于椭圆曲线数学理论的公钥密码系统,由Neal Koblitz和Victor Miller在1985年独立提出。相比RSA,ECC在相同安全强度下密钥更短(256位ECC ≈ 3072位RSA…...
Auto-Coder使用GPT-4o完成:在用TabPFN这个模型构建一个预测未来3天涨跌的分类任务
通过akshare库,获取股票数据,并生成TabPFN这个模型 可以识别、处理的格式,写一个完整的预处理示例,并构建一个预测未来 3 天股价涨跌的分类任务 用TabPFN这个模型构建一个预测未来 3 天股价涨跌的分类任务,进行预测并输…...
现代密码学 | 椭圆曲线密码学—附py代码
Elliptic Curve Cryptography 椭圆曲线密码学(ECC)是一种基于有限域上椭圆曲线数学特性的公钥加密技术。其核心原理涉及椭圆曲线的代数性质、离散对数问题以及有限域上的运算。 椭圆曲线密码学是多种数字签名算法的基础,例如椭圆曲线数字签…...
JVM暂停(Stop-The-World,STW)的原因分类及对应排查方案
JVM暂停(Stop-The-World,STW)的完整原因分类及对应排查方案,结合JVM运行机制和常见故障场景整理而成: 一、GC相关暂停 1. 安全点(Safepoint)阻塞 现象:JVM暂停但无GC日志,日志显示No GCs detected。原因:JVM等待所有线程进入安全点(如…...
学习STC51单片机32(芯片为STC89C52RCRC)OLED显示屏2
每日一言 今天的每一份坚持,都是在为未来积攒底气。 案例:OLED显示一个A 这边观察到一个点,怎么雪花了就是都是乱七八糟的占满了屏幕。。 解释 : 如果代码里信号切换太快(比如 SDA 刚变,SCL 立刻变&#…...
Device Mapper 机制
Device Mapper 机制详解 Device Mapper(简称 DM)是 Linux 内核中的一套通用块设备映射框架,为 LVM、加密磁盘、RAID 等提供底层支持。本文将详细介绍 Device Mapper 的原理、实现、内核配置、常用工具、操作测试流程,并配以详细的…...
C++使用 new 来创建动态数组
问题: 不能使用变量定义数组大小 原因: 这是因为数组在内存中是连续存储的,编译器需要在编译阶段就确定数组的大小,以便正确地分配内存空间。如果允许使用变量来定义数组的大小,那么编译器就无法在编译时确定数组的大…...
AI病理诊断七剑下天山,医疗未来触手可及
一、病理诊断困局:刀尖上的医学艺术 1.1 金标准背后的隐痛 病理诊断被誉为"诊断的诊断",医生需通过显微镜观察组织切片,在细胞迷宫中捕捉癌变信号。某省病理质控报告显示,基层医院误诊率达12%-15%,专家会诊…...
