How to avoid the Beachball

I’ve forgotten something again. Is there a way to avoid getting the beachball in a Mac app during a long loop? I’d rather not put it in a thread.

The only way is to thread it.

Rats. Okay, thank you.

Just curious, why does that response earn a “rats”?

Because I’m updating the contents of a Listbox, and doing that from a thread is about as awkward as it gets. One has to jump through a bunch of hoops to avoid ThreadAccessingUIExceptions. I also do not want this to be asynchronous. I need to manage events in a specific order. Lastly, once put in a thread, no matter how high the priority is set, the code functions slower than when it’s in the main thread. So, RATS.

Do your updating directly in a timer Action event handler. That will prevent the beachball just as well, without the thread.

It is relatively easy to do a loop in a multiple timer :

[code]const start as integer = 0
const max as integer = 100
static pointer as integer = start

if pointer < max then
//do stuff
pointer = pointer + 1
else
me.mode = Timer.ModeOff
pointer = start
end if[/code]

That works, but the result is astonishingly, unuseably slow.

I want it to work as fast as possible, synchronously, without showing the $%^&* beachball. Using a thread slows it down and makes things so awkward the programming logic barely hangs together. A timer makes it so slow I might as well alert user to go make a sandwich while waiting for the listbox to update.

Is there really no way to just suppress the stupid beachball?

I suppose what I will have to do is show a progress bar. I have been avoiding it because this is one of those cases where the time to wait is really too short to show a progress bar, but it’s also too long to show the beachball. The beachball is a bad sign. It makes users (and me) nervous. I want to do everything I can to avoid its appearance. Unless anyone else has a better idea, I guess a progress bar it will have to be.

[quote=388483:@Aaron Hunt]That works, but the result is astonishingly, unuseably slow.

I want it to work as fast as possible, synchronously, without showing the $%^&* beachball. Using a thread slows it down and makes things so awkward the programming logic barely hangs together. A timer makes it so slow I might as well alert user to go make a sandwich while waiting for the listbox to update.

Is there really no way to just suppress the stupid beachball?[/quote]

Maybe you have set the period too long ?

Also, you can speed things up if you have many iterations by performing a small for next loop in each Action event. If you don’t go over, say, 10 iterations (I suspect 100 or even 1,000 would be OK), you should not see the beach ball.

[code]const start as integer = 0
const max as integer = 100
static pointer as integer = start

if pointer < max then
For i as integer = 1 to 10
//do stuff
pointer = pointer + 1
if pointer = max then return
next
else
me.mode = Timer.ModeOff
pointer = start
end if[/code]

If you make a ListBox invisible it considerably speeds up the population of the rows so you might be able to come up with an elegant Loading… type gizmo.

Disable the scrollbars.
Load empty rows in a tight loop, without doing any data access.
enable the scrollbars

In the cellTextPaint event, if the text is blank, get the real data and change the text.
That way you only ever have data to actually get at any one time.

No, it was set to 0.

That’s a good strategy. I tried it too, and it helps, but it’s still too slow. So slow that the progress bar makes the most sense now.

Thank you all for your help!

It’s really just one hoop, and it’s not a big one. Do this:

  • update all of your data structures in the Thread.
  • when it’s ready to be displayed, fire a one-shot timer that calls Listbox.Invalidate
  • the listbox should get the data from this shared data structure.

That can be a little more tricky, hard to say. It’s very doable though.

Properly threaded code will run nearly as fast as code in the main event loop. There shouldn’t be any noticeable performance penalty.

I wonder if you are doing things in an inefficient manner?

If you just need to update a listbox fast, there are performance optimizations discussed here: https://forum.xojo.com/46439-fill-listbox-from-database-quickly/0

Steve Wilson, Jeff Tullin, and Michael Diehr – pardon, I had replied above before seeing your messages. Those are all good ideas, and before moving to a progress bar I am looking into your suggestions. Thank you.

Note that a progressbar won’t magically change anything. You’d probably just end up with both a progressbar and beachball showing, unless you use a thread or timer. And the progressbar wouldn’t “progress” until your tight loop in the main thread completed anyway.

[quote=388488:@Jeff Tullin]In the cellTextPaint event, if the text is blank, get the real data and change the text.
That way you only ever have data to actually get at any one time.[/quote]

Or if the text can become stale, don’t ever populate the text at all. Only paint current current data each time a cell needs to be drawn. You are still only dealing with (at most) visible cells, so generally speaking should be fast enough to avoid beachballs.

Last I knew the Xojo Listbox would not fire the CellTextPaint event if the cell had no text though, so you needed to populate them with a blank. That may have changed, as I tend to use the piDog DataView now as it has so many features I have come to enjoy.

Kem also has a very good data on demand listbox class. I’ve even used it while pulling data from a remote Postgres database and it is still amazingly fast, even if you drag the vertical slider quickly.

I know how to handle progress bars so that no beachball appears. Forced periodic updating of the progress bar normally solves the problem, letting things “breathe” as it were. The end result is also slower, of course.

Yes, I’ve used Kem’s DODListbox in other projects and it works brilliantly, backed by a database. In this case I’ve got other requirements for the list. There are a lot of good points above I’m working with now.

Ohhhh lord I hope you don’t mean DoEvents.

No, although I have used that before, I’m aware that it’s frowned upon. DelayMBS with a small value normally works in similar situations. It obviously slows things down, since that’s exactly it’s function.