Qt编程指南 VX:hao541022348
- ■ 将C++对象注册到 QML中,在QML使用C++对象(Q_INVOKABLE宏)
- ■ C++对象注册到元对象系统
- ■ Q_INVOKABLE 宏
- ■ 演示步骤
- ■ 将C++对象注册到 QML中,在QML使用C++对象(Q_PROPERTY宏 属性绑定)
- ■ C++对象注册到元对象系统
- ■ Q_PROPERTY 宏
- ■ 演示代码
- ■ 将 C++类注册到 QML,并在QML声明一个对象并进行访问
- ■ C++类注册到qml中
- ■ 演示代码
- ■ 将 C++类注册到 QML,信号与槽进行交互
- ■ C++类注册到qml中
- ■ 演示代码
- ■ 将C++中的数据模型注册到C++中,qml进行访问 --- 模型视图
- ■ C++数据模型对象注册到元对象系统
- ■ 演示代码
- ■ C++使用QML
- ■ 代码演示
■ 将C++对象注册到 QML中,在QML使用C++对象(Q_INVOKABLE宏)
在 C++ 里定义了一个对象,然后将这个对象注册到 QML 里面。在 QML 里面访问的就是 C++ 定义的对象。
■ C++对象注册到元对象系统
QQmlApplicationEngine::rootContext()->setContextProperty()
■ Q_INVOKABLE 宏
Q_INVOKABLE
Q_INVOKABLE 是个宏定义,这个宏将 函数 声明为元对象系统可调用的函数
- Q_INVOKABLE 是个宏定义
- 这个宏定义 针对的是 函数, 不是变量
- 经过Q_INVOKABLE 声明过得函数 可以被元对象系统调用
- QtQuick 也在元对象系统内,所以在 QML 中也可以访问这个被声明了的函数
■ 演示步骤
- 新建qml工程,里面只有一个main.cpp 与一个默认的 main.qml
- 创建一个C++ 的类 MyQmlClass 来与 QML 进行交互(重点关注 函数 Q_INVOKABLE宏 )
- 将 C++ 的对象,注册到 QML中去 QQmlApplicationEngine::rootContext()->setContextProperty()
- 做完了这两步,我们就做了 C++ 的工作,在 QML 中 就可以 直接拿到对象 myQmlImp 来调用使用 Q_INVOKABLE 定义的方法了。
- 改造 QML (定义一个“获取”的 Button 与 显示获取值得 Label, 点击 Button 就会获取 C++ 中的 m_value 值, 在 Lable 上进行展示。)
MyQmlClass.h
#ifndef MYQMLCLASS_H
#define MYQMLCLASS_H#include <QObject>
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 <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>#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 : 对象引用,对象指针,这里就是&myQmlImpengine.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.3Window {visible: truewidth: 640height: 480title: qsTr("Hello World")Label{ //Label用于显示获取C++的值id: label //显示控件,唯一标识ID:labeltext: "" //初始化内容清空anchors.bottom: getBtn.top //显示控件的下方放到btn的上方anchors.left: getBtn.left //显示控件的左方与btn的左侧对齐}Button{ //Button 用于获取值id: getBtn //按钮控件,唯一标识ID:getBtntext: "获取" //按钮显示文字width: 120 //按钮宽度height: 40 //按钮高度anchors.centerIn: parent //按钮放到窗口中心onClicked: { //点击按钮事件;label.text = myQmlImp.getValue()}}
}
到这里,我们就在 QML 中获取了 C++ 代码中的值。可能到这里还有老师感觉不太真实,那么我们就继续进行验证,我们的思路是这样的:
- 我们增加一个 TextFiled 用于输入我们想给 。
- 再增加一个 “设置” 按钮,将 1中输入的值,给C++ 中的 m_Value 设值。
- 然后我们再点击刚才的 “获取” 按钮,将 C++ 中的 m_Value 值读取并显示出来。
这时候我们需要对原来的QML进行改造,新增加一个输入框TextField ,进行数值输入; 还需要新增加一个“设置” 按钮,点击按钮将值赋给 C++ 中的 m_Value
main.qml
import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Controls 2.3Window {visible: truewidth: 640height: 480title: qsTr("Hello World") Label{ //Label用于显示获取C++的值id: label //显示控件,唯一标识ID:labeltext: "" //初始化内容清空anchors.bottom: getBtn.top //显示控件的下方放到btn的上方anchors.left: getBtn.left //显示控件的左方与btn的左侧对齐}Button{ //Button 用于获取值id: getBtn //按钮控件,唯一标识ID:getBtntext: "获取" //按钮显示文字width: 120 //按钮宽度height: 40 //按钮高度anchors.centerIn: parent //按钮放到窗口中心onClicked: { //点击按钮事件;label.text = myQmlImp.getValue()}}TextField{ //文字输入控件id: textField //唯一IDwidth: getBtn.width //也可以直接设置成120height: getBtn.height //也可以直接设置成40anchors.top: getBtn.bottom //放到“获取”按钮下方10个像素anchors.topMargin: 10anchors.left: getBtn.left //与“获取”按钮左对齐}Button{id: setBtntext: "设置"width: textField.width //可以设置成getBtn.width或者120height: textField.height //可以设置成getBtn.height或者40anchors.top: textField.bottomanchors.left: textField.leftonClicked: {var value = textField.textmyQmlImp.setValue(value)} }
}
■ 将C++对象注册到 QML中,在QML使用C++对象(Q_PROPERTY宏 属性绑定)
在 C++ 里定义了一个对象,然后将这个对象注册到 QML 里面。在 QML 里面访问的就是 C++ 定义的对象。
■ C++对象注册到元对象系统
QQmlApplicationEngine::rootContext()->setContextProperty()
■ Q_PROPERTY 宏
使用 Q_PROPERTY 定义交互的属性
Q_PROPERTY:用于声明属性的宏
■ 演示代码
person.h
#ifndef PERSON_H
#define PERSON_H#include <QObject>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 <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#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.5Window {visible: truewidth: 480height: 800title: qsTr("Hello World")Column {spacing: 10TextField {placeholderText: "请输入姓名"text: person.name // 与Person对象的name属性绑定onTextChanged: person.name = text // 当文本改变时,更新Person对象的name属性}Slider {from: 0to: 100value: person.age // 与Person对象的age属性绑定onValueChanged: person.age = value // 当滑块值改变时,更新Person对象的age属性}Text {text: "姓名:" + person.name}Text {text: "年龄:" + person.age}}
}
■ 将 C++类注册到 QML,并在QML声明一个对象并进行访问
■ C++类注册到qml中
qmlRegisterType 就是一个函数模板。将 C++ 的类型注册到 QML 系统中,并且带有版本号,方便版本管理。 我们就把main.cpp 中的函数改造一下:
■ 演示代码
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#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.0Window {visible: truewidth: 640height: 480title: qsTr("Hello World")MyQmlClass{id: myQmlImp}Label{ //Label用于显示获取C++的值id: label //显示控件,唯一标识ID:labeltext: "" //初始化内容清空anchors.bottom: getBtn.top //显示控件的下方放到btn的上方anchors.left: getBtn.left //显示控件的左方与btn的左侧对齐}Button{ //Button 用于获取值id: getBtn //按钮控件,唯一标识ID:getBtntext: "获取" //按钮显示文字width: 120 //按钮宽度height: 40 //按钮高度anchors.centerIn: parent //按钮放到窗口中心onClicked: { //点击按钮事件;label.text = myQmlImp.getValue()}}TextField{ //文字输入控件id: textField //唯一IDwidth: getBtn.width //也可以直接设置成120height: getBtn.height //也可以直接设置成40anchors.top: getBtn.bottom //放到“获取”按钮下方10个像素anchors.topMargin: 10anchors.left: getBtn.left //与“获取”按钮左对齐}Button{id: setBtntext: "设置"width: textField.width //可以设置成getBtn.width或者120height: textField.height //可以设置成getBtn.height或者40anchors.top: textField.bottomanchors.left: textField.leftonClicked: {var value = textField.textmyQmlImp.setValue(value)}}
}
■ 将 C++类注册到 QML,信号与槽进行交互
C++ 对象可以发出信号,而QML中的元素可以连接到这些信号上。这样,当C++ 对象的状态发生变化时,可以通过信号与槽机制将这些变化传递给QML界面。
■ C++类注册到qml中
qmlRegisterType 就是一个函数模板。将 C++ 的类型注册到 QML 系统中,并且带有版本号,方便版本管理。 我们就把main.cpp 中的函数改造一下:
■ 演示代码
myobject.h
#include <QObject>
#include <QtDebug>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 <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#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.0Window {visible: truewidth: 480height: 800title: qsTr("Hello World") /* 定义 sendToCpp 信号 */signal sendToCpp(string message) /* Connections 组件用于连接 myObject 的 onMySignal 信号 */Connections {target: myObjectonMySignal: 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")}
}
■ 将C++中的数据模型注册到C++中,qml进行访问 — 模型视图
模型视图(Model-View):可以使用C++ 中的数据模型(QStandardItemModel)来提供数据给QML界面。
QML中的视图元素(如ListView或GridView)可以使用这些模型来显示数据。
■ C++数据模型对象注册到元对象系统
QQmlApplicationEngine::rootContext()->setContextProperty()
■ 演示代码
mymodel.h
#ifndef MYMODEL_H
#define MYMODEL_H#include <QAbstractListModel>
#include <QList>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, "alice@example.com"});m_persons.append({"Bob", 30, "bob@example.com"});m_persons.append({"Charlie", 35, "charlie@example.com"});
}
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 <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#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.5Window {visible: truewidth: 480height: 800title: qsTr("Hello World")ListView {anchors.fill: parentmodel: myModeldelegate: Item {width: parent.widthheight: 60Column {Text { text: name }Text { text: age }Text { text: email }}}}
}
■ C++使用QML
■ 代码演示
main.qml
import QtQuick 2.7
import QtQuick.Window 2.2Item {id: rootvisible: truewidth: 640height: 480//title: qsTr("Hello World")property string msg: "I am QML Item"signal callCpp(string arg1, string arg2)MainForm {anchors.fill: parentmouseArea.onClicked: {Qt.quit();}}Rectangle {anchors.fill: parentcolor: "blue"objectName: "rect"}MouseArea {anchors.fill: parentonClicked: {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 <QObject>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 <QDebug>
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 <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlProperty>
#include <QQuickView>
#include <QQuickItem>
#include <QMetaObject>
#include <QDebug>#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();// 获取QQuickItemQQuickItem *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的方法,传递和获取参数均可以
鼠标点击