Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. QML and Qt Quick
  4. Best way to implement a list of option
Forum Updated to NodeBB v4.3 + New Features

Best way to implement a list of option

Scheduled Pinned Locked Moved Solved QML and Qt Quick
5 Posts 3 Posters 814 Views 2 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • D Offline
    D Offline
    DavidM29
    wrote on last edited by DavidM29
    #1

    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 :

    0_1552561597280_20190314_1158442.jpg

    Thank you for your help !

    1 Reply Last reply
    0
    • sierdzioS Offline
      sierdzioS Offline
      sierdzio
      Moderators
      wrote on last edited by
      #2

      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.

      (Z(:^

      D 1 Reply Last reply
      1
      • sierdzioS sierdzio

        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.

        D Offline
        D Offline
        DavidM29
        wrote on last edited by DavidM29
        #3

        @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...)

        Gojir4G 1 Reply Last reply
        0
        • D DavidM29

          @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...)

          Gojir4G Offline
          Gojir4G Offline
          Gojir4
          wrote on last edited by
          #4

          @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.

          Model:
          .h

          #ifndef GENERICPROPERTYLISTMODEL_H
          #define GENERICPROPERTYLISTMODEL_H
          #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
          {
              Q_OBJECT
          
          public:
          
              enum _roles{
                  ROLE_NAME = Qt::UserRole + 1,
                  ROLE_VALUE,
                  ROLE_TYPE,
                  ROLE_EXTRA,
              };
          
              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);
          
          private:
          
              GenericPropertyList m_list;
          };
          
          #endif // GENERICPROPERTYLISTMODEL_H
          
          

          .cpp

          #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();
          
              switch(role){
                  case ROLE_NAME: return m_list.at(index.row()).name;
          
                  case Qt::DisplayRole:
                  case Qt::EditRole:
                  case ROLE_VALUE: return m_list.at(index.row()).value;
          
                  case ROLE_TYPE: return m_list.at(index.row()).value.typeName();
                  case ROLE_EXTRA: return m_list.at(index.row()).extra;
              }
          
          
              // 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;
          }
          

          main.cpp:

          #include "genericpropertylistmodel.h"
          
          #include <QGuiApplication>
          #include <QQmlApplicationEngine>
          #include <QQmlContext>
          
          int main(int argc, char *argv[])
          {
              QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
          
              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");
              model.setGenericPropertyList(list);
          
              QQmlApplicationEngine engine;
              engine.rootContext()->setContextProperty("propertyListModel", &model);
              engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
              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")
          
              ListView{
                  anchors.fill: parent
                  model: propertyListModel
                  delegate: RowLayout {
                      spacing: 20
                      height: 50
                      width: parent.width
                      Text{
                          Layout.preferredWidth: 80
                          text: role_name + "(" + (typeof role_value) + ") " + role_type
                      }
          
                      Loader{
                          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
                                  else
                                      return intComponent
          
                              } else {
                                  return undefined
                              }
          
                          }
                      }
                  }
              }
          
              Component{
                  id: boolComponent
                  Switch{
                      text: _name
                      checked: _value
                      onCheckedChanged: _value = checked
                  }
              }
              Component{
                  id: realComponent
                  DoubleSpinBox{
                      realFrom: 0
                      realTo: 10000
                      realValue: _value
                      onRealValueChanged: if(_value !== realValue) _value = realValue
                  }
              }
              Component{
                  id: intComponent
                  SpinBox{
                      from: 0
                      to: 10000
                      value: _value
                      onValueChanged: if(_value !== value) _value = value
                  }
              }
          
              Component{
                  id: stringComponent
                  TextEdit{
                      text: _value
                      onTextChanged: _value = text
                  }
              }
          
              Component{
                  id: listComponent
                  ComboBox{
                      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.

          D 1 Reply Last reply
          4
          • Gojir4G Gojir4

            @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.

            Model:
            .h

            #ifndef GENERICPROPERTYLISTMODEL_H
            #define GENERICPROPERTYLISTMODEL_H
            #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
            {
                Q_OBJECT
            
            public:
            
                enum _roles{
                    ROLE_NAME = Qt::UserRole + 1,
                    ROLE_VALUE,
                    ROLE_TYPE,
                    ROLE_EXTRA,
                };
            
                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);
            
            private:
            
                GenericPropertyList m_list;
            };
            
            #endif // GENERICPROPERTYLISTMODEL_H
            
            

            .cpp

            #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();
            
                switch(role){
                    case ROLE_NAME: return m_list.at(index.row()).name;
            
                    case Qt::DisplayRole:
                    case Qt::EditRole:
                    case ROLE_VALUE: return m_list.at(index.row()).value;
            
                    case ROLE_TYPE: return m_list.at(index.row()).value.typeName();
                    case ROLE_EXTRA: return m_list.at(index.row()).extra;
                }
            
            
                // 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;
            }
            

            main.cpp:

            #include "genericpropertylistmodel.h"
            
            #include <QGuiApplication>
            #include <QQmlApplicationEngine>
            #include <QQmlContext>
            
            int main(int argc, char *argv[])
            {
                QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
            
                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");
                model.setGenericPropertyList(list);
            
                QQmlApplicationEngine engine;
                engine.rootContext()->setContextProperty("propertyListModel", &model);
                engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
                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")
            
                ListView{
                    anchors.fill: parent
                    model: propertyListModel
                    delegate: RowLayout {
                        spacing: 20
                        height: 50
                        width: parent.width
                        Text{
                            Layout.preferredWidth: 80
                            text: role_name + "(" + (typeof role_value) + ") " + role_type
                        }
            
                        Loader{
                            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
                                    else
                                        return intComponent
            
                                } else {
                                    return undefined
                                }
            
                            }
                        }
                    }
                }
            
                Component{
                    id: boolComponent
                    Switch{
                        text: _name
                        checked: _value
                        onCheckedChanged: _value = checked
                    }
                }
                Component{
                    id: realComponent
                    DoubleSpinBox{
                        realFrom: 0
                        realTo: 10000
                        realValue: _value
                        onRealValueChanged: if(_value !== realValue) _value = realValue
                    }
                }
                Component{
                    id: intComponent
                    SpinBox{
                        from: 0
                        to: 10000
                        value: _value
                        onValueChanged: if(_value !== value) _value = value
                    }
                }
            
                Component{
                    id: stringComponent
                    TextEdit{
                        text: _value
                        onTextChanged: _value = text
                    }
                }
            
                Component{
                    id: listComponent
                    ComboBox{
                        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.

            D Offline
            D Offline
            DavidM29
            wrote on last edited by
            #5

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

            1 Reply Last reply
            0

            • Login

            • Login or register to search.
            • First post
              Last post
            0
            • Categories
            • Recent
            • Tags
            • Popular
            • Users
            • Groups
            • Search
            • Get Qt Extensions
            • Unsolved