Qt 使用QDialog实现界面遮罩的示例(蒙版)
作者:slowlytalk 发布时间:2022-10-31 07:58:37
写应用程序的过程中,弹窗是个避免不了的功能,显示中,假设弹窗背景色和主窗口背景色相差不多,甚至是一样的时候,就会存在一个比较严重的人机交互和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
来源:https://juejin.cn/post/7091616795076853797
猜你喜欢
- 本文实例讲述了基于私钥加密公钥解密的RSA算法C#实现方法,是一种应用十分广泛的算法。分享给大家供大家参考之用。具体方法如下:一、概述RSA
- DeferredResult的超时处理,采用委托机制,也就是在实例DeferredResult时给予一个超时时长(毫秒),同时在onTime
- 1、什么是FeignFeign 是 Spring Cloud Netflix 组件中的一个轻量级 RESTful 的 HTTP 服务客户端,
- Spring AOP后置通知修改响应httpstatus1.定义Aspect/** * 响应体切面 * 后置通知修改httpstatus *
- SpringBoot配置文件的替换使用spring.profiles.active在工作中,测试或上线的时候一定会遇到的问题就是修改配置。一
- 把idea中的项目导入github仓库中步骤详解做完项目进行云端保存是很必要的,我都是存放在github中。所以废活少说直接开始啦。前提是已
- 本文实例为大家分享了java实现猜拳游戏的具体代码,供大家参考,具体内容如下package com.farsight.session7;im
- Spring Boot如何实现分布式锁的自动释放在分布式系统中,为了保证数据的一致性和可靠性,常常需要使用分布式锁。在实际开发中,我们可以使
- 本文主要介绍了java(包括springboot)读取resources下文件方式实现,分享给大家,具体如下:1、使用项目内路径读取,该路径
- 第一步新建txt文件,写入内容我是放在D盘下的,数据以逗号隔开的,是英文逗号第二步读取数据在需要读取数据的页面,添加代码,就可以了 priv
- 前言:2022年Java将有什么新的特性和改进,我相信很多Java开发者都想知道。结合Java语言架构师布莱恩·格茨(
- 本文实例为大家分享了SpringMVC实现文件上传和下载的具体代码,供大家参考,具体内容如下文件上传第一步,加入jar包:commons-f
- 本文实例讲述了Java实现多个wav文件合成一个的方法。分享给大家供大家参考,具体如下:前面一篇介绍了java切割wav音频文件的方法,这里
- 这篇文章主要介绍了springboot配置aop切面日志打印过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习
- 背景近期因实际项目需要,在特定操作下触发定位请求,取到用户位置及附近位置。问题:经初步选型,最终决定接入百度定位,按照百度定位SDK And
- 前言Set 表示由无重复对象组成的集合,也是集合框架中重要的一种集合类型,直接扩展自 Collection 接口。在一个 Set 中,不能有
- 一、查询中排除标识字段1.1 测试查询@Testpublic void findAllTest() { List&
- 1.前置准备默认服务器上的hadoop服务已经启动本地如果是windows环境,需要本地配置下hadoop的环境变量本地配置hadoop的环
- 文章按照 Socket 的 创建、连接、传输数据、释放资源的过程来写。给出方法、参数的详细信息。一,网络基础说到 Socket,需要学习一下
- Maven Release当我们的项目达到了当前的目标,在经过检测后不需要改变。这时我们就需要将SNAPSHOT版本打包成RELEASE版本