【QT入门】 Qt自定义控件与样式设计之QPushButton实现鼠标悬浮按钮弹出对话框

往期回顾:

【QT入门】 Qt自定义控件与样式设计之qss选择器-CSDN博客

【QT入门】 Qt自定义控件与样式设计之QLineEdit的qss使用-CSDN博客

【QT入门】Qt自定义控件与样式设计之QPushButton常用qss-CSDN博客

【QT入门】 Qt自定义控件与样式设计之QPushButton实现鼠标悬浮按钮弹出对话框

一、最终效果

鼠标悬浮弹出对话框的功能:最终要实现纯代码设计出一个音量按钮,当鼠标悬浮在上面的时候,显示滑块,并可以自己调节大小,鼠标离开后滑块消失。

二、具体实现

 1、鼠标事件捕捉

首先实现一个基本功能,就是窗口能识别鼠标,鼠标放上去就显示,不放上去就不显示,这个功能很简单只需要重写两个函数

void enterEvent(QEvent * event) override;
void leaveEvent(QEvent * event) override;

在Widget主窗口写这两个类,你那么就能实现鼠标放进主窗口有反应,拿出来也有反应

void Widget::enterEvent(QEvent * event)
{
    qDebug()<<"11111";
}

void Widget::leaveEvent(QEvent * event)
{
    qDebug()<<"22222";
}

2、加载主布局 

Widget类加载主布局,主类没操作,就是设置大小,创建布局,把按钮放进去。

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

    resize(800, 600);

    //创建水平布局
    QHBoxLayout *pHlay = new QHBoxLayout(this);

    //创建音量按钮并添加到布局水平布局里
    CVolumeButton* pVolumeButton = new CVolumeButton(this);
    pHlay->addWidget(pVolumeButton);
}

3、创建滑块 

 3.1cvolumesliderdialog类创建滑块

这个类本身继承自QDialog,主要作用是创建QSlider对象并添加到布局中。

QSlider是用于控制边界值的经典小部件。它允许用户沿水平或垂直凹槽移动QSlider 的滑块,并将 滑块 的位置转换为合法范围内的整数值。

    //设置对话框的固定大小为(40, 200)
    this->setFixedSize(40, 200);
    
    //创建一个垂直布局管理器pVLay,
    QVBoxLayout* pVLay = new QVBoxLayout(this);
    m_pSlider = new QSlider(this);
    
    //将一个垂直方向的QSlider对象m_pSlider添加到布局中
    m_pSlider->setOrientation(Qt::Vertical);
    pVLay->addWidget(m_pSlider);
3.2设置窗口标志

另外比较重要的一点,把设置窗口标志为无边框窗口和ToolTip提示框样式

这里用到一个ToolTip,ToolTip = Popup | Sheet ,可以实现滑块悬浮时显示,离开时消失

setFixedSize(40, 120);
    //设置窗口标志为无边框窗口和ToolTip提示框样式
    //ToolTip = Popup | Sheet,
    setWindowFlags(Qt::FramelessWindowHint | Qt::ToolTip);   //ToolTip : 悬浮时显示,离开时消失
    setStyleSheet("QDialog{background-color: rgba(54, 54, 54, 0.5);}");  //0.5表示透明度,0表示全透明、1表示不透明;
//也可以使用百分百表示如: frm->setStyleSheet(“QFrame{background-color: rgba(255, 0, 0, 50%);}”);
3.3修复bug

bug: bool QWidget::event(QEvent *event)这个方法设置popup后,dialog会有窗口阴影

需要去除就重写event函数,

bool CVolumeSliderDialog::event(QEvent* event)
{
    //定义一个静态布尔变量class_amended,用于跟踪是否已经修改了窗口类。
    static bool class_amended = false;

    //事件类型为WinIdChange,表示窗口标识已更改。
    if (event->type() == QEvent::WinIdChange)
    {
        //获取对话框的窗口句柄
        HWND hwnd = (HWND)winId();

        //检查是否已经修改了窗口类
        if (class_amended == false)
        {
            class_amended = true;//将class_amended设置为true,表示已经修改了窗口类
            DWORD class_style = ::GetClassLong(hwnd, GCL_STYLE);//获取窗口类的样式
            //将CS_DROPSHADOW样式从窗口类样式中移除,这样可以去除窗口阴影
            class_style &= ~CS_DROPSHADOW;
            //将修改后的窗口类样式应用到窗口
            ::SetClassLong(hwnd, GCL_STYLE, class_style); // windows系统函数
        }
    }

    //调用基类QWidget的event函数来处理事件,确保其他事件得到正常处理
    return QWidget::event(event);
}

