0
votes

I have:

  1. a c++ model class (Bank) for storing simple data objects (Coefficient):
typedef quint32 Coefficient;

class Bank : public QObject
{
    Q_OBJECT
    Q_PROPERTY(QList<Coefficient> coefficients READ coefficients WRITE setCoefficients NOTIFY coefficientsChanged)

public:
    explicit Bank(QList<Coefficient> coefficients = QList<Coefficient>(), QObject *parent = nullptr);

    QList<Coefficient> coefficients() const;
    void setCoefficients(QList<Coefficient> coefficients);

    // ...

signals:
    void coefficientsChanged(QList<Coefficient> coefficients);

private:
    QList<Coefficient> _coefficients;
};
  1. an AbstractListModel class (BankClass) for working with data throug views:
class Bank;

class BankModel : public QAbstractListModel
{
    Q_OBJECT
    Q_PROPERTY(Bank* bank READ bank WRITE setBank)

public:
    enum {
        CoefficientRole = Qt::UserRole
    };

    explicit BankModel(QObject *parent = nullptr);

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

    QVariant data(const QModelIndex &index, int role = CoefficientRole) const override;
    bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;

    Qt::ItemFlags flags(const QModelIndex& index) const override;
    QHash<int, QByteArray> roleNames() const override;

    Bank* bank() const;
    void setBank(Bank* bank);

private:
    Bank* _bank;
};
  1. a QObjectList-based model to store several AbstractListModel's (BankModel's)
class CoefficientListModel;

class ConfigModel : public QObject
{
    Q_OBJECT
    Q_PROPERTY(QList<QObject*> banks READ banks WRITE setBanks NOTIFY banksChanged)

public:
    explicit ConfigModel(QList<QObject*> banks = QList<QObject*>(), QObject *parent = nullptr);

    QList<QObject*> banks() const;
    void setBanks(QList<QObject*> banks);

signals:
    void banksChanged(QList<QObject*> banks);

private:
    QList<QObject*> _banks;
};
  1. and a qml view to set it all up (main.qml):
Window {
    // ...
    ListView {
        // ...
        model: cfg // ConfigModel
        delegate: ConfigDelegate {
            // ...
            ListView {
                model: model.bank // BankModel
                delegate: BankDelegate {
                    some int property: coefficient // my role in bank model
                }
            }
        }
    }
}

where cfg is a registered in main.cpp object:

int main(int argc, char *argv[])
{
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);

    QGuiApplication app(argc, argv);

    qmlRegisterType<CoefficientListModel>("Config", 1, 0, "CoefficientListModel");
    qmlRegisterType<ConfigModel>("Config", 1, 0, "ConfigModel");

    ConfigModel cfg;
    cfg.setBanks(QList<QObject*>()
                 << new CoefficientListModel(QList<Coefficient>({ Coefficient { 11 }, Coefficient { 21 }, Coefficient { 31 }, Coefficient { 41 } }))
                 // ...
                 << new CoefficientListModel(QList<Coefficient>({ Coefficient { 15 }, Coefficient { 25 }, Coefficient { 35 }, Coefficient { 45 } }))
                 );

    QQmlApplicationEngine engine;
    engine.rootContext()->setContextProperty(QStringLiteral("cfg"), &cfg);
    engine.rootContext()->setContextProperty(QStringLiteral("client"), &client);
    engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
    if (engine.rootObjects().isEmpty())
        return -1;

    return app.exec();
}

So, how to set c++ models in this qml depth from upper level? Or what's wrong here? Is it right application architecture?

It should looks like in the picture below. enter image description here

Edit:

Now I've got a class as a tree item:

class CoefficientItem
{
public:
    CoefficientItem(quint32 value = -1, int row = 0, CoefficientItem* parent = nullptr);
    ~CoefficientItem();

    CoefficientItem *childAt(int i);
    int childrenCount();
    CoefficientItem *parent();
    quint32 value() const;
    int row() const;

    void setValue(const quint32& value);
    void setParent(CoefficientItem* parent_item);

    void appendChild(CoefficientItem* child);

private:
    quint32 _value;
    CoefficientItem* _parent_item;
    QHash<int, CoefficientItem*> _child_items;
    int _row_number;
};

