如何在Qt中使用QTableView显示简单的QMap?

7
我有一个名为map的QMap。我从数据库中初始化了这个map的几行数据。现在我将这个map发送到另一个包含GUI类的类中。在我的GUI中,我有一个TableView项。我需要以任何顺序在这个TableView中显示这个map。
我看过几个例子,但它们都是针对只有一个字段的一个向量。并且它们使用另一个类来形成视图。我想知道是否有人之前做过这个,并能帮助我解决问题。
2个回答

12

QMap包装在QAbstractTableModel的子类中,并将其设置为视图。以下是一个基本的功能示例:

文件 "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

文件 "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();
}

使用示例:

// ...
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);
// ...

它将显示一个如下所示的表格视图:

enter image description here


4
setMap 函数中,你需要执行 beginModelReset()endModelReset() 来重置模型。 - Claudiu
2
我不建议这样做。使用.keys()和.values()方法会使其非常低效。这两种方法都会创建一个新的容器,然后将所有数据复制到这个新容器中。更糟糕的是,这个新容器是QList,除了少数类型之外,它本身并不是一个高效的容器。您需要为每次调用data()执行此操作,而data()经常被调用。因此,请不要这样做。高效的项模型需要一个随机访问容器来支持它们。std::vector是您的首选。 - André
1
@André,Bernard正在寻找一种在QTableView中显示QMap的方法,这就是答案所涉及的内容。我同意您提出的使用.keys()和.values()会引入时间惩罚,但这就是您在QMap中拥有的全部内容。 QList对于随机访问并不低效,QList与std::list并不相似,它确实具有基于常数时间的索引访问,就像std::vector一样。请注意使用at()而不是[]以保证更好的性能。 - mhcuervo
3
不,这不是你全部拥有的。你可以将数据复制到一个连续的容器中。你也可以使用QMap的迭代器并将其推进到正确的行(可能要优化以使其不总是从begin()开始,而是从最后使用的、begin()或end()中最接近的那个位置开始)。虽然不理想,但比使用.keys()和.values()要好得多。对于大多数类型,QList是指向堆分配值的指针的一个连续容器。这样并不高效。在Qt6中,它正在被逐步淘汰。 - André
LOL @André 如果有人问如何折一架纸飞机,你就告诉他们如何折一架纸飞机。但过分强调最好用其他材料(如钢材)制作,以便可以容纳人员,则有点过火。所以我仍然认为,mhcuervo提供了一个相当简明的答案来回答问题。请记住,这是一个分享和帮助的社区,而不是让别人按照你想要的方式做事情。有时候简单就是更好的,例如,制作一个hello world应用程序不需要任何设计模式。成为一个开发人员,分享知识,不要强迫别人。 - Jacques Ramsden
使用QMap键对应行索引怎么样?这样,您就不必以某种方式“循环”底层数据。此外,偏移计算依赖于QMap属性来保持项目排序。使用QHash,它将不再起作用。但我也想知道是否有一种方法在插入项时在模型中保留“索引行”信息,具有自定义角色? - claude joliat

4

正如@André所说,依赖于QMapkeys()values()方法的解决方案根本不高效,不应该使用。 相反,您应该使用迭代器!

因此,使用相同的.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

这个CPP代码大概是这样的:

#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();
}

主要变化在于MapModel::data中使用了迭代器,正如@Claudiu所评论的那样,在MapModel::setMap中必须使用beginModelResetendModelReset(以允许您能够更改模型上的地图并向视图发出信号)。
如果您需要一些示例,我已经为FamilyModel2上的QAbstractListModel完成了这项工作。这是头文件cpp文件。它使用一个地图的包装器,您可以在这里找到。 请检查构造函数FamilyModel2::FamilyModel2,其中连接了外部信号以管理条目的插入,并在数据更新时发出dataChanged信号;)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接