为了节省代码,把精力放在技术本身上,我们直接复用上篇博客中的main.qml来演示
Qml代码
//main.qml
import QtQuick 2.13
import QtQuick.Window 2.13
//窗口节点
Window {
id: root
visible: true
width: 640
height: 480
title: "Qt Quick Demo"
IconButton {
anchors.fill: parent
anchors.margins: 100
icon: "images/1.png"
text: "Print Mouse Position"
}
}
//IconButton.qml
IconButtonForm {
id: _root
width: 640
height: 480
//点击信号
signal clicked(var mouse)
//点击事件
clickRect.onClicked: {
_root.clicked(mouse)
}
//监听属性变化
onWidthChanged: {
console.debug("IconButton width change")
}
//Qml方法
function sum(a, b) {
return a + b
}
}
C++将Qml加载为对象
C++可以通过QQmlComponent和QQuickView两种方式来获得Qml中的对象
//QQmlComponent
int main(int argc, char *argv[]) {
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
QQmlComponent component(&engine, "qrc:///main.qml"); //注意路径写法
QObject *object = component.create();
qdebug << object->metaObject()->className(); //打印结果为:QQuickWindowQmlImpl
QQuickWindow *window = qobject_cast<QQuickWindow *>(object);
qdebug << window->width();
return app.exec();
}
//QQuickView
int main(int argc, char *argv[]) {
QGuiApplication app(argc, argv);
QQuickView view; //QQuickView不支持用Window作为根节点
view.setSource(QUrl("qrc:///IconButton.qml")); //注意路径写法
view.show();
QObject *object = view.rootObject();
qdebug << object->metaObject()->className(); //打印结果为:IconButton_QMLTYPE_1
QQuickItem *item = qobject_cast<QQuickItem *>(object);
qdebug << item->width();
return app.exec();
}
C++控制Qml对象属性
C++控制Qml对象属性有两种方式,一种是get/set,一种是read/write
这两种方式的区别在于,后者会移除属性之前已有的约束
比如我们设置了【width:height】,在调用了QQmlProperty::write()之后,这个约束便会解除
C++从Qml中读取的对象属性,全部通过QVariant进行封装
当通过C++改变Qml中的对象属性时,会触发Qml中的属性监听器,比如width改变时,onWidthChanged也会执行
int main(int argc, char *argv[]) {
QGuiApplication app(argc, argv);
QQuickView view;
view.setSource(QUrl("qrc:///IconButton.qml"));
view.show();
QObject *object = view.rootObject();
object->property("width");
object->setProperty("width", 1000);
QQmlProperty(object, "width").read();
QQmlProperty(object, "width").write(1000);
return app.exec();
}
C++调用Qml对象方法
QQuickView view;
view.setSource(QUrl("qrc:///IconButton.qml"));
view.show();
QObject *object = view.rootObject();
QVariant returnValue;
QMetaObject::invokeMethod(object, "sum", Q_RETURN_ARG(QVariant, returnValue), Q_ARG(QVariant, 1), Q_ARG(QVariant, 2));
qdebug << returnValue;
C++槽函数响应Qml信号
C++中的槽函数和Qml中的信号,按照正常的connect方式连接即可,因为connect方法里面的参数本质上只是字符串,剩下工作都由Qt编译器来自动完成,既然我们已经知道信号和槽的名字,直接写进入即可
为了定义一个C++槽函数,我们需要增加一个QObject类,由于Qt要求必须将Q_OBJECT宏写在头文件中,我们将main.cpp改成main.h,这样就可以保证所有代码在同一个文件中,虽然不太规范,但方便讲解
//main.h
class QmlClass : public QObject {
Q_OBJECT
public slots:
void onMouseClick(QVariant mouse) {
QObject *pEvent = mouse.value<QObject *>();
qdebug << pEvent->property("button");
qdebug << pEvent->property("y");
qdebug << pEvent->property("y");
}
};
int main(int argc, char *argv[]) {
QGuiApplication app(argc, argv);
QQuickView view;
view.setSource(QUrl("qrc:///IconButton.qml"));
view.show();
QObject *qmlObject = view.rootObject();
QmlClass qmlClass;
QObject::connect(qmlObject, SIGNAL(clicked(QVariant)), &qmlClass, SLOT(onMouseClick(QVariant)));
return app.exec();
}
Qml使用C++中的类
//main.h
#include <qlib.h>
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQuickView>
class CppClass : public QObject {
Q_OBJECT
//定义一个属性,可以在Qml中访问这个属性
Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged)
public:
QString name() {
return mName;
}
//名字改变时,发出信号,可以在Qml中响应这个信号
void setName(QString &name) {
if (name == mName) return;
QString temp = mName;
mName = name;
emit nameChanged(temp, name);
}
//定义一个方法,可以在Qml中访问这个方法
Q_INVOKABLE void printName() {
qdebug << name();
}
signals:
//定义名字改变的信号,Qml中会自动生成名为onNameChanged的信号处理器
void nameChanged(QString oldName, QString newName);
private:
QString mName;
};
int main(int argc, char *argv[]) {
QGuiApplication app(argc, argv);
//注册在Qml中的模块名和类名
qmlRegisterType<CppClass>("cpp", 1, 0, "CppClass");
QQuickView view;
view.setSource(QUrl("qrc:///IconButton.qml"));
view.show();
return app.exec();
}
//IconButton.qml
import QtQuick 2.13
import cpp 1.0
IconButtonForm {
id: _root
width: 640
height: 480
//使用C++中的类
CppClass {
id: cppClass
name: "hello" //使用C++中的属性
//响应C++中的信号
onNameChanged: {
console.debug("onNameChanged:", oldName, newName)
printName() //调用C++中的方法
}
}
//点击时改变cppClass的名称
clickRect.onClicked: {
cppClass.name = Math.random()
}
}