在qml中,我们一般都是希望使用qml做界面展示,而数据处理转由c++处理;
在此篇博客,将介绍如何在c++中给qml定义全局对象;在c++中如何定义对象给qml使用。
1 给qml定义全局对象
正常我们定义了一个qml项目后,main函数是这样的:
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
if (engine.rootObjects().isEmpty())
return -1;
return app.exec();
}
QQmlApplicationEngine 是 Qt 框架中用于加载和运行基于 QML 的应用程序的核心类,结合了 QQmlEngine 和 QQmlComponent 的功能,支持从单个 QML 文件加载应用程序,并实现 C++ 与 QML 的双向通信。
那么,就可以使用QQmlApplicationEngine去获得全局上下文对象QQmlContext,通过使用上下文对象,就可以给qml设置一个全局的变量值;
获得上下文对象:
// 获得全局对象,上下文对象
QQmlContext *context = engine.rootContext();
给qml设置一个全局变量:
// 给qml设置一个全局变量;
context->setContextProperty("SCREEN_WIDTH", 800);
这样,就可以在qml中使用该变量了,例如在mian.qml文件内使用:
import QtQuick 2.9
import QtQuick.Window 2.2
import QtQuick.Controls 2.14
Window {
id: root
visible: true
width: SCREEN_WIDTH // 直接使用
height: 500
title: qsTr("Hello World")
color: "white"
// 如果qml内部有定义重名变量,那么会优先使用qml内部定义的变量,而不会使用C++定义的变量
//property int SCREEN_WIDTH: 500
}
注意:如果qml内部有定义重名变量,那么会优先使用qml内部定义的变量;另外,定义全局变量会有性能消耗问题。
2 在c++中定义对象给qml使用
自定义C++类MyObject继承自QObject;有两个成员变量,int m_iValue 和 QString m_sString;并且给他俩定义get和set方法;另外在定义两个信号;最后通过Q_PROPERTY将两个成员变量暴露给元对象。
myobject.h
#ifndef MYOBJECT_H
#define MYOBJECT_H
#include <QObject>
class MyObject : public QObject
{
Q_OBJECT
public:
MyObject(QObject *parent = nullptr); // 构造函数
~MyObject();
const int &iValue() const;
void setIIValue(const int &newIValue);
const QString &sString() const;
void setSString(const QString &newSString);
signals:
void iValueChanged();
void sStringChanged();
private:
int m_iValue;
QString m_sString;
Q_PROPERTY(int iValue READ iValue WRITE setIIValue NOTIFY iValueChanged)
// Q_PROPERTY(QString sString READ sString WRITE setSString NOTIFY sStringChanged)
// 如果值是函数内部成员变量的值,可使用MEMBER去设置,与READ sString WRITE setSString实现效果一致
Q_PROPERTY(QString sString MEMBER m_sString NOTIFY sStringChanged)
};
#endif // MYOBJECT_H
myobject.cpp
#include "myobject.h"
MyObject::MyObject(QObject *parent) : QObject(parent)
{
}
MyObject::~MyObject()
{
}
const int &MyObject::iValue() const
{
return m_iValue;
}
void MyObject::setIIValue(const int &newIValue)
{
if (m_iValue == newIValue) {
return;
}
m_iValue = newIValue;
emit iValueChanged();
}
const QString &MyObject::sString() const
{
return m_sString;
}
void MyObject::setSString(const QString &newSString)
{
if (m_sString == newSString) {
return;
}
m_sString = newSString;
emit sStringChanged();
}
Q_PROPERTY的参数讲解
Q_PROPERTY(int iValue READ iValue WRITE setIIValue NOTIFY iValueChanged)
int iValue 指定是给qml使用的变量名,推荐与c++类成员变量名类似;
READ iValue 指的是通过iValue函数去读取值;
WRITE setIIValue 指的是通过setIIValue函数去修改iValue值;
NOTIFY iValueChanged 指的是当iValue值被修改后,会发送的信号;
然后main函数中使用qmlRegisterType函数对自定义类进行注册,注册后,就可以在qml那边导入使用了。
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include "myobject.h"
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
// 获得全局对象,上下文对象
QQmlContext *context = engine.rootContext();
// 给qml设置一个全局变量;如果qml内部有定义重名变量,那么会优先使用qml内部定义的变量;另外,定义全局变量会有性能问题
context->setContextProperty("SCREEN_WIDTH", 800);
// 注册,在需要使用的地方 import MyObj 1.0
qmlRegisterType<MyObject>("MyObj", 1, 0, "MyObject");
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
if (engine.rootObjects().isEmpty())
return -1;
return app.exec();
}
其中,MyObj是给qml那边导入时使用的模块名字,1 和 0 指的是版本,最后的MyObject就是自定义类名;
然后就可以在qml中 import MyObj 1.0 导入使用了。
import QtQuick 2.9
import QtQuick.Window 2.2
import QtQuick.Controls 2.14
import MyObj 1.0 // 导入自定义模块
Window {
id: root
visible: true
width: SCREEN_WIDTH
height: 500
title: qsTr("Hello World")
color: "white"
MyObject {
iValue: 20
sString: "this is a custom obj.";
Component.onCompleted: {
console.log("iValue:", 20, " sString:", sString)
}
}
}
3 番外
Q_PROPERTY为什么要指定NOTIFY信号呢?
在这里与qml的绑定有关系;
在qml中,当给一个变量以冒号':'方式赋值时,这两个变量是互相绑定的;例如:
property int testValue: myObj.iValue
当myObj.iValue被修改时,就会触发信号通知testValue也一并修改!
案例代码:定义变量testValue使用冒号被myObject.iValue赋值,定义按钮在onClicked槽函数中修改myObj.iValue值,观察testValue是否也被修改;
import QtQuick 2.9
import QtQuick.Window 2.2
import QtQuick.Controls 2.14
import MyObj 1.0 // 导入自定义模块
Window {
id: root
visible: true
width: SCREEN_WIDTH
height: 500
title: qsTr("Hello World")
color: "white"
property int testValue: myObj.iValue // 绑定了
onTestValueChanged: {
console.log("testValue:", testValue)
}
Button {
width: 100; height: 50
onClicked: {
myObj.iValue = 50;
}
}
MyObject {
id: myObj
iValue: 20
sString: "this is a custom obj.";
Component.onCompleted: {
console.log("iValue:", 20, " sString:", sString)
}
}
}
当点击按钮后,修改的是myObj.iValue,但testValue也一并被修改了,由此证明,使用冒号赋值时,他俩是会绑定在一起的。
注意,使用 = 赋值时,不会有绑定的效果!!!