目录
3,暴露变量:用 Q_PROPERTY() 宏,将成员变量导出为Qt属性,并允许QML属性绑定:
6,实战:测试平台:Qt 5.15.0 MinGW 32-bit:
- 参考博客:https://blog.csdn.net/weixin_37459951/article/details/72901831
- 参考博客:https://blog.csdn.net/qq_34139994/article/details/105195328
- 参考博客:https://blog.csdn.net/taohe_0/article/details/51353311
- 参考博客:https://www.cnblogs.com/itrena/p/5938245.html
1,效果演示:
2,概述:
这里主要学习,如何将C++类的:成员变量、成员方法、信号,暴露给 QML。达到QML与C++的互操作的目的。
参考资料:《Qt 官方文档》, 章节:The Property System 个别地方引用了官方原文。
3,暴露变量:用 Q_PROPERTY() 宏,将成员变量导出为Qt属性,并允许QML属性绑定:
在继承 QObject 的类中,使用 Q_PROPERTY() 宏声明属性。Q_PROPERTY() 语法:
Q_PROPERTY(type name
(READ getFunction [WRITE setFunction] |
MEMBER memberName [(READ getFunction | WRITE setFunction)])
[RESET resetFunction]
[NOTIFY notifySignal]
[REVISION int]
[DESIGNABLE bool]
[SCRIPTABLE bool]
[STORED bool]
[USER bool]
[CONSTANT]
[FINAL])
[REQUIRED]
The property name and type and the READ function are required. The type can be any type supported by QVariant, or it can be a user-defined type. The other items are optional, but a WRITE function is common. The attributes default to true except USER, which defaults to false. ——(Qt 官方文档)译:【属性的 name 、type 、READ函数 是必需的。属性的类型可以是QVariant支持的任何类型,也可以是用户定义的类型。其他项是可选的,但是WRITE函数也很常用。除了USER属性,其他属性默认为真。】
READ funcR:定义了读取属性的接口funcR,funcR是const类型,返回值类型为属性类型,不能带参数。
WRITE funcW:定义了设置属性的接口funcW,funcW必须带有一个属性类型的参数,返回值类型void。
MEMBER var:MEMBER指明了成员变量var即可读也可写的,相当于同时使用了READ和WRITE关键字。不同的是,READ和WRITE关键字通过类的接口来实现,而MEMBER关键字是通过类的对象直接访问来实现的。
“ Note that a NOTIFY signal must be specified to allow QML property bindings. ”( 注意,必须指定一个 NOTIFY 信号来允许QML属性绑定 )
...
Q_PROPERTY(QString text MEMBER m_text NOTIFY textChanged)
signals:
void textChanged(const QString &newText);
private:
QString m_text;
...
4,暴露方法:用 Q_INVOKABLE 宏:
通过以下两种方式把类方法暴露到QML。如果C++方法有QObject类型的参数时,在QML中使用对象ID或var类型参数来进行参数传递。
(1)使用宏 Q_INVOKABLE 宏标记公共方法。
(2)把方法声明为public槽函数 public slots,(声明为protected槽函数 protected slots。测试了一下,也可行)
5,暴露信号:
信号无需特殊处理。将类或类的对象成功注册到在QML后,可直接使用。使用方法:(以注册对象为例)
testObj.refreshTime.connect(refreshCurrentTime)
testObj: 注册到QML的对象名
refreshTime: testObj对象的时间刷新信号
refreshCurrentTime: qml中定义的refreshTime信号处理函数
6,实战:测试平台:Qt 5.15.0 MinGW 32-bit:
使用方法:将类或类的对象注册到QML。
例子:注册过程在 main() 中完成:==>>【 有关如何导入C++类或对象,看这里!!! 】
完整工程下载:【直达电梯 ... QML与C++对象间的互操作:工程Demo】
完整代码如下:
main().cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QtQuick>
#include "myclass.h"
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QCoreApplication::setAttribute(Qt::AA_UseSoftwareOpenGL); //窗体尺寸变化时防闪屏
QQuickWindow::setTextRenderType(QQuickWindow::NativeTextRendering); //本地字体
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
// 注册 C++ 对象
MyClass testObj;
engine.rootContext()->setContextProperty("testObj", &testObj);
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);
// logo
app.setWindowIcon(QIcon(":/image/logo.png"));
return app.exec();
}
myclass.h
#ifndef MYCLASS_H
#define MYCLASS_H
#include <QObject>
#include <QString>
#include <QTimer>
class MyClass : public QObject
{
Q_OBJECT
// 使用 MEMBER 关键字
Q_PROPERTY(int age MEMBER m_nAge NOTIFY ageChanged)
Q_PROPERTY(QString name MEMBER m_strName NOTIFY nameChanged)
public:
explicit MyClass(QObject *parent = nullptr);
Q_INVOKABLE void testFunc01();
void testFunc03();
public slots:
void testFunc02();
private:
void getCurrentTime();
signals:
void ageChanged();
void nameChanged(const QString &newName);
//普通信号
void refreshTime(const QString time);
private:
QString m_strName;
int m_nAge;
QTimer m_timer;
};
#endif // MYCLASS_H
myclass.cpp
#include "myclass.h"
#include <QDateTime>
#include <QString>
#include <QDebug>
MyClass::MyClass(QObject *parent) : QObject(parent){
this->m_strName = "悟空";
this->m_nAge = 18;
connect(&m_timer, &QTimer::timeout, this, &MyClass::getCurrentTime);
this->m_timer.start(1000); //启动定时器
}
void MyClass::testFunc01(){
qDebug()<<"Q_INVOKABLE 声明的公有方法 ...";
}
void MyClass::testFunc02(){
qDebug()<<"public 槽函数 ...";
}
void MyClass::testFunc03(){
qDebug()<<"普通公有方法 ...";
}
void MyClass::getCurrentTime(){
QString currentTime = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss");
this->refreshTime(currentTime);
}
main.qml
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Window 2.15
Window {
id: root
width: 360; height: 400
visible: true
title: qsTr("QML And C++ Class ...")
color: "#E6E6FA"
Column {
x: (root.width-width)/2
y: 10
spacing: 5
MyLabel {
id: nameLabel
}
MyLabel {
id: ageLabel
}
MyLabel {
id: timeLabel
}
Button {
id: btn01
width: 260
highlighted: true
text: "Q_INVOKABLE 声明的公有方法"
onClicked: testObj.testFunc01()
}
Button {
id: btn02
width: 260
highlighted: true
text: "public slots 公有槽函数"
onClicked: testObj.testFunc02()
}
}
function refreshCurrentTime(strTime){
timeLabel.text = strTime
}
function changeName(){
nameLabel.text = testObj.name
}
function changeAge(){
ageLabel.text = testObj.age
}
Component.onCompleted: {
testObj.refreshTime.connect(refreshCurrentTime)
testObj.onNameChanged.connect(changeName)
testObj.onAgeChanged.connect(changeAge)
nameLabel.text = qsTr(testObj.name)
ageLabel.text = qsTr(String(testObj.age))
}
/** 辅助测试控件 *********************************/
Text {
id: log
text: qsTr("这里显示错误信息")
x: testInfo.x
anchors.bottom: testInfo.top
anchors.bottomMargin: 5
}
TestSeting{
id: testInfo
x: (root.width-width)/2
anchors.bottom: parent.bottom
anchors.bottomMargin: 10
}
}
MyLabel.qml
import QtQuick 2.15
import QtQuick.Controls 2.15
Rectangle {
width: 260; height: 30
color: "#606060"
border.color: "#303030"
property alias text: info.text
Text {
id: info
text: qsTr("MyLabel")
font.pixelSize: 24
color: "#00FFFF"
anchors.centerIn: parent
}
}
TestSeting.qml
import QtQuick 2.15
import QtQuick.Controls 2.15
Rectangle {
id: root
width: 260; height: 100
border.color: "gray"
Grid {
id: layout
columns: 2
spacing: 5
anchors.centerIn: root
// 修改testObj 的 m_strName 属性
Text {
width: 100; height: 25
text: qsTr("修改 m_strName:")
verticalAlignment: Text.AlignVCenter
}
TextField {
width: 120; height: 25
text: "悟空"
background: Rectangle{
color: "#FFE0E0"
border.color: "black"
}
onTextChanged: { testObj.name = text }
}
// 修改testObj 的 m_nAge 属性
Text {
width: 100; height: 25
text: qsTr("修改 m_nAge:")
verticalAlignment: Text.AlignVCenter
}
TextField {
width: 120; height: 25
text: "18"
background: Rectangle{
color: "#FFE0E0"
border.color: "black"
}
onTextChanged: {
var age = Number(text)
if(isNaN(age))
log.text = "m_aAge 数值无效 ..."
else{
testObj.age = age
log.text = "这里显示错误信息"
}
}
}
}
}