在 C++ 里定义了一个对象,然后将这个对象注册到 QML 里面。在 QML 里面访问的就是 C++ 定义的对象。
QQmlApplicationEngine::rootContext()->setContextProperty()
Q_INVOKABLE
Q_INVOKABLE 是个宏定义,这个宏将 函数 声明为元对象系统可调用的函数
MyQmlClass.h
#ifndef MYQMLCLASS_H
#define MYQMLCLASS_H
#include
class MyQmlClass : public QObject
{
Q_OBJECT
public:
explicit MyQmlClass(QObject *parent = nullptr);
Q_INVOKABLE void setValue(int value); //这个宏将 函数 声明为元对象系统可调用的函数
Q_INVOKABLE int getValue(); //这个宏将 函数 声明为元对象系统可调用的函数
signals:
private:
int m_Value;
};
#endif // MYQMLCLASS_H
MyQmlClass.cpp
#include "MyQmlClass.h"
MyQmlClass::MyQmlClass(QObject *parent) : QObject(parent)
{
}
void MyQmlClass::setValue(int value)
{
m_Value = value;
}
int MyQmlClass::getValue()
{
return m_Value;
}
打开 main.cpp ,通过 QML 引擎 QQmlApplicationEngine 进行注册。
#include
#include
#include
#include "MyQmlClass.h"
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
///
//声明 对象 首先定义了一个C++ 的对象 myQmlImp ,
MyQmlClass myQmlImp;
//将对象进行注册到QML中
//key :自定义字符串,为了好记,我们这里叫做对象的名字 "myQmlImp"
//value : 对象引用,对象指针,这里就是&myQmlImp
engine.rootContext()->setContextProperty("myQmlImp", &myQmlImp);
///
const QUrl url(QStringLiteral("qrc:/main.qml"));
QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
&app, [url](QObject *obj, const QUrl &objUrl) {
if (!obj && url == objUrl)
QCoreApplication::exit(-1);
}, Qt::QueuedConnection);
engine.load(url);
return app.exec();
}
main.qml我们的思路很简单,就是 QML 中来调用上面 C++ 暴露出来的读写函数。所以我们在QML 中定义一个 “获取” Button ,点击它我们就来调用C++中的 getValue() 函数,然后我们需要一个Label 将获取的 C++ 的值进行展示
import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Controls 2.3
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
Label{ //Label用于显示获取C++的值
id: label //显示控件,唯一标识ID:label
text: "" //初始化内容清空
anchors.bottom: getBtn.top //显示控件的下方放到btn的上方
anchors.left: getBtn.left //显示控件的左方与btn的左侧对齐
}
Button{ //Button 用于获取值
id: getBtn //按钮控件,唯一标识ID:getBtn
text: "获取" //按钮显示文字
width: 120 //按钮宽度
height: 40 //按钮高度
anchors.centerIn: parent //按钮放到窗口中心
onClicked: { //点击按钮事件;
label.text = myQmlImp.getValue()
}
}
}
到这里,我们就在 QML 中获取了 C++ 代码中的值。可能到这里还有老师感觉不太真实,那么我们就继续进行验证,我们的思路是这样的:
main.qml
import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Controls 2.3
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
Label{ //Label用于显示获取C++的值
id: label //显示控件,唯一标识ID:label
text: "" //初始化内容清空
anchors.bottom: getBtn.top //显示控件的下方放到btn的上方
anchors.left: getBtn.left //显示控件的左方与btn的左侧对齐
}
Button{ //Button 用于获取值
id: getBtn //按钮控件,唯一标识ID:getBtn
text: "获取" //按钮显示文字
width: 120 //按钮宽度
height: 40 //按钮高度
anchors.centerIn: parent //按钮放到窗口中心
onClicked: { //点击按钮事件;
label.text = myQmlImp.getValue()
}
}
TextField{ //文字输入控件
id: textField //唯一ID
width: getBtn.width //也可以直接设置成120
height: getBtn.height //也可以直接设置成40
anchors.top: getBtn.bottom //放到“获取”按钮下方10个像素
anchors.topMargin: 10
anchors.left: getBtn.left //与“获取”按钮左对齐
}
Button{
id: setBtn
text: "设置"
width: textField.width //可以设置成getBtn.width或者120
height: textField.height //可以设置成getBtn.height或者40
anchors.top: textField.bottom
anchors.left: textField.left
onClicked: {
var value = textField.text
myQmlImp.setValue(value)
}
}
}
在 C++ 里定义了一个对象,然后将这个对象注册到 QML 里面。在 QML 里面访问的就是 C++ 定义的对象。
QQmlApplicationEngine::rootContext()->setContextProperty()
使用 Q_PROPERTY 定义交互的属性
Q_PROPERTY:用于声明属性的宏
person.h
#ifndef PERSON_H
#define PERSON_H
#include
class Person : public QObject
{
Q_OBJECT
/* 使用 Q_PROPERTY 定义交互的属性 */
Q_PROPERTY(QString name READ getName WRITE setName NOTIFY nameChanged)
Q_PROPERTY(int age READ getAge WRITE setAge NOTIFY ageChanged)
public:
explicit Person(QObject *parent = nullptr) : QObject(parent), m_name(""),m_age(0)
{
}
/* 为属性提供 getter 和 setter 方法 */
QString getName() const { return m_name; }
void setName(const QString& name) { m_name = name; emit nameChanged(); }
int getAge() const { return m_age; }
void setAge(int age) { m_age = age; emit ageChanged(); }
signals:
/* 信号与属性对应,通过信号通知其他对象属性的变化 */
void nameChanged();
void ageChanged();
private:
QString m_name;
int m_age;
};
#endif // PERSON_H
main.cpp
#include
#include
#include
#include "person.h"
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
// 创建Person对象
Person person;
QQmlApplicationEngine engine;
/* 将Person对象作为QML上下文属性 */
engine.rootContext()->setContextProperty("person", &person);
const QUrl url(QStringLiteral("qrc:/main.qml"));
QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
&app, [url](QObject *obj, const QUrl &objUrl) {
if (!obj && url == objUrl)
QCoreApplication::exit(-1);
}, Qt::QueuedConnection);
engine.load(url);
return app.exec();
}
main.qml
import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Controls 2.5
Window {
visible: true
width: 480
height: 800
title: qsTr("Hello World")
Column {
spacing: 10
TextField {
placeholderText: "请输入姓名"
text: person.name // 与Person对象的name属性绑定
onTextChanged: person.name = text // 当文本改变时,更新Person对象的name属性
}
Slider {
from: 0
to: 100
value: person.age // 与Person对象的age属性绑定
onValueChanged: person.age = value // 当滑块值改变时,更新Person对象的age属性
}
Text {
text: "姓名:" + person.name
}
Text {
text: "年龄:" + person.age
}
}
}
qmlRegisterType 就是一个函数模板。将 C++ 的类型注册到 QML 系统中,并且带有版本号,方便版本管理。 我们就把main.cpp 中的函数改造一下:
#include
#include
#include
#include "MyQmlClass.h"
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
// 方式一:注册定义好的对象到 QML
// MyQmlClass myQmlImp;
// engine.rootContext()->setContextProperty("myQmlImp", &myQmlImp);
// 方式二:注册类到 QML 对象
qmlRegisterType<MyQmlClass>("com.company.myqmlclass", 1, 0, "MyQmlClass");
const QUrl url(QStringLiteral("qrc:/main.qml"));
QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
&app, [url](QObject *obj, const QUrl &objUrl) {
if (!obj && url == objUrl)
QCoreApplication::exit(-1);
}, Qt::QueuedConnection);
engine.load(url);
return app.exec();
}
其中:qmlRegisterType 模板函数中的 “com.company.myqmlclass” 为自定义的控件名称类似于C++中的库名称。我们在 QML 中需要 import 这个控件名, “MyQmlClass” 为 C++ 注册的类名, 1和0 为自定义版本号,方便版本管理。
import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Controls 2.3
import com.company.myqmlclass 1.0
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
MyQmlClass{
id: myQmlImp
}
Label{ //Label用于显示获取C++的值
id: label //显示控件,唯一标识ID:label
text: "" //初始化内容清空
anchors.bottom: getBtn.top //显示控件的下方放到btn的上方
anchors.left: getBtn.left //显示控件的左方与btn的左侧对齐
}
Button{ //Button 用于获取值
id: getBtn //按钮控件,唯一标识ID:getBtn
text: "获取" //按钮显示文字
width: 120 //按钮宽度
height: 40 //按钮高度
anchors.centerIn: parent //按钮放到窗口中心
onClicked: { //点击按钮事件;
label.text = myQmlImp.getValue()
}
}
TextField{ //文字输入控件
id: textField //唯一ID
width: getBtn.width //也可以直接设置成120
height: getBtn.height //也可以直接设置成40
anchors.top: getBtn.bottom //放到“获取”按钮下方10个像素
anchors.topMargin: 10
anchors.left: getBtn.left //与“获取”按钮左对齐
}
Button{
id: setBtn
text: "设置"
width: textField.width //可以设置成getBtn.width或者120
height: textField.height //可以设置成getBtn.height或者40
anchors.top: textField.bottom
anchors.left: textField.left
onClicked: {
var value = textField.text
myQmlImp.setValue(value)
}
}
}
C++ 对象可以发出信号,而QML中的元素可以连接到这些信号上。这样,当C++ 对象的状态发生变化时,可以通过信号与槽机制将这些变化传递给QML界面。
qmlRegisterType 就是一个函数模板。将 C++ 的类型注册到 QML 系统中,并且带有版本号,方便版本管理。 我们就把main.cpp 中的函数改造一下:
myobject.h
#include
#include
class MyObject : public QObject
{
Q_OBJECT
public:
explicit MyObject(QObject *parent = nullptr) : QObject(parent) {}
signals:
void mySignal(QString message);
public slots:
void mySlot(const QString& message) { qDebug() << "Received message from QML:" << message;
emit mySignal("Hello from C++");
}
};
main.cpp
#include
#include
#include
#include "myobject.h"
int main(int argc, char *argv[])
{
/* 启用Qt应用程序的高DPI缩放功能 */
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
/* 创建一个Qt应用程序的实例 */
QGuiApplication app(argc, argv);
/* 将自定义 C++ 类型注册到 QML 中的函数*/
qmlRegisterType<MyObject>("com.example", 1, 0, "MyObject");
QQmlApplicationEngine engine;
const QUrl url(QStringLiteral("qrc:/main.qml"));
/* 将 QQmlApplicationEngine 对象的 objectCreated 信号连接到一个 lambda 函数上 */
/* lambda 函数用于在 QML 文件中的根对象被创建时进行处理,检查对象是否成功创建,如果创建失败则退出应用程序 */
QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
&app, [url](QObject *obj, const QUrl &objUrl) {
if (!obj && url == objUrl)
QCoreApplication::exit(-1);
}, Qt::QueuedConnection);
/* 加载QML文件并显示用户界面 */
engine.load(url);
return app.exec();
}
main.qml
import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Controls 2.5
import com.example 1.0
Window {
visible: true
width: 480
height: 800
title: qsTr("Hello World")
/* 定义 sendToCpp 信号 */
signal sendToCpp(string message)
/* Connections 组件用于连接 myObject 的 onMySignal 信号 */
Connections {
target: myObject
onMySignal: console.log("Received message from C++:", message)
}
MyObject {
id: myObject
/* 将 onMySignal 信号传递到 sendToCpp信号上,便于 QML 处理 */
onMySignal: sendToCpp(message)
}
Button {
text: "Send message to C++"
anchors.centerIn: parent
/* 单击按钮时,会将信号传递到 C++ 的 mySlot 槽上 */
onClicked: myObject.mySlot("Hello from QML")
}
}
模型视图(Model-View):可以使用C++ 中的数据模型(QStandardItemModel)来提供数据给QML界面。
QML中的视图元素(如ListView或GridView)可以使用这些模型来显示数据。
QQmlApplicationEngine::rootContext()->setContextProperty()
mymodel.h
#ifndef MYMODEL_H
#define MYMODEL_H
#include
#include
class MyModel : public QAbstractListModel
{
Q_OBJECT
public:
explicit MyModel(QObject *parent = nullptr);
enum {
NameRole = Qt::UserRole + 1,
AgeRole,
EmailRole
};
// 重写以下几个虚函数
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
QHash<int, QByteArray> roleNames() const override;
private:
struct Person {
QString name;
int age;
QString email;
};
QList<Person> m_persons;
};
#endif // MYMODEL_H
mymodel.cpp
#include "mymodel.h"
MyModel::MyModel(QObject *parent)
: QAbstractListModel(parent)
{
// 初始化一些数据
m_persons.append({"Alice", 25, "[email protected]"});
m_persons.append({"Bob", 30, "[email protected]"});
m_persons.append({"Charlie", 35, "[email protected]"});
}
int MyModel::rowCount(const QModelIndex &parent) const
{
Q_UNUSED(parent);
return m_persons.count();
}
QVariant MyModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
return QVariant();
if (index.row() >= m_persons.count() || index.row() < 0)
return QVariant();
const Person &person = m_persons[index.row()];
if (role == NameRole)
return person.name;
else if (role == AgeRole)
return person.age;
else if (role == EmailRole)
return person.email;
return QVariant();
}
QHash<int, QByteArray> MyModel::roleNames() const
{
QHash<int, QByteArray> roles;
roles[NameRole] = "name";
roles[AgeRole] = "age";
roles[EmailRole] = "email";
return roles;
}
main.cpp
#include
#include
#include
#include "mymodel.h"
int main(int argc, char *argv[])
{
/* 启用Qt应用程序的高DPI缩放功能 */
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
/* 创建一个Qt应用程序的实例 */
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
MyModel myModel;
engine.rootContext()->setContextProperty("myModel", &myModel);
const QUrl url(QStringLiteral("qrc:/main.qml"));
/* 将 QQmlApplicationEngine 对象的 objectCreated 信号连接到一个 lambda 函数上 */
/* lambda 函数用于在 QML 文件中的根对象被创建时进行处理,检查对象是否成功创建,如果创建失败则退出应用程序 */
QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
&app, [url](QObject *obj, const QUrl &objUrl) {
if (!obj && url == objUrl)
QCoreApplication::exit(-1);
}, Qt::QueuedConnection);
/* 加载QML文件并显示用户界面 */
engine.load(url);
return app.exec();
}
main.qml
import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Controls 2.5
Window {
visible: true
width: 480
height: 800
title: qsTr("Hello World")
ListView {
anchors.fill: parent
model: myModel
delegate: Item {
width: parent.width
height: 60
Column {
Text { text: name }
Text { text: age }
Text { text: email }
}
}
}
}
main.qml
import QtQuick 2.7
import QtQuick.Window 2.2
Item {
id: root
visible: true
width: 640
height: 480
//title: qsTr("Hello World")
property string msg: "I am QML Item"
signal callCpp(string arg1, string arg2)
MainForm {
anchors.fill: parent
mouseArea.onClicked: {
Qt.quit();
}
}
Rectangle {
anchors.fill: parent
color: "blue"
objectName: "rect"
}
MouseArea {
anchors.fill: parent
onClicked: {
console.log("onClicked, callCpp")
root.callCpp(root.msg, "notify cpp")
}
}
onHeightChanged: {
console.log("onHeightChanged execute")
}
onWidthChanged: {
console.log("onWidthChanged execute")
}
//QML中的方法可以被cpp调用,也可以作为槽函数
function qmlFun(val_arg) {
console.log("qmlFun execute", val_arg, "return qmlFun_return_result")
return "qmlFun_return_result"
}
//注意槽函数参数为var类型
function invokeFromCpp(arg1, arg2) {
console.log("invokeFromCpp execute ", arg1, arg2)
}
}
cbusiness.h
#ifndef CBUSINESS_H
#define CBUSINESS_H
#include
class CBusiness : public QObject
{
Q_OBJECT
public:
explicit CBusiness(QObject *parent = 0);
signals:
void callQml(const QVariant &arg1,const QVariant &arg2);
public slots:
void invokeFromQml(const QString &arg1,const QString &arg2);
};
#endif // CBUSINESS_H
cbusiness.cpp
#include "cbusiness.h"
#include
CBusiness::CBusiness(QObject *parent) : QObject(parent)
{
}
void CBusiness::invokeFromQml(const QString &arg1,const QString &arg2)
{
qDebug() << "CBusiness::" << __FUNCTION__ << arg1 << arg2;
qDebug() << "CBusiness::" << __FUNCTION__ << " emit callQml";
emit callQml("I am cpp", "notify qml");
}
main.cpp
#include
#include
#include
#include
#include
#include
#include
#include "cbusiness.h"
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
/*
可以用QQmlComponent\QQuickView\QQuickWidget的C++代码加载QML文档
当使用QQuickView时,qml的根不能是Window
*/
QQuickView view(QUrl("qrc:/main.qml"));
view.show();
// 获取到qml根对象的指针
QObject *qmlObj = view.rootObject();
/*
修改qml属性值的方法
QObject::setProperty() QQmlProperty QMetaProperty::write()
*/
// 通过QObject设置属性值
qDebug() << "cpp: " << "set qml property height";
QQmlProperty(qmlObj, "height").write(500); //qmlObj->setProperty("height",500);
// 通过QObject获取属性值
qDebug() << "cpp: " << "get qml property height" << qmlObj->property("height").toDouble();
// C++访问qml的其它属性
qDebug() << "cpp: " << "get qml property msg" << qmlObj->property("msg").toString();
// 获取QQuickItem
QQuickItem *item = qobject_cast<QQuickItem*>(qmlObj);
// 通过QQuickItem设置属性值
qDebug() << "cpp: " << "set qml property width";
item->setWidth(300);
// 通过QQuickItem获取属性值
qDebug() << "cpp: " << "get qml property width" << item->width();
// 通过object name访问加载的QML对象
// QObject::findChildren()可用于查找具有匹配object name属性的子项
QObject *qmlRect = qmlObj->findChild<QObject*>("rect");
if(qmlRect)
{
qDebug() << "cpp: " << "get rect color" << qmlRect->property("color");
}
// C++调用QML方法
QVariant valReturn;
QVariant valArg = "I am cpp";
//Q_RETURN_ARG()和Q_Arg()参数必须制定为QVariant类型
QMetaObject::invokeMethod(qmlObj, "qmlFun",
Q_RETURN_ARG(QVariant,valReturn),
Q_ARG(QVariant,valArg));
qDebug() << "cpp: " << "QMetaObject::invokeMethod result" << valReturn.toString(); //qml函数中返回“ok”
CBusiness cppObj;
// cpp和qml信号与槽关联
// qml信号绑订cpp的槽,用QString类型
QObject::connect(qmlObj, SIGNAL(callCpp(QString, QString)), &cppObj, SLOT(invokeFromQml(QString, QString)));
//关联cpp信号与qml槽
// cpp的信号绑定qml槽,用QVariant类型
QObject::connect(&cppObj, SIGNAL(callQml(QVariant, QVariant)), qmlObj, SLOT(invokeFromCpp(QVariant, QVariant)));
return app.exec();
}
测试结果
信号和槽的绑定在c++代码中完成,在c++中可以修改qml的属性,获取qml的属性,调用qml的方法,传递和获取参数均可以