1

Is it possible to have a model within a model accessible via qml and each model are different classes

eg modelclass 1 has a vector of a subclass of model
i.e parent model can have many sub model

I tried to return a QAbstractItemModel as a role of QAbstractItemModel parent but it seems thats not possible any ideas of how to do these stuff??

[Edit 1]

The code :

message.h

#include <QAbstractListModel>
#include <QStringList>

//![0]
class Animal
{
 public:
     Animal(const QString &type, const QString &size);
 //![0]

     QString type() const;
     QString size() const;

 private:
     QString m_type;
     QString m_size;
  //![1]
};

class AnimalModel : public QAbstractListModel
{
  Q_OBJECT
  public:
  enum AnimalRoles {
    TypeRole = Qt::UserRole + 1,
    SizeRole
    };

 AnimalModel(QObject *parent = 0);
 //![1]

 void addAnimal(const Animal &animal);

 int rowCount(const QModelIndex & parent = QModelIndex()) const;

 QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) 
 const;

 protected:
    QHash<int, QByteArray> roleNames() const;
 private:
    QList<Animal> m_animals;
 //![2]
 };
//![2]

model.h

#include <QAbstractListModel>
#include <QStringList>
#include <QDebug>
#include <message.h>

//![0]
//!
//!
class lister{
public:
    int id;
    AnimalModel *list=nullptr;
    void addlist(){
        list->addAnimal(Animal("Wolf", "Medium"));
        list->addAnimal(Animal("Polar bear", "Large"));
        list->addAnimal(Animal("Quoll", "Small"));
    }

};

class TestModel : public QAbstractListModel
{


public:

    enum EventRoles {
        ModelRole = Qt::UserRole + 1,
        IdRole = Qt::UserRole + 2
    };

    TestModel()
    {
        m_roles[ ModelRole] = "mm";
        m_roles[ IdRole] = "id";
        //  setRoleNames(m_roles);
    }

    int rowCount(const QModelIndex & = QModelIndex()) const
    {
        return mylist.count();
    }

    QVariant data(const QModelIndex &index, int role) const
    {
        if(role == IdRole)
        {
            return mylist.at(index.row()).id;
        }
        else if(role == ModelRole)
        {
            return QVariant::fromValue(mylist.at(index.row()).list);
        }
        return 0;
    }

    void addData(){
        static int a=0;
        qDebug()<<a<<"value";
        beginInsertRows(QModelIndex(), rowCount(), rowCount());
        lister temp;
        temp.id=a;
        temp.addlist();
        a++;
        mylist<<temp;
        temp.id=a;
        temp.addlist();
        a++;
        mylist<<temp;
        endInsertRows();
    }

    QHash<int, QByteArray> roleNames() const {
        QHash<int, QByteArray> roles;
        roles[ModelRole] = "mm";
        roles[IdRole] = "id";
        return roles;
    }
    QList<lister> mylist;
    QHash<int, QByteArray> m_roles;
};

message.cpp

#include "message.h"

Animal::Animal(const QString &type, const QString &size)
    : m_type(type), m_size(size)
{
}

QString Animal::type() const
{
    return m_type;
}

QString Animal::size() const
{
    return m_size;
}

AnimalModel::AnimalModel(QObject *parent)
    : QAbstractListModel(parent)
{
}

void AnimalModel::addAnimal(const Animal &animal)
{
    beginInsertRows(QModelIndex(), rowCount(), rowCount());
    m_animals << animal;
    endInsertRows();
}

int AnimalModel::rowCount(const QModelIndex & parent) const {
    Q_UNUSED(parent);
    return m_animals.count();
}

QVariant AnimalModel::data(const QModelIndex & index, int role) const {
    if (index.row() < 0 || index.row() >= m_animals.count())
        return QVariant();

    const Animal &animal = m_animals[index.row()];
    if (role == TypeRole)
        return animal.type();
    else if (role == SizeRole)
        return animal.size();
    return QVariant();
}

//![0]
QHash<int, QByteArray> AnimalModel::roleNames() const {
    QHash<int, QByteArray> roles;
    roles[TypeRole] = "type";
    roles[SizeRole] = "size";
    return roles;
}
//![0]

main.cpp

#include "model.h"

#include <QGuiApplication>
#include <qqmlengine.h>
#include <qqmlcontext.h>
#include <qqml.h>
#include <QtQuick/qquickitem.h>
#include <QtQuick/qquickview.h>

