QWidget上叠加半透明QML组件显示方案

方案一 使用QQuickView 叠加到Widget上可以正常通过setGeometry修改位置,但是设置半透明效果时无法透过QML组件看到底层widget。本质上两个是独立的窗口无法做到半透明。改方案通过ImageProvider获取QML叠加窗口所在位置截图作为背景实现半透明效果

一、QML实现细节

根节点需为Rectangle

示例代码:

import QtQuick 2.12

import QtQuick.Controls 2.12

Rectangle {

id: root

width: 400

height: 300

anchors.centerIn: parent

复制代码
signal loginSuccess()   // ✅ 登录成功信号

Rectangle {
    anchors.fill: parent
    radius: 10
    color: "#1E2E3C"
    opacity: 0.50
}

Column {
    anchors.centerIn: parent
    spacing: 16
    width: 260

    Text {
        text: "用户登录"
        anchors.horizontalCenter: parent.horizontalCenter
        font.pixelSize: 18
        color: "#40C7FF"
    }

    TextField {
        id: userField
        placeholderText: "用户名"
        width: parent.width
    }

    TextField {
        id: pwdField
        placeholderText: "密码"
        echoMode: TextInput.Password
        width: parent.width
    }

    Row {
        id: btnRow
        spacing: 30
        width: parent.width
        height: 40

        Button {
            text: "注册"
            width: (btnRow.width - btnRow.spacing) / 2

            onClicked: {
                console.log("注册成功!")
            }
        }

        Button {
            text: "登录"
            width: (btnRow.width - btnRow.spacing) / 2

            onClicked: {
                // ✅ 防止未定义对象导致崩溃
                if (userField && pwdField &&
                    userField.text === "admin" &&
                    pwdField.text === "123456") {

                    if (root && root.loginSuccess)
                        root.loginSuccess()

                } else {
                    if (errorText)
                        errorText.visible = true
                }
            }
        }
    }


    Text {
        id: errorText
        text: "用户名或密码错误"
        color: "red"
        visible: false
        font.pixelSize: 12
    }
}

}

二、QWidget上叠加实现细节

复制代码
#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QGraphicsView>
#include <QGraphicsScene>
#include <QtQuickWidgets/QQuickWidget>
#include <QTimer>
#include <QQuickView>

#include "myquickwidget.h"
#include "imageprovider.h"

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = nullptr);
    ~MainWindow();
    void loadQml();
    QImage grabBackgroundForQml();
    Q_INVOKABLE QImage requestBackground();
signals:
    void geometryChanged();  // 用于同步 QML 覆盖层位置
    void aboutToClose();   // 通知外部我将关闭
    void backgroundChanged();

protected:
    void resizeEvent(QResizeEvent *event) override;
    void moveEvent(QMoveEvent *event) override;
    void closeEvent(QCloseEvent *event) override;

private:
    Ui::MainWindow *ui;
//    MyQuickWidget *qml;
    QWidget *qml;
    QQuickView * m_view;

    BackgroundImageProvider* m_bgProvider;
    QTimer* m_updateTimer;
    int panelX, panelY, panelW, panelH;

    void fixQmlPosition(QQuickWidget *qml);

};

#endif // MAINWINDOW_H

mainwindow.cpp

复制代码
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QQuickItem>
#include <QQuickView>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <QDebug>

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
//    qml = new QQuickWidget(this);
//    qml->setGeometry(0, 0, width(), height());
//    qml->setSource(QUrl("qrc:/student/StuMain.qml"));
//    qml->setClearColor(Qt::transparent);
//    qml->setAttribute(Qt::WA_AlwaysStackOnTop);
//    qml->setAttribute(Qt::WA_TranslucentBackground);
////    qml->setAttribute(Qt::WA_TransparentForMouseEvents);
//    qml->setResizeMode(QQuickWidget::SizeRootObjectToView);
//    qml->raise();

    // 4) 当 QML 加载完成后,强制设置 root size(处理 implicit size 问题)
//    connect(qml, &QQuickWidget::statusChanged, this, [=](QQuickWidget::Status s){
//        if (s == QQuickWidget::Ready) {
//            fixQmlPosition(qml);
//        }
//    });


     panelX = 200;
     panelY = 200;
     panelW = 400;
     panelH = 300;
    //获取图像截图作为背景
    auto provider = new BackgroundImageProvider();
    // 保存指针,以便得到背景后调用 provider->setImage();
    m_bgProvider = provider;

    QTimer *timer = new QTimer(this);
    connect(timer, &QTimer::timeout, this, [this]() {
        QRect area(panelX, panelY, panelW, panelH);
        QImage full = this->grab().toImage();
        QImage cropped = full.copy(area);
        m_bgProvider->setImage(cropped);
        emit backgroundChanged();
//        qDebug() << "emit backgroundChanged()";
    });
    timer->start(30);   // 30ms 实时刷新背景 (~33 FPS)

}


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