注意这个完全可以作为一个通用类,需要的时候直接使用。

4、创建音量按钮

cvolumebutton类实现音量按钮的创建和滑块的绑定以及逻辑实现

主要是几个逻辑实现
 

1、纯代码创建一个音量按钮
2、创建滑块并定位在按钮上方合适位置
3、判断是否点击按钮需要静音来改变滑块上的位置
4、通过定时器判断鼠标是否离开按钮和滑块区域,离开滑块就消失
4.1首先通过重写paintEvent方法,实现了在CVolumeButton控件上绘制一个按钮外观的功能 
//代码可重用
void CVolumeButton::paintEvent(QPaintEvent*)
{
    //QStylePainter用于绘制基于QStyle的外观元素
    QStylePainter p(this);

    //QStyleOptionButton用于存储按钮控件的外观选项,如颜色、边框等
    QStyleOptionButton option;

    //调用initStyleOption方法初始化option对象
    //initStyleOption通常用于设置控件的外观选项,以便正确绘制控件
    initStyleOption(&option);

    //使用QStylePainter对象p绘制一个CE_PushButton类型的控件,参数为option对象。
    //这将根据option中的外观选项绘制按钮的外观,如背景、文本等
    p.drawControl(QStyle::CE_PushButton, option);
}
4.2然后在鼠标进入声音按钮时,创建并显示一个音量滑块对象,将其定位在合适的位置,并启动一个定时器。同时,建立了一个信号与槽的连接,以便在滑块数值变化时通知其他部分。 
void CVolumeButton::enterEvent(QEvent* event)
{
    //判断并初始化滑块对象,确保只有在需要时才创建滑块对象
    if (!m_pVolumeSliderDlg)
        m_pVolumeSliderDlg = new CVolumeSliderDialog(this);


    QPoint p1 = this->mapToGlobal(QPoint(0, 0));  //声音按钮左上角相对于桌面的绝对位置
    QRect rect1 = this->rect();    //获取声音按钮的矩形区域
    //获取滑块对象的矩形区域,rect包含标题栏,去掉标题栏后height不变
    QRect rect2 = m_pVolumeSliderDlg->rect();

    //计算滑块对象在水平方向上的位置,使其水平居中于声音按钮
    //这里计算很简单,就是按钮的一半减去滑块的一半,就是多出来的那部分
    //按钮的x加上多出来那部分,就能得到滑块居中的x位置
    int x = p1.x() + (rect1.width() - rect2.width()) / 2;

    //计算滑块对象在垂直方向上的位置,使其位于声音按钮上方并略微偏移5px
    //按钮的y减去滑块的长度再减去偏移的5px,就是滑块的左上角y位置
    int y = p1.y() - rect2.height() - 5;
    //滑块对象移动到计算得到的位置
    m_pVolumeSliderDlg->move(x, y);   //move是相对于桌面原点的位置

    m_pVolumeSliderDlg->show();//显示滑块对象
    m_timerId = startTimer(250);//动一个定时器,每250毫秒触发一次定时器事件

    connect(m_pVolumeSliderDlg, &CVolumeSliderDialog::sig_SliderValueChanged, [=](int value) {
        emit sig_VolumeValue(value);
    });
}
4.3检测鼠标按下事件实现静音
void CVolumeButton::mousePressEvent(QMouseEvent* event)
{
    if (event->button() == Qt::LeftButton)//检查鼠标按下的按钮是否是左键
    {
        m_isMute = !m_isMute; //切换静音状态,真假切换
        if (m_isMute)//如果当前为静音状态
        {
            //如果滑块对象存在,将滑块值设置为0,表示静音状态
            if (m_pVolumeSliderDlg)
                m_pVolumeSliderDlg->setSliderValue(0);
        }
        else
        {
            //如果滑块对象存在,将滑块值设置为50,表示非静音状态下的默认音量
            if (m_pVolumeSliderDlg)
                m_pVolumeSliderDlg->setSliderValue(50);
        }
    }
}
4.4在定时器事件中检测鼠标位置,如果鼠标移出音量滑块对象或声音按钮的区域,则隐藏音量滑块对象;如果音量滑块对象不存在或不可见,则停止定时器。
void CVolumeButton::timerEvent(QTimerEvent* event)
{
    //检查音量滑块对象是否存在且可见
    if ((m_pVolumeSliderDlg != nullptr) && (m_pVolumeSliderDlg->isVisible()))
    {
        QPoint p1 = QCursor::pos();   //鼠标绝对位置
        if (m_pVolumeSliderDlg)
        {
            QRect rect1 = this->rect();  //获取声音按钮的矩形区域
            QRect rect2 = m_pVolumeSliderDlg->rect();//获取音量滑块对象的矩形区域
            QRect rect3 = m_pVolumeSliderDlg->geometry();//获取音量滑块对象的几何信息

            QPoint p2 = this->mapToGlobal(QPoint(0, 0));   //声音按钮左上角相对于桌面的绝对位置

            //已知:音量框宽40 > 按钮宽30
            //创建一个矩形区域,用于检测鼠标位置是否在音量滑块对象或声音按钮上
            QRect area(rect3.left(), rect3.top(), rect2.width(), p2.y() + rect1.height() - rect3.top()); //左上宽高

            if (!area.contains(p1))//如果鼠标位置不在矩形区域内
            {
                m_pVolumeSliderDlg->hide();//隐藏音量滑块对象
            }
        }
    }
    else //如果鼠标位置在矩形区域内
    {
        //停止定时器,即终止定时器事件的触发
        killTimer(m_timerId);
    }
}

