Qt Table model to Tree model - c++

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.

Related

Ordered ManyToMany relation in Django with custom Field

In Django, I would like to have an ordered many-to-many-relation. Assume I have, say, the models OrderedList and Item, and I want to be able to insert Item()s into an OrderedList() at a specific index, I want to be able to retrieve the Item()s of an OrderedList() in their order and also to change the order of Item()s on an OrderedList
I already found Define an order for ManyToManyField with django and https://github.com/gregmuellegger/django-sortedm2m
Both the github repo and the accepted answer in the SO question are working with the same architecture: They create an additional integer field, say order_index, on the junction ("Through") table which represents the position of the Item() on the OrderedList().
Honestly, I do not like that too much. If I see this correctly, having the order stored on the junction table can create inefficiency when I want to reorder Item()s: Imagine, I want to change the position of an Item() on an OrderedList() which has n Item()s. This means O(n) database updates to reorganize the order indices.
I would like to avoid this. I think of an architecture where I have an ordinary many-to-many-relation and one additional column on the OrderedList table which holds a list of Item ids, say items_order. In this architecture, I need one database update and one list operation on items_order - which should be way faster, I guess.
I believe the best way for this is to create a custom model Field. The docs state how to create a custom model Field (https://docs.djangoproject.com/en/2.1/howto/custom-model-fields/) and I can create my items_order field like this. But I did not find how to make a custom Field which, besides creating the order_list, also creates the junction table and takes care of updating the items_order whenever a new related Item() is added or removed from the relation. I think, I should subclass the ManyToMany Field (https://docs.djangoproject.com/en/2.1/_modules/django/db/models/fields/related/#ManyToManyField). But I don't know how to do this, so could you give me some guidance here?

Update of QTreeView without changing selection

I have a model that retrieves data from a table in a database from a certain SQL query, and shows the items in a QTreeView. The characteristics are:
the data comes from a table, but has an underlying tree structure (some rows are parents that have rows below them as children)
this tree structure is shown in the QTreeView
the children are selectable in the QTreeView (not so the parents)
the table in the database gets updated continuously
in the updates, a children can be added to any existing parent
periodically (with a QTimer) the QTreeView is updated with the contents of the table
Since the children are added at any time to any parent, the first silly approach when updating the QTreeView is clearing it all, and append all the rows again, in form of parent or children, to the QTreeView. This is a 0-order approximation, and it is indeed terrible inefficient. In particular, the following problems appear:
Any existing selection is gone
Any expanded parent showing its children is collapsed (unless ExpandAll is active)
The view is reset to show the very first row.
What is the best solution to this problem? I mean, the first solution I will try will be not to clear the QTreeView, but instead parse all the returned rows from the table, and check for each of them whether the corresponding item in the QTreeView exists, and add it if not. But I wonder if there is a trickiest solution to engage a given table in a database with a QTreeView (I know this exists for a QTableView, but then the tree structure is gone).
This thread mentions a general approach, but this might get tricky quickly, but I am not sure how this would work if the underlying model is changing constantly (i.e. the QModelIndex becoming invalid).
Worst case is that you will have to write your own mechanism to remember the selection before updating and then re-applying it.
I assume you use some model/view implementation? You could enhance your model with a safe selection handling, in case the example mentioned above does not work for you.
I guess this is the case for a self-answer.
As I presumed, after a careful analysis of what data is retrieved from the database, I had to do the following "upgrades" to the retrieval code:
I retrieve, along with the fields I want to show in the view, two identifiers, one for grouping rows and one for sorting items into groups
I also retrieve the internal record ID (an increasing integer number) from the table in the database, in order to ask only for new records in the next retrieval.
In the model population code, I added the following:
I first scan the initial records that may belong to existing groups in the model
When, in the scanning, I reach the last group in the model, this implies that the rest of retrieved records belong to new groups (remember the records are retrieved sorted such that items that belong to the same group are retrieved together)
Then start creating groups and adding items to each group, until we use all the records retrieved.
Finally, it is very important:
the use beginInsertRows() and endInsertRows() before and after inserting new items in the model
capture the sorting status of the view (with sortIndicatorSection() and sortIndicatorOrder()) and re-apply this sorting status after updating the model (with sortByColumn())
Doing that the current position and selection in the QTreeView receiving the model updates are preserved, and the items in the view are added and the view updated transparently for the user.

How to signal QAbstractItemModel::dataChanged with changes to few columns only?

After updating the model data of my derived class of a QAbstractItemModel, I do not want the listeners (assume a QTreeView) to completely update all of the tree representation, but only the data in an individual column of most of the rows.
Example: I want to only update the second column of my items, so I thought I had to emit dataChanged(createIndex(0,1,&root), createIndex(rowCount(),1,&root);
However, this still updates the complete tree for some reason - is that expected behaviour? Does it have to do with the underlying model organisation (which works very similar to Qt's own simple tree example)? Or does it have yet other reasons I did not consider?

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.

How to display two Qt models using external data in a single tree view

I'm working on a C++/Qt project. I have two business models (one is a hierarchical tree-like structure, i.e. film categories/sub-categories, and the other one is a simple vector, i.e. film titles which can belong only to a subcategory) and I want to display both in a unique tree-view, where leaf nodes can belong to both models and non-leaf nodes belong to the first model. In addition to this view, I also want to display in model specific views, a tree-view for the first model and a list view for the second one.
I've considered 3 approaches:
1) Create one QAbstractItemModel for each business model and another one to represent the mixed model. Thus, each view is associated with only one model.
2) Create only two QAbstractItemModel for each business model and implement a special view that deals with that information.
3) Use a QStandardItem model and implement subclasses of QStandardItem for both my business model elements.
Because I'm working with external data, I don't want to duplicate any information if possible.
What do you think is the best/proper approach to follow? Any implementation advices?
QDataWidgetMapper is your best bet in that one.
Make one model of your data. I would make one tree model, which is option c of your choices, but could be implementing your own tree model.
For the list view, see if you can't set the tree model on it as well, and use setRootIndex to only show the list of items you want to see. I know it works on table and tree views, so I assume it would also work on a list view.