void MainWindow::loadQml()
{
//    qml = new MyQuickWidget(this);
//    qml->setGeometry(500, 400, 1400, 1300);
//    qml->setSource(QUrl("qrc:/student/LoginPanel.qml"));
//    qml->setClearColor(Qt::transparent);
//    qml->setAttribute(Qt::WA_AlwaysStackOnTop);
//    qml->setAttribute(Qt::WA_TranslucentBackground);
////    qml->setResizeMode(QQuickWidget::SizeRootObjectToView);
//    qml->setResizeMode(QQuickWidget::SizeViewToRootObject);
//    qml->raise();


      //使用QQuickView 叠加到Widget上可以正常通过setGeometry修改位置,但是设置半透明效果时无法透过QML组件看到底层widget。本质上两个是独立的窗口无法做到半透明
    QQuickView *view = new QQuickView();

    QQmlEngine *engine = view->engine();
    engine->addImageProvider(QLatin1String("background"), m_bgProvider);
    m_bgProvider->setImage(this->grab().toImage());
    view->rootContext()->setContextProperty("mainWin", this);
    view->setSource(QUrl("qrc:/student/LoginPanel.qml"));
    view->setColor(Qt::transparent);


    qml = QWidget::createWindowContainer(view, this);
    qml->setGeometry(panelX, panelY, panelW, panelH);

    qml->raise();
    m_view = view;
}

QImage MainWindow::grabBackgroundForQml()
{
    // this 为底层 QWidget (MainWindow)
    return this->grab().toImage();
}
Q_INVOKABLE QImage MainWindow::requestBackground()
{
    m_bgProvider->setImage(this->grab().toImage());
    return this->grab().toImage();
}

void MainWindow::resizeEvent(QResizeEvent *)
{
    emit geometryChanged();
}

void MainWindow::moveEvent(QMoveEvent *)
{
    emit geometryChanged();
}
void MainWindow::closeEvent(QCloseEvent *event)
{
    emit aboutToClose();
    QMainWindow::closeEvent(event);
}

void MainWindow::fixQmlPosition(QQuickWidget *qml)
{
    if (!qml || !qml->rootObject())
        return;

    QQuickItem *root = qml->rootObject();
    root->setX(0);            // ★ 强制 root 左上角对齐
    root->setY(0);
    root->setWidth(qml->width());
    root->setHeight(qml->height());
}

方案二 使用QQuickWidget加载QML文件

可以自动实现半透明效果。

存在问题:

1.鼠标事件上下层无法自由切换获取焦点,需增加事件转发(未调通)

2.窗口位置setGeometry未起作用

复制代码
   qml = new QQuickWidget(this);
   qml->setGeometry(0, 0, width(), height());
   qml->setSource(QUrl("qrc:/student/StuMain.qml"));
   qml->setClearColor(Qt::transparent);
   qml->setAttribute(Qt::WA_AlwaysStackOnTop);
   qml->setAttribute(Qt::WA_TranslucentBackground);
//    qml->setAttribute(Qt::WA_TransparentForMouseEvents);
   qml->setResizeMode(QQuickWidget::SizeRootObjectToView);
   qml->raise();

    4) 当 QML 加载完成后,强制设置 root size(处理 implicit size 问题)
   connect(qml, &QQuickWidget::statusChanged, this, [=](QQuickWidget::Status s){
       if (s == QQuickWidget::Ready) {
           fixQmlPosition(qml);
       }
   });
相关推荐
island13141 小时前
CANN GE(图引擎)深度解析:计算图优化管线、内存静态规划与异构任务的 Stream 调度机制
开发语言·人工智能·深度学习·神经网络
坚持就完事了1 小时前
Java中的集合
java·开发语言
魔芋红茶1 小时前
Python 项目版本控制
开发语言·python
云小逸1 小时前
【nmap源码解析】Nmap OS识别核心模块深度解析:osscan2.cc源码剖析(1)
开发语言·网络·学习·nmap
冰暮流星1 小时前
javascript之二重循环练习
开发语言·javascript·数据库
风指引着方向1 小时前
自定义算子开发入门:基于 CANN op-plugin 的扩展实践
开发语言
Fairy要carry1 小时前
面试-GRPO强化学习
开发语言·人工智能
Liekkas Kono1 小时前
RapidOCR Python 贡献指南
开发语言·python·rapidocr
张张努力变强2 小时前
C++ STL string 类:常用接口 + auto + 范围 for全攻略,字符串操作效率拉满
开发语言·数据结构·c++·算法·stl
xyq20242 小时前
Matplotlib 绘图线
开发语言