Qt 如何在不复制的情况下为已经存在的树状数据结构实现QAbstractItemModel?

Qt 如何在不复制的情况下为已经存在的树状数据结构实现QAbstractItemModel?,qt,qtreeview,qabstractitemmodel,Qt,Qtreeview,Qabstractitemmodel,在阅读了和的文档和示例之后,我仍然对如何正确地实现一个模型感到困惑 由于我想为现有的分层数据结构提供一个模型,因此我避免使用or以及与数据复制和同步相关的问题。但是,我未能实现一个工作项模型(问题太多,发布代码是没有用的) 在阅读之后,似乎很清楚,但仅仅依靠模型来告诉给定索引的父级。因此,如果不定义至少另一个用于存储此类关系的帮助器类,则似乎不可能为现有数据结构提供抽象树模型。但是,我仍然无法正确地实现该模型 假设数据结构如下所示: struct Property { QString n

在阅读了和的文档和示例之后,我仍然对如何正确地实现一个模型感到困惑

由于我想为现有的分层数据结构提供一个模型,因此我避免使用or以及与数据复制和同步相关的问题。但是,我未能实现一个工作项模型(问题太多,发布代码是没有用的)

在阅读之后,似乎很清楚,但仅仅依靠模型来告诉给定索引的父级。因此,如果不定义至少另一个用于存储此类关系的帮助器类,则似乎不可能为现有数据结构提供抽象树模型。但是,我仍然无法正确地实现该模型

假设数据结构如下所示:

struct Property {
    QString name;
    QString value;
};
struct Node {
    QString name;
    QVector<Property> properties;
};
struct Track {
    int length;
    QString channel;
};
struct Model {
    QVector<Node> nodes;
    QVector<Track> tracks;
};

如何实现子类来访问现有数据而不进行复制?

下面是我的解决方案

首先,我最初认为
QModelIndex
无法存储父子关系是正确的。实际上,方法
QModelIndex::parent
只是调用
qabstractemmodel::parent
,实现父方法的任务留给model类。当底层模型是正确的树时,指向树节点的指针可以存储在
QModelIndex
类中,但在我的例子中,我们处理的是一个“虚拟”树,这种关系不可用。因此,我们不得不引入某种额外的存储,以便能够知道我们在树中的位置。如果
QModelIndex
本机支持使用指向父索引的指针,则此问题将更容易解决。但是由于
QModelIndex
是一个值类,我们不能有一个指向父类的指针,而是必须将所有父类索引存储在
QModelIndex
类中,也许Qt开发人员有一些好办法不这样做。因此,我在
QModelIndex
的内部指针字段中存储了一个
QVector
。有一些事情需要注意,比如避免分配超过必要的索引,还记得在不再需要这些索引时释放内存(我们不能在这里使用QObject层次结构)。当模型是读写模型时,可能需要注意其他问题,但在本例中,我处理的是只读模型

我的实现如下。方法
rowCount
data
定义这个特定的虚拟树。其他方法可以抽象成一个可以重用的类

class MyModel : public QAbstractItemModel
{
    Q_OBJECT

private:
    struct IndexData
    {
        QVector<QModelIndex> parents;
    };

public:
    explicit MyModel(QObject *parent = nullptr);
    ~MyModel();

    QVariant data(const QModelIndex &index, int role) const override;
    QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override;
    QModelIndex parent(const QModelIndex &index) const override;
protected:
    IndexData * indexData(const QModelIndex &index) const;
    QList<int> indexPath(const QModelIndex &index) const;
    QString indexString(const QModelIndex &index) const;
    QString indexString(int row, int column, const QModelIndex &parent) const;
public:
    int indexDepth(const QModelIndex &index) const;
    int rowCount(const QModelIndex &parent = QModelIndex()) const override;
    int columnCount(const QModelIndex &parent = QModelIndex()) const override;
    Qt::ItemFlags flags(const QModelIndex &index) const override;

private:
    QMap<QString, IndexData*> indexData_;
    Model model;
};
classmymodel:publicqabstractemodel
{
Q_对象
私人:
结构索引数据
{
QVector双亲;
};
公众:
显式MyModel(QObject*parent=nullptr);
~MyModel();
QVariant数据(常量QModelIndex&index,int角色)常量覆盖;
QModelIndex索引(int行,int列,常量QModelIndex&parent=QModelIndex())常量重写;
QModelIndex父项(常量QModelIndex&index)常量覆盖;
受保护的:
索引数据*索引数据(常量QModelIndex和index)常量;
QList indexPath(constqmodelindex&index)const;
QString indexString(const QModelIndex&index)const;
QString索引字符串(int行、int列、常量QModelIndex&parent)常量;
公众:
int indexDepth(constqmodelindex&index)const;
int rowCount(const QModelIndex&parent=QModelIndex())常量重写;
int columnCount(常量QModelIndex&parent=QModelIndex())常量覆盖;
Qt::ItemFlags标志(常量QModelIndex和index)常量覆盖;
私人:
QMap索引数据;
模型;
};
实施:

MyModel::MyModel(QObject *parent)
    : QAbstractItemModel(parent)
{
    model.nodes.resize(2);
    model.nodes[0].name = "node1";
    model.nodes[0].properties.resize(2);
    model.nodes[0].properties[0].name = "property1";
    model.nodes[0].properties[0].value = "value1";
    model.nodes[0].properties[1].name = "property2";
    model.nodes[0].properties[1].value = "value2";
    model.nodes[1].name = "node2";
    model.nodes[1].properties.resize(1);
    model.nodes[1].properties[0].name = "property1";
    model.nodes[1].properties[0].value = "someValue";
    model.tracks.resize(3);
    model.tracks[0].length = 2;
    model.tracks[0].channel = "A";
    model.tracks[1].length = 4;
    model.tracks[1].channel = "B";
    model.tracks[2].length = 3;
    model.tracks[2].channel = "C";
}

MyModel::~MyModel()
{
    for(auto v : indexData_) delete v;
}

QVariant MyModel::data(const QModelIndex &index, int role) const
{
    if(!index.isValid() || role != Qt::DisplayRole) return {};

    int d = indexDepth(index);
    auto path = indexPath(index);

    if(d == 1) return "Model";
    if(d == 2 && path[0] == 0 && path[1] == 0) return "Nodes";
    if(d == 2 && path[0] == 0 && path[1] == 1) return "Tracks";
    if(d == 3 && path[0] == 0 && path[1] == 0) return QString("Node \"%1\"").arg(model.nodes[path[2]].name);
    if(d == 4 && path[0] == 0 && path[1] == 0) return "Properties";
    if(d == 5 && path[0] == 0 && path[1] == 0 && path[3] == 0) return QString("Property %1 = %2").arg(model.nodes[path[2]].properties[path[4]].name, model.nodes[path[2]].properties[path[4]].value);
    if(d == 3 && path[0] == 0 && path[1] == 1) return QString("Track %1...").arg(index.row() + 1);
    return {};
}

QModelIndex MyModel::index(int row, int column, const QModelIndex &parent) const
{
    QString dataKey = indexString(row, column, parent);
    auto it = indexData_.find(dataKey);
    IndexData *data;
    if(it == indexData_.end())
    {
        data = new IndexData;
        const_cast<MyModel*>(this)->indexData_.insert(dataKey, data);
        if(parent.isValid())
        {
            data->parents.append(parent);
            data->parents.append(indexData(parent)->parents);
        }
    }
    else
    {
        data = it.value();
    }
    return createIndex(row, column, data);
}

QModelIndex MyModel::parent(const QModelIndex &index) const
{
    if(!index.isValid()) return {};
    auto data = indexData(index);
    if(data->parents.empty()) return {};
    return data->parents.at(0);
}

MyModel::IndexData * MyModel::indexData(const QModelIndex &index) const
{
    if(!index.internalPointer()) return nullptr;
    return reinterpret_cast<IndexData*>(index.internalPointer());
}

QList<int> MyModel::indexPath(const QModelIndex &index) const
{
    QList<int> path;
    auto data = indexData(index);
    for(int i = data->parents.size() - 1; i >= 0; i--)
        path.push_back(data->parents[i].row());
    path.push_back(index.row());
    return path;
}

QString MyModel::indexString(const QModelIndex &index) const
{
    return indexString(index.row(), index.column(), index.parent());
}

QString MyModel::indexString(int row, int column, const QModelIndex &parent) const
{
    QString pre = parent.isValid() ? indexString(parent) + "." : "";
    return pre + QString("[%1,%2]").arg(row).arg(column);
}

int MyModel::indexDepth(const QModelIndex &index) const
{
    if(!index.isValid()) return 0;
    return 1 + indexDepth(index.parent());
}

int MyModel::rowCount(const QModelIndex &parent) const
{
    if(!parent.isValid()) return 1; // root item

    int d = indexDepth(parent);
    auto path = indexPath(parent);

    //if(d == 0) return 1; // root item
    if(d == 1) return 2;
    if(d == 2 && path[0] == 0 && path[1] == 0) return model.nodes.size();
    if(d == 2 && path[0] == 0 && path[1] == 1) return model.tracks.size();
    if(d == 3 && path[0] == 0 && path[1] == 0) return 1;
    if(d == 4 && path[0] == 0 && path[1] == 0 && path[3] == 0) return model.nodes[path[2]].properties.size();
    return 0;
}

int MyModel::columnCount(const QModelIndex &parent) const
{
    return 1;
}

Qt::ItemFlags MyModel::flags(const QModelIndex &index) const
{
    if(index.isValid()) return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
    return {};
}
MyModel::MyModel(QObject*parent)
:qabstractemmodel(父级)
{
模型。节点。调整大小(2);
model.nodes[0].name=“node1”;
model.nodes[0]。properties.resize(2);
model.nodes[0]。属性[0]。name=“property1”;
model.nodes[0]。属性[0]。value=“value1”;
model.nodes[0]。属性[1]。name=“property2”;
model.nodes[0]。属性[1]。value=“value2”;
model.nodes[1].name=“node2”;
model.nodes[1].properties.resize(1);
model.nodes[1]。属性[0]。name=“property1”;
model.nodes[1]。属性[0]。value=“someValue”;
model.tracks.resize(3);
model.tracks[0]。长度=2;
model.tracks[0]。channel=“A”;
model.tracks[1],长度=4;
model.tracks[1]。channel=“B”;
model.tracks[2],长度=3;
model.tracks[2]。channel=“C”;
}
MyModel::~MyModel()
{
对于(自动v:indexData_uu)删除v;
}
QVariant MyModel::数据(常量QModelIndex&index,int角色)常量
{
if(!index.isValid()| | role!=Qt::DisplayRole)返回{};
int d=索引深度(索引);
自动路径=索引XPath(索引);
如果(d==1)返回“Model”;
如果(d==2&&path[0]==0&&path[1]==0)返回“节点”;
如果(d==2&&path[0]==0&&path[1]==1)返回“Tracks”;
如果(d==3&&path[0]==0&&path[1]==0)返回QString(“节点\%1\”).arg(model.nodes[path[2]].name);
如果(d==4&&path[0]==0&&path[1]==0)返回“属性”;
如果(d==5&&path[0]==0&&path[1]==0&&path[3]==0)返回QString(“属性%1=%2”).arg(model.nodes[path[2]].properties[path[4].name,model.nodes[path[2].properties[path[4].value);
如果(d==3&&path[0]==0&&path[1]==1)返回QString(“磁道%1…”).arg(index.row()+1);
返回{};
}
QModelIndex MyModel::index(int行、int列、常量QModelIndex&parent)常量
{
QString数据键=索引字符串(行、列、父级);
auto it=索引数据查找(数据键);
索引数据*数据;
if(it==indexData_uxdata.end())
{
数据=新索引数据;
const_cast(this)->indexData.insert(数据键,数据);
if(parent.isValid())
{
数据->父项.append(父项);
数据->父项.append(索引数据(父项)->父项);
}
}
其他的
{
data=it.value();
}
返回cr
MyModel::MyModel(QObject *parent)
    : QAbstractItemModel(parent)
{
    model.nodes.resize(2);
    model.nodes[0].name = "node1";
    model.nodes[0].properties.resize(2);
    model.nodes[0].properties[0].name = "property1";
    model.nodes[0].properties[0].value = "value1";
    model.nodes[0].properties[1].name = "property2";
    model.nodes[0].properties[1].value = "value2";
    model.nodes[1].name = "node2";
    model.nodes[1].properties.resize(1);
    model.nodes[1].properties[0].name = "property1";
    model.nodes[1].properties[0].value = "someValue";
    model.tracks.resize(3);
    model.tracks[0].length = 2;
    model.tracks[0].channel = "A";
    model.tracks[1].length = 4;
    model.tracks[1].channel = "B";
    model.tracks[2].length = 3;
    model.tracks[2].channel = "C";
}

MyModel::~MyModel()
{
    for(auto v : indexData_) delete v;
}

QVariant MyModel::data(const QModelIndex &index, int role) const
{
    if(!index.isValid() || role != Qt::DisplayRole) return {};

    int d = indexDepth(index);
    auto path = indexPath(index);

    if(d == 1) return "Model";
    if(d == 2 && path[0] == 0 && path[1] == 0) return "Nodes";
    if(d == 2 && path[0] == 0 && path[1] == 1) return "Tracks";
    if(d == 3 && path[0] == 0 && path[1] == 0) return QString("Node \"%1\"").arg(model.nodes[path[2]].name);
    if(d == 4 && path[0] == 0 && path[1] == 0) return "Properties";
    if(d == 5 && path[0] == 0 && path[1] == 0 && path[3] == 0) return QString("Property %1 = %2").arg(model.nodes[path[2]].properties[path[4]].name, model.nodes[path[2]].properties[path[4]].value);
    if(d == 3 && path[0] == 0 && path[1] == 1) return QString("Track %1...").arg(index.row() + 1);
    return {};
}

QModelIndex MyModel::index(int row, int column, const QModelIndex &parent) const
{
    QString dataKey = indexString(row, column, parent);
    auto it = indexData_.find(dataKey);
    IndexData *data;
    if(it == indexData_.end())
    {
        data = new IndexData;
        const_cast<MyModel*>(this)->indexData_.insert(dataKey, data);
        if(parent.isValid())
        {
            data->parents.append(parent);
            data->parents.append(indexData(parent)->parents);
        }
    }
    else
    {
        data = it.value();
    }
    return createIndex(row, column, data);
}

QModelIndex MyModel::parent(const QModelIndex &index) const
{
    if(!index.isValid()) return {};
    auto data = indexData(index);
    if(data->parents.empty()) return {};
    return data->parents.at(0);
}

MyModel::IndexData * MyModel::indexData(const QModelIndex &index) const
{
    if(!index.internalPointer()) return nullptr;
    return reinterpret_cast<IndexData*>(index.internalPointer());
}

QList<int> MyModel::indexPath(const QModelIndex &index) const
{
    QList<int> path;
    auto data = indexData(index);
    for(int i = data->parents.size() - 1; i >= 0; i--)
        path.push_back(data->parents[i].row());
    path.push_back(index.row());
    return path;
}

QString MyModel::indexString(const QModelIndex &index) const
{
    return indexString(index.row(), index.column(), index.parent());
}

QString MyModel::indexString(int row, int column, const QModelIndex &parent) const
{
    QString pre = parent.isValid() ? indexString(parent) + "." : "";
    return pre + QString("[%1,%2]").arg(row).arg(column);
}

int MyModel::indexDepth(const QModelIndex &index) const
{
    if(!index.isValid()) return 0;
    return 1 + indexDepth(index.parent());
}

int MyModel::rowCount(const QModelIndex &parent) const
{
    if(!parent.isValid()) return 1; // root item

    int d = indexDepth(parent);
    auto path = indexPath(parent);

    //if(d == 0) return 1; // root item
    if(d == 1) return 2;
    if(d == 2 && path[0] == 0 && path[1] == 0) return model.nodes.size();
    if(d == 2 && path[0] == 0 && path[1] == 1) return model.tracks.size();
    if(d == 3 && path[0] == 0 && path[1] == 0) return 1;
    if(d == 4 && path[0] == 0 && path[1] == 0 && path[3] == 0) return model.nodes[path[2]].properties.size();
    return 0;
}

int MyModel::columnCount(const QModelIndex &parent) const
{
    return 1;
}

Qt::ItemFlags MyModel::flags(const QModelIndex &index) const
{
    if(index.isValid()) return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
    return {};
}