Important: Please read the Qt Code of Conduct -

Best way to implement a list of option

  • Hello,

    I would like to implement a list of option on a page. That page has a defined size and the amount of option is to big to fit the page.
    So I wanted to make a Scrollview so I can make a list of those options. Those options are different types of qml object or component.

    But I'm not sure it is the best way to do it and it seems a bit tricky to make it work properly.

    For you to know I'm stuck with QuickControls 1.4 can't move to QuickControls 2.x because my target is not compatible.

    Here is an example of what I want :


    Thank you for your help !

  • Moderators

    Keep your data in a model (subclass of QAbstractItemModel), use a ListView to display your data. In your delegate, use (for example) a Loader to load proper control per data type.

  • @sierdzio
    Thank you for your reply,

    Do you have an exemple of this kind of implementation ? Because I don't really see how to handle multiple types of component.
    (The option is usualy a text then some kind of component to get the associated value (button, combobox, switch, textInput...)

  • @DavidM29 I tried to make something.

    It works but I'm not sure this is the best way to do it according to your needs. I'm using QVariant to store property values as it is already compatible with Model-View system.


    #include <QAbstractListModel>
    struct GenericProperty{
        GenericProperty(const QString &name = QString(), const QVariant &value = QVariant(), const QVariant &extra = QVariant()){
            this->name = name;
            this->value = value;
            this->extra = extra;
        QString name;
        QVariant value;
        QVariant extra;
    typedef QList<GenericProperty> GenericPropertyList;
    class GenericPropertyListModel : public QAbstractListModel
        enum _roles{
            ROLE_NAME = Qt::UserRole + 1,
        explicit GenericPropertyListModel(QObject *parent = nullptr);
        // Basic functionality:
        int rowCount(const QModelIndex &parent = QModelIndex()) const override;
        QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
        virtual QHash<int,QByteArray> roleNames() const Q_DECL_OVERRIDE;
        // Editable:
        bool setData(const QModelIndex &index, const QVariant &value,
                     int role = Qt::EditRole) override;
        Qt::ItemFlags flags(const QModelIndex& index) const override;
        GenericPropertyList genericPropertyList() const;
        void setGenericPropertyList(const GenericPropertyList &list);
        GenericPropertyList m_list;


    #include "genericpropertylistmodel.h"
    GenericPropertyListModel::GenericPropertyListModel(QObject *parent)
        : QAbstractListModel(parent)
    int GenericPropertyListModel::rowCount(const QModelIndex &parent) const
        // For list models only the root node (an invalid parent) should return the list's size. For all
        // other (valid) parents, rowCount() should return 0 so that it does not become a tree model.
        if (parent.isValid())
            return 0;
        return m_list.count();
    QVariant GenericPropertyListModel::data(const QModelIndex &index, int role) const
        if (!index.isValid())
            return QVariant();
            case ROLE_NAME: return;
            case Qt::DisplayRole:
            case Qt::EditRole:
            case ROLE_VALUE: return;
            case ROLE_TYPE: return;
            case ROLE_EXTRA: return;
        // FIXME: Implement me!
        return QVariant();
    QHash<int, QByteArray> GenericPropertyListModel::roleNames() const
        QHash<int, QByteArray> roles;
        roles[ROLE_NAME] = "role_name";
        roles[ROLE_VALUE] = "role_value";
        roles[ROLE_TYPE] = "role_type";
        roles[ROLE_EXTRA] = "role_extra";
        return roles;
    bool GenericPropertyListModel::setData(const QModelIndex &index, const QVariant &value, int role)
        if(role == ROLE_TYPE || role == ROLE_NAME)
            return false;
        if (data(index, role) != value) {
            // FIXME: Implement me!
            GenericProperty gp = m_list.value(index.row());
            if(role == ROLE_VALUE)
                gp.value = value;
            else if(role == ROLE_EXTRA)
                gp.extra = value;
            m_list.replace(index.row(), gp);
            emit dataChanged(index, index, QVector<int>() << role);
            return true;
        return false;
    Qt::ItemFlags GenericPropertyListModel::flags(const QModelIndex &index) const
        if (!index.isValid())
            return Qt::NoItemFlags;
        return Qt::ItemIsEditable; // FIXME: Implement me!
    GenericPropertyList GenericPropertyListModel::genericPropertyList() const
        return m_list;
    void GenericPropertyListModel::setGenericPropertyList(const GenericPropertyList &list)
        m_list = list;


    #include "genericpropertylistmodel.h"
    #include <QGuiApplication>
    #include <QQmlApplicationEngine>
    #include <QQmlContext>
    int main(int argc, char *argv[])
        QGuiApplication app(argc, argv);
        GenericPropertyListModel model;
        GenericPropertyList list;
        list << GenericProperty("String", QString("A string value"));
        list << GenericProperty("Bool", true);
        list << GenericProperty("Int", static_cast<quint32>(1234));
        list << GenericProperty("Double", static_cast<double>(1234));
        list << GenericProperty("List", QVariantList() << "One" << "Two" << "Three");
        QQmlApplicationEngine engine;
        engine.rootContext()->setContextProperty("propertyListModel", &model);
        if (engine.rootObjects().isEmpty())
            return -1;
        return app.exec();

    Usage from QML:

    import QtQuick 2.9
    import QtQuick.Controls 2.3
    import QtQuick.Layouts 1.2
    ApplicationWindow {
        visible: true
        width: 640
        height: 480
        title: qsTr("Hello World")
            anchors.fill: parent
            model: propertyListModel
            delegate: RowLayout {
                spacing: 20
                height: 50
                width: parent.width
                    Layout.preferredWidth: 80
                    text: role_name + "(" + (typeof role_value) + ") " + role_type
                    property var _name : role_name
                    property var _value : role_value
                    property var _extra : role_extra
                    on_ValueChanged: role_value = _value
                    on_ExtraChanged: role_extra = _extra
                    Layout.minimumWidth: 120
                    Layout.fillHeight: true
                    sourceComponent: {
                        console.log(role_name + " - " + typeof role_value)
                        if(typeof role_value == "boolean"){
                            return boolComponent
                        } else if(typeof role_value == "string"){
                            return stringComponent
                        } else if(role_value instanceof Array){
                            return listComponent
                        } else if(typeof role_value == "number"){
                            if(role_type === "double")
                                return realComponent
                                return intComponent
                        } else {
                            return undefined
            id: boolComponent
                text: _name
                checked: _value
                onCheckedChanged: _value = checked
            id: realComponent
                realFrom: 0
                realTo: 10000
                realValue: _value
                onRealValueChanged: if(_value !== realValue) _value = realValue
            id: intComponent
                from: 0
                to: 10000
                value: _value
                onValueChanged: if(_value !== value) _value = value
            id: stringComponent
                text: _value
                onTextChanged: _value = text
            id: listComponent
                model: _value
                currentIndex: _extra
                onCurrentIndexChanged: _extra = currentIndex

    Note that I have added an "extra" property to be able to store the selected index with the list type. Note also I didn't handle the "button" type.

    Another solution could be to have a list of QObject derived classes with some properties accessible from QML and then access these objects from QML.

  • @Gojir4
    Thank you,
    I'm looking at your solution !
    I'll tell you if I do have any problem

Log in to reply