都看到这里了,点个赞再走呗朋友~

加油吧,预祝大家变得更强!

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

观察者模式:实现高效事件驱动编程的策略

在软件开发中&#xff0c;观察者模式是一种关键的行为型设计模式&#xff0c;用于建立对象间的一种依赖关系&#xff0c;使得当一个对象改变状态时&#xff0c;所有依赖于它的对象都会得到通知并被自动更新。这种模式是事件监听和响应编程的基石。本文将详细介绍观察者模式的定…

【JAVA基础篇教学】第十篇:Java中Map详解说明

博主打算从0-1讲解下java基础教学&#xff0c;今天教学第十篇&#xff1a;Java中Map详解说明。 在 Java 编程中&#xff0c;Map 接口代表了一种键值对的集合&#xff0c;每个键对应一个值。Map 接口提供了一系列操作方法&#xff0c;可以方便地对键值对进行增删改查等操作。本…

【汇编】_Visual Studio2019写32位汇编

目录 第一步&#xff1a;创建新项目 1. 空项目—下一步 2. 选择位置—填写项目名—创建 第二步&#xff1a;项目生成依赖项 1. 右击项目名—生成依赖项—生成自定义 2. 选中masm—确定 第三步&#xff1a;创建源文件 1. 源文件—添加—新建项 2. 选择C文件—创建新文件…

数据库被rmallox勒索病毒加密,如何还原?

近年来&#xff0c;网络安全问题日益严峻&#xff0c;勒索病毒作为其中的一种恶意软件&#xff0c;已成为网络安全领域的一大难题。其中&#xff0c;rmallox勒索病毒以其高度的隐蔽性和破坏性&#xff0c;给不少企业和个人带来了严重损失。本文将从rmallox勒索病毒的特点、传播…

Unity-超级方便的Excel 读写插件

超级无敌棒棒糖&#x1f58c; &#x1f32d;功能介绍&#x1f355; Demo&#x1f333;准备一个数据类&#x1f333;准备一个Excel&#x1f333;导入Excel&#x1f333;行数据自动转换&#x1f333;导出到Excel &#x1f371;新增映射字段类型 &#x1f32d;功能介绍 &#x1f…

监控系统泛滥:CTO 面临的隐形成本危机

在信息技术飞速发展的今天&#xff0c;构建和维护现代化的数字系统变得日益复杂和关键&#xff1b;在这样的背景下&#xff0c;监控系统的作用变得尤为突出。正如业界广泛流传的一句经验之谈“无监控&#xff0c;不运维”所揭示的道理一样&#xff0c;对于任何具有一定复杂性的…

进程程序替换

文章目录 程序替换原理替换函数函数解释调用举例 程序替换原理 用新进程的代码和数据覆盖旧进程的代码和数据&#xff0c;没有创建新进程&#xff0c;用旧进程的壳执行了新进程。 站在被替换进程的角度&#xff1a;本质就是程序从磁盘加载到了内存。 怎么加载呢&#xff1f;…

【电控笔记6】电流回路+延迟效应

问题提出 数字控制系统的delay: 5.4节有介绍T0=0.5TS 低通滤波器的时间常数? 可用示例程序 m2 2 1b 如下图画出开环系统的伯德图进行比较,如图 2-2-4 所示,由于延迟组件会侵蚀系统的相位,因此从图可以看出,加入延迟效应后,q轴电流回路的相位裕度(Phase Margin) 从…

【数据结构】单链表(二)

目录 1.查找数据 2.指定位置插入和删除节点 2.1 指定位置之前插入节点 2.2 指定位置之后插入节点 2.3 删除指定位置节点 2.4 删除指定位置之后的节点 3.销毁链表 我们接着上一篇【数据结构】单链表&#xff08;一&#xff09;-CSDN博客 来继续实现单链表 1.查找数据 在…

