一直对于QT的理解和使用都停留在主窗口程序和控制台程序,虽然QT的新东西QML听过也接触过,但是基本上没梳理过调用流程。趁着旧项目要使用QML技术,现在就将C++和QML交互进行总结。
目录
一. QML中使用C++业务类
二.C++使用QML
三.QVariant中方法canConvert和convert使用总结
在C++和QML中均可以定义信号和槽,并且均可以完成信号和槽的绑定
基本流程如下:
1.将C++注册进入QML引擎,C++类型在qml中当成一个子组件使用
2.在qml中完成信号与槽的绑定
测试代码:
1.QML定义
main.qml
import QtQuick 2.7
import QtQuick.Window 2.2
import WorkClass 1.0
Window {
id: root
visible: true
width: 640
height: 480
title: qsTr("Hello World")
signal qmlSignal1
signal qmlSignal2(string strValue, int iValue)
MainForm {
anchors.fill: parent
mouseArea.onClicked: {
Qt.quit();
}
}
MouseArea{
anchors.fill: parent
acceptedButtons: Qt.LeftButton | Qt.RightButton
onClicked: {
if(mouse.button === Qt.LeftButton){
console.log(' Qt.LeftButton')
bussiness.strValue = "HelloCpp"
bussiness.intValue = 2022
bussiness.sendSignal()
}else{
console.log(' Qt.RightButton')
root.qmlSignal1()
root.qmlSignal2('hesy', 2000)
}
}
}
//加载子页面qrcode.qml
//Loader {
// id:accountQRCode
// source:"qrcode.qml"
// x:(root.width - root.height / 3) / 2
// y:root.height / 3
// visible: true
//}
CBusiness{
id: bussiness
property int addpro: 0
onIntValueChanged: {
console.log('qml onIntValueChanged', "begin change addpro ", addpro)
addpro++
console.log('qml onIntValueChanged', addpro)
}
onAddproChanged: {
console.log('qml onAddFun', addpro)
}
}
Component.onCompleted: {
bussiness.onSignal1.connect(function(){console.log('no name qml function')})
bussiness.onSignal2.connect(qmlProcess1)
root.qmlSignal1.connect(bussiness.slot1)
root.qmlSignal2.connect(bussiness.slot2)
console.log('Component.onCompleted')
}
function qmlProcess1(str, value){
console.log('qmlProcess1', str, value)
}
}
备注:
另外在qml中也可以使用Connections绑定C++业务类函数和qml中的方法,例如 qrcode.qml,其中target:bussiness代表上面的id: bussiness,这样main.qml中包含qrcode.qml,实现了qml之间的调用,以及多个qml和C++之间的调用。
import QtQuick 2.0
Item {
Image {
id: qrcode
sourceSize.width: parent.width
sourceSize.height: parent.height
source: ""
}
Text {
id: tips
x: parent.x
y: parent.y
text: qsTr("请扫码")
font.pixelSize: 24
visible: false
}
Connections {
target:bussiness
onAccountQRCodeGenerated: {
console.log("onAccountQRCodeGenerated invoke !");
qrcode.source = "image://qrcode/account"
}
onAccountScannedSuccess: {
console.log("onAccountScannedSuccess invoke!");
qrcode.visible = false
tips.visible = true
}
onAccountLoginSuccess: {
console.log("onAccountLoginSuccess invoke !");
tips.text = qsTr("登录成功, 欢迎!")
}
}
}
C++中通过发射信号调用qml中方法,对应方法名为on+大写字母开头函数
emit accountQRCodeGenerated();
emit accountScannedSuccess();
emit accountLoginSuccess();
2.C++业务类定义和实现
cbusiness.h
#ifndef CBUSINESS_H
#define CBUSINESS_H
#include
class CBusiness : public QObject
{
Q_OBJECT
Q_PROPERTY(QString strValue READ getStrValue WRITE setStrValue NOTIFY strValueChanged)
Q_PROPERTY(int intValue READ getIntValue WRITE setIntValue NOTIFY intValueChanged)
public:
explicit CBusiness(QObject *parent = 0);
Q_INVOKABLE void sendSignal();
void setStrValue(QString strValue);
QString getStrValue() const;
void setIntValue(int intValue);
int getIntValue() const;
signals:
void signal1();
void signal2(QString strValue, int intValue);
void strValueChanged(QString strValue);
void intValueChanged(int intValue);
public slots:
void slot1();
void slot2(QString strValue, int intValue);
private:
//类的属性
QString m_strValue;
int m_intValue;
};
#endif // CBUSINESS_H
cbusiness.cpp
#include "cbusiness.h"
#include
CBusiness::CBusiness(QObject *parent) : QObject(parent), m_strValue(""), m_intValue(0)
{
}
void CBusiness::sendSignal()
{
qDebug() << "CBusiness::" << __FUNCTION__;
emit signal1();
emit signal2(m_strValue, m_intValue);
}
void CBusiness::setStrValue(QString strValue)
{
qDebug() << "CBusiness::" << __FUNCTION__ << strValue;
m_strValue = strValue;
emit strValueChanged(strValue);
qDebug()<< "CBusiness::" << "emit strValueChanged" << endl;
}
QString CBusiness::getStrValue() const
{
qDebug() << "CBusiness::" << __FUNCTION__;
return m_strValue;
}
void CBusiness::setIntValue(int intValue)
{
qDebug() << "CBusiness::" << __FUNCTION__;
m_intValue = intValue;
emit intValueChanged(intValue);
qDebug()<< "CBusiness::" << "emit intValueChanged";
}
int CBusiness::getIntValue() const
{
qDebug() << "CBusiness::" << __FUNCTION__;
return m_intValue;
}
void CBusiness::slot1()
{
qDebug() << "CBusiness::" << __FUNCTION__;
}
void CBusiness::slot2(QString strValue, int intValue)
{
qDebug() << "CBusiness::" << __FUNCTION__;
qDebug() << "CBusiness:: " << strValue << " " << intValue;
}
3.主调函数
main.cpp
#include
#include
#include "cbusiness.h"
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
//qmlRegisterType注册C++类型至QML
//arg1:import时模块名
//arg2:主版本号
//arg3:次版本号
//arg4:QML类型名
qmlRegisterType("WorkClass", 1, 0, "CBusiness");
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
if (engine.rootObjects().isEmpty())
return -1;
return app.exec();
}
4.测试结果
鼠标左键点击:
说明:
信号和槽的绑定在qml中完成,鼠标左键按下,首先打印:qml: Qt.LeftButton
bussiness.strValue = "HelloCpp" 触发 CBusiness::setStrValue(
因为如下:
Q_PROPERTY(QString strValue READ getStrValue WRITE setStrValue NOTIFY strValueChanged)
此处= "HelloCpp" 其实是调用的setStrValue
)调用
bussiness.intValue = 2022触发CBusiness::setIntValue(
因为如下: Q_PROPERTY(int intValue READ getIntValue WRITE setIntValue NOTIFY intValueChanged)
此处= 2022其实是调用的setIntValue
)调用, 然后调用emit strValueChanged(strValue);,触发qml中 onIntValueChanged调用,接着addpro++触发onAddproChanged调用
最后调用CBusiness::sendSignal(),触发emit signal1(); emit signal2(m_strValue, m_intValue);信号,调用qml的槽
function(){console.log('no name qml function')
和
function qmlProcess1(str, value)
鼠标右键点击:
说明:
鼠标右键,通过qml信号调用到c++的槽函数,并且还传递参数给c++
基本流程如下:
1.在C++中获得qml对象指针
2.在C++中完成信号和槽的绑定
测试代码:
1.QML定义
main.qml
import QtQuick 2.7
import QtQuick.Window 2.2
Item {
id: root
visible: true
width: 640
height: 480
//title: qsTr("Hello World")
property string msg: "I am QML Item"
signal callCpp(string arg1, string arg2)
MainForm {
anchors.fill: parent
mouseArea.onClicked: {
Qt.quit();
}
}
Rectangle {
anchors.fill: parent
color: "blue"
objectName: "rect"
}
MouseArea {
anchors.fill: parent
onClicked: {
console.log("onClicked, callCpp")
root.callCpp(root.msg, "notify cpp")
}
}
onHeightChanged: {
console.log("onHeightChanged execute")
}
onWidthChanged: {
console.log("onWidthChanged execute")
}
//QML中的方法可以被cpp调用,也可以作为槽函数
function qmlFun(val_arg) {
console.log("qmlFun execute", val_arg, "return qmlFun_return_result")
return "qmlFun_return_result"
}
//注意槽函数参数为var类型
function invokeFromCpp(arg1, arg2) {
console.log("invokeFromCpp execute ", arg1, arg2)
}
}
2.C++业务类定义和实现
cbusiness.h
#ifndef CBUSINESS_H
#define CBUSINESS_H
#include
class CBusiness : public QObject
{
Q_OBJECT
public:
explicit CBusiness(QObject *parent = 0);
signals:
void callQml(const QVariant &arg1,const QVariant &arg2);
public slots:
void invokeFromQml(const QString &arg1,const QString &arg2);
};
#endif // CBUSINESS_H
cbusiness.cpp
#include "cbusiness.h"
#include
CBusiness::CBusiness(QObject *parent) : QObject(parent)
{
}
void CBusiness::invokeFromQml(const QString &arg1,const QString &arg2)
{
qDebug() << "CBusiness::" << __FUNCTION__ << arg1 << arg2;
qDebug() << "CBusiness::" << __FUNCTION__ << " emit callQml";
emit callQml("I am cpp", "notify qml");
}
3.主调函数
main.cpp
#include
#include
#include
#include
#include
#include
#include
#include "cbusiness.h"
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
/*
可以用QQmlComponent\QQuickView\QQuickWidget的C++代码加载QML文档
当使用QQuickView时,qml的根不能是Window
*/
QQuickView view(QUrl("qrc:/main.qml"));
view.show();
// 获取到qml根对象的指针
QObject *qmlObj = view.rootObject();
/*
修改qml属性值的方法
QObject::setProperty() QQmlProperty QMetaProperty::write()
*/
// 通过QObject设置属性值
qDebug() << "cpp: " << "set qml property height";
QQmlProperty(qmlObj, "height").write(500); //qmlObj->setProperty("height",500);
// 通过QObject获取属性值
qDebug() << "cpp: " << "get qml property height" << qmlObj->property("height").toDouble();
// C++访问qml的其它属性
qDebug() << "cpp: " << "get qml property msg" << qmlObj->property("msg").toString();
// 获取QQuickItem
QQuickItem *item = qobject_cast(qmlObj);
// 通过QQuickItem设置属性值
qDebug() << "cpp: " << "set qml property width";
item->setWidth(300);
// 通过QQuickItem获取属性值
qDebug() << "cpp: " << "get qml property width" << item->width();
// 通过object name访问加载的QML对象
// QObject::findChildren()可用于查找具有匹配object name属性的子项
QObject *qmlRect = qmlObj->findChild("rect");
if(qmlRect)
{
qDebug() << "cpp: " << "get rect color" << qmlRect->property("color");
}
// C++调用QML方法
QVariant valReturn;
QVariant valArg = "I am cpp";
//Q_RETURN_ARG()和Q_Arg()参数必须制定为QVariant类型
QMetaObject::invokeMethod(qmlObj, "qmlFun",
Q_RETURN_ARG(QVariant,valReturn),
Q_ARG(QVariant,valArg));
qDebug() << "cpp: " << "QMetaObject::invokeMethod result" << valReturn.toString(); //qml函数中返回“ok”
CBusiness cppObj;
// cpp和qml信号与槽关联
// qml信号绑订cpp的槽,用QString类型
QObject::connect(qmlObj, SIGNAL(callCpp(QString, QString)), &cppObj, SLOT(invokeFromQml(QString, QString)));
//关联cpp信号与qml槽
// cpp的信号绑定qml槽,用QVariant类型
QObject::connect(&cppObj, SIGNAL(callQml(QVariant, QVariant)), qmlObj, SLOT(invokeFromCpp(QVariant, QVariant)));
return app.exec();
}
4.测试结果
信号和槽的绑定在c++代码中完成,在c++中可以修改qml的属性,获取qml的属性,调用qml的方法,传递和获取参数均可以
鼠标点击
鼠标点击后,通过qml的信号callCpp调用c++的槽函数invokeFromQml,然后再通过c++的信号函数callQml调用qml的槽函数invokeFromCpp
1.canConvert只是报告QVariant进行两个类型之间转换的能力,例如QString和Int类型之间的转换,关注类型
2.convert判断的是QVariant进行两个类型的数据之间转换的能力,例如“123”和123之间的转换,关注数据
举例:
QString str1 = "Qt5.7.0";
QVariant var1 = str1;
qDebug() << var1.canConvert(QVariant::Int); // true
qDebug() << var1.convert(QVariant::Int); // false
qDebug() << var1.toString(); // "0"
var1 = str1;
qDebug() << var1.convert(QVariant::String); // true
qDebug() << var1.toString(); // "Qt5.7.0"
QString str2 = "789";
QVariant var2 = str2;
qDebug() << var2.canConvert(QVariant::Int); // true
qDebug() << var2.convert(QVariant::Int); // true
qDebug() << var2.toString(); // "789"