I want to summarize What to do. I have an DataObject
class which have members:
QString first;QString last;QList<SubObject*> m_sublist;
I am using QAbstractListModel
to this. I can refer first and last to listview but I can't refer to like m_sublist[0].lesson
. It gives me error like:
Cannot read property 'lesson' of undefined.
My code: dataobject.h
class SubObject :public QObject
{
Q_OBJECT
public:
SubObject(const QString &lesson,QObject *parent = 0);
const QString lesson;
private:
// bool operator==(const SubObject* &other) const {
// return other->lesson == lesson;
// }
};
class DataObject :public QObject{
Q_OBJECT
public:
DataObject(const QString &firstName,
const QString &lastName,
const QList<SubObject*> &sublist);
QString first;
QString last;
QList<SubObject*> m_sublist;
};
simplelistmodel.h
class SimpleListModel : public QAbstractListModel {
Q_OBJECT
public:
SimpleListModel(QObject *parent=0);
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
int rowCount(const QModelIndex &parent = QModelIndex()) const;
QHash<int,QByteArray> roleNames() const { return m_roleNames; }
private:
// Q_DISABLE_COPY(SimpleListModel);
QList<DataObject*> m_items;
static const int FirstNameRole;
static const int LastNameRole;
static const int SubListRole;
QHash<int, QByteArray> m_roleNames;
};
simplelistmodel.cpp
const int SimpleListModel::FirstNameRole = Qt::UserRole + 1;
const int SimpleListModel::LastNameRole = Qt::UserRole + 2;
const int SimpleListModel::SubListRole = Qt::UserRole + 3;
SimpleListModel::SimpleListModel(QObject *parent) :
QAbstractListModel(parent) {
// Create dummy data for the list
QList<SubObject*> mysublist;
mysublist.append(new SubObject("MAT"));
mysublist.append(new SubObject("FEN"));
DataObject *first = new DataObject(QString("Arthur"), QString("Dent"),mysublist);
DataObject *second = new DataObject(QString("Ford"), QString("Prefect"),mysublist);
DataObject *third = new DataObject(QString("Zaphod"), QString("Beeblebrox"),mysublist);
m_items.append(first);
m_items.append(second);
m_items.append(third);
// m_roleNames = SimpleListModel::roleNames();
m_roleNames.insert(FirstNameRole, QByteArray("firstName"));
m_roleNames.insert(LastNameRole, QByteArray("lastName"));
m_roleNames.insert(SubListRole, QByteArray("subList"));
}
int SimpleListModel::rowCount(const QModelIndex &) const {
return m_items.size();
}
QVariant SimpleListModel::data(const QModelIndex &index,
int role) const {
if (!index.isValid())
return QVariant(); // Return Null variant if index is invalid
if (index.row() > (m_items.size()-1) )
return QVariant();
DataObject *dobj = m_items.at(index.row());
switch (role) {
case Qt::DisplayRole: // The default display role now displays the first name as well
case FirstNameRole:
return QVariant::fromValue(dobj->first);
case LastNameRole:
return QVariant::fromValue(dobj->last);
case SubListRole:
return QVariant::fromValue(dobj->m_sublist);
default:
return QVariant();
}
}
main.cpp
int main(int argc, char *argv[]) {
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
SimpleListModel model;
QQmlContext *classContext = engine.rootContext();
classContext->setContextProperty("absmodel",&model);
engine.load(QUrl(QStringLiteral("qrc:/myuiscript.qml")));
return app.exec(); }
myuiscript.qml
import QtQuick 2.0
import QtQuick.Window 2.0
Window {
id: bgRect
width: 200
height: 200
color: "black"
visible: true
ListView {
id: myListView
anchors.fill: parent
delegate: myDelegate
model: absmodel
}
Component {
id: myDelegate
Item {
width: 200
height: 40
Rectangle {
anchors.fill: parent
anchors.margins: 2
radius: 5
color: "lightsteelblue"
Row {
anchors.verticalCenter: parent.verticalCenter
Text {
text: firstName
color: "black"
font.bold: true
}
Text {
text: subList[0].lesson
color: "black"
}
}
}
}
}
}
I can't find any solution. Virtual data model returns single type of objects. FirsName
is a string. I cant refer listview delegate like firstName(rolename)
. Also LastName
is refered like lastName(rolename)
. But I can't refer subList(roleNames)
like sublist[0].lesson
.
My target is very simple. I want to refer single type (int,QString ....)
to text in delegate by using rolename. I can't refer collection type(QList<SubObject*>)
to text in delegate using rolename(subList[0].lesson)
. How to achive them?
Let's fix it step by step. This line text: subList[0].lesson
in QML produce the error message
TypeError: Cannot read property 'lesson' of undefined
That means subList[0]
is an undefined object and QML engine cannot read any property, including lesson
, from this object. In fact, subList
returned from model is a well-defined QList<SubObject*>
object, but not subList[0]
since QList<SubObject*>
is not a QML list. To correctly pass a list from C++ to QML, return a QVariantList
instead of QList
.
//class DataObject
DataObject(const QString &firstName,
const QString &lastName,
const QVariantList &sublist);
QVariantList m_sublist; //use QVariantList instead of QList<SubObject*>
//---
//SimpleListModel::SimpleListModel
QVariantList mysublist; //use QVariantList instead of QList<SubObject*>
mysublist.append(QVariant::fromValue(new SubObject("MAT", this))); //remember parent
mysublist.append(QVariant::fromValue(new SubObject("FEN", this)));
//...
Now, subList[0]
can be accessed in QML, but not subList[0].lesson
. To access properties in a C++ class, explicitly define the property with Q_PROPERTY
macro.
class SubObject :public QObject
{
Q_OBJECT
Q_PROPERTY(QString lesson READ getLesson NOTIFY lessonChanged)
public:
SubObject(const QString &lesson,QObject *parent = 0):
QObject(parent), m_lesson(lesson){;}
QString getLesson() const {return m_lesson;}
signals:
void lessonChanged();
private:
QString m_lesson;
};
And the QML code works now.