c# wpf datagrid 简单试验

1.概要 datagrid 一个列表类的控件 2.代码 <Window x:Class"WpfApp2.Window3"xmlns"http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x"http://schemas.microsoft.com/winfx/2006/xaml"xmlns:d"http://schemas.mic…

关于 STM32WL LSE 添加反馈电阻后无法起振问题

1. 问题描述 客户调试 STM32WLE5JB 样机的时候遇到这样一个问题&#xff1a;在调试 LPUART&#xff0c;不打开外部时钟的时候&#xff0c;能够正常打印&#xff0c;若开启外部的 HSE 和 LSE 后就没有打印。 2. 问题确认 发现上述问题时&#xff0c;客户使用 STM32CubeMX 生成…

数字图像处理项目——模糊图像边缘检测算法设计及实现(论文/代码)

完整的论文代码见文章末尾 以下为部分内容 摘要 本研究旨在针对大脑核磁图像中的黑色腔体进行有效分割&#xff0c;以提供可靠的腔体定位和分析。为此&#xff0c;采用了三种常用的图像分割方法&#xff1a;8邻域区域生长法、Canny算子边缘检测和8邻域边界跟踪法。 首先&…

ES13:类的新增特性、最外层的await、at...

1-类的新增特性 类私有属性和方法&#xff1a;# class Person{// 不需要传参、一开始就需要初始化的&#xff0c;就可以在类的最外面直接声明这个成员state{a:1,b:2}constructor(name,age){this.namename;this.ageage;}}在属性和方法前加#表示私有 #obj{} #prest(){}静态成员…

数据结构--链式栈

一.链式栈的栈顶在哪里? 二.链栈的结构: typedef struct LSNode{ int data; struct LSNode* next; }LSNode ,*PLStack; //链栈的节点.由于栈顶在第一个数据节点,所以不需要top指针 三.链式栈的实现: //初始化LSNode* p (LSNode*)malloc(sizeof(LSNode));assert(p ! NULL)…

C语言调用Python

目录 1.直接调用python语句 头文件引用 2.调用无参有参函数 1、调用无参函数 1.建立nopara.py文件 2.使用c语言根据上面流程进行调用 2、调用有参函数 1.建立nopara.py文件 2.使用c语言根据上面流程进行调用 C语言调用python需要我们已经安装好了libpython3的 dev依赖…

【Shell语言学堂】数组练习题

数组练习 1、使用数组和循环实现冒泡排序2、将冒泡排序的代码重构为2个函数&#xff0c;2个关系是a函数调用b函数自定义数组参数&#xff1a; 3、声明一个存储的全整数数组&#xff0c;对其中的每一个值进行10处理4、对硬盘使用空间占比的排序5、对当前目录的文件大小进行排序 …

小型企业网络安全指南

许多小型企业刚刚起步&#xff0c;没有大公司所拥有的相同资源来保护其数据。他们不仅可能没有资金来支持多样化的安全计划&#xff0c;而且也可能没有人力或时间。 网络犯罪分子知道小型企业缺乏这些资源&#xff0c;并利用这些资源来谋取利益。遭受网络攻击后&#xff0c;小…

7、configMap

1、configMap是什么 类似与pod的配置中心&#xff0c;不会因为pod的创建销毁&#xff0c;相关配置发生改变 pod定义硬编码意味着需要有效区分⽣产环境与开发过程中的pod 定义。为了能在多个环境下复⽤pod的定义&#xff0c;需要将配置从pod定义描 述中解耦出来。 2、向容器中…

2024年MathorCup数模竞赛C题超详细解题思路

妈妈杯本次比赛报名队伍号高达12500&#xff0c;这也就意味着大概一万只队伍参加报名&#xff0c;仅仅在报名人数这一项&#xff0c;妈妈杯已经成为美赛国赛之后的第三大竞赛。C题作为本次竞赛最简单也最容易获奖的题目&#xff0c;本文将给大家带来手把手超详细解题思路。 注…

【Git教程】(十二)工作流之项目设置 — 何时使用工作流,工作流的结构,项目设置概述、执行过程及其实现 ~

Git教程 工作流之项目设置 1️⃣ 何时使用工作流2️⃣ 工作流的结构3️⃣ 概述4️⃣ 使用要求5️⃣ 执行过程及其实现5.1 基于项目目录创建一个新的版本库5.2 以文件访问的方式共享版本库5.3 用 Git daemon 来共享版本库5.4 用 HTTP 协议来共享版本库5.5 用 SSH 协议来共享版…