方案一 使用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);
}
});