最近有个毕设是用qml做界面,用C++写代码,遇到了需要用C++给QML中GridView的model赋值、更新等操作。
摸索1
一开始自然就想到使用ListModel,然后QMetaObject::invokeMethod来操作ListModel,代码为:(ListModel的objectName设为dataModel,id也设为dataModel)
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/view/qmls/main.qml")));
QObject *topLevel=engine.rootObjects().value(0);
QQuickWindow *window=qobject_cast<QQuickWindow*>(topLevel);
QObject *model=window->findChild<QObject*>("dataModel");
QVariant retValue;
bool result=QMetaObject::invokeMethod(model,'get', Qt::DirectConnection,Q_RETURN_ARG(QVariant,retValue),Q_ARG(int,0));
qDebug()<<result;
window->showFullScreen();
return app.exec();
}
以上代码就是调用ListModel自带的get方法获取到对应的值,并返回给retValue。理想很丰满,现实很骨感,result值为false,这就意味着方法调用失败,失败原因一般有两个:
- 方法名错误
- 参数错误
于是我觉得先将ListModel全部方法与入口参数,出口参数打印出来看看先
const QMetaObject *mo=model->metaObject();
for(int i=0;i<mo->methodCount();i++)
{
qDebug()<<mo->method(i).methodSignature()+"-outArg-"+mo->method(i).returnType();
}
结果为:
"destroyed(QObject*)" -outArg- 43
"destroyed()" -outArg- 43
"objectNameChanged(QString)" -outArg- 43
"deleteLater()" -outArg- 43
"_q_reregisterTimers(void*)" -outArg- 43
"dataChanged(QModelIndex,QModelIndex,QVector<int>)" -outArg- 43
"dataChanged(QModelIndex,QModelIndex)" -outArg- 43
"headerDataChanged(Qt::Orientation,int,int)" -outArg- 43
"layoutChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)" -outArg- 43
"layoutChanged(QList<QPersistentModelIndex>)" -outArg- 43
"layoutChanged()" -outArg- 43
"layoutAboutToBeChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)" -outArg- 43
"layoutAboutToBeChanged(QList<QPersistentModelIndex>)" -outArg- 43
"layoutAboutToBeChanged()" -outArg- 43
"rowsAboutToBeInserted(QModelIndex,int,int)" -outArg- 43
"rowsInserted(QModelIndex,int,int)" -outArg- 43
"rowsAboutToBeRemoved(QModelIndex,int,int)" -outArg- 43
"rowsRemoved(QModelIndex,int,int)" -outArg- 43
"columnsAboutToBeInserted(QModelIndex,int,int)" -outArg- 43
"columnsInserted(QModelIndex,int,int)" -outArg- 43
"columnsAboutToBeRemoved(QModelIndex,int,int)" -outArg- 43
"columnsRemoved(QModelIndex,int,int)" -outArg- 43
"modelAboutToBeReset()" -outArg- 43
"modelReset()" -outArg- 43
"rowsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)" -outArg- 43
"rowsMoved(QModelIndex,int,int,QModelIndex,int)" -outArg- 43
"columnsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)" -outArg- 43
"columnsMoved(QModelIndex,int,int,QModelIndex,int)" -outArg- 43
"submit()" -outArg- 1
"revert()" -outArg- 43
"resetInternalData()" -outArg- 43
"countChanged()" -outArg- 43
"clear()" -outArg- 43
"remove(QQmlV4Function*)" -outArg- 43
"append(QQmlV4Function*)" -outArg- 43
"insert(QQmlV4Function*)" -outArg- 43
"get(int)" -outArg- 1052
"set(int,QQmlV4Handle)" -outArg- 43
"setProperty(int,QString,QVariant)" -outArg- 43
"move(int,int,int)" -outArg- 43
"sync()" -outArg- 43
returnType返回的是int值,其对应的表为:
Constant | Value | Description |
---|---|---|
QMetaType::Void | 43 | void |
QMetaType::Bool | 1 | bool |
QMetaType::Int | 2 | int |
QMetaType::UInt | 3 | unsigned int |
QMetaType::Double | 6 | double |
QMetaType::QChar | 7 | QChar |
QMetaType::QString | 10 | QString |
QMetaType::QByteArray | 12 | QByteArray |
QMetaType::VoidStar | 31 | void * |
QMetaType::Long | 32 | long |
QMetaType::LongLong | 4 | LongLong |
QMetaType::Short | 33 | short |
QMetaType::Char | 34 | char |
QMetaType::ULong | 35 | unsigned long |
QMetaType::ULongLong | 5 | ULongLong |
QMetaType::UShort | 36 | unsigned short |
QMetaType::SChar | 40 | signed char |
QMetaType::UChar | 37 | unsigned char |
QMetaType::Float | 38 | float |
QMetaType::QObjectStar | 39 | QObject * |
QMetaType::QVariant | 41 | QVariant |
QMetaType::QCursor | 74 | QCursor |
QMetaType::QDate | 14 | QDate |
QMetaType::QSize | 21 | QSize |
QMetaType::QTime | 15 | QTime |
QMetaType::QVariantList | 9 | QVariantList |
QMetaType::QPolygon | 71 | QPolygon |
QMetaType::QPolygonF | 86 | QPolygonF |
QMetaType::QColor | 67 | QColor |
QMetaType::QSizeF | 22 | QSizeF |
QMetaType::QRectF | 20 | QRectF |
QMetaType::QLine | 23 | QLine |
QMetaType::QTextLength | 77 | QTextLength |
QMetaType::QStringList | 11 | QStringList |
QMetaType::QVariantMap | 8 | QVariantMap |
QMetaType::QVariantHash | 28 | QVariantHash |
QMetaType::QIcon | 69 | QIcon |
QMetaType::QPen | 76 | QPen |
QMetaType::QLineF | 24 | QLineF |
QMetaType::QTextFormat | 78 | QTextFormat |
QMetaType::QRect | 19 | QRect |
QMetaType::QPoint | 25 | QPoint |
QMetaType::QUrl | 17 | QUrl |
QMetaType::QRegExp | 27 | QRegExp |
QMetaType::QRegularExpression | 44 | QRegularExpression |
QMetaType::QDateTime | 16 | QDateTime |
QMetaType::QPointF | 26 | QPointF |
QMetaType::QPalette | 68 | QPalette |
QMetaType::QFont | 64 | QFont |
QMetaType::QBrush | 66 | QBrush |
QMetaType::QRegion | 72 | QRegion |
QMetaType::QBitArray | 13 | QBitArray |
QMetaType::QImage | 70 | QImage |
QMetaType::QKeySequence | 75 | QKeySequence |
QMetaType::QSizePolicy | 121 | QSizePolicy |
QMetaType::QPixmap | 65 | QPixmap |
QMetaType::QLocale | 18 | QLocale |
QMetaType::QBitmap | 73 | QBitmap |
QMetaType::QMatrix | 79 | QMatrix |
QMetaType::QTransform | 80 | QTransform |
QMetaType::QMatrix4x4 | 81 | QMatrix4x4 |
QMetaType::QVector2D | 82 | QVector2D |
QMetaType::QVector3D | 83 | QVector3D |
QMetaType::QVector4D | 84 | QVector4D |
QMetaType::QQuaternion | 85 | QQuaternion |
QMetaType::QEasingCurve | 29 | QEasingCurve |
QMetaType::QJsonValue | 45 | QJsonValue |
QMetaType::QJsonObject | 46 | QJsonObject |
QMetaType::QJsonArray | 47 | QJsonArray |
QMetaType::QJsonDocument | 48 | QJsonDocument |
QMetaType::QModelIndex | 42 | QModelIndex |
QMetaType::QPersistentModelIndex | 50 | QPersistentModelIndex (since 5.5) |
QMetaType::QUuid | 30 | QUuid |
QMetaType::QByteArrayList | 49 | QByteArrayList |
QMetaType::User | 1024 | Base value for user types |
QMetaType::UnknownType | 0 | This is an invalid type id. It is returned from QMetaType for types that are not registered |
竟然查不到值,只能去看源代码,最终查得是QQmlV4Function。
从上面可以知道是我的返回值参数写错了,对于其他方法我测试了是可以调用的。问题就是QQmlV4Function怎么用?他的头文件在哪?谷歌了好久无果放弃了。。。
一个折中的办法就是自己在ListMode里写一个js方法,在js里面调用get方法,c++再去调用自己那方法就可以实现返回值:
qml:
function getItem(index)
{
return dataModel.get(index);
}
c++:
bool result=QMetaObject::invokeMethod(model,'getItem', Qt::DirectConnection,Q_RETURN_ARG(QVariant,retValue),Q_ARG(QVariant,QVariant::fromValue(0)));
若是这样的话,ListModel的append方法也是要自己写,那不是很麻烦。。所以产生了能否使用一个类来代替ListModel。
摸索2
答案是有的,我采用的思路就是将一个C++类暴露给qml,使得在qml可以调用c++的方法。
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
//关键代码
QQmlContext *content=engine.rootContext();
DesignBLL *designBLL=new DesignBLL();
content->setContextProperty("DesignBLL",designBLL);
//
engine.load(QUrl(QStringLiteral("qrc:/view/qmls/main.qml")));
QObject *topLevel=engine.rootObjects().value(0);
QQuickWindow *window=qobject_cast<QQuickWindow*>(topLevel);
window->showFullScreen();
return app.exec();
}
关键代码部分意思就是将DesignBLL 类实例化后直接暴露给QML,在QML里可以直接使用DesignBLL .方法()调用C++的方法。
DesignBLL.h代码为:
#ifndef DESIGNBLL_H
#define DESIGNBLL_H
#include <QObject>
#include <QQmlContext>
#include "DesignModel.h"
//类必须继承与QObject
class DesignBLL :public QObject{
Q_OBJECT
public :
DesignBLL();
Q_INVOKABLE QList<QObject*> getModel();//必须加上Q_INVOKABLE才可以在QML被调用
private:
QList<QObject*> data;//作用等同与ListModel
};
#endif // DESIGNBLL_H
DesignBLL.cpp:
#include "DesignBLL.h"
//用作测试,在实例化时先添加一些测试数据
DesignBLL::DesignBLL(){
for(int ii=0;ii<35;ii++){
data.append(new DesignModel("#FFFFFF"));
}
}
QList<QObject*> DesignBLL::getModel(){
return data;
}
还要再定义一个DesignModel类,作用相当于ListElement
DesignModel.h
#ifndef DESIGNMODEL_H
#define DESIGNMODEL_H
#include<QObject>
#include<QVariant>
class DesignModel:public QObject{
Q_OBJECT
Q_PROPERTY(QString backColor READ backColor WRITE setBackColor NOTIFY backColorChanged)
public:
DesignModel(const QString &backColor,QObject *parent = 0);
QString backColor() const;
void setBackColor(const QString &backColor);
signals:
void backColorChanged();
private:
QString m_backColor;
};
#endif // DESIGNMODEL_H
主要就是定义1个元素backColor,之所以要加上Q_PROPERTY是因为使其可以在qml中被访问。
DesignModel.cpp:
#include "DesignModel.h"
DesignModel::DesignModel(const QString &backColor,QObject *parent):QObject(parent),m_backColor(backColor)
{
}
QString DesignModel::backColor() const{
return m_backColor;
}
void DesignModel::setBackColor(const QString &backColor){
m_backColor=backColor;
emit backColorChanged();
}
这样一来在QML里调用getModel方法就可以了。
main.qml:
GridView{
id:designBox
...
objectName: "designBox"
Component.onCompleted: {
designBox.model=DesignBLL.getModel();
}
delegate:Rectangle{
height: 50
width: 50
color:model.modelData.backColor
}
}
运行结果正确,其他方法可以自己添加。这样一来
在QML里操作C++类数据:
designBox.model[index].backColor=”#FOFOFO”;
在C++里对数据进行更改界面也会同时更新
例如修改第二个的背景色
DesignModel *tmp=dynamic_cast<DesignModel*>(data.at(2));
tmp->setBackColor("#FF00FF");
或者
QObject *obj=data[2];
obj->setProperty("backColor",QVariant::fromValue(QString("#FF00FF")));
最终选用了第二种比较完美的方案。