当前位置: 首页 > news >正文

【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);
}

此时运行程序,在标签上点击时,就会在控制台打印按下还是释放,并显示点击的位置:

image-20230911094301640


1.2 鼠标移动事件

鼠标移动,与鼠标按下和释放,在判断按键时有些许不同

如果 mouseMoveEvent 实现如下:

void LabelX::mouseMoveEvent(QMouseEvent* ev)
{qDebug() << "mouseMoveEvent: " << ev->button() << ev->pos() << ev->globalPos();
}

运行结果如下:

image-20230911095642358

我明明按下的是左键,但是打印的却是没有按键按下

因为,此时不能使用 ev->button(),而是要使用 ev->buttons(),如下:

void LabelX::mouseMoveEvent(QMouseEvent* ev)
{// 而是要用buttons()方法qDebug() << "mouseMoveEvent: " << ev->buttons() << ev->pos() << ev->globalPos();
}

此时,就可以正确打印了,如下:

image-20230911095838664

可见,刚开始移动只按左键,移动过程中又按下了右键,也是可以识别到的。

在移动过程中,判断有左键按下的代码,如下:

void LabelX::mouseMoveEvent(QMouseEvent* ev)
{if ( ev->buttons() & Qt::LeftButton ) {qDebug() << "左键移动中: " << "x=" << ev->x() << ", y=" << ev->y();}
}

这样,鼠标按下、移动、释放的整体效果,如下:

image-20230911100344699


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();}
}

此时,在标签上悬浮移动时,也可以跟踪到鼠标,如下:

image-20230911101622483


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);
}

此时,运行效果如下:

image-20230911102727749


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 范围内移动了,如下:

mousemove

相关文章:

【QT开发笔记-基础篇】| 第四章 事件QEvent | 4.4 鼠标按下、移动、释放事件

本章要实现的整体效果如下&#xff1a; QEvent::MouseButtonPress ​ 鼠标按下时&#xff0c;触发该事件&#xff0c;它对应的子类是 QMouseEvent QEvent::MouseMove ​ 鼠标移动时&#xff0c;触发该事件&#xff0c;它对应的子类是 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是一款精密的电压-电流转换器是最广泛应用之一。原因有二&#xff1a;一是线性度非常好、二是价格便宜。总结成一点&#xff0c;就是性价比高。 典型电路 最终电路 Z1二极管处输出电流表达式&#xff1a;…...

SpringBoot自带模板引擎Thymeleaf使用详解①

目录 前言 一、SpringBoot静态资源相关目录 二、变量输出 2.1 在templates目录下创建视图index.html 2.2 创建对应的Controller 2.3 在视图展示model中的值 三、操作字符串和时间 3.1 操作字符串 3.2 操作时间 前言 Thymeleaf是一款用于渲染XML/HTML5内容的模板引擎&am…...

推荐算法——Apriori算法原理

0、前言&#xff1a; 首先名字别读错&#xff1a;an pu ruo ao rui 【拼音发音】Apriori是一种推荐算法推荐系统&#xff1a;从海量数据中&#xff0c;帮助用户进行信息的过滤和选择。主要推荐方法有&#xff1a;基于内容的推荐、协同过滤推荐、基于关联规则的推荐、基于知识的…...

vue ant 隐藏 列

vue ant 隐藏 列 如果你使用的是Vue和Ant Design Vue组件库&#xff0c;你可以使用v-if指令来实现条件渲染来隐藏列。以下是一个示例代码&#xff1a; <template><a-table :columns"columns" :data-source"data"><template v-slot:custom…...

java基础之初始化顺序

初始化顺序 在类中变量定义的顺序决定了它们初始化的顺序。在创建任何java对象时&#xff0c;都是依次调用父类非静态初始化块、父类构造器执行初始化、本类的非静态初始化块、本类构造器执行初始化 public class House { // 构造器之前 Window w1 new Window(1); Ho…...

FFmpeg 命令:从入门到精通 | ffmpeg filter(过滤器 / 滤镜)

