Append QStandardItem in two QStandardItemModel at the same time - c++

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

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 update a QStringListModel?

I have the following code:
QStringListModel* m=new QStringListModel(gc.get_lista_file());
ui->lista_immagini_listView->setModel(m);
where gc.get_lista_file() returns a QStringList object and lista_immagini_listView is a QListView.
I need to update my lista_immagini_listView adding a string when I press a button, but if I add my new string to my QStringList object it doesn't update my model (I read the QStringList is passed by copy, so it isn't connected to the model). So, I have to update my QStringListModel but in this way I have to update 2 object (QStringList and QStringListModel) and doesn't seem a good practice.
What is the best way (if exists) to resolve it?
QStringListModel does not allow you to simply add a string (sadly). Simply updating the QStringList does not work because the model stores a copy of the list.
There are basically two ways to get the desired behavior:
1. Reset:
This is the simple way. You just take the list from the model, add the string and reassign it:
QStringList list = m->stringList();
list.append("someString");
m->setStringList(list);
This method does work, but has one big disadvantage: The view will be reset. Any selections the user may have, sorting or the scroll-position will be lost, because the model gets reset.
2. Using the Model:
The second approach is the proper way of doing, but requires some more work. In this you use the functions of QAbstractItemModel to first add a row, and then changing it's data:
if(m->insertRow(m->rowCount())) {
QModelIndex index = m->index(m->rowCount() - 1, 0);
m->setData(index, "someString");
}
This one does properly update the view and keeps it's state. However, this one gets more complicated if you want to insert multiple rows, or remove/move them.
My recommendation: Use the 2. Method, because the user experience is much better. Even if you use the list in multiple places, you can get the list after inserting the row using m->stringList().
You need to only use the string list provided by the QStringListModel - don't keep a separate copy, use QStringListModel::stringList() for reading only. To modify the list, use the model's methods: insertRows, removeRows and setData instead of using QStringList methods.

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.

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.