在Qt界面开发时,用信号槽可以很容易实现各个窗口控件之间的交互,qml也是可以的,qml和C++可以相互调用,可以在qml代码中调用C++的类对象,也可以用C++类来获取qml的控件对象,下面分别介绍这两这种用法,需要源码的可以翻到最后直接下载。
Qt 提供了两种在 QML 环境中使用 C++对象的方式∶
方式1:在C+中实现一个类,注册为 QML 环境的一个类型,在 QML 环境中使用该类型创建对象。
方式2:在 C++中构造一个对象,将这个对象设置为 QML 的上下文属性,在QML 环境中直接使用该属性。
不管哪种方式,对要导出的 C++类都有要求,不是一个类的所有方法、变量都可以在 QML 语境中使用,定义可以导出的 C++类 前提条件 要想将一个类或对象导出到 QML 中,下列的前提条件必须满足∶
(1)从 QObject 或 QObject 的派生类继承,并使用Q_OBJECT宏,这和使用信号与槽的前提条件一样的,这两个条件是为了让一个类能够进入Qt强大的元对象系统(meta-object system)中,只有使用元对象系统,一个类的某些方法或属性才可能通过字符串形式的名字来调用,才可以在 QML 中被访问。
(2)成员函数想在qml中被调用,则需要在声明前加上Q_INVOKABLE
(3)槽函数可以用类对象在qml代码中直接调用
(4)C++的成员属性可以用Q_PROPERTY宏设置
(5)枚举体需要用Q_ENUMS导出
下面介绍方式1, 例如下面这个类
#ifndef TESTBOX_H #define TESTBOX_H #includeclass TestBox : public QObject { Q_OBJECT Q_ENUMS(ColorType) Q_PROPERTY(int mValue READ getValue WRITE setValue) public: explicit TestBox(QObject *parent = nullptr); enum ColorType { Red, Green, Blue }; // 成员函数想在qml中被调用,则需要在声明前加上Q_INVOKABLE Q_INVOKABLE int fun1(); int getValue() { return m_value; } Q_INVOKABLE void setValue(int value) { m_value = value; } signals: void sig_Value(); public slots: void on_Get(); private: int m_value = 0; }; #endif // TESTBOX_H
类定义好后,在main.cpp中注册
#include#include#include "testbox.h" int main(int argc, char *argv[]) { QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); QGuiApplication app(argc, argv); qmlRegisterType("cpp.qt.TestBox", 1, 0, "TestBox"); QQmlApplicationEngine engine; 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注册类TestBox,版本是1.0, 这个版本号可以自己定义。
最后在qml代码中调用
import QtQuick 2.12 import QtQuick.Window 2.12 import QtQuick.Controls 2.5 import cpp.qt.TestBox 1.0 Window { visible: true width: 640 height: 480 title: qsTr("Hello World") TestBox{ id:tb mValue:123 //可以在这里给对象赋值 } Button{ id:btn1 text:"getValue" anchors.left: parent.left anchors.leftMargin: 40 anchors.top: parent.top anchors.topMargin: 60 onClicked: { tb.on_Get() //调用槽函数 } } Button{ id:btn2 text:"setValue" anchors.left: btn1.left anchors.top: btn1.bottom anchors.topMargin: 20 onClicked: { tb.setValue(3456) } } Button{ id:btn3 text:"getenum" anchors.left: btn2.left anchors.top: btn2.bottom anchors.topMargin: 10 onClicked: { valueTextFeild.text = TestBox.Blue //调用枚举 } } Button{ id:btn4 text:"invoke fun" anchors.left: btn3.left anchors.top: btn3.bottom anchors.topMargin: 10 onClicked: { valueTextFeild.text = tb.fun1() //调用普通成员函数 } } TextField { id:valueTextFeild anchors.left: btn1.right anchors.leftMargin: 15 anchors.top: btn1.top } //链接信号槽 Connections{ target: tb onSig_Value:{ valueTextFeild.text = tb.mValue //获取成员属性值 } } }
运行界面如下:
方式2:
在main.cpp中直接创建C++类对象,例如下面的代码,
engine.rootContext()->setContextProperty("tb", new TestBox);
new对象 tb, 然后在qml中就可以直接使用tb了。
代码如下:
#include#include#include "testbox.h" #includeint main(int argc, char *argv[]) { QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); QGuiApplication app(argc, argv); //qmlRegisterType("cpp.qt.TestBox", 1, 0, "TestBox"); QQmlApplicationEngine engine; engine.rootContext()->setContextProperty("tb", new TestBox); 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(); }
这种方式有弊端,枚举值如法直接在qml中获得,可以用数字代替。
在C++代码中获取qml控件时,需要用到objectName
Text{ objectName: "textLabel" text:"Hello World" anchors.centerIn: parent font.pixelSize: 26 } Button{ objectName: "quitBtn" anchors.right: parent.right anchors.rightMargin: 10 anchors.bottom: parent.bottom anchors.bottomMargin: 10 text:qsTr("退出") }
上面的控件在声明时,都设置了属性objectName, qt程序在初始化时有个对象树,在对象树中根据objectName调用findChild可以获取到控件对象指针
//C++获取qml的控件 QObject* pQuitBtn = pRoot->findChild("quitBtn"); if(pQuitBtn) { QObject::connect(pQuitBtn, SIGNAL(clicked()), &app, SLOT(quit())); } QObject *pText = pRoot->findChild("textLabel"); if(pText) { bool bRet = QMetaObject::invokeMethod(pText, "setText", Q_ARG(QString, "AAAA")); qDebug() << "bRet = " << bRet; //调用失败,没有该方法 pText->setProperty("color", QColor::fromRgb(255,0,0)); }
以上具体功能,可以看我的代码,链接如下:
https://gitee.com/qwerwo/qml_code
3个工程