FFmpeg 命令&#xff1a;从入门到精通 | ffmpeg filter&#xff08;过滤器 / 滤镜&#xff09; FFmpeg 命令&#xff1a;从入门到精通 | ffmpeg filter&#xff08;过滤器 / 滤镜&#xff09;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小技巧的系列文章。这是第四篇&#xff0c;快速合并字典。 在Python的使用中&#xff0c;有时候需要将两个 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基本指令一 一、学习前提(环境配置&#xff09;①安装Xshell和云服务器推荐②Xshell用途如下图③打开Xshell 二、 Linux基本指令①whoami和who指令②pwd、ls、ls -l三个指令ls指令扩充 ③cd指令前提了解有了上面的认识&#xff0c;我们就可以开始cd指令的学习了 ④tree指令…...

conda常用命令参数,指定版本,依赖库文件,创建虚拟环境,删除,激活,退出,内部安装包,pip通过代理安装包

以下是conda的常用命令和参数&#xff1a; 1. 创建虚拟环境&#xff1a; - 创建一个新的虚拟环境&#xff1a;conda create -n 环境名 pythonx.x - 使用指定的依赖文件创建虚拟环境&#xff1a;conda create -n 环境名 --file requirements.txt 2. 激活虚拟环境&#x…...

【锁的区别】C++线程库和POSIX线程库锁的区别

C线程库和POSIX线程库锁的区别 C线程库代码段的互斥&#xff1a;mutex、recursive_mutex、timed_mutex、recursive_timed_mutex互斥量mutex&#xff1a;直接进行lock()或者unlock()递归互斥锁recursive_mutex&#xff1a;可以多次加锁&#xff0c;意味着加几次锁就需要解几次锁…...

网络层·IP协议

承接前文TCP协议-CSDN博客 简介 协议头格式 网段划分(重要) 划分方法 IP地址的数量限制(背景介绍) 私有IP地址和公网IP地址(提出解决思路) NAT技术(解决方法) 路由 网络层 在复杂的网络环境中确定一个合适的路径 IP协议 主机: 配有IP地址, 可以认为就是你的电脑; 路由器:…...

RabbitMQ学习笔记(下):延迟队列,发布确认高级,备份交换机

十、延迟队列 延迟队列 概念&#xff1a; 延迟队列使用场景&#xff1a; 流程图&#xff1a; 延迟队列整合Springboot 导入依赖&#xff1a; <dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot…...

Python 无废话-基础知识面向对象编程详解

类定义 如何理解万物皆对象&#xff1f; 生活中一些事物&#xff0c;动物&#xff08;可爱的小狗、调皮的小猫&#xff09;、交通工具&#xff08;比亚迪U8汽车、飞机&#xff09;、人&#xff08;学生、教师&#xff09;…… 这些对象都有着独特或共性的属性和方法来描述其…...

凉鞋的 Unity 笔记 106. 第二轮循环场景视图Sprite Renderer

106. 第二轮循环&场景视图&Sprite Renderer 从这一篇开始&#xff0c;我们开始进行第二轮循环。 这次我们至少能够在游戏运行窗口看到一些东西。 首先还是在场景层次窗口进行编辑&#xff0c;先创建一个 Sprite&#xff0c;操作如下: 创建后&#xff0c;会在 Scene …...

Vue中如何进行分布式路由配置与管理

Vue中的分布式路由配置与管理 随着现代Web应用程序的复杂性不断增加&#xff0c;分布式路由配置和管理成为了一个重要的主题。Vue.js作为一种流行的前端框架&#xff0c;提供了多种方法来管理Vue应用程序的路由。本文将深入探讨在Vue中如何进行分布式路由配置与管理&#xff0…...

Solidity 合约漏洞,价值 38BNB 漏洞分析

Solidity 合约漏洞&#xff0c;价值 38BNB 漏洞分析 1. 漏洞简介 https://twitter.com/NumenAlert/status/1626447469361102850 https://twitter.com/bbbb/status/1626392605264351235 2. 相关地址或交易 攻击交易&#xff1a; https://bscscan.com/tx/0x146586f05a451313…...

