QTableWidget performance optimization - c++

I have a QTableWidget that can display a huge number of elements (like, 20000 elements). Displaying per se and scrolling up and down works find, however, populating the widget works extremely slowly. I found out that constructing the QVector of elements (strings) that are displayed works very fast, however, inserting the elements into the QTableWidget is very slow.
I need to implement filtration over the elements, so if the user filters half of the elements out with a wildcard, it's still necessary to clean the QTreeWidget and insert 10000 elements back (or hide 10000 elements which is equally slow). Reasonably fast performance is critical here because the user can't wait for several minutes every time he presses a button.
Valgrind doesn't help much as apparently much of the resources are eaten by some implicitly called functions, particularly QHeaderView::sectionSize() and QHeaderView::isSectionHidden()

Migrate Your code to model-view pattern.
Create a model (subclass QStandardItemModel) and place all Your data there.
Display all the data in QTableView, ensure everything is OK
Now, You can use QSortFilterModel model for fast data-filtering, or you can subclass QProxyModel for more complex filters.

Related

How can I loop only the page records from the selected one to the latest?

I'm trying to loop all records displayed in a page, from the selected one to the end of the rows:
For example here, as I'm selecting only the 5th row it will loop through 5th and 6th row (as there are no more rows below)
What I've been trying is this:
ProdOrderLine := Rec;
REPEAT
UNTIL ProdOrderLine.NEXT = 0;
But it will loop through all records in the table which are not even displayed in the page...
How can I loop only the page records from the selected one to the latest?
Try Copy instead of assignment. Assignment only copies values of there field from one instance of record-variable to another, it died not copy filters or keys (sort order).
Alas, I have to mention that this is uncommon scenario to handle records like this in BC. General best practice approach would be to ask user to select all the records he or she needs with the shift+click, ctrl+click or by dragging the mouse. In that case you will use SetSelectionFiler to instantly grab ask the selected records.
This is how it works across the system and this how user should be taught to work. It is a bad idea to add a way to interact with record that only works in one page in the whole system even if users are asking for it bursting into tears. They probably just had this type of interaction in some other system they worked with before. I know this is a tough fight but it worth it. It is for the sake of stability (less coding = less bugs) and predictability (a certain way of interaction works across all the pages) of the system.

QListWidget performance with many custom items

I have a list with about 2500 custom items. I set them with:
const std::vector<const Items::AbstractItem *> results = _engine.request(text);
if (!results.empty())
{
for (auto i : results){
QListWidgetItem *lwi = new QListWidgetItem;
_results->addItem(lwi);
ListItemWidget *w = new ListItemWidget;
w->setName(i->name());
w->setTooltip(i->path());
_results->setItemWidget(lwi, w);
}
_results->setFixedHeight(std::min(5,_results->count()) * 48); // TODO
_results->show();
}
This takes about 5 seconds on an i5-4590. Hiding the widget is twice as fast. Is this normal or do I have look for errors?
A few ideas:
Try assigning proper parents to your QWidgets, thats way the layout doesn't have to do this
mapping for you. This should help performance.
Call setUpdatesEnable(false) before starting the insert, and to true after it's done
As for hiding the widget while adding large amounts of items, this will help to alleviate extraneous update calls. The second suggestion above should mitigate that.
I think this is fully expected behavior for controls like Lists or Trees that are not based on any data model. And I believe that the data model was invented mainly to fix this issue.
In your situation you have a ListWidget control that stores its data on its own. You need to pass all 2500 items before your app can go on, and you need to do this even if your list shows only 10 items at a time. Even if you just run and close your app, the user won't see all the items but you still need to pass them to your ListWidget. Some GUI frameworks use internal allocation of items and in such case they can optimize things a bit, you could do the same if you allocated your Items in chunks but it's still not a good solution.
Now let's say you introduce some object that could be asked about item properties. The Control will ask about some item and your object will respond with the contents. Your object don't even need to know about all your items, it will just learn when needed.
You Control can ask about few first items and stop when it realize it can fill up its entire height. This way you can avoid work that is not needed for now. The Control can also ask about the item count, so it can set-up its vertical slider.
It needs to be said that the model will not solve your problem automatically, it's just a programming paradigm that allows you to do it better.
So the solution for you would be to replace your QListWidget with a QListView and implement you own data model inheriting QAbstractListModel. You could pass the results to the model and it will pass the items data when needed.
If your QListWidgetItem's always has fixed size, call setUniformItemSizes on your QListWidget, pass true.