and tree model:

class ConfigModel : public QAbstractItemModel
{
    Q_OBJECT

    enum { CoefficientRole = Qt::UserRole };

public:
    explicit ConfigModel(const QVector<QVector<quint32>>& config, CoefficientItem* root = nullptr, QObject *parent = nullptr);
    ~ConfigModel();

    QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override;
    QModelIndex parent(const QModelIndex &index) const override;

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

    QVariant data(const QModelIndex &index, int role = CoefficientRole) const override;
    bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;

    Qt::ItemFlags flags(const QModelIndex& index) const override;

    void setModelUp(const QVector<QVector<quint32> >& config);

private:
    CoefficientItem* _root_item;
};

And the problem now is in access to internal level of tree model. In main.qml i have a list view:

ListView {
    anchors.fill: parent
    model: DelegateModel {
        model: cfg
        delegate: BankView {}
    }
}

BankView.qml is the delegate which consists of BankViewHeader.qml (as a checkbox) and BankViewDescription.qml (as a Component with List of TextFields)

BankView.qml:

Rectangle {
    id: bank_view
    width: parent.width
    height: bank_view_column.height
    color: "#946782"

    Column {
        id: bank_view_column
        spacing: 0
        clip: true

        BankViewHeader {
            id: checker
            text: qsTr("Bank " + model.index)
            checked: true
            implicitWidth: bank_view.width
        }

        BankViewDescription {
            id: description
            isOpened: checker.checked
            width: bank_view.width
        }
    }

    Component.onCompleted: checker.checked = false
}

BankViewDescription.qml:

RowLayout {
    // ...
    ListView {
        model: DelegateModel {
            model: model // <-- problem here ???
            delegate: Row {
                TextField {
                    //... use my role here - coefficient (uint32)
                }
            }
            // ...
        }
        // ...
    }
}

So what's wrong? no any errors in log, and top level of tree works fine (i think), view shows 4 banks as were initialized in main.cpp.

enter image description here

1
I think you should use QList<QObject*> (where QObject is actually a Bank) to have a list of all your Bank's, that way you don't have to specify a indexed property on your root Context for every bank you ever want to makeAmfasis
and how i will get access to every coefficient in qml?Василий Пупкин
As QML doesn't (yet maybe) understand QVector I would make a QList property on the Bank class. And you can actually use typedef instead of struct with one member, unless you are planning to have more members, then you could consider using Q_GADGETAmfasis
Look, for example. I have: 1. (Coefficient) a complex object model (not only int value as a field); 2. (Bank) a c++ model for storing several objects (with Q_PROPERTY for container of objects); 3. (BankModel) an AbstractListModel for edit data and work throug qml view; 4. (ConfigModel) a QObjectList-based model for storing several (BankModel's) AbstracktListModel's. 5. Register config model object as ConfigModel in main.cpp and set it at model field in ListView in main.qml. Is it right application architecture yet?Василий Пупкин

1 Answers

0
votes

So, consider to EDIT part and with Amfasis help the solution is: for that structure (2d data storage like on the picture at question) we should make a tree model, and after that we should set our qml correct structure.

The qml structure in my case:

// model view in main.qml
ListView {
    // ...
    model: DelegateModel {
        id: bank_list_model
        model: cfg // my ConfigModel
        delegate: BankView {
            bankIndex: bank_list_model.modelIndex(index)
        }
    }
}

BankView.qml:

Rectangle {  // your any item
    property alias bankIndex: description.bankIndex

    // ...

    BankViewDescription {
        id: description
        // ...
    } 
}

BankViewDescription.qml:

RowLayout {  // your any item
    property alias bankIndex: regs_list_model.rootIndex

    // ...

    ListView {
        model: DelegateModel {
            id: regs_list_model
            model: cfg
            delegate: Row {
                TextField {
                    text: coefficient // use your role
                }
            }
        }
    }
}

So here we link DelegateModel rootIndex property throug our items with aliases.

PS: I forgot to override roleNames method on ConfigModel at edit part.

PPS: If you know a more simple way to describe a model-view connection or app structure, you are welcome.