QT 窗体遮罩

当主窗体需要弹出一个窗体用于给用户输入时,如果只是单存的显示出来,就会显得很单调;

如果加上背景黑色半透明的话,效果就会很不错!如下图效果:

使用

新建项目,并新建窗体用于显示弹窗;

主窗体是widget,手动新建的窗体是dialogform

然后在项目中添加如下两个代码文件

maskwidget.h

cpp 复制代码
#ifndef MASKWIDGET_H
#define MASKWIDGET_H

/**
 * 1. 可设置需要遮罩的主窗体,自动跟随主窗体位置显示遮罩面积
 * 2. 只需要将弹窗窗体的名称一开始传入队列即可,足够简单
 * 3. 可设置透明度
 * 4. 可设置遮罩层颜色
 * 5. 不阻塞消息循坏
 */

#include <QWidget>

#ifdef quc
class Q_DECL_EXPORT MaskWidget : public QWidget
#else
class MaskWidget : public QWidget
#endif

{
    Q_OBJECT
public:
    static MaskWidget *Instance();
    explicit MaskWidget(QWidget *parent = 0);

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

private:
    static QScopedPointer<MaskWidget> self;
    QWidget *mainWidget;        //需要遮罩的主窗体
    QStringList dialogNames;    //可能弹窗的窗体对象名称集合链表

public Q_SLOTS:
    void setMainWidget(QWidget *mainWidget);
    void setDialogNames(const QStringList &dialogNames);
    void setBgColor(const QColor &bgColor);
    void setOpacity(double opacity);

private:
    QColor m_bgColor; // 添加颜色成员变量
};

#endif // MASKWIDGET_H

maskwidget.cpp

cpp 复制代码
#pragma execution_character_set("utf-8")

#include "maskwidget.h"
#include "qmutex.h"
#include "qdesktopwidget.h"
#include "qapplication.h"
#include "qdebug.h"

QScopedPointer<MaskWidget> MaskWidget::self;
MaskWidget *MaskWidget::Instance()
{
    if (self.isNull()) {
        static QMutex mutex;
        QMutexLocker locker(&mutex);
        if (self.isNull()) {
            self.reset(new MaskWidget);
        }
    }

    return self.data();
}


MaskWidget::MaskWidget(QWidget *parent) : QWidget(parent)
{
    mainWidget = nullptr;
    m_bgColor = QColor(0, 0, 0);

    // 设置初始透明度
    setOpacity(0.7);

    // 不设置主窗体则遮罩层大小为默认桌面大小
    this->setGeometry(qApp->desktop()->geometry());

    // 修改窗口标志,增强兼容性
    this->setWindowFlags(Qt::FramelessWindowHint | Qt::Tool);

    // 设置属性,允许透明区域点击穿透(部分Linux桌面环境支持)
    this->setAttribute(Qt::WA_TransparentForMouseEvents, false);

    // 绑定全局事件,过滤弹窗窗体进行处理
    qApp->installEventFilter(this);
}

void MaskWidget::setMainWidget(QWidget *mainWidget)
{
    if (this->mainWidget != mainWidget) {
        this->mainWidget = mainWidget;
    }
}

void MaskWidget::setDialogNames(const QStringList &dialogNames)
{
    if (this->dialogNames != dialogNames) {
        this->dialogNames = dialogNames;
    }
}

void MaskWidget::setOpacity(double opacity)
{
    this->setWindowOpacity(opacity);

    // Linux兼容性:同时使用样式表设置透明度
    int alpha = static_cast<int>(opacity * 255);
    QString style = QString("background-color: rgba(%1, %2, %3, %4);")
                    .arg(m_bgColor.red())
                    .arg(m_bgColor.green())
                    .arg(m_bgColor.blue())
                    .arg(alpha);
    this->setStyleSheet(style);
}

void MaskWidget::setBgColor(const QColor &bgColor)
{
    m_bgColor = bgColor;
    QPalette palette = this->palette();
    palette.setBrush(QPalette::Window, bgColor);
    this->setPalette(palette);

    // 更新样式表
    setOpacity(this->windowOpacity());
}

void MaskWidget::showEvent(QShowEvent *)
{
    if (mainWidget != 0) {
        this->setGeometry(mainWidget->geometry());
    }
}

bool MaskWidget::eventFilter(QObject *obj, QEvent *event)
{
    if (event->type() == QEvent::Show) {
        if (dialogNames.contains(obj->objectName())) {
            this->show();
            this->activateWindow();
            QWidget *w = (QWidget *)obj;
            w->activateWindow();
        }
    } else if (event->type() == QEvent::Hide) {
        if (dialogNames.contains(obj->objectName())) {
            this->hide();
        }
    } else if (event->type() == QEvent::WindowActivate) {
        //当主窗体激活时,同时激活遮罩层
        if (mainWidget != 0) {
            if (obj->objectName() == mainWidget->objectName()) {
                if (this->isVisible()) {
                    this->activateWindow();
                }
            }
        }
    }

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

之后,就可以在主窗体(widget.cpp)中添加如下代码进行测试:

widget.cpp

cpp 复制代码
#include "widget.h"
#include "ui_widget.h"


#include <QPushButton>
#include <QDialog>
#include <QDebug>

#include "maskwidget/maskwidget.h"
#include "dialogform.h"

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

    initMaskWidget();


    QPushButton *btn = new QPushButton("dialog弹窗", this);
    btn->move(width() / 2, height() / 2);
    connect(btn, &QPushButton::clicked, this, &Widget::onBtnClicked);
}

Widget::~Widget()
{
    delete ui;
}

void Widget::initMaskWidget()
{
    qDebug() << "Initializing MaskWidget...";

    MaskWidget *mask = MaskWidget::Instance();
    mask->setMainWidget(this);  // 设置主窗口

    // 设置需要遮罩的对话框名称
    QStringList dialogNames;
    dialogNames << "DialogForm";  // 必须与窗口设置的objectName一致

    mask->setDialogNames(dialogNames);
    mask->setBgColor(QColor(0, 0, 0));  // 使用黑色背景
    mask->setOpacity(0.6);  // 增加不透明度

    qDebug() << "MaskWidget initialized with dialogNames:" << dialogNames;
}

void Widget::onBtnClicked()
{
    DialogForm d;
    d.exec();
//    d.show();		
}

重点关注void Widget::initMaskWidget()是如何处理的。

相关推荐
mumu_wangwei3 小时前
【QFS】Golang自研的QFS分布式文件系统,QFS文件系统使用
开发语言·后端·golang
techdashen4 小时前
在 Rust 异步接口的丛林中生存:从同步 I/O 到手写异步状态机
开发语言·后端·rust
为思念酝酿的痛4 小时前
Linux线程
linux·服务器·后端
小江的记录本4 小时前
【Kafka核心】Kafka 3.0+ KRaft模式(替代ZooKeeper)核心原理与优势
java·数据库·分布式·后端·zookeeper·kafka·rabbitmq
星栈4 小时前
我在 Rust 全栈项目里用 JWT 做无状态认证
前端·后端·全栈
苍何4 小时前
实测 GLM5.1 高速版,快到离谱还不掉智商
后端
苍何4 小时前
企业微信新出的 AI 能力,用完想安利给全公司
后端
苍何4 小时前
每月省一千,我雇了支 7×24 云端 Agent 团队
后端
苍何4 小时前
12 天 4.2K 的 Star,我的 GPT-image2 开源项目火了!
后端