Can I get all instances of some class in Qt? - c++

I have implemented QAbstractItemModel and QSortFilterProxyModel with it as a source model. I have one itemModel instance and several filterModel instances. In each filterModel I have some variable value of which is calculated based on filtered data from itemModel. When itemModel is recreated (all items removed and the new ones are inserted back) value of this variable should be set to default (or just be recalculated after filtering is done).
The problem is that I don't have some list of pointers to filterModel instances to iterate by them. Sure I can create such static list in constructor. But probably MOC did it instead of me and saved such list inside of QMetaObject of this class?

Your filter proxy implementation is broken. The proxies are fully informed of each and every change to the model - that's why the models are QObjects and emit change signals! There's nothing preventing you from changing the "some variable" as appropriate, when the source model indicates that it has changed.
You need to think of your "some variable" as depending on some data (and structure!) from the source model. Every time the source model indicates changes to the dependent data or structure, your proxy must recompute the value - or at least set a flag so that the value might be recomputed lazily when needed.

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 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.

Supporting multi-add/delete (and undo/redo) with a QAbstractItemModel (C++)

Greetings,
I've been writing some nasty code to support the undo/redo of deletion of an arbitrary set of objects from my model. I feel like I'm going about this correctly, as all the other mutators (adding/copy-pasting) are subsets of this functionality.
The code is nastier than it needs to me, mostly because the only way to mutate the model involves calling beginInsertRows/beginRemoveRows and removing the rows in a range (just doing 1 row at a time, no need to optimize "neighbors" into a single call yet)
The problem with beginInsertRows/beginRemoveRows is that removal of a row could affect another QModelIndex (say, one cached in a list). For instance:
ParentObj
->ChildObj1
->ChildObj2
->ChildObj3
Say I select ChildObj1 and ChildObj3 and delete them, if I remove ChildObj1 first I've changed ChildObj3's QModelIndex (row is now different). Similar issues occur if I delete a parent object (but I've fixed this by "pruning" children from the list of objects).
Here are the ways I've thought of working around this interface limitation, but I thought I'd ask for a better one before forging ahead:
Move "backwards", assuming a provided list of QModelIndices is orderered from top to bottom just go from bottom up. This really requires sorting to be reliable, and the sort would probably be something naive and slow (maybe there's a smart way of sorting a collection of QModelIndexes? Or does QItemSelectionModel provide good (ordered) lists?)
Update other QModelIndeces each time an object is removed/added (can't think of a non-naive solution, search the list, get new QModelIndeces where needed)
Since updating the actual data is easy, just update the data and rebuild the model. This seems grotesque, and I can imagine it getting quite slow with large sets of data.
Those are the ideas I've got currently. I'm working on option 1 right now.
Regards,
Dan O
Think of beginRemoveRows/endRemoveRows, etc. as methods to ask the QAbstractItemModel base class to fix up your persistent model indexes for you instead of just a way of updating views, and try not to confuse the QAbstractItemModel base class in its work on those indexes. Check out http://labs.trolltech.com/page/Projects/Itemview/Modeltest to exercise your model and see if you are keeping the QAbstractItemModel base class happy.
Where QPersistentModelIndex does not help is if you want to keep your undo/redo data outside of the model. I built a model that is heavily edited, and I did not want to try keeping everything in the model. I store the undo/redo data on the undo stack. The problem is that if you edit a column, storing the persistent index of that column on the undo stack, and then delete the row holding that column, the column's persistent index becomes invalid.
What I do is keep both a persistent model index, and a "historical" regular QModelIndex. When it's time to undo/redo, I check if the persistent index has become invalid. If it has, I pass the historical QModelIndex to a special method of my model to ask it to recreate the index, based on the row, column, and internalPointer. Since all of my edits are on the undo stack, by the time I've backed up to that column edit on the undo stack, the row is sure to be there in the model. I keep enough state in the internalPointer to recreate the original index.
I would consider using a "all-data" model and a filter proxy model with the data model. Data would only be added to the all-data model, and never removed. That way, you could store your undo/redo information with references to that model. (May I suggest QPersistentModelIndex?). Your data model could also track what should be shown, somehow. The filter model would then only return information for the items that should be shown at a given time.