How to make a wide virtual Tree-View Control with only two levels fast?

I basically want to have the same virtual performance I can get with a List-View control. With a List-View control you can set an ItemCount and in the LVN_GETDISPINFO notification you then fill in the information for the items once they are scrolled visible.
Now, the virtual functionality the Tree-View provides is good for very deep trees, so you would only add items once a node expands (via TVN_ITEMEXPANDING), and TVN_GETDISPINFO can be used for filling in item information once the item is scrolled visible. But what to do if you have an "always expanded" two-level tree (just for design matters) where TVN_ITEMEXPANDING wouldn't be of any use and only want to add the items once they would be visible. The problem is, there's no such thing as SetItemCount() or similar to already resize the tree.
In my case, the filling of item information (text, image, selected image) isn't the expensive part, but the inserting of items (all at one level) is.
One option would be to only insert the items which would be visible plus one invisible one, once the invisible one gets visible (detected in TVN_GETDISPINFO), I'd insert a few more and so on. But then the scrollbar would always get smaller the more I scroll down, I think that's weird.
Are there any other ideas to achieve what I want except from drawing my own control?
The whole tree would just look like this, pretty much a list, it's just that I like the tree-look.
RootNode
|
|--Item 1
|--Item 2
|--Item 3
|--Item 4
|--Item 5
|--Item 6
|--Item 7
...
|__Item 1000
As stated in many other posts, the really expensive part about the Tree-View control is using InsertItem and DeleteItem. A quick way to improve performance for those operations is making use of SetRedraw. Not only does it hide the flickering but does really speed things up, since the drawing seems to be expensive - even though TVN_GETDISPINFO is used.
Also, it's faster to rename existing items and change their data instead of deleting and adding new ones. So when I have a big list and know that the next update will contain about the same amount +/- a couple ones, I go iterate through the items, rename them, change their lparams and sync (i.e. remove/add) the remaining ones in accordance to the new data. Depending on the size of the list, making those extra calculations can have a huge performance improvement.
The Win32 TreeView control does not support the kind of virtual mode you are looking for. So you will need a custom control.

Adding row to QTableView slows the app down after a lot of inserts

I have a QTableView thats using a model i made which extends the QAbstractTableModel. The model uses a
QList< QVector<QString> * >
as the collection. The table view is used to display logging messages from the application so the collection will eventually get extremely large... After it inserts a few thousand rows i notice the table view starts to slow down a lot and eventually the view freezes for a few seconds before refreshing.. Is it the type of collection im using thats making it slow down soo much? Is there a better way to store the data thats being inserted? Does the QTableView support a large amount of data?
Edit
Posted code on Qt forumn:
http://www.qtforum.org/article/37326/qttableview-slows-down-when-a-lot-of-data-is-inserted.html
I have successfully used QTableView displaying ~10000 rows so QTableView is capable of supporting it but your collection leaves to be desired.
QList is very expensive to insert in the middle since you have to reallocate everything sitting below the index you are trying to insert at granted you are only shifting pointers around but still.
Normally for data storage I would use an std::vector< data_struct * > rather then using vectors of strings. QVariant is quite capable of presenting integers and other types so you don't have to do the conversion up front.
The best suggestion that I can come up with would be to run gprof or a similar tool to time where exactly you are spending time and then address that particular piece.

most effective row removal strategy for QStandardItemModel

I have a QStandardItemModel with several 100,000 records of data, and a QSortFilterProxyModel on top of it for filtering and sorting capabilities. I want to remove a substantial number of records, say 100,000, based on the value of one of the columns.
The current implementation iterates over the source model, tests for the value in the appropriate column, and calls removeRow. This turns out to be an extremely slow approach, I don't know why (I've already turned off the signalling of the source model and the sortfilterproxymodel).
What is a more efficient approach?
Can the QSortFilterProxyModel help, e.g. by creating a selection of records to be deleted, and using removeRows?
Thanks,
Andreas
QAbstractItemModel::removeRows() is a candidate, provided that the rows are contiguous. If the model is sorted by the column you are using to do the removal test, then you should be able to use this.
The more effecient approach would be implementing your own model with QAbstractItemModel interface instead of using QStandardItemModel. Then you can build custom indexes which will help you increase performance while deleting items.