QTableView preserve selection after model refresh - c++

I tried to build a user interface which displays the content of a table while the data is refreshed every second.
Therefore I have a chain of models:
QSqlTableModel - to access the tables content
MyModel - inherited from QIdentityProxyModel to modify the data a bit (source is the TableModel)
SomeFilterModels - which have MyModel as source
This chain ends in a QTableView. Because the QSqlTableModel is refreshed every second,
any selection in the TableView is removed every second also. Now I had two Ideas to fix this.
Prevent the TableModel from detecting changes. Which was not working very well.
Catching some events fired before and after the model is about to change to store and restore the current selection. Sadly the QIdentityProxyModel does not forward signals like modelAboutToBeReset or modelReset or dataChanged .. it is also impossible to reemit those signals from MyModel because they are private.
I was searching for other ways to counter those refresh problems without success. But I can't imagine that I am the first person who uses a chain of proxy models combined with a periodic model refresh and selections.
Can anyone give me some tips?
Thanks in advance.
Maybe worth to note:
One QSqlTableModel is used for many TableViews. (With a different FilterProxyModel chain.) So I can't just stop refreshing because one View has a selection.
You may think that I know when I call the models refresh method. But for now it is a bit to complicated to pass this trough my ui architecture. I mean the model is updated and the TableView has already a connection to the updated model through some ProxyModels. There should be no need for another way of communication.
Hope my question makes sense.

QAbstractItemModel includes a number of signals that can help you know when the data in the model is or will be changing. In particular, it has the following signals:
dataChanged
headerDataChanged
modelAboutToBeReset
modelReset
columnsAboutToBeInserted
columnsAboutToBeMoved
columnsAboutToBeRemoved
columnsInserted
columnsMoved
columnsRemoved
rowsAboutToBeInserted
rowsAboutToBeMoved
rowsAboutToBeRemoved
rowsInserted
rowsMoved
rowsRemoved
Given that you lose the selection, I assume that the bolded signals are the ones you want to be concerned with, because the default Qt behavior is to preserve selection if they can where the columns or rows are removed/inserted and it doesn't affect the selection.
Once you are connected to these signals, in modelAboutToBeReset I suggest getting IDs for the cells that you can reuse to select them again, and in modelReset then using those IDs to get the QModelIndexs and using them to again select the same cells.

Related

Display modified data from QAbstractListModel in QTableView

I have a QAbstractListModel that has a bunch of custom objects stored in it, and you can access the different fields of the custom objects in the model by specifying a role (if this is an improper use of Qt roles let me know because I must be confused). I want to display this data in a user friendly QTableView. I can get things displaying using a proxy model, but the issue is I don't want to display the raw values, I want to display specific data derived from the raw data. So for instance, I don't want a column for both ItemA.foo and ItemA.bar, I want to display just ItemA.foo - ItemA.bar in a single column. And to add to that, I want the automatic update functionality you get with models where if either ItemA.foo or ItemA.bar change, I want the difference column to automatically update and recalculate.
I would think that the way to do this would be to use some kind of table proxy model that listens to the source model, and then populates its own fields with the values derived from the source model and listens for dataChanged() signals from the source model. Then you plug this proxy model in to a QTableView. But to me this sounds like something that should be done in a view. Or is this something that should be done by the delegate? I could even go so far as to do these calculations in the base model itself and add roles specific to these values that should be displayed in the table, but that sounds like I am really overloading the responsibilities of the model.
TLDR: How do you manipulate data from a model in a QTableView? Should I do the data manipulation in the base model and then send that to the QTableView? Should I use a proxy model that manipulates the base data and sends it to the QTableView? Or am I completely misunderstanding something?
and you can access the different fields of the custom objects in the model by specifying a role
If you look at the documentation for Qt::ItemDataRole, you would see that Qt models should indeed provide different data for different roles but each role means some distinguished purpose of the data corresponding to the role. For example, the most commonly used role is probably Qt::DisplayRole as the data for this role defines the content displayed in the view e.g. it's the text in the table cell. If you are satisfied with all the other aspects of the view - font, background etc - you can just return empty QVariant for corresponding roles from your model, the view would figure out these details on its own. If you are using roles as a substitute for columns i.e. to return different pieces of conceptually the same data item, it is probably not the intended use of roles.
For the other part of your question - you can customize the appearance of data displayed in the view through the use of a delegate. For example, you could subclass QStyledItemDelegate, override its displayText method to display ItemA.foo - ItemA.bar instead of just ItemA.foo and then set this delegate into the column of your view corresponding to ItemA.foo via setItemDelegateForColumn. The tricky part here would be to detect changes in both ItemA.foo and ItemA.bar columns which would affect the text displayed in the delegate. A while back I implemented a dedicated class in one of my projects which listens to changes in some column of the original model and "reroutes" the change into another column through signal emitting. I did it to solve this very issue - to catch changes in what delegate should display although technically another model column is affected into which the delegate is not set.

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?

