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

这篇具有很好参考价值的文章主要介绍了【QT开发笔记-基础篇】| 第四章 事件QEvent | 4.4 鼠标按下、移动、释放事件。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

本章要实现的整体效果如下:

【QT开发笔记-基础篇】| 第四章 事件QEvent | 4.4 鼠标按下、移动、释放事件,《QT开发笔记-基础篇》,qt,c++,事件,无边框窗口

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. 添加一个自定义的标签 LabelX
    LabelX* 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);
}

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

【QT开发笔记-基础篇】| 第四章 事件QEvent | 4.4 鼠标按下、移动、释放事件,《QT开发笔记-基础篇》,qt,c++,事件,无边框窗口


1.2 鼠标移动事件

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

如果 mouseMoveEvent 实现如下:

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

运行结果如下:

【QT开发笔记-基础篇】| 第四章 事件QEvent | 4.4 鼠标按下、移动、释放事件,《QT开发笔记-基础篇》,qt,c++,事件,无边框窗口

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

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

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

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

【QT开发笔记-基础篇】| 第四章 事件QEvent | 4.4 鼠标按下、移动、释放事件,《QT开发笔记-基础篇》,qt,c++,事件,无边框窗口

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

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

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

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

【QT开发笔记-基础篇】| 第四章 事件QEvent | 4.4 鼠标按下、移动、释放事件,《QT开发笔记-基础篇》,qt,c++,事件,无边框窗口


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

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

【QT开发笔记-基础篇】| 第四章 事件QEvent | 4.4 鼠标按下、移动、释放事件,《QT开发笔记-基础篇》,qt,c++,事件,无边框窗口


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. 添加一个 QLabel
    widget = 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);
}

此时,运行效果如下:

【QT开发笔记-基础篇】| 第四章 事件QEvent | 4.4 鼠标按下、移动、释放事件,《QT开发笔记-基础篇》,qt,c++,事件,无边框窗口


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 鼠标按下、移动、释放事件,《QT开发笔记-基础篇》,qt,c++,事件,无边框窗口文章来源地址https://www.toymoban.com/news/detail-719954.html

到了这里,关于【QT开发笔记-基础篇】| 第四章 事件QEvent | 4.4 鼠标按下、移动、释放事件的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 王道计网 第四章笔记

    生活在网络层的“工人”是路由器,他负责各种异构网络的连接,但是因为他只生活在前三层所以从网络层之上的东西他不能管理,所以网路层之上的数据对于路由器来说必须是相同的、透明的。 常见的网络层协议有IP 和 ICMP TCP IP传输层协议 FTP应用层协议 一句话区分IP和M

    2024年02月14日
    浏览(35)
  • python笔记:第四章使用字典

    说白了就是键值对的映射关系 不会丢失数据本身关联的结构,但不关注数据的顺序 是一种可变类型 键的类型:字典的键可以是任何不可变的类型,如浮点数,字符串,元组 可以从其他映射或键值对创建字典 将字符串格式设置功能用于字典 使用format_map将两者结合起来 就地

    2024年02月13日
    浏览(25)
  • 西瓜书学习笔记---第四章 决策树

    目录 一、题目要求 二、数据集介绍  三、决策树模型 3.1 决策树模型介绍 3.2 决策树算法原理 3.2.1 决策树的建立 3.2.2 决策树的划分(1)—信息增益ID3 3.2.3 决策树的划分(2)—Gini指数CART 3.2.4 预剪枝pre-pruning 3.2.5 后剪枝post-pruning 3.3 决策树算法核心代码解释 四、运行结果

    2024年02月04日
    浏览(29)
  • 操作系统-笔记-第四章-文件管理

    一、第一章——操作系统的概念 二、第二章——【进程】 二、第二章——【线程】​编辑 二、第二章——【进程调度】 二、第二章——【进程同步与互斥】 二、第二章——【锁】 三、第三章——内存管理 四、第四章——文件管理 五、第五章——输入输出管理 🚀 学习心

    2024年02月11日
    浏览(27)
  • 第四章:前端框架Vue基础入门

    本章学习目标: 了解Vue框架架构思想 掌握Vue核心指令 掌握计算属性与监视 掌握组件化开发模式 官方文档:https://cn.vuejs.org/guide/introduction.html. 文档可选择使用optionsAPI(选项式API)阅读,或者CompositionApi(组合式API)阅读。选项式API更适合平滑从vue2过渡,以下示例均以Compositi

    2024年02月12日
    浏览(33)
  • 计算机网络基础第四章

    主要任务是把 分组 从源端传到目的端,为分组交换网上的不同主机提供通信服务。网络层传输单位是 数据报 。 功能一:路由选择与分组转发( 最佳路径 ) 功能二:异构网络互联 功能三:拥塞控制(若所有结点都来不及接受分组,而要丢弃大量分组的话,网络就处于 拥塞

    2024年02月16日
    浏览(74)
  • 计算机网络-笔记-第四章-网络层

    一、第一章——计算机网络概述 二、第二章——物理层 三、第三章——数据链路层 四、第四章——网络层 五、第五章——运输层 六、第六章——应用层 目录 ​​​​​​​ 四、第四章——网络层 1、网络层概述 (1)虚电路服务——面向连接 (2)虚电路服务——无连接

    2024年02月11日
    浏览(35)
  • JAVA学习笔记——第四章 运算符

    🔥 博客主页 : A_SHOWY 🎥 系列专栏 :力扣刷题总结录 数据结构  云计算  数字图像处理  力扣每日一题_ 运算符是一种特殊的符号,用于表示数据的运算、赋值和比较 取模 %的本质: a - (int)a / b * b//当a是小数时 自增 独立语句使用时,++i和i++没有区别的。但是如果作

    2024年01月20日
    浏览(44)
  • 第四章--Redis基础知识和面试题

    Redis 是一个基于内存的k-v结构数据库 基于内存存储,读写性能高 适合存储热点数据(热点商品, 资讯, 新闻) 企业应用广泛 Redis入门 简介: 应用场景 ⭐缓存 任务队列 消息队列 分布式锁 数据类型 常用命令 redis常用命令链接 redis.net.cn java中操作redis 介绍 :::info redis启动默认有16个

    2024年02月07日
    浏览(37)
  • 西瓜书读书笔记整理(五)—— 第四章 决策树

    4.1.1 什么是决策树算法 决策树算法 是一种通过构建 树形结构 进行分类和回归的机器学习算法。 决策树由结点 (node) 和有向边 (directed edge) 组成。结点有两种类型:内部结点 (internal node) 和叶结点 ( leaf node)。内部结点表示一个特征或属性,叶结点表示一个类。 4.1.2 决策树学习

    2024年02月13日
    浏览(31)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包