Qt 使用QDialog实现界面遮罩的示例(蒙版)

 更新时间:2022年04月29日 11:43:25   作者:slowlytalk  
界面遮罩在很多时候都可以用到,例如弹窗,本文主要介绍了Qt 使用QDialog实现界面遮罩的示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

写应用程序的过程中,弹窗是个避免不了的功能,显示中,假设弹窗背景色和主窗口背景色相差不多,甚至是一样的时候,就会存在一个比较严重的人机交互和UI显示的问题,找到弹窗的边界是比较麻烦的一件事。但是如果我们能在弹窗显示的时候被主窗口其他的部位增加一个背景色,这个背景色与弹窗的背景色形成比较大的色差,那么是不是就能够很容易的找到弹窗的边界。因此,我们实现一个自定义组件,可随便设置需要遮罩的主窗口,并且能够让其跟随主窗口的移动而移动。

先来看下效果:

  • 根据需求功能,我们需要提供设置主窗口的接口,同样的,并不是说所有的窗口都需要进行遮罩,那么我们也同样需要知道哪些窗口是需要遮罩的,因此,还需要提供一个判断的标准,在一个工程里面,每个UI文件的objectName是独一份的,因此我们可以通过这些objectName来判断哪些dialog需要遮罩。
  • 该类是在需要被遮罩的dialog显示出来的时候自动调用显示,而不需要手动调用,因此需要检测全局的事件循环。

以上,我们来看下该组件的头文件定义:

#ifndef MASK_WIDGET_H
#define MASK_WIDGET_H

#include <QDialog>

namespace Ui {
    class MaskWidget;
}
class MaskWidget : public QDialog
{
    Q_OBJECT
    Q_PROPERTY(QStringList names READ names WRITE setNames DESIGNABLE true)

public:

    static MaskWidget *instance();

    void setMainWidget(QWidget* pWidget);
    
    QStringList names() const;
 	void setNames(const QStringList& names);


protected:
    bool eventFilter(QObject *obj, QEvent *event);

private:
    explicit MaskWidget(QWidget *parent = Q_NULLPTR);
    ~MaskWidget();

private:
    Ui::MaskWidget* ui;
   
    QStringList m_listName{ QStringList() };

    QWidget* m_pMainWidget{ Q_NULLPTR };

    static MaskWidget* m_pSelf;
};

#endif // MASK_WIDGET_H

由上面的类定义也能够看出来,这个组件还是比较简单的,简单到只有两个接口和一个事件过滤函数,所以下面,我们来具体看下其中的实现。

首先是千篇一律的单例实现,该组件在整个工程中独一份就好,多了可能就会出现你想不到的情况(多层覆盖或者冲突了):

MaskWidget * MaskWidget::m_pSelf = Q_NULLPTR;

MaskWidget * MaskWidget::instance()
{
	if (m_pSelf == Q_NULLPTR)
	{
		m_pSelf = new MaskWidget;
	}
	return m_pSelf;
}

在其构造中,我们需要设置一些window相关的属性,并且将该窗口先隐藏起来,要不然程序一打开就会看到整个上面有一层灰蒙蒙的遮罩。其实最主要的是需要在其构造函数里面注册事件过滤。

MaskWidget::MaskWidget(QWidget *parent) : QDialog(parent), ui(new Ui::MaskWidget)
{
    ui->setupUi(this);
    hide();
    setWindowFlags(Qt::FramelessWindowHint | Qt::Tool |  Qt::WindowDoesNotAcceptFocus);
    qApp->installEventFilter(this);
}

在主程序启动之后,我们还要做两件事,也就是我们前面说的两个接口需要调用实现,一个是设置需要遮罩的主窗口,一个是需要设置弹出需要遮罩的窗口的名称,先看下设置主窗口。

void MaskWidget::setMainWidget(QWidget *pWidget)
{
    this->setFixedSize(QSize(pWidget->width(), pWidget->height()));
    this->setParent(pWidget);
    this->move(pWidget->x(), pWidget->y());
}

由上面可以看出,设置主窗口之后,我们将该组件的父类也设置为了主窗口,这样就能保证该组件显示出来的时候一定是以设置的主窗口为父节点进行显示,并且能够铺满整个主窗口。

显示窗口的设置也是比较简单的属性的操作方式,如下:

void MaskWidget::setNames(const QStringList& names)
{
     if(m_listName == names)
     {
         return;
     }
     m_listName = names;
 }

 QStringList MaskWidget::names() const
 {
     return names;
 }

在整个过程中,其实最主要的是事件过滤函数的实现,该函数基本包含了该组件的基本功能,下面我们看下该函数的实现。

