PDA

View Full Version : QTreeView: How to refresh the view?



Kira
30th March 2012, 10:22
Hi,

I am using a QTreeView with an QAbstractItemModel to display my data.
My data changes sometimes and I need to inform the QTreeView about the changed data, how do I do this?

For example, when I first call setModel() on the QTreeView there is no data.
After this I add ~20 items to the data list.. but the QTreeView doesn't refresh...

I tried to emit "dataChanged" and "layoutChanged", but it didn't work.. my current workarround is:


treeView->setModel(NULL);
treeView->setModel(&model);

what's the correct way to do this?

Kira

wysota
30th March 2012, 10:26
You have to use beginInsertRows() and endInsertRows() while adding data to the model.

Kira
30th March 2012, 10:33
Okay, will try this.

And is there a way to do a complete refresh? Because sometimes there isn't only a simple row insertion, but a complete update of the whole data.

aamer4yu
30th March 2012, 11:12
If you see doc for beginInsertRows and endInstertRows as Wysota said, you will notice its ROWS, not ROW.
So you can specify the indexes accordingly.

wysota
30th March 2012, 11:15
And is there a way to do a complete refresh?

Yes, there is. Look at the methods and signals available for QAbstractItemModel.

Kira
30th March 2012, 11:28
I did look at the methods and signals of QAbstractItemModel already, all I found was "dataChanged" and "layoutChanged".. but this didn't work (Or I used it wrong?).

About beginInsertRows/endInsertRows: When I am adding 20 rows where each row has 10 children.. how would I call beginInsertRows? What do I pass as parent? The rows I am adding in a batch have different parents. Or do I call this method only for the top-level hirarchy?

wysota
30th March 2012, 12:18
I did look at the methods and signals of QAbstractItemModel already, all I found was "dataChanged" and "layoutChanged".. but this didn't work (Or I used it wrong?).
Then look again, I see 18 signals in the class.


About beginInsertRows/endInsertRows: When I am adding 20 rows where each row has 10 children.. how would I call beginInsertRows?
If you do that in one go then you just call it for the parent rows. The view will notice the children by itself then.


The rows I am adding in a batch have different parents. Or do I call this method only for the top-level hirarchy?

You can call those functions more than once, you know.

Kira
30th March 2012, 12:41
Wysota, why do you count the signals instead of simply telling me which one I need?!

