This is the challenge I am working on. Would be happy about suggestions:
Challenge: I have a window with up to 20 ListBoxes (approx. 2000 cells) on different TabPanels which display stock quotes. Whenever a price changes, the Cell.Text and Cell.TextColor of that changed price should change. Nothing in the Listbox should flicker and appear to be changed, only the cell of the changed price.
Current solution: A Thread receives updated quotes every 5 seconds in the background. Every 5 seconds a timer script runs and updates the Listboxes with the new prices. The script iterates over all cells in all ListBoxes and cell by cell compares if a value has changed. If it had, the cell value and TextColor is changed.
Problem with current solution: It takes 1-2 seconds to update every single cell. During that time, the application is not responding.
Thus, I see the following options to go forward:
a) outsource the comparison of old and new value change to the Thread and let the script only repaint the ListBoxes (this should decrease the running time of the UI change script)
b) Pause the UI update for some milliseconds to allow clicks etc. (If I am not mistaken, animations work like that, too)
Is there any “best” way on how it is supposed to be done? Or maybe another better alternative?
The timer should update a limited number of cells at a time. The first time it fires, it updates cells 0-100, the next time it updates 101-200, etc. That frees the UI to process clicks and update the display without pausing or flickering. Basically, while the code in the timer is running, the UI is frozen. Limit the duration of the timer Action event.
You say that you iterate over all cells to see if the price has changed. That means that you compare the Listbox cells with something. (And I guess the cell-by-cell comparing is blocking the 1-2 seconds)
How about you make another ‘something’ that contains the current data of the Listbox. That way you can compare the new data with the current data (still within the thread) and you don’t have to use the Listbox to figure out what has changed.
And maybe then store the changes and use a CallLater so that another method can update the changed cells.
To me Marco points in the right direction, prevent any iterating through cells and don’t use them as data store. Use DB instead what gives you additional boni when shutting down your app. If you would add a sample, we all could do a little optimizing session.
In memory sqlite databases are brilliant for this type of project. It is also not necessary to update listboxes on panels other than the currently selected one. Trigger a refresh on tabpanel.change only.
As @Tomas Jakobs says there seems to be plenty of opportunity to optimize the UI refresh.
Summarizing it, this is the solution:
a) Only change ListBoxes on visible Tabs (with TabPanel.change)
b) Do not use ListBox as data storage, but a non visible “storage” class or a (in memory) sqlite database, so that the Thread can do more work in the background
c) If the UI Update still takes too long, pause it repetitively for 1ms to prevent freezing
Another thing you can do is set listbox.visible=False while refilling the listbox, then =True when you’re done updating. It makes the updating much, much faster, and I don’t recall seeing any flicker.
I know you already have your solution but here is what I’ve found.
I have successfully filtered and updated a listbox filtering 50k items with 3 columns.
Use an in memory database/SQL to create a record set containing the data to show in the listbox.
Always ListBox.DeleteAllRows then addrows with new data.
Restore scroll position and listindex.
The result is almost instant with no visible flicker (Even on windows). I rarely loop to change listbox cells, with lists containing more than a dozen rows.