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);
       }
   });
相关推荐
布莱克60510 分钟前
在C++中,构造函数能不能是虚函数?析构函数为什么要虚?
开发语言·c++·构造函数·析构函数·虚函数
落魄实习生17 分钟前
Agent Scope Java 2.x 系列【1】核心架构
java·开发语言·架构
一水鉴天18 分钟前
三套语言:STL SPL/ SQL 20260928(元宝)
开发语言
Sarvartha40 分钟前
数组基础知识
java·开发语言
泡海椒1 小时前
金融报表开发:jquick-pdf K 线图 PDF 可视化解决方案
java·开发语言·金融·pdf
孙启超1 小时前
【AI开发之Rust】第 17 课:项目总览与核心架构 —— AI 助手 Rust 核心从 0 到 1
开发语言·后端·rust
码匠许师傅1 小时前
【C++三方组件】Asio 上篇:回调式 TCP 客户端与服务端
开发语言·c++·tcp/ip
神仙别闹2 小时前
基于C语言实现 TINY+ 编译器
c语言·开发语言·数据库
Wang's Blog2 小时前
Java 项目实战: 外卖平台优化-从库Slave配置与主从复制验证
java·开发语言
知识分享小能手2 小时前
C++ 学习教程,从入门到精通,C++内存模型和名称空间 — 详细知识点总结(9)
开发语言·c++·学习