Axel
Axel

Reputation: 11

Qt: Store a Tree (QTreeView + QStandardItemModel) in a Database Table

As you can see from the title, I use a QStandardItemModel to store a Tree Structure. I manipulate this structure inside a QTreeView, than I need to save it on a database in this format:

|id|Parent|Child |
| 1|      |ITEM01|
| 2|     1|ITEM02|
| 3|     2|ITEM03|
| 4|     3|ITEM04|
| 5|     4|ITEM05|
| 6|     5|ITEM06|
| 7|     6|ITEM07|
| 8|     3|ITEM08|
| 9|     3|ITEM09|
|10|     3|ITEM10|

That represent this structure:

ITEM01
 ║
 ╠═► ITEM02
 ║    ║
 ║    ╚═► ITEM03
 ║         ║
 ║         ╠═► ITEM04
 ║         ║    ║
 ║         ║    ╚═► ITEM05
 ║         ║         ║
 ║         ║         ╚═► ITEM06
 ║         ║              ║
 ║         ║              ╚═► ITEM07
 ║         ║
 ║         ╠═► ITEM08
 ║         ╠═► ITEM09
 ║         ╚═► ITEM10

in other words "id" represent uniquely a node, the field "parent" is a reference to the parent node. The root node is an empty field.

My question is: how to get an uniquely id (in int format), from QStandardItemModel? I've tried with QModelIndex::row() and QPersistentModelIndex::row() but it seems not to be unique.

Many Thanks.

Upvotes: 1

Views: 663

Answers (2)

Mike
Mike

Reputation: 654

You can iterate over QAbstractItemModel using this approach.

void iterate(const QModelIndex & index, const QAbstractItemModel * model,
             const std::function<void(const QModelIndex&, int)> & fun,
             int depth = 0)
{
    if (index.isValid())
        fun(index, depth);
    if ((index.flags() & Qt::ItemNeverHasChildren) || !model->hasChildren(index)) return;
    auto rows = model->rowCount(index);
    auto cols = model->columnCount(index);
    for (int i = 0; i < rows; ++i)
        for (int j = 0; j < cols; ++j)
            iterate(model->index(i, j, index), model, fun, depth+1);
}
void dumpData(QAbstractItemView * view) {
    iterate(view->rootIndex(), view->model(), [](const QModelIndex & idx, int depth){
        qDebug() << depth << ":" << idx.row() << "," << idx.column() << "=" << idx.data();
    });
}

Then get item's parent item using QModelIndex::parent() and finally get unique ids of both parent's item and item itself using QModelIndex::internalId().

Upvotes: 0

The easiest approach would be to assign the indices from an incrementing counter as you traverse the tree in a certain depth-first order, e.g. postorder - left...right, root.

If you store the id and parent as roles of your element, then:

int assignIdsPostorder(QStandardItem * e, int idRole, int parentRole, int counter = 0) {
  auto const N = e->rowCount();
  for (int i = 0; i < N; ++i)
    counter = assignIdsDFS(e->child(i), idRole, parentRole, counter);
  if (e->setData(counter, idRole)) counter++;
  for (int i = 0; i < N; ++i)
    e->child(i)->setData(e->data(idRole), parentRole);
  return counter;
}

A minor modification would accommodate storing the id and parent in individual columns.

If the indices don't need to be valid at all times, then you can generate them lazily with memoization (caching). A QStandardItem-derived element would reimplement data() to perform partial tree traversals to generate its ids. The order of traversal would need to be adapted to the properties of your tree if you'd want to avoid the case of potentially having to annotate the entire tree on a single id query.

Upvotes: 1

Related Questions