//![0]
int main(int argc, char ** argv)
{
    QGuiApplication app(argc, argv);

    TestModel model;
//    model.addAnimal(Animal("Wolf", "Medium"));
//    model.addAnimal(Animal("Polar bear", "Large"));
//    model.addAnimal(Animal("Quoll", "Small"));
    model.addData();
    model.addData();
        model.addData();


        qRegisterMetaType<AnimalModel*>("AnimalModel*" );

    QQuickView view;
    view.setResizeMode(QQuickView::SizeRootObjectToView);
    QQmlContext *ctxt = view.rootContext();
    ctxt->setContextProperty("myModel", &model);
//![0]

    view.setSource(QUrl("qrc:view.qml"));
    view.show();

    return app.exec();
}

view.qml

import QtQuick 2.0
import QtQml 2.0

ListView {
    anchors.fill: parent
    model: myModel //this is your main model

    delegate:
        Rectangle {
        height: 100
        width: 100
        color: "red"
        Text {
            id: cc
            text: id
        }

        ListView {
            anchors.fill: parent
            model: mm //the internal QVariantList
            delegate: Rectangle {
                anchors.right:  parent.right
                width: 50
                height: 50
                color: "green"
                border.color: "black"
                Text {
                    text: type //role to get data from internal model
                }
            }
        }
    }
}
Bugs
  • 4,491
  • 9
  • 32
  • 41
Dravigon
  • 73
  • 1
  • 8
  • See here: https://stackoverflow.com/questions/35160909/how-to-create-a-generic-object-model-for-use-in-qml/35161903#35161903 As for the QML ListModel - it doesn't support nesting of any kind. – dtech Jun 25 '17 at 16:54
  • Did you return a `QAbstractItemModel` by pointer or by value ? It should work by pointer. Can you show us some code ? – GrecKo Jun 25 '17 at 21:09
  • @GrecKo it can't be returned by value. – dtech Jun 25 '17 at 21:12
  • Yes that's what I was thinking while writing it, but I asked anyway to ensure it was correctly done. – GrecKo Jun 25 '17 at 21:14
  • @dtech: If you don't use the `ListModel { ListElement { ... } }`-notation to initialize your `ListModel`, you can have more complex constructs that might even contain sub-`ListModel`s. – derM - not here for BOT dreams Jun 26 '17 at 07:10
  • IIRC even manually populating the model using another model as a list element property was not an option, ending up in a message that nesting is not supported. It is possible to have `QtObject` derived with models as their properties but that's needlessly obstructive. – dtech Jun 26 '17 at 08:52
  • @dtech: I am talking about: `ListModel { Component.onCompleted: { append({submodel: [ { subrole1 : 'hello', subrole2: 'world'}, { subrole1: 'hallo', subrole2: 'welt' } ] }); console.log(get(0), get(0).submodel, get(0).submodel.get(0), get(0).submodel.get(1)) } }` As you can see, the submodels are implicitly transformed into regular `ListModel`s. You can also append arbitrary `QtObjects`, which is fun to have functions in the model, but tricky to handle the bindings. – derM - not here for BOT dreams Jun 26 '17 at 09:01
  • In that case you don't have actual models, just plain dumb arrays, stiff and inefficient. Also, last time I checked, you actually couldn't have arrays in qml model items: https://stackoverflow.com/questions/37069565/qml-listmodel-append-broken-for-object-containing-an-array/37070138?noredirect=1#comment72946768_37070138 – dtech Jun 26 '17 at 10:37
  • That is not what the `...toString()`-method tells. The array `submodel` will be implicitly converted into a fully fledged `ListModel`. OFC, there is no dataChanged-signal for the parent model, when the submodel's `dataChanged`, but this should be easily fixed by handling later appropriatly. ;-) – derM - not here for BOT dreams Jun 26 '17 at 11:09
  • Well, if you look at the linked question, I was getting a model "converted" however its items were undefined. Maybe they fixed it since then but it is still a rather backward and clumsy way to do things, in the face of the generic item model I linked to, which does it all - nesting, declarative declaration, bindings, notifications, amorphism, you name it. – dtech Jun 26 '17 at 11:40
  • Please don't append solved to the title. By marking an answer as accepted those around now know this question has been solved. – Bugs Jun 28 '17 at 18:08
  • possibly duplication of https://stackoverflow.com/questions/31996279/nested-list-in-qml-data-models-in-models – ניר Apr 26 '22 at 08:16
  • Does this answer your question? [Nested list in qml: data models in models](https://stackoverflow.com/questions/31996279/nested-list-in-qml-data-models-in-models) – ניר Apr 26 '22 at 08:17

2 Answers2

2

Yes its fine.

You need to return a pointer to your sub model object wrapped in a variant,

QVariant::fromValue(&subModel) 

You probably also need to register your model pointer with Metatype system using

qRegisterMetaType<MySubModelClass*>("MySubModelClass*" );
Mark Ch
  • 2,840
  • 1
  • 19
  • 31
  • so where should i put this? qRegisterMetaType("MySubModelClass*" ); in main ?? – Dravigon Jun 26 '17 at 15:48
  • yeah i done as you said see in edit 1 but the program is now not executing i think its not accepting the model... ? – Dravigon Jun 26 '17 at 16:10
  • Let's take it one bug a time then... Firstly, all role names must start with a lower case letter – Mark Ch Jun 26 '17 at 18:59
  • Secondly you must call endInsertRows() after beginInsertRows() – Mark Ch Jun 26 '17 at 19:02
  • Thirdly, when you call list->addAnimal() you are dereferencing an uninitialised pointer – Mark Ch Jun 26 '17 at 19:46
  • Fourth, There is no role in the model called stringList. Im giving up, there's far too much wrong with the code that's been posted. Spend some time debugging it. – Mark Ch Jun 26 '17 at 19:52
  • i would like to chat to you do you have a gitter account or any means by which i can contact you?? please help me with this – Dravigon Jun 27 '17 at 16:07
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/147741/discussion-between-dravigon-and-mark-ch). – Dravigon Jun 27 '17 at 16:16
  • done the corrections you said, during debugging I get segmentation fault in the subclass's beginInsertRows() – Dravigon Jun 27 '17 at 16:18
