前言
在QT开发中,如果遵循MVC架构的话,一般使用QML负责VIEW层显示,使用C++负责数据采集处理。本文仅对QML中使用C++自定义对象,QML的信号与C++自定义对象的槽函数,以及QML中定义的槽函数与C++自定义对象的信号等三部分作一些测试和说明。
1.QML中使用C++自定义对象
1.1.创建自定义C++类
步骤和widgets项目创建c++类一样,如果使用场景可以确定为全局唯一,我们可以直接创建单例。
MyObject.h
class MyObject : public QObject
{
Q_OBJECT
public:
explicit MyObject(QObject *parent = nullptr);
int nValue() const;
void setNValue(int newNValue);
int nStaticValue() const;
void setNStaticValue(int newNStaticValue);
const QString &sStr() const;
void setSStr(const QString &newSStr);
private:
int m_nValue;
int m_nStaticValue;
QString m_sStr;
Q_PROPERTY(int nValue READ nValue WRITE setNValue NOTIFY nValueChanged)
Q_PROPERTY(int nStaticValue READ nStaticValue WRITE setNStaticValue NOTIFY nStaticValueChanged)
Q_PROPERTY(QString sStr MEMBER m_sStr NOTIFY sStrChanged)
signals:
void nValueChanged();
void nStaticValueChanged();
void sStrChanged();
};
说明
1.在头文件中定义了三个私有变量,其余都是通过 快捷键alt+enter自动生成的。
如果你在使用该快捷键只能弹出下面三个按钮,说明你的QT版本低于5.15,想要使用上述特性则需要将当前QT版本升级到5.15以上。
2.cpp文件内容是上述快捷键自动生成的,此处略过。
1.2.在合适的地方注册自定义C++供QML使用
int main(int argc, char *argv[])
{
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
#endif
QGuiApplication app(argc, argv);
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);
//注册
qmlRegisterType<MyObject>("MyObj", 1, 0, "MyObject");
engine.load(url);
return app.exec();
}
注意:在engine.load(url)之前注册,尤其是注册单例时,需要确保qml加载时该单例对象已经实例化。
1.3.QML文件中使用自定义C++对象
import QtQuick 2.15
import QtQuick.Window 2.15
import MyObj 1.0
Window {
width: 640
height: 480
visible: true
title: qsTr("Hello World")
property int value: width
property int staticValue: width
MyObject{
id: myobj
nValue: width
nStaticValue: 10
sStr: "hello"
}
//当value以 = 号赋值的方式,将变成静止的值
Component.onCompleted: {
staticValue = myobj.nStaticValue
console.log(myobj.sStr)
}
onWidthChanged: {
console.log("staticValue:", staticValue)
console.log("value:", myobj.nValue)
}
}
2.QML的信号与C++自定义对象的槽函数
QT的特色之一就是信号槽了,使用信号槽可以使编程变得极为灵活。
2.1.QML中创建信号
//此信号在main.cpp中绑定信号槽
signal sigQml(var i, var s)
//此信号在qml文件中绑定信号槽
signal sigQml2(int i, string s)
//此信号在main.cpp中绑定信号槽
signal sigQmlNoarg()
MyObject中添加槽函数如下:
public Q_SLOTS:
void slotsCpp(QVariant i, QVariant s);
void slotsCpp2(int i, QString s);
void slotsCppClicked();
2.2.QML中直接绑定C++对象中的槽
Component.onCompleted: {
sigQml2.connect(myobj.slotsCpp2)
}
注意:此处sigQml2的参数类型为int及String可以被slotsCpp2自动识别。
2.3.在C++中绑定QML定义的信号
这里处理稍微麻烦一点,我们需要获取qml中的元素队列,从中遍历出我们需要的对象,然后就是widget项目一样进行绑定即可。
int main(int argc, char *argv[])
{
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
#endif
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
//注册
qmlRegisterType<MyObject>("MyObj", 1, 0, "MyObject");
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);
//在load之后再connect
auto list = engine.rootObjects();
QObject* root = list.first();
//连接信号槽,注意这里的参数类型都是 QVariant
QObject::connect(root, SIGNAL(sigQml(QVariant,QVariant)), &MyObject::getInstance(), SLOT(slotsCpp(QVariant,QVariant)));
QObject::connect(root, SIGNAL(sigQmlNoarg()), &MyObject::getInstance(), SLOT(slotsCppClicked()));
return app.exec();
}
要注意的是这里需要使用QVariant类型,不然会导致信号槽绑定不了。
2.4.小结
通过以上两种信号槽绑定方式的对比,我们能直接感受到2.2QML中直接绑定C++对象中的槽方式更简单,且支持不同的参数类型,而2.3.在C++中绑定QML定义的信号方式则较麻烦,代码量大,尤其是在页面多重嵌套的情况下,获取单个控件可能工作量增大。
3.QML中定义的槽函数与C++自定义对象的信号
3.1.c++自定义对象中创建信号
signals:
//在qml中调用
void signalCpp(int i, QString s);
//在c++中调用
void signalCpp2(QVariant i, QVariant s);
并且给自定义对象增加一个获取单例的方法:
//头文件
static MyObject * getInstance();
//源文件
static MyObject * __myObj = nullptr;
MyObject * MyObject::getInstance()
{
if (__myObj == nullptr)
{
__myObj = new MyObject();
}
return __myObj;
}
3.2.注册单例
int main(int argc, char *argv[])
{
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
#endif
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
//qmlRegisterType 需要创建对象才能使用
// qmlRegisterType<MyObject>("MyObj", 1, 0, "MyObject");
//qmlRegisterSingletonInstance 使我们可以直接使用单例对象
qmlRegisterSingletonInstance("MyObj", 1, 0, "MyObject", MyObject::getInstance());
………
………
3.2.1.使用上下文方式储存单例
除了上述注册单例的方式,也可将此单例保存在QML的上下文中,如此同样可以被QML直接使用。
engine.rootContext()->setContextProperty("MyObject", MyObject::getInstance());
3.3.QML中绑定C++对象中的信号
property int clickCount: 0
//创建槽函数
function slotQml(i,s)
{
console.log("slotQml", i, s)
}
//绑定信号槽
Connections{
target: MyObject
function onSignalCpp(i,s)
{
slotQml(i,s)
}
}
Button {
objectName: "btn1"
width: 100
height: 30
onClicked: {
//发送信号
MyObject.signalCpp(clickCount, "clickCount" + clickCount)
clickCount++
}
}
Connections函数中的“target: MyObject”即3.2注册单例中的对象名。
3.4.在C++中绑定C++自定义的信号和QML定义的槽
qml中触发:
Button{
objectName: "btn2"
y: 60
width: 100
height: 30
onClicked: {
//发送信号
MyObject.signalCpp2(clickCount, "clickCount" + clickCount)
clickCount++
}
}
int main(int argc, char *argv[])
{
……
//qmlRegisterSingletonInstance 使我们可以直接使用单例对象
qmlRegisterSingletonInstance("MyObj", 1, 0, "MyObject", MyObject::getInstance());
……
engine.load(url);
//在load之后再connect
auto list = engine.rootObjects();
QObject* root = list.first();
//绑定信号槽
QObject::connect(MyObject::getInstance(), SIGNAL(signalCpp2(QVariant,QVariant)), root, SLOT(slotQml(QVariant,QVariant)));
return app.exec();
}
3.5.小结
通过3.3.QML中绑定C++对象中的信号和3.4.在C++中绑定C++自定义的信号和QML定义的槽两种方式对比,直观的感受还是qml中绑定信号槽来得简洁一点,当然在c++绑定也不是不可以。
4.补充(QML和c++函数相互调用)
4.1.QML端调用c++中定义的函数
这个比较简单,只要在自定义对象的函数前使用Q_INVOKABLE 宏即可。
4.2.C++端调用QML中定义的函数
我们一般不会去调用QML中定义的函数,但是QT提供了这个方法。
因为QML中的对象其实也都是继承自QObject,所以一样可以用元对象系统来获取方法属性等。
qml文件中定义函数:
function funcRes(i,s)
{
return "ok";
}
c++中调用该函数:
QVariant res;
QVariant arg1 = 1;
QVariant arg2 = "jack" ;
QMetaObject::invokeMethod(root, "funcRes", Q_RETURN_ARG(QVariant, res), Q_ARG(QVariant, arg1), Q_ARG(QVariant, arg2));
qDebug() << res;
本篇博客到此完结~