Question

I have a QMap called map. I initialize this map with couple of row of data from my database. Now I send this map to another class which contain GUI classes. In my GUI I have a TableView item. I need to show this map in any order in this TableView.

I have seen couple of examples, but all of them are for one vector which has only one field. And they used another class to form the view. I wondered if anyone has done this before and can help me with that.

Was it helpful?

Solution

Wrap the QMap in a subclass of QAbstractTableModel and set it to the view. Following, a basic functional example:

File "mapmodel.h"

#ifndef MAPMODEL_H
#define MAPMODEL_H

#include <QAbstractTableModel>
#include <QMap>

class MapModel : public QAbstractTableModel
{
    Q_OBJECT
public:

    enum MapRoles {
        KeyRole = Qt::UserRole + 1,
        ValueRole
    };

    explicit MapModel(QObject *parent = 0);
    int rowCount(const QModelIndex& parent = QModelIndex()) const;
    int columnCount(const QModelIndex& parent = QModelIndex()) const;
    QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const;
    inline void setMap(QMap<int, QString>* map) { _map = map; }

private:
    QMap<int, QString>* _map;
};

#endif // MAPMODEL_H

File "mapmodel.cpp"

#include "mapmodel.h"

MapModel::MapModel(QObject *parent) :
    QAbstractTableModel(parent)
{
    _map = NULL;
}

int MapModel::rowCount(const QModelIndex& parent) const
{
    if (_map)
        return _map->count();
    return 0;
}

int MapModel::columnCount(const QModelIndex & parent) const
{
    return 2;
}

QVariant MapModel::data(const QModelIndex& index, int role) const
{
    if (!_map)
        return QVariant();
    if (index.row() < 0 ||
        index.row() >= _map->count() ||
        role != Qt::DisplayRole) {
        return QVariant();
    }
    if (index.column() == 0)
        return _map->keys().at(index.row());
    if (index.column() == 1)
        return _map->values().at(index.row());
    return QVariant();
}

Example of use:

// ...
QMap<int, QString> map;
map.insert(1, "value 1");
map.insert(2, "value 2");
map.insert(3, "value 3");

MapModel mapmodel;
mapmodel.setMap(&map);

YourTableView.setModel(&mapmodel);
// ...

It will show a table view populated as follows:

enter image description here

OTHER TIPS

As @André said the solution relying on QMap keys() and values() methods is not efficient at all and shouldn't be used. Instead you should use iterators!

so using the same .h

#ifndef MAPMODEL_H
#define MAPMODEL_H

#include <QAbstractTableModel>
#include <QMap>

class MapModel : public QAbstractTableModel
{
    Q_OBJECT
public:

    enum MapRoles {
        KeyRole = Qt::UserRole + 1,
        ValueRole
    };

    explicit MapModel(QObject *parent = 0);
    int rowCount(const QModelIndex& parent = QModelIndex()) const;
    int columnCount(const QModelIndex& parent = QModelIndex()) const;
    QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const;
    void setMap(QMap<int, QString>* map);

private:
    QMap<int, QString>* _map;
};

#endif // MAPMODEL_H

the cpp would be something like this:

#include "mapmodel.h"

MapModel::MapModel(QObject *parent) :
    QAbstractTableModel(parent), _map(nullptr)
{}

void MapModel::setMap(QMap<int, QString>* map)
{
    beginModelReset();
    _map = map;
    endModelReset();
}

int MapModel::rowCount(const QModelIndex& parent) const
{
    if (_map)
        return _map->count();
    return 0;
}

int MapModel::columnCount(const QModelIndex & parent) const
{
    return 2;
}

QVariant MapModel::data(const QModelIndex& index, int role) const
{
    if (!_map || !index.isValid() || index.row() >= _map->count() || role != Qt::DisplayRole)
        return QVariant();

    auto it = _map.cbegin();
    it += index.row();

    if (index.column() == 0)
        return it.key();
    if (index.column() == 1)
        return it.value();

    return QVariant();
}

The main change is in MapModel::data where you use an iterator and as @Claudiu commented, you must use beginModelReset and endModelReset in MapModel::setMap. (to allow you to be able to change the map on your model and signal your Views)

If you want some examples, I've done it for QAbstractListModel on FamilyModel2. Here is the header, here the cpp. It is using a wrapper for the Map that you can find here. Check the constructor FamilyModel2::FamilyModel2 where external signals are connected in order to manage insertion of entries and also emit dataChanged when your data is updated ;)

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top