JavaEar 专注于收集分享传播有价值的技术资料

What to use instead of QStringList?

I'm new to Qt, so I'm not sure what to use.

I thought I would have a QStringList to give to my QStringListModel and display it in a ListView.

Now, however, I need to divide QStringList in the values of 2 types. So, I need to have string + some typeId, not just one string, but QStringList is for one-dimensional list only.

Anyone could give an advice on what is the best way to try to implement this?

2个回答

    最佳答案
  1. If you need dictionary that contains QString and any other type I suggest you to use

      QMap<QString, YourType> myMap;
    

    Here you have some example of usage:

    QMap<int, QString> myMap;
    myMap.insert(1,"A");
    myMap.insert(2,"B");
    myMap[3] = "C";
    
    foreach(int i, myMap.keys()) qDebug() << myMap[i];
    
  2. 参考答案2
  3. The solution is to use QAbstractListModel subclass as a Qt Quick model. An example of base class for a models (I use it for convenience):

    // abstractobjectlistmodel.h
    #pragma once
    
    #include <QtCore>
    
    struct AbstractObjectListModel
            : public QAbstractListModel
    {
    
        explicit AbstractObjectListModel(QObject * const parent = Q_NULLPTR)
            : QAbstractListModel{parent}
        { ; }
    
        int rowCount(QModelIndex const & parent = {}) const Q_DECL_OVERRIDE Q_DECL_FINAL
        {
            Q_UNUSED(parent);
            return items.count();
        }
    
        QVariant data(QModelIndex const & index, const int role = Qt::DisplayRole) const Q_DECL_OVERRIDE Q_DECL_FINAL
        {
            if (!index.isValid()) {
                return {};
            }
            switch (role) {
            case Qt::UserRole : {
                return QVariant::fromValue(items[index.row()].data());
            }
            default : {
                return {};
            }
            }
        }
    
        QHash< int, QByteArray > roleNames() const Q_DECL_OVERRIDE Q_DECL_FINAL
        {
            auto roleNames = QAbstractListModel::roleNames();
            roleNames.insert(Qt::UserRole, "modelData");
            return roleNames;
        }
    
        Q_INVOKABLE
        virtual
        QObject * get(int row) const
        {
            if (row < 0) {
                return {};
            }
            if (row >= rowCount()) {
                return {};
            }
            return items[row];
        }
    
        void remove(int row, int count = 1)
        {
            Q_ASSERT(count > 0);
            Q_ASSERT(row >= 0);
            Q_ASSERT(row + count <= rowCount());
            beginRemoveRows({}, row, row + count - 1);
            while (0 < count) {
                items.takeAt(row)->deleteLater();
                --count;
            }
            endRemoveRows();
        }
    
        void clear()
        {
            if (!items.isEmpty()) {
                remove(0, rowCount());
            }
        }
    
    protected :
    
        ~AbstractObjectListModel() Q_DECL_OVERRIDE Q_DECL_EQ_DEFAULT; // derived classes should not meant to be manipulated polymorphically
    
        QList< QPointer< QObject > > items;
    
        void insert(int row, QObject * const item)
        {
            item->setParent(this);
            beginInsertRows({}, row, row);
            items.insert(row, item);
            endInsertRows();
        }
    
        void append(QObject * const item)
        {
            insert(rowCount(), item);
        }
    
    };
    

    But one need to override get to access items' Q_PROPERTY properties (in addition to dynamic ones):

    // type of element
    class Project
            : public QObject
    {
    
        Q_OBJECT
    
        Q_PROPERTY(QString name MEMBER name NOTIFY nameChanged)
        Q_PROPERTY(QString path MEMBER path NOTIFY pathChanged)
    
    public :
    
        Project(QString name, QString path,
                QObject * const parent = Q_NULLPTR)
            : QObject{parent}
            , name{name}
            , path{path}
        { ; }
    
    Q_SIGNALS :
    
        void nameChanged(QString name);
        void pathChanged(QString path);
    
    private :
    
        QString name;
        QString path;
    
    };
    
    // custom model
    class ProjectsListModel
            : public AbstractObjectListModel
    {
    
        Q_OBJECT
    
    public :
    
        explicit ProjectsListModel(QObject * const parent = Q_NULLPTR)
            : AbstractObjectListModel{parent}
        { ; }
    
        void appendProject(QString name, QString path)
        {
            AbstractObjectListModel::append(::new Project{name, path});
        }
    
        Q_INVOKABLE
        Project *
        get(int row) const Q_DECL_OVERRIDE
        {
            return qobject_cast< Project * >(AbstractObjectListModel::get(row));
        }
    
    };
    

    Before use one need to register concrete model with qmlRegisterType< ProjectsListModel >();. Properties of Project class are avaliable in delegate and highlight by means of members of modelData.

    Another example:

    struct TimeZoneModel Q_DECL_FINAL
            : public QAbstractListModel
    {
    
        Q_OBJECT
    
    public :
    
        explicit TimeZoneModel(QObject * const parent = Q_NULLPTR)
            : QAbstractListModel{parent}
        { ; }
    
        int rowCount(QModelIndex const & parent = {}) const Q_DECL_OVERRIDE
        {
            Q_UNUSED(parent);
            return timeZoneIds.count();
        }
    
        QVariant data(QModelIndex const & index, const int role = Qt::DisplayRole) const Q_DECL_OVERRIDE
        {
            if (!index.isValid() || (role > Qt::UserRole + 4)) {
                return {};
            }
            QTimeZone timeZone{timeZoneIds[index.row()]};
            if (!timeZone.isValid()) {
                return {};
            }
            return roleData(timeZone, role);
        }
    
        QHash< int, QByteArray > roleNames() const Q_DECL_OVERRIDE
        {
            auto roleNames = QAbstractListModel::roleNames();
            int i = Qt::UserRole;
            for (const auto role : {"modelData", "id", "comment", "name", "country"}) {
                roleNames.insert(i++, role);
            }
            return roleNames;
        }
    
        Q_INVOKABLE
        QByteArray get(int row) const
        {
            if (row < 0) {
                return {};
            }
            if (row >= rowCount()) {
                return {};
            }
            return timeZoneIds[row];
        }
    
    private :
    
        QVariant roleData(QTimeZone const & timeZone, int role = Qt::UserRole) const
        {
            switch (role) {
            case Qt::UserRole : {
                QVariantMap modelData;
                const auto names = roleNames();
                while (++role < Qt::UserRole + 5) {
                    modelData.insert(QString::fromUtf8(names[role]), roleData(timeZone, role));
                }
                return modelData;
            }
            case Qt::UserRole + 1: {
                return QString::fromUtf8(timeZone.id());
            }
            case Qt::UserRole + 2 : {
                return timeZone.comment();
            }
            case Qt::UserRole + 3 : {
                return timeZone.displayName(QTimeZone::StandardTime);
            }
            case Qt::UserRole + 4 : {
                return QLocale::countryToString(timeZone.country());
            }
            default : {
                return {};
            }
            }
        }
    
        const QByteArrayList timeZoneIds = QTimeZone::availableTimeZoneIds();
    
    };
    

    In addition to access via modelData's fields (modelData.id, modelData.comment etc) all the symbols are accessible directly (i.e. id, comment etc) in delegate and highlight contexts of the ListView.

    Model TimeZoneModel is const and can be injected into global scope directly without any performance drawbacks:

    QQmlApplicationEngine engine;
    
    TimeZoneModel timeZoneModel;
    Q_SET_OBJECT_NAME(timeZoneModel);
    qmlRegisterType< TimeZoneModel >();
    const auto rootContext = engine.rootContext();
    rootContext->setContextProperty(timeZoneModel.objectName(), &timeZoneModel);