Explain the parent/child row/column relationship for QStandardItem and QStandardItemModel - c++

I am trying to use the QStandardItem and QStandardItemModel to drive a TreeView in Qt.
In Qt the typical model to control the TreeView, TableView & ListView includes the concept of rows and columns, but also parent/child relationships.
I find the documentation very confusing, because it seems these are interdependent (e.g. to add a child to a QStandardItem, appendRow() is used), but nowhere I have found is dedicated to explaining exactly how.
If I had to guess, I would say: "rows are equivalent to children. columns have meaning only within an item itself". Is this correct?

Qt item model is recursive: each item is a table of items. It is incorrect to say that rows are equivalent to children: each index can have a child, no matter what its row column.
Of course, not all views can handle all models. Specifically, no Qt's built-in views can handle children in columns other than 0. But you certainly could implement a view that does!

Related

Qt Table model to Tree model

In Qt I have a sqlite database which I'm pulling in. One of the tables (configTable) has a QSqlTableModel attached.
The table has a simple 2-column key/value structure. The keys are strings with folder-like values such as "general/name", "general/version", "foo/bar/baz", etc. Values are just arbitrary variants.
I'd like to display this data in an easier-to-browse QTreeView instead of a QTableView, as my key structure lends itself very nicely to that.
Before I go reimplementing classes and all sorts of crazy things - is there an elegant solution to this? And if I reimplement or extend classes, which ones should I look at?
Thank you.
You have to do the parsing+mapping between the list of value/value/value and a tree model yourself. But there is a (tricky) Qt way to do this yes.
The Qt Model-View architecture can represent many different structures of data, based on the QAbstractItemModel class. A Qt model must implement some functions to tell the view : how many columns, row, children etc.
A list model (Qt provides QAbstractListModel), is basically a model that says to the view :
I have one root item (all data items are represented by a QModelIndex, root has an invalid parent)
This root item has only one column
This root item has as many rows as your list has elements
A tree model will return the appropriate children for each QModelIndex. The abstract model of Qt actually allows each child item to be a table (QModelIndex always has a parent and a row-column index).
Long story short, you have to create a proxy model (QAbstractProxyModel or a suitable subclass, but for your need I don't think there is one). This proxy will transform the data your QSqlTableModel is sending, and this is where you can tell the view that you actually have a tree and not a list.
Your root items are the items from your database list of keys (first element of the foo/bar/whatever), but you need to regroup all the root items that has the same key.
AFAIK you can make it only manually.
Basically, because how did you think Qt knows how to convert your data into tree model.

Using an existing (row, column) C++ model with QtQuick (Grid, TableView)

I have some models that use the QAbstractItemModel rules for retrieving and providing data for a table.
My example model has multiple columns and rows. Unfortunately QtQuick widgets can only handle a single column. Other "columns" are added to the QtQuick widgets by way of roles. So multiple columns in the view match to the same column in the model. Other model columns are ignored, as explained in this question and its answer
I was thinking that it should not be too difficult to provide an abstraction for QML to be used on the C++ side (as a QAbstractProxyModel) which when asked for row N, modulos it by the column count of the source model and retrieves the data from the resulting actual column. This would appear to work for Grid, but won't work for TableView as it relies on TableViewColumn and role names instead of using only continuous row indices. For that, the proxy model would need to distinguish by the role which column of the source model to retrieve from.
The snippet present in the answer to http://qt-project.org/forums/viewthread/41793 does that for adoping QSqlTableModel, but still misses translating a lot of the signals to be usable. Like I imagine if the SQL source model would emit columnsInserted, it should translate to a signal dataChanged with the new roles chosen for that column and a change of the available role names. The QMLifyProxyModel appears to be better, but not production ready and dead for 4 years now, it seems.
How can we best fix this so that the two worlds work together fluently, according to the official recommendation? Why don't QtQuick views use the (row, column) notation that QAbstractItemModel and QTableView has been using already?
Part of the difficulty is that the way QtQuick use models is stricter than QWidget views', so it could be worth trying to achieve this the other way around; converting models to use roles and use a proxy model to map roles to column indexes + headerData the same way that TableViewColumn does, but for QWidget views. The column insertion and removal signals should be easier to handle if the source is a static number of roles rather than a changing number of columns.
Sadly this won't help for built-in or more complex models.
Most QtQuick views have been designed for 1D models when phones were its design target. Roles were used to map unordered properties of a single row/item to their scripted name.
TableView came a few years later and seems to be the one that should have done the extra mile to allow using 2D models, but it would probably have been quite some additional work at a time where QtQuick was already quite a beast, especially since TableView was mostly written in QML itself.

Append QStandardItem in two QStandardItemModel at the same time

First I want to thank you in advance for your answers. I insert one QStandardItem in one QStandardItemModel and then display this model in a Tree View (A). After this I append the same item (pointer) to a new QStandardItemModel wich is associated with another Tree View (B). It is only displayed one empty item instead one item with the same text in this second case. If I make a copy of the item, the result the expected. Why can I not append the same item in two different models?
In this case is not useful to use QSortFilterProxyModel because modelA and modelB have the same data but following a very different structure.
Thank you very much.
If it is not possible to do this, which solution you suggest me? I though maintaining a correspondence between table models by using hash tables, but I think there is a easier solution.
I copy one code example.
QStandardItem * item = generateExampleItem();
modelA->invisibleRootItem()->appendRow(item); // will be visible to the user
modelB->invisibleRootItem()->appendRow(item); // will be invisible to the user
ui.treeViewA->setModel(modelA);
ui.treeViewB->setModel(modelB);
ui.treeViewA->show();
ui.treeViewB->show();

How to maintain a link between parent QStandardItems and database row ids in a tree model?

I have a little problem I am trying to figure out, I am working on a QT app that is using the QTreeView and I have a bunch of categories which have children, so they look like
Parent 1
Parent 2
- Child 1
- Child 2
Parent 3
and so on and so forth, so in my database I have rows which have all the regular details (name, id, date created, etc) and the ones which are children have their parent specified (so pid=parent row id). I then loop over then using the standard QSqlQuery stuff. But the problem I am running into is this...
Items are added to the treeview by QStandardItem* item = new QStandardItem(icon, name); and then appending the row model->appendRow(item); but my children need to call parentitem->appendRow(item); so the QStandardItem* item of the parent. But how can I find out what that is without storing every single item?
Moral of the story is, is there a way to do one of the following that won't destroy performance.
Store the QStandardItem* item in an array that I could reference the parent in the childs loop?
Assign an ID or something to QStandardItem* item which I could then reference when adding a child.
Generate a TreeView model from an array, where the children array elements get added as children?
Something else I haven't thought of...
I can't seem to find any good examples of QTreeView with children from a database.
All you need is a QMap<int, QStandardItem*> rowItemMap. When you retrieve a row from the database with a given row id, you immediately create an item and add it to the map. You then add it to a parent that you look up in the map. You'll need to create a dummy parent as the root item. There's nothing wrong with storing pointers to items. For reasonable amounts of items, it won't matter. If you think of storing more than 10k items, you may want to think of using a view that offers transitive closure up to a certain depth of the tree. It'd then be much easier to map such a view, via QSqlTableModel, directly onto the tree, without having to copy the entire tree from the database into a temporary model.

Creating Qt models for tree views

I'm writing an application in Qt (with C++) and I need to represent an object structure in a tree view. One of the ways to do this is to create a model for this, but I'm still quite confused after reading the Qt documentation about the subject.
The "structure" I have is pretty simple - there's a Project object that holds Task objects in a std::vector container. These tasks can also hold child tasks.
I've already written methods to read & write these projects to/from XML files using Qt's XML classes.
Is there any more documentation or "recommended reading" for creating models from scratch? How do you recommend I start implementing this?
As an alternative to what was said by Virgil in a comment to the question, you could use QStandardItemModel class for your model and just build your tree using this class. Below is an example:
QStandardItemModel* model = new QStandardItemModel();
QStandardItem* item0 = new QStandardItem(QIcon("test.png"), "1 first item");
QStandardItem* item1 = new QStandardItem(QIcon("test.png"), "2 second item");
QStandardItem* item3 = new QStandardItem(QIcon("test.png"), "3 third item");
QStandardItem* item4 = new QStandardItem("4 forth item");
model->appendRow(item0);
item0->appendRow(item3);
item0->appendRow(item4);
model->appendRow(item1);
ui->treeView->setModel(model);
When the UI (view) is destroyed, delete model. Documentation:
https://doc.qt.io/qt-5/qstandarditemmodel.html
https://doc.qt.io/qt-5/qstandarditem.html
The basic trick to get this working is really to get the model to data structure mapping right. Something that might seem hard, but needn't be.
First, using the QAbstractItemModel::createIndex to build model indexes, you can refer to your own data structure through the pointer or uint32 that you can add to the index, depending on which instance of createIndex that you choose to use.
Second, having the structure clear in mind (as you seem to have), it is quite easy to write the parent and index functions. The key here is to understand that the model root is an unintialized QModelIndex instance. I.e. QModelIndex::isValid() == false indicates root.
Third, if you go multi-column, remember that only the first column has children.
Fourth, to check that you do things the expected way, do use the ModelTest class. It monitors and checks your model, so that you follow the conventions that the Qt model view classes expect.