2

Here is an implementation with DelegateModel introducet in Qt 5.13

import QtQuick 2.12
import QtQuick.Window 2.12
import com.example 1.0
import QtQml.Models 2.12
import QtQuick.Layouts 1.3

Window {
    visible: true
    width: 800
    height: 480
    title: qsTr("Hello World")

    DelegateModel
    {
        id: delegateModel
        model : MyModel{}

        delegate:ColumnLayout
        {
            width: parent.width
            Row
            {
                Text
                {
                    text: qsTr("Word:")
                    color: "red"
                }
                Text
                {
                    text: word
                }
            }
            Text
            {
                color: "red"
                text: qsTr("In Other Languages:")
            }
            ListView {
                model: translations
                height: 50
                delegate:
                    Rectangle
                {
                    height: 20
                    width: 100
                    Text {
                        anchors.right: parent.right
                        text: translations[index]
                    }
                }
            }
        }
    }

    ListView
    {
        model : delegateModel
        anchors.centerIn: parent
        width: parent.width/2
        height: parent.height
    }

}

MyModel.h

#include <QAbstractListModel>

class MyModel : public QAbstractListModel
{
    Q_OBJECT
public:
    enum WordRoles
    {
        WordRole = Qt::UserRole+1,
        TranslationsModelRole
    };
    explicit MyModel(QObject* parent=nullptr);
    int rowCount(const QModelIndex &parent) const override;
    QVariant data(const QModelIndex &index, int role) const override;
    QHash<int, QByteArray> roleNames() const override;
private:
    struct Word
    {
        QString word;
        QStringList translations;
    };
    QList<Word> m_words;
};

MyModel.cpp

#include<MyModel.h>
#include <QList>

MyModel::MyModel(QObject *parent)
    :QAbstractListModel(parent)
{
    m_words={{"apple",{"elma","Apfel"}},
            {"ball",{"top","ball"}},
            {"friend",{"arkadas","freund"}}
        };
}

int MyModel::rowCount(const QModelIndex &parent) const
{
    Q_UNUSED(parent)
    return m_words.size();
}

QVariant MyModel::data(const QModelIndex &index, int role) const
{
    if(index.row()>=m_words.size() || index.row()<0)
        return QVariant();
    if ( role == WordRole)
    {
        return m_words[index.row()].word;
    }
    else if ( role == TranslationsModelRole)
    {
        return m_words[index.row()].translations;
    }
    return QVariant();
}

QHash<int, QByteArray> MyModel::roleNames() const
{
    QHash<int,QByteArray> roles;
    roles[WordRoles::WordRole] = "word";
    roles[WordRoles::TranslationsModelRole]="translations";
    return roles;
}

main.cpp

#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <MyModel.h>
int main(int argc, char *argv[])
{
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);

    QGuiApplication app(argc, argv);
    qmlRegisterType<MyModel>("com.example",1,0,"MyModel");
    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);
    engine.load(url);

    return app.exec();
}

Result is ListView inside ListViev:

enter image description here

Muhammet Ali Asan
  • 1,486
  • 22
  • 39