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

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

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

相关推荐
用户805533698031 天前
不止三件套:QObject 属性系统全关键字与运行时反射!
c++·qt
xcyxiner1 天前
DicomViewer (vcpkg Windows和ubuntu编译)7
qt
Quz6 天前
QML Hello World 入门示例
qt
xcyxiner9 天前
DicomViewer (dcmtk读取dcm文件)5
qt
xcyxiner10 天前
DicomViewer (后台线程处理文件)4
qt
xcyxiner10 天前
DicomViewer (添加模型类)3
qt
xcyxiner11 天前
DicomViewer (目录调整) 2
qt
xcyxiner11 天前
dcmtk vtk vtk-dicom(gdcm) 编译(debug) v2
qt
LDR00613 天前
Type-C 快充全面升级!LDR6601 赋能个人护理便携电机,重塑剃须刀 / 理发器新体验
c语言·开发语言
雪碧聊技术13 天前
Tree.js是什么?一文讲透
开发语言·javascript·ecmascript