1、QML调用(实例化)C++类
方法一:C++使用属性系统Q_PROPERTY
优点:
- 可以方便的利用Qt的属性系统从QML中访问C++类中的属性。在QML程序中定义该C++类时会直接实例化该类。
缺点:
- 无法在C++中对QML中实例化的类对象进行操作;
- 只能访问使用属性系统Q_PROPERTY声明的属性;
- 无法设置QML中访问指定的对象,即无法访问C++中的单例;
详细描述:
- 使用Qt Creator中的"Qt Quick Application"模板创建一个新的项目;
注意:取消选中New Project Wizard的Define Project Details部分中的With ui.qml文件选项。
- 添加C++类,命名为Call_Back,作为QML中使用的C++类,类的方法如下:
头文件
#ifndef CALL_BACK_H
#define CALL_BACK_H
#include <QObject>
#include <QString>
class Call_Back: public QObject
{
Q_OBJECT
//只读,只写,改变信号
Q_PROPERTY(QString userName READ userName WRITE setuserName NOTIFY userNameChanged)
public:
explicit Call_Back(QObject *parent = nullptr);
QString userName;
void setuserName(const QString &userName);
void callClassFunction();
signals:
void userNameChanged();
private:
QString m_userName;
};
#endif // CALL_BACK_H
源文件
#include "call_back.h"
#include <QDebug>
// 构造函数
Call_Back::Call_Back(QObject *parent):
QObject(parent)
{
qDebug() << "-------Class construct-------";
m_userName = "hello world";
}
QString Call_Back::userName()
{
qDebug() << __FILE__ << __func__ << m_userName;
return m_userName;
}
void Call_Back::setuserName(const QString &userName)
{
qDebug() << __FILE__ << __func__ << userName;
if (userName == m_userName)
return;
m_userName = userName;
emit userNameChanged();
}
void Call_Back::callClassFunction()
{
qDebug() << __FILE__ << __func__ << __LINE__;
}
每次值改变时,该setUserName函数都会>发出userNameChanged信号。可以在QML中使用onUserNameChanged处理该信号。
- 在main.cpp中注册该方法到QML
包含头文件Call_Back.h
使用qmlRegisterType<BackEnd>("io.qt.examples", 1, 0, "Call_Back");注册方法; - 将main.qml的内容替换如下:
main.qml
import QtQuick 2.6
import QtQuick.Controls 2.0
import io.qt.examples 1.0
//io.qt.examples 是使用qmlRegisterType进行注册的方法,名称必须和注册时一致
ApplicationWindow {
id: root
width: 300
height: 480
visible: true
BackEnd {
id: backend
}
Text {
id: txt1
x: 20
y: 20
text: backend.userName
}
TextField {
text: backend.userName
placeholderText: qsTr("User name")
anchors.centerIn: parent
onTextChanged:{
backend.userName = text
}
}
}
每当m_userName的值发生变化时,setUserName函数就会发出userNameChanged信号。可以使用onUserNameChanged处理程序从QML处理信号。
方法二:C++暴露(公开)方法到QML
- QML可以通过C++代码中定义的功能轻松扩展。由于 QML 引擎与Qt 元对象系统的紧密集成,任何由QObject 派生类适当公开的功能都可以从 QML 代码访问。这使得C++数据和功能可以直接从QML访问,通常只需很少或不需要修改。
- QML 引擎能够通过元对象系统自检QObject实例。这意味着任何 QML 代码都可以访问QObject 派生类实例的以下成员:
1、属性property
2、信号函数signals
3、public槽函数和使用 Q_INVOKABLE 声明的public函数允许元对象系统调用他们。
(此外,如果已使用Q_ENUMS声明枚举,则可以使用枚举。
属性:
- 为了最大程度地与QML进行互操作,任何可写的属性都应该具有关联的NOTIFY信号,只要属性值发生变化就会发出该信号。例如:
Q_PROPERTY(QString author READ author WRITE setAuthor NOTIFY authorChanged)这用来实现QML的属性绑定机制。
信号: - 可以从QML代码访问QObject派生类型的任何公共信号。
- QML引擎自动为从QML使用的QObject派生类型的任何信号创建信号处理程序。信号传递的所有参数都可以通过参数名称在信号处理程序中获得。同样可以在第一篇中找到demo。
函数-方法:
任何QObject派生类型的方法都可以从QML代码访问,只要它是: - 标有Q_INVOKABLE宏的公共方法
- 一种public slots的方法
QML支持调用重载的C ++函数。如果有多个具有相同名称但不同参数的C ++函数,则将根据提供的参数的数量和类型调用正确的函数。
以下示例,点击窗口时会调用 Q_INVOKABLE 方法 Q_INVOKABLE void callClassFunction()和公有槽函数 — void slotSetAuthor()
message.h
#ifndef MESSAGE_H
#define MESSAGE_H
#include <QObject>
#include <QDebug>
class Message : public QObject
{
Q_OBJECT
Q_PROPERTY(QString author READ author WRITE setAuthor NOTIFY authorChanged)
public:
void setAuthor(const QString &a) {
qDebug() << __FILE__ << __func__ << __LINE__ << a;
if (a != m_author) {
m_author = a;
emit authorChanged();
}
}
QString author() const {
qDebug() << __FILE__ << __func__ << __LINE__;
return m_author;
}
// 定义QML访问函数
Q_INVOKABLE void callClassFunction(){
qDebug() << __FILE__ << __func__ << __LINE__;
}
public slots:
void slotSetAuthor(){
qDebug() << __FILE__ << __func__ << __LINE__;
static int i = 0;
++i;
QString str = QString("Tim%1").arg(i);
setAuthor(str);
}
signals:
void authorChanged();
private:
// 私有属性值
QString m_author;
};
#endif // MESSAGE_H
qml文件
import QtQuick 2.5
import QtQuick.Window 2.2
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
MouseArea {
anchors.fill: parent
onClicked: {
// 调试信息
console.log(qsTr('Clicked on background. Text: "' + msg.author + '"'))
msg.author = msg.author + " hello world"
msg.callClassFunction()
msg.slotSetAuthor()
}
}
Text {
text: msg.author // invokes Message::author() to get this value
Component.onCompleted: {
msg.author = "Jonah" // invokes Message::setAuthor()
}
}
}
main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <message.h>
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
Message msg;
QQmlApplicationEngine engine;
//用连接上下文函数使QML可以访问msg对象内的方法和属性
//注意: 此处的“msg”必须小写字母开头,QML才能访问C++对象的函数与属性
engine.rootContext()->setContextProperty("msg",&msg);
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
return app.exec();
}
2、C++访问QML属性与函数
所有的QML对象类型都是源自QObject类型,因此,QML引擎可以使用Qt元对象系统动态的实例化QML对象,并获取所创建对象的属性与方法。也就是说一旦创建了QML对象,就可以使用C++获取它的属性、函数与信号处理。
主要从以下几个方面介绍:
1、从C++加载QML对象,并读写属性
2、调用QML的函数
3、接收QML发出的信号
1、QML属性的读写
假设存在一个名为MyItem.qml的文件,内容如下:
import QtQuick 2.0
Item {
width: 100; height: 100
}
加载QML文档的两种方法
在C++中,QML文档可以使用 QQmlComponent或 QQuickView来加载。加载方法如下:
// Using QQmlComponent
// QML引擎声明
QQmlEngine engine;
// QML组件声明,并使用引擎加载QML文件。
QQmlComponent component(&engine,
QUrl::fromLocalFile("MyItem.qml"));
// 创建一个QObject实例
QObject *object = component.create();
...
delete object;
使用QQmlComponent需要调用QQmlComponent::create()创建一个QObject 实例。
// Using QQuickView
QQuickView view;
view.setSource(QUrl::fromLocalFile("MyItem.qml"));
view.show();
QObject *object = view.rootObject();
QQuickView会自动的创建一个QObject实例,可以通过view.rootObject()来获取。
获取QObject对象之后可以通过以下两种方法来设置属性:
object->setProperty("width", 500); //法1
QQmlProperty(object, "width").write(500); //法2
法2相对于法1,会移除上例中的width绑定属性;
除此之外,还可以将对象强制转换为实际类型,然后使用调用函数来设置属性。如下所示:
QQuickItem *item = qobject_cast<QQuickItem*>(object);
item->setWidth(500);
此时可以使用QMetaObject::invokeMethod()和QObject::connect()来调用item的行数和信号处理。详情见后文。
1.1 通过objectName来获取QML对象
假设MyItem.qml的内容如下,Item中存在一个objectName为“rect”的矩形
import QtQuick 2.0
Item {
width: 100; height: 100
Rectangle {
anchors.fill: parent
objectName: "rect"
}
}
则 上述 矩形可以通过 如下方式获取:
QObject *rect = object->findChild<QObject*>("rect");
if (rect)
rect->setProperty("color", "red");
如果存在多个objectName相同的对象,比如listView的Delegate中设置了一个特定的对象名称,则可以使用QObject::findChildren()来获取所有子对象。
警告: 尽管可以从C++中获取和操作QML对象,但是除非为了测试和原型设计,并不建议这样做。因为QML和C++集成的优势之一是能够在QML中实现与C++逻辑和后端分离的UI。
1.2 从C++访问QML对象类型的成员
在QML中使用 property 声明的属性可以在C++中被获取。假设MyItem.qml的内容如下:
// MyItem.qml
import QtQuick 2.0
Item {
property int someNumber: 100
}
则someNumber属性值可以通过QQmlProperty,QObject::setProperty()和QObject::property()来访问。
QQmlEngine engine;
QQmlComponent component(&engine, "MyItem.qml");
QObject *object = component.create();
// 读取 属性值 并转换为int型
qDebug() << "Property value:" << QQmlProperty::read(object, "someNumber").toInt();
// 设置属性值
QQmlProperty::write(object, "someNumber", 5000);
qDebug() << "Property value:" << object->property("someNumber").toInt();
object->setProperty("someNumber", 100);
注意:你必须通过QObject::setProperty(), QQmlPropertyor QMetaProperty::write()这三种方法来设置QML的属性,才能够保证QML引擎对你的修改可知。
2. 调用QML函数
所有的QML函数都暴露在Qt元对象系统中,可以被C++使用QMetaObject::invokeMethod()来访问。向QML传递的函数参数和QML的返回值需要在C++中转换为QVariant值,因为这是QML函数参数和返回值的通用数据类型。例如:
// MyItem.qml
import QtQuick 2.0
Item {
function myQmlFunction(msg) {
console.log("Got message:", msg)
return "some return value"
}
}
C++代码如下:
// main.cpp
QQmlEngine engine;
QQmlComponent component(&engine, "MyItem.qml");
QObject *object = component.create();
QVariant returnedValue;
QVariant msg = "Hello from C++";
QMetaObject::invokeMethod(object, "myQmlFunction",
Q_RETURN_ARG(QVariant, returnedValue),
Q_ARG(QVariant, msg));
qDebug() << "QML function returned:" << returnedValue.toString();
delete object;
3.连接QML信号
所有的QML信号均可以在C++中被访问,可以像普通Qt的C++信号一样使用。
QML信号参数为:string
// MyItem.qml
import QtQuick 2.0
Item {
id: item
width: 100; height: 100
signal qmlSignal(string msg)
MouseArea {
anchors.fill: parent
onClicked: item.qmlSignal("Hello from QML")
}
}
C++处理方式如下:
class MyClass : public QObject
{
Q_OBJECT
public slots:
void cppSlot(const QString &msg) {
qDebug() << "Called the C++ slot with message:" << msg;
}
};
int main(int argc, char *argv[]) {
QGuiApplication app(argc, argv);
QQuickView view(QUrl::fromLocalFile("MyItem.qml"));
QObject *item = view.rootObject();
MyClass myClass;
QObject::connect(item, SIGNAL(qmlSignal(QString)),
&myClass, SLOT(cppSlot(QString)));
view.show();
return app.exec();
}
需要注意的是,当信号参数为QML对象时,参数类型应该为var,C++中槽函数的参数应该为QVariant。
QML文件如下:
import QtQuick 2.0
Item {
id: item
width: 100; height: 100
signal qmlSignal(var anObject)
MouseArea {
anchors.fill: parent
onClicked: item.qmlSignal(item)
}
}
C++处理方式如下:
class MyClass : public QObject
{
Q_OBJECT
public slots:
void cppSlot(const QVariant &v) {
qDebug() << "Called the C++ slot with value:" << v;
QQuickItem *item = qobject_cast<QQuickItem*>(v.value<QObject*>());
qDebug() << "Item dimensions:" << item->width()
<< item->height();
}
};
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
QQuickView view(QUrl::fromLocalFile("MyItem.qml"));
QObject *item = view.rootObject();
MyClass myClass;
QObject::connect(item, SIGNAL(qmlSignal(QVariant)),
&myClass, SLOT(cppSlot(QVariant)));
view.show();
return app.exec();
}
3.嵌入C++对象到QML
- 通过C++加载QML时,如果能嫁闺女C++对象嵌入到QML中可能会很用用。这使得可以在QML中调用嵌入对象的方法,或者将C++对象实例作为QML视图的数据模型。
- 通过QQmlContext类可以将C++数据注入QML对象。此类(QQmlContext)将数据公开给QML对象的上下文,以便可以直接从QML代码的范围内引用数据。
1. 设置一个简单属性的上下文属性
例如,下面的QML代码中将访问一个当前域不存在的值currentDateTime
// MyItem.qml
import QtQuick 2.0
Text { text: currentDateTime }
currentDateTime可以通过C++代码使用QQmlContext::setContextProperty()直接嵌入到QML中。代码如下:
QQuickView view;
// 将属性currentDateTime设置链接属性,嵌入到QML中
view.rootContext()->setContextProperty("currentDateTime", QDateTime::currentDateTime());
view.setSource(QUrl::fromLocalFile("MyItem.qml"));
view.show();
注意: 由于QML计算的所有相关表达式都是在特定的上下文中计算的,因此如果更改了上下文,则所有绑定的属性都将重新计算。因此在除了初始化程序之外,应该小心使用上下文属性,这可能导致应用程序性能下降。
2.设置一个对象作为上下文属性
上下文属性可以包含QVariant或QObject *值。这意味着可以使用此方法注入自定义的C ++对象,并且可以在QML中直接修改和读取这些对象。修改上述实例,向QML中嵌入一个QObject实例,而不是一个QDateTime值。
C++:
class ApplicationData : public QObject
{
Q_OBJECT
public:
Q_INVOKABLE QDateTime getCurrentDateTime() const {
return QDateTime::currentDateTime();
}
};
int main(int argc, char *argv[]) {
QGuiApplication app(argc, argv);
QQuickView view;
ApplicationData data;
view.rootContext()->setContextProperty("applicationData", &data);
view.setSource(QUrl::fromLocalFile("MyItem.qml"));
view.show();
return app.exec();
}
QML:
// MyItem.qml
import QtQuick 2.0
Text { text: applicationData.getCurrentDateTime() }
注意:从C++返回到QML的日期/时间值可以通过Qt.formatDateTime()和相关函数进行格式化。
2.1 QML处理C++对象的信号
如果QML需要接收上下文的信号,可以通过Connections类型来连接信号。如下:
Text {
text: applicationData.getCurrentDateTime()
Connections {
target: applicationData
onDataChanged: console.log("The application data changed!")
}
}
上下文属性对与在QML视图中使用基于C++的数据模型非常有用。
文章转自博客园(Nlj):https://www.cnblogs.com/Nlj-Moon/p/16849680.html