I know there are 18 and as I said I already tried the two that make sense for me: dataChanged() and layoutChanged() (of course including layoutAboutToBeChanged() as it's written in the docs).. so am I missing something? If yes, would you please be so kind and tell me?

wysota
30th March 2012, 12:54
Wysota, why do you count the signals instead of simply telling me which one I need?!
I already told you once and you were not able to extrapolate on the knowledge you were given. I will tell you now again and then you will come back in two hours asking what to do if you want to delete rows from the model. The docs are not that long, it's much quicker to read through the whole page instead of bouncing around forums and waiting for people to point you back to the docs. It's also much quicker to look at one of the numerous great examples available in the docs.


I know there are 18 and as I said I already tried the two that make sense for me: dataChanged() and layoutChanged() (of course including layoutAboutToBeChanged() as it's written in the docs).. so am I missing something? If yes, would you please be so kind and tell me?

An obvious one to try would be modelReset(), wouldn't it? If you start there, you will certainly come up with a proper sequence of calls you need to make to reset a model, especially since you were already given information on how to add rows to the model.

philw
25th November 2014, 02:17
wysota's responses like this come up on google searches. They should all be deleted. Almost always useless in trying to find a quick answer to a well formulated question.

anda_skoa
25th November 2014, 05:33
wysota's responses like this come up on google searches. They should all be deleted. Almost always useless in trying to find a quick answer to a well formulated question.

I am afraid I don't understand.
What would a search result of a question without answers be good for?

Cheers,
_

d_stranz
25th November 2014, 21:01
Wysota:

Join Date Jan 2006
Posts 31,660

This sort of speaks for itself. I think Wysota can be forgiven if he sometimes doesn't respond well to people who expect to be spoon-fed code in answer to their questions.

ttrop
19th December 2014, 21:11
I know this question was originally asked something like 2.5 years ago, but I've recently been trying to teach myself Qt (PyQt, actually), and I had this same question, which is how I found this post. Anyway...

In my case, I periodically update an underlying data set with raw data. My QAbstractItemModel-derived data model gets its data from this underlying data set. So, I ran into this issue of trying to figure out how to get the view to update every time the underlying data was updated. After a couple days of researching this on the web and trying various things, I came up with the very simple approach of just adding a method, called emitDataChanged(), to my model subclass. Here's the Python code for this method:

def emitDataChanged(self):
self.dataChanged.emit(QModelIndex(), QModelIndex())

From what I understand, this signal tells the view that "all the data has changed", so the view repaints everything. In the code that updates the underlying data, once all the data has been updated, I just call this method on the model, and the view automagically updates.

The nice thing about this approach is that the current state of the view remains intact. The approaches I tried in the beginning involved the whole beginModelReset/endModelReset thing, and although that caused the view to update, it also caused any expanded nodes to be collapsed, such that only the top-level nodes were visible after the update. It also caused the currently selected item to be lost. Using the dataChanged.emit(...) approach caused the view to behave exactly as I wanted it to: just repaint the whole thing. No selection lost, no nodes collapsed.

So that's it. Just thought I'd share in the hopes that this might help someone else in the future. Good luck.

t_3
24th January 2015, 19:39
I know this question was originally asked something like 2.5 years ago, ....
another 3.5 years later i'd like to thank you for sharing this find. i also had a case where some underlying data constantly changes, but even the structure of the tree stays static. now a single one-liner allows updating hundreds of rows (driven by slider inputs) without the need to recreate the whole thing...

wysota
24th January 2015, 23:32
Just remember this is ok-ish for some models (and their use) but not for others. It's not a magic wand you can use in any case. There is more to it than redrawing a view when a model is modified.

enderson
20th March 2015, 07:23
Another 3.5 years late also I'd like to thank you very much for sharing this. What a wonderful method!

neuronet
11th October 2015, 02:56
You can also do model.reset() which forces all the views to update themselves, refetch all the visible data.

Emitting the dataChanged signal is better if you are just making minor changes.

This is from Summerfield's PyQt book:

Sorting the data makes all model indexes invalid and means that the views are now showing the wrong data. The model must notify the views that they need to update themselves by retrieving fresh data. One way to do this is to emit a dataChanged() signal, but for big changes it is more efficient to call QAbstractTableModel.reset(); this tells all associated views that everything is out-of-date and forces them to update themselves.

Note: Use beginResetModel() and endResetModel() instead whenever possible. Use reset only if there is no way to call beginResetModel() before invalidating the model. Otherwise it could lead to unexpected behaviour, especially when used with proxy models.

neuronet
13th October 2015, 19:06
self.dataChanged.emit(QModelIndex(), QModelIndex())


Strangely when I run that from my model, my delegate doesn't actually call the sizeHint method, which is what I need, it only calls the paint method in my delegate. So this doesn't work for me. Calling model.layoutChanged.emit() works though.

t_3
30th November 2015, 23:11
Note: Use beginResetModel() and endResetModel() instead whenever possible. Use reset only if there is no way to call beginResetModel() before invalidating the model. Otherwise it could lead to unexpected behaviour, especially when used with proxy models.
very helpful & important advice;
when using proxy models this is imo the only way to properly update everything up to the view(s), when populating the model with all new content.


Strangely when I run that from my model, my delegate doesn't actually call the sizeHint method, which is what I need, it only calls the paint method in my delegate. So this doesn't work for me. Calling model.layoutChanged.emit() works though.
sounds reasonable; repainting the whole view (because of layoutChanged()) is probably more expensive than just repainting the cells, so one can decide what to do, depending how the inner data changes, or the layout is organized...