Is Qt’s model–view system intended to work with one model or multiple models?

I’m trying to display some tabular data with a QTableView subclass and a QAbstractTableModel subclass. I can’t get the data to show up, but before I start really pounding on it I want to make sure that I’m using models in the way they were intended.
The data layer of my application periodically receives new data and distributes the data to the other parts of the application by calling slots like
void new_data_received(QSharedPointer<Measurement> measurement)
where Measurement is my data class. This allows the data to be passed around without being copied (some of my data classes are very large). Measurements are immutable; the table view that displays them doesn’t allow any editing.
Measurement is a subclass of QAbstractTableModel, so whenever I receive a new measurement I call set_model on my QTableView subclass instance with the new data as a parameter. (In the time before the first measurement is received there is no model set on the table view.)
Are Qt’s view classes intended to be used like this, with a new model being set every so often? Or should there be just one instance of the model class, with the same lifetime as the table view, that receives the new data and emits dataChanged? The latter seems like it adds unnecessary structure—at least in my case—but maybe that’s the way the system was designed to be used.
I don't think your Measurement class should be a subclass of QAbstractTableModel. It should represent raw data instead. So maybe a struct with some parameters or a list of structs will be a right type for your data class.
Then you should implement a custom model where incoming data are added to. So, when new data arrives that model will automatically update all the views connected to it. In this case new data affects directly your model only, not the views.
I suppose resetting view's model every time is not the right way to do what you want.

How to access another field/combo in the rowedittemplate during creation of a grid row when selectionChanged or checkValue happens?

The situation is as follows :
I am creating a row in the grid. I have several properties among which are one combo called 'department' and one field called 'name'. The business rule is that all the 'names' in a 'department' must be unique. The grid does not load all the department-name combinations so I have to make a call to the back-end. I want to make this call when
selectionChanged on the 'department' combo happens or
when 'checkValue' of the validator options of the 'name' filed happens.
This way I check when either changes. The problem is that this happens during creation and there are no rows in the datasource and no accumulated rows in the transaction log.
How can I access the fields of the 'rowEditTemplate' during creation during these particular events in order to check my values? Is there any other/better way to achieve this?
The editors are not created until you do the first edit. You could use the editRowStarted event to attach your editors logic. They are obtainable using the editorForKey method.
editRowStarted: function (evt, ui) {
var comboEditor = ui.owner.editorForKey("ProductDescription");
}
I created a small fiddle that assigns a data source for the combo on editRowStarted. It should work as a starting point for what you are trying to achieve.
http://jsfiddle.net/hfen0qea/

How to disable Autoscroll to top in a QTableView when the underlying model changes?

I have created my own class that derives from QTableView, the associated model is is derived from QAbstractTableModel. The data in the model is performance data(process name, mem usage etc.) which continuously gets updated with a short interval.
Updating the model is done by first removing all data (enclosed with a beginRemoveRows/endRemoveRows) and then "readding" the slightly changed data as a batch operation (enclosed with beginInsertRows/endInsertRows).
How do I disable the vertical autoscrolling to the top whenever I clear/readd?
Working solution
The scrollbar movement was due to signalling removing of all data using beginRemoveRows/endRemoveRows. In order to avoid this I instead emitted a dataChanged(...) signal with the start/stop indexes of the updated list of items if it was smaller than the existing model, and with indexes from the existing model if the updated list was bigger. I also used a begin/endRemoveRows if the updated list was smaller and a begin/endInsertRows if the updated list was bigger.