QML学习笔记(四十三)QML与C++交互:上下文属性暴露
前言
在之前的学习中,我们已经对qml的基本组件和布局方式有了大概的了解,已经具备开发一个完善界面的基本能力。然而光会写界面是远远不够的,我们还需要让QML界面和业务代码连接起来也就是说,我们需要学会——QML和C++之间的交互。
之前的学习已经介绍过,QML更偏向于UI界面层,而C++应当负责复杂的业务逻辑判断。
本节我们将学习在QML端如何链接调用到C++对象的函数接口。我利用的是之前的一个qml例子,里面实现了一个登录窗口,上面可以输入用户名和密码,我希望在点击登录后,将用户名密码传递到C++端进行一些业务逻辑,然后返回到QML端。
用到的方法是上下文属性暴露,你可能对此有些迷茫,实际上这是接口名字的翻译。setContextProperty
——设置上下文属性。
一、C++端
先贴上登录类的C++头文件代码:
#pragma once
#include <QMainWindow>
#include <QQuickWidget>class QmlLogin : public QMainWindow {Q_OBJECTpublic:QmlLogin(QWidget* parent = nullptr);~QmlLogin();// 可被QML调用的接口Q_INVOKABLE void regularMethod();Q_INVOKABLE QString regularMethodWithReturn(QString name, int age);public slots:void handleLogin(const QString& username, const QString& password);private:QQuickWidget* m_quickWidget; // 保存QQuickWidget指针
};
我这里打算直接new一个QQuickWidget,然后在构造函数中加载qml。这一点是区别于纯qml工程的。这里设计了三个接口,都可以被qml端调用。
1.regularMethod和regularMethodWithReturn是public的一个公共接口,这里必须要添加Q_INVOKABLE宏 ,代表接口可被调用的意思,否则qml端无法识别调用。
2.槽函数handleLogin可以直接被调用。
然后,我再附上cpp代码:
#include "QmlLogin.h"
#include <QQmlContext>
#include <QQuickWidget>
#include <QDebug>
#include <QQmlError>
#include <QQuickItem>
#include <QQuickView>
#include <QQmlProperty>QmlLogin::QmlLogin(QWidget* parent): QMainWindow(parent)
{m_quickWidget = new QQuickWidget(this);// 添加调试信息qDebug() << "Loading QML file...";QUrl qmlUrl("qrc:/LoginWidget.qml");qDebug() << "QML URL:" << qmlUrl.toString();// 将QmlLogin实例暴露给QML上下文m_quickWidget->rootContext()->setContextProperty("loginHandler", this);m_quickWidget->setSource(qmlUrl);// 检查是否有错误if (m_quickWidget->status() == QQuickWidget::Error) {for (const QQmlError &error : m_quickWidget->errors()) {qDebug() << "QML Error:" << error.toString();}}m_quickWidget->setResizeMode(QQuickWidget::SizeRootObjectToView);setCentralWidget(m_quickWidget);resize(400, 300);m_quickWidget->show();
}QmlLogin::~QmlLogin()
{
}void QmlLogin::handleLogin(const QString& username, const QString& password)
{qDebug() << "登录请求 - 用户名:" << username << "密码:" << password;// 这里添加您的登录业务逻辑
}
void QmlLogin::regularMethod()
{qDebug()<<"regularMethod";
}QString QmlLogin::regularMethodWithReturn(QString name, int age)
{qDebug()<<"regularMethodWithReturn"<<name<<age;return QString("%1: %2years old").arg(name).arg(age);
}
代码比较简单,这里重点讲:
m_quickWidget->rootContext()->setContextProperty("loginHandler", this);
设置上下文属性——这里将this指针作为一个属性,暴露给了qml引擎,并取了别名loginHandler。之后qml端就可以像调用对象一样调用loginHandler的接口了。
值得一提的事,这里虽然是通过QQuickWidget来调用rootContext的,但实际上链接到的是QQuickWidget里的engine里的rootContext。如果你是在main中直接通过QQmlEngine加载qml的,那调用QQmlEngine的rootContext就行了。
只要 C++ 对象通过 setContextProperty(“name”, obj) 注册到 同一个 QQmlEngine,所有由该引擎加载的 QML 文件(无论多少个窗口/组件)全局共享这个 “name” ——不需要重新 import、不需要重新创建实例,直接写对象名即可访问其属性/信号/槽。
一个引擎,一次注册,处处可用。
二、QML端
import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Controls 2.12
import QtQuick.Layouts 1.12Rectangle {x: 0y: 0width: 400height: 300color: "#fff0f0"Column {anchors.centerIn: parentspacing: 20Text {text: "登录"font.pixelSize: 24anchors.horizontalCenter: parent.horizontalCenter}TextField {id: usernameFieldplaceholderText: "用户名"width: 200}TextField {id: passwordFieldplaceholderText: "密码"echoMode: TextInput.Passwordwidth: 200}Button {text: "登录"anchors.horizontalCenter: parent.horizontalCenterwidth: 100onClicked: {console.log("登录按钮被点击")loginHandler.handleLogin(usernameField.text, passwordField.text)}}Button {text: "测试"anchors.horizontalCenter: parent.horizontalCenterwidth: 100onClicked: {console.log("测试按钮被点击")loginHandler.regularMethod()var string = loginHandler.regularMethodWithReturn("Jame", 20)console.log(string)}}}
}
这是一个相当简洁的登录窗口,我们点击登录按钮后,会直接调用暴露的对象属性,然后产生c++端的打印。
loginHandler.handleLogin(usernameField.text, passwordField.text)
接下来如果需要网络上的请求校验的话,就可以直接在c++端去执行了。
接下来看测试按钮,这个按钮和登录功能无关,纯粹是为了测试添加了Q_INVOKABLE宏的接口能否被调用:
可以看到,点击测试按钮后,从qml到c++,再到qml获取到返回值进行打印,整个流程都是通畅的。这个返回值是一个简单的字符串拼接,模拟了在c++端进行复杂业务逻辑计算后,将结果返回到qml端进行显示。
三、总结
本节介绍了通过上下文属性暴露的方法,让qml端可以直接通过c++对象的属性,调用它的一些功能接口,属于是qml对c++的单向调用。
下一节,将介绍一下c++端对qml端的主动调用,然后去获取qml界面中一些组件的属性信息。