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);
       }
   });
相关推荐
努力进修4 小时前
【JavaEE初阶】告别小白!Java IO 流读写 + 文件操作实战
java·开发语言·java-ee
沐知全栈开发4 小时前
Vue3 Ajax(Axios)详解
开发语言
电子_咸鱼4 小时前
【QT——信号和槽(1)】
linux·c语言·开发语言·数据库·c++·git·qt
deephub4 小时前
PyCausalSim:基于模拟的因果发现的Python框架
开发语言·python·机器学习·因果发现
weixin_307779134 小时前
Jenkins Declarative Pipeline:现代CI/CD的声明式实践指南
开发语言·ci/cd·自动化·jenkins·etl
CoderYanger4 小时前
D.二分查找-基础-2529. 正整数和负整数的最大计数
java·开发语言·数据结构·算法·leetcode·职场和发展
E***U9454 小时前
Java 校招 / 社招:Spring Boot 项目实战指南
java·开发语言·spring boot
一叶之秋14124 小时前
QT常用控件(一)
服务器·开发语言·qt
爱尔兰极光4 小时前
Python--常量和变量
开发语言·python