XML Group端口详解

在XML数据映射过程中&#xff0c;经常需要对数据进行分组聚合操作。例如&#xff0c;当处理包含多个物料明细的XML文件时&#xff0c;可能需要将相同物料号的明细归为一组&#xff0c;或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码&#xff0c;增加了开…...

使用van-uploader 的UI组件,结合vue2如何实现图片上传组件的封装

以下是基于 vant-ui&#xff08;适配 Vue2 版本 &#xff09;实现截图中照片上传预览、删除功能&#xff0c;并封装成可复用组件的完整代码&#xff0c;包含样式和逻辑实现&#xff0c;可直接在 Vue2 项目中使用&#xff1a; 1. 封装的图片上传组件 ImageUploader.vue <te…...

自然语言处理——Transformer

自然语言处理——Transformer 自注意力机制多头注意力机制Transformer 虽然循环神经网络可以对具有序列特性的数据非常有效&#xff0c;它能挖掘数据中的时序信息以及语义信息&#xff0c;但是它有一个很大的缺陷——很难并行化。 我们可以考虑用CNN来替代RNN&#xff0c;但是…...

什么?连接服务器也能可视化显示界面?:基于X11 Forwarding + CentOS + MobaXterm实战指南

文章目录 什么是X11?环境准备实战步骤1️⃣ 服务器端配置(CentOS)2️⃣ 客户端配置(MobaXterm)3️⃣ 验证X11 Forwarding4️⃣ 运行自定义GUI程序(Python示例)5️⃣ 成功效果![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/55aefaea8a9f477e86d065227851fe3d.pn…...

如何在网页里填写 PDF 表格?

有时候&#xff0c;你可能希望用户能在你的网站上填写 PDF 表单。然而&#xff0c;这件事并不简单&#xff0c;因为 PDF 并不是一种原生的网页格式。虽然浏览器可以显示 PDF 文件&#xff0c;但原生并不支持编辑或填写它们。更糟的是&#xff0c;如果你想收集表单数据&#xff…...

初探Service服务发现机制

1.Service简介 Service是将运行在一组Pod上的应用程序发布为网络服务的抽象方法。 主要功能&#xff1a;服务发现和负载均衡。 Service类型的包括ClusterIP类型、NodePort类型、LoadBalancer类型、ExternalName类型 2.Endpoints简介 Endpoints是一种Kubernetes资源&#xf…...

R语言速释制剂QBD解决方案之三

本文是《Quality by Design for ANDAs: An Example for Immediate-Release Dosage Forms》第一个处方的R语言解决方案。 第一个处方研究评估原料药粒径分布、MCC/Lactose比例、崩解剂用量对制剂CQAs的影响。 第二处方研究用于理解颗粒外加硬脂酸镁和滑石粉对片剂质量和可生产…...

push [特殊字符] present

push &#x1f19a; present 前言present和dismiss特点代码演示 push和pop特点代码演示 前言 在 iOS 开发中&#xff0c;push 和 present 是两种不同的视图控制器切换方式&#xff0c;它们有着显著的区别。 present和dismiss 特点 在当前控制器上方新建视图层级需要手动调用…...

MySQL JOIN 表过多的优化思路

当 MySQL 查询涉及大量表 JOIN 时&#xff0c;性能会显著下降。以下是优化思路和简易实现方法&#xff1a; 一、核心优化思路 减少 JOIN 数量 数据冗余&#xff1a;添加必要的冗余字段&#xff08;如订单表直接存储用户名&#xff09;合并表&#xff1a;将频繁关联的小表合并成…...

Python环境安装与虚拟环境配置详解

本文档旨在为Python开发者提供一站式的环境安装与虚拟环境配置指南&#xff0c;适用于Windows、macOS和Linux系统。无论你是初学者还是有经验的开发者&#xff0c;都能在此找到适合自己的环境搭建方法和常见问题的解决方案。 快速开始 一分钟快速安装与虚拟环境配置 # macOS/…...