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

news2024/11/23 10:45:38

往期回顾:

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

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

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

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

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

相关文章

木头姐最新访谈:英伟达“卖早了”没什么,新买的Coinbase涨得更好

木头姐表示&#xff0c;虽然英伟达在本轮AI竞赛中处于领先地位&#xff0c;但要证明其定价合理&#xff0c;还需要找到更多能够证明AI正在加速企业收入增长的证据。她仍看好特斯拉的长期前景&#xff0c;主要基于其在自动驾驶和人形机器人领域的领先地位。 木头姐精彩观点 我们…

外观模式:简化复杂系统的统一接口

在面向对象的软件开发中&#xff0c;外观模式是一种常用的结构型设计模式&#xff0c;旨在为复杂的系统提供一个简化的接口。通过创建一个统一的高级接口&#xff0c;这个模式帮助客户端通过一个简单的方式与复杂的子系统交互。本文将详细介绍外观模式的定义、实现、应用场景以…

Java 怎么捕捉 Windows 中前台窗口的改变?

在Java中捕捉Windows中前台窗口的改变通常需要使用JNI&#xff08;Java Native Interface&#xff09;来调用Windows API。Windows API提供了一系列函数来获取有关窗口和进程的信息&#xff0c;通过使用这些函数&#xff0c;我们可以实现在Java程序中监视和捕捉Windows前台窗口…

redux-persist

npm install redux-persist 目录&#xff1a;&#xff08;分模块&#xff09; 之前未加入持久化的代码&#xff1a; import { legacy_createStore as createStore } from redux import { combineReducers } from "redux"; import countReducer from ./modules/cou…

小红的白色字符串

题目描述 小红拿到了一个字符串&#xff0c;她准备将一些字母变成白色&#xff0c;变成白色的字母看上去就和空格一样&#xff0c;这样字符串就变成了一些单词。 现在小红希望&#xff0c;每个单词都满足以下两种情况中的一种&#xff1a; 1.开头第一个大写&#xff0c;其余为…

基于linux进一步理解核间通讯

芯片架构分为同构和异构: 如下图TC397: 如下图TDA4: 如下图STM32MP157: 非对称多处理结构(AMP): AMP 结构是指每个内核运行自己的 OS 或同一 OS 的独立实例&#

bugku-web-file_get_contents

<?php extract($_GET); if (!empty($ac)){$f trim(file_get_contents($fn));if ($ac $f){echo "<p>This is flag:" ." $flag</p>";}else{echo "<p>sorry!</p>";} } ?> 这里涉及到几个不常用的函数 这里直接构…

UE 模型学习

1. UE中任何模型都是有多个三角面构成的&#xff0c;模型有一个顶点数组&#xff0c;根据右手螺旋定则和三个顶点顺序确定三角面的法线。 注&#xff1a;当三角面的法线方向与相机法线方向相同时&#xff0c;此时看不见三角面&#xff1b;当两法线方向相反&#xff0c;此时才能…

企业搭建分销商城系统要注意什么?

分销商城系统是一种专门设计出来的电商平台&#xff0c;旨在通过构建多层级的分销网络体系&#xff0c;帮助企业将产品或服务高效地推广和销售至终端消费者。那么&#xff0c;搭建分销商城系统要注意什么呢&#xff1f;我们从优势、功能、玩法来进行分析。 一、分销商城有哪些…

Faster RCNN训练自己的数据集【傻瓜式教程】

一、下载源码 本文采用的源码是&#xff1a;https://github.com/dBeker/Faster-RCNN-TensorFlow-Python3 二、配置环境 由于本文是小白教程&#xff0c;光写几个环境怕有人配置不好或者配置版本搞乱。Faster RCNN配置环境比较复杂。我在这直接贴图我的环境版本图&#xff1a…

基础的unicorn模拟简介与库函数调用方案与代码实例

运行环境&#xff1a;python 基本的导入&#xff1a;from unicorn import * 简介 1. unicorn对象的初始化&#xff1a; UC Uc(unicorn_const.UC_ARCH_X86,unicorn_const.UC_MODE_16)Uc接收的二值分别指定将模拟的架构和程序位数。后续操作的寄存器&#xff08;如rax、eax、…

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

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

机器学习基础入门(一)(机器学习定义及分类)

机器学习定义 给予计算机无需特意带有目的性编程便有学习能力的算法 深度学习算法 主要有监督学习和非监督学习两类 监督学习&#xff08;supervised learning&#xff09; 定义 1、学习由x映射到y的映射关系 2、主动给予机器学习算法正确示例&#xff0c;算法通过示例来学习…

NodeJS解压版环境配置

前往官网下载最新版NodeJS 下载地址&#xff1a;Node.js — Download Node.js 如下图选择“Prebuilt Binaries”(预构建二进制文件)进行下载 解压缩下载的zip压缩包 创建node_global与node_cache文件夹 node_global Node全局目录 node_cache Node缓存目录 设置…

php站长在线工具箱源码优化版

环境要求 PHP > 7.4MySQL > 5.6fileinfo扩展使用Redis缓存需安装Redis扩展 源码下载地址&#xff1a;php站长在线工具箱源码优化版.zip

创新之作:淘宝扭蛋机小程序,让购物与娱乐完美结合

淘宝扭蛋机小程序&#xff0c;是淘宝平台为您精心打造的一款集娱乐与购物于一体的全新体验。在这里&#xff0c;每一次的扭动都蕴含着无限的惊喜与乐趣&#xff0c;让您在轻松愉快的氛围中&#xff0c;发现更多心仪的商品&#xff0c;享受更多购物的乐趣。 我们深知您对新鲜事…

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

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

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

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

Vue3 ts环境下的PropType

简介 在Typscript中&#xff0c;我们可以使用PropType进行类型的推断与验证。在日常的开发中我们常常会遇到下面这样的场景&#xff1a; 我们通过request请求从服务端获取了一条数据&#xff0c;数据是个Array的格式&#xff0c;Array中的每个元素又是一个对象&#xff0c;像下…

BFS宽度优先搜索例题(蓝桥杯)——逃跑的牛

问题描述&#xff1a; 农夫John的一头牛逃跑了&#xff0c;他想要将逃跑的牛找回来。现假设农夫John和牛的位置都在一条直线上&#xff0c;农夫John的初始位置为N&#xff08;0≤N≤100,000&#xff09;&#xff0c;牛的初始位置为K&#xff08;0≤K≤100,000&#xff09;。农夫…