bool MaskWidget::eventFilter(QObject *obj, QEvent *event)
{
    if(event->type() == QEvent::Hide)
    {
        if(m_listName.contains(obj->objectName()))
        {
            hide();
        }
        return QObject::eventFilter(obj, event);
    }

    if (event->type() == QEvent::Show) 
    {
        if (!m_listName.contains(obj->objectName()))
        {
            return QObject::eventFilter(obj, event);
        }

        show();

        auto pWidget = dynamic_cast<QWidget*>(obj);     //将object转换为普通QWidget
        if (Q_NULLPTR == pWidget)
        {
            return QObject::eventFilter(obj, event);
        }
        pWidget->activateWindow();
        pWidget->setFocus(Qt::ActiveWindowFocusReason);
        stackUnder(pWidget);    //将该窗口设置放到弹窗的下面

        if(Q_NULLPTR == m_pMainWidget)
        {
            return QObject::eventFilter(obj, event);
        }

        m_pMainWidget->stackUnder(this);    //将主窗口设置放到该组件界面下方,就能够有一个比较清晰的层次关系

        //下面是实现将弹窗的位置移动到主程序的正中间,在这边实现的目的是为了减少代码量,毕竟写代码能偷的懒还是一定要偷的
        QRect screenGeometry = m_pMainWidget->geometry();
        int x = screenGeometry.x() + (screenGeometry.width() - pWidget->width()) / 2;
        int y = screenGeometry.y() + (screenGeometry.height() - pWidget->height()) / 2;
        pWidget->move(x, y);
    } 

    return QObject::eventFilter(obj, event);
}

以上,该组件的全部功能介绍完了。

使用的过程中了,直接包含文件就能够使用,需要注意的是,弹出的dialog窗口的基类必须QDialog,并且在调用时使用QDialog::exec()函数实现模态。如果不实现模态的话,会出现一些意外,当然这些意外并不影响使用,只是交互上面会比较不友好。假设你的主程序不能移动,那么就会很不友好。

TestDialog dlg;
if(QDialog::Accept == dlg.exec())
{

}

测试代码链接:https://gitee.com/Gqian_com/customcomponent/tree/master/maskwidget

到此这篇关于Qt 使用QDialog实现界面遮罩的示例(蒙版)的文章就介绍到这了,更多相关Qt  QDialog界面遮罩内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:

相关文章

  • 深入解析Linux下\r\n的问题

    深入解析Linux下\r\n的问题

    本篇文章是对Linux下\r\n的问题进行了详细的分析介绍,需要的朋友参考下
    2013-05-05
  • C语言面试常见考点排序总结

    C语言面试常见考点排序总结

    深处开发岗,其实排序也是绕不开的环节,其中冒泡排序,选择排序,插入排序,归并排序,快速排序,堆排序也是我在秋招以来频繁问到的技术点,今天我们来重点聊聊排序
    2021-11-11
  • C++常见的stl容器与相关操作 示例解析

    C++常见的stl容器与相关操作 示例解析

    所谓容器,就是可以承载,包含元素的一个器件,它是STL六大组件之一,是容器、算法、迭代器中最重要也是最核心的一部分
    2022-10-10
  • C语言实现的统计php代码行数功能源码(支持文件夹、多目录)

    C语言实现的统计php代码行数功能源码(支持文件夹、多目录)

    这篇文章主要介绍了C语言实现的统计php代码行数功能源码,支持文件夹、多级目录的统计,在一些环境中会用到这个功能,需要的朋友可以参考下
    2014-08-08
  • VC中使用ADO开发数据库应用程序简明教程

    VC中使用ADO开发数据库应用程序简明教程

    这篇文章主要介绍了VC中使用ADO开发数据库应用程序的方法,结合实例形式详细讲述了ADO的原理及VC使用ADO开发数据库应用程序的相关技巧,具有一定参考借鉴价值,需要的朋友可以参考下
    2016-06-06
  • C++选择文件夹代码的封装

    C++选择文件夹代码的封装

    这篇文章主要介绍了C++选择文件夹代码的封装,实例展示了将选择文件夹功能代码封装为一个类并对其进行实例化调用的过程,对于学习C++程序设计有不错的参考价值,需要的朋友可以参考下
    2014-10-10
  • EasyC++编写头文件

    EasyC++编写头文件

    这篇文章主要介绍了C++编写头文件,在一个C++程序中,只包含两类文件——.cpp文件和.h文件。其中,.cpp文件被称作C++源文件,里面放的都是C++的源代码;而.h文件则被称作C++头文件,里面放的也是C++的源代码,感兴趣的小伙伴一起来看下面文章的详细介绍吧
    2021-12-12
  • C++利用伴随阵法实现矩阵求逆

    C++利用伴随阵法实现矩阵求逆

    这篇文章主要为大家详细介绍了C++如何利用伴随阵法实现矩阵求逆,文中的示例代码讲解详细,具有一定的学习和借鉴价值,需要的可以参考一下
    2023-02-02
  • 深入理解c/c++ 内存对齐

    深入理解c/c++ 内存对齐

    这篇文章主要介绍了c/c++ 内存对齐,有需要的朋友可以参考一下
    2014-01-01
  • C++实现简易五子棋游戏

    C++实现简易五子棋游戏

    这篇文章主要为大家详细介绍了C++实现简易五子棋游戏,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-07-07

最新评论