Progress bar issue in El Capitan

I have a ProgressBar in a window that I repeatedly update during a lengthy operation using the following code:

ctlProgress.Value = ctlProgress.Value + 1 ctlProgress.Refresh

This has worked fine for months, but in the El Capitan beta, the bar doesn’t update. It just sits there empty, then pops all the way to full when the process is finished. Anyone know if this is a bug in El Capitan or an issue with Xojo that is appearing in El Capitan?

Why are you calling Refresh? Are you doing work on the main thread that hangs the application until it’s complete?

You should put the lengthy process in a thread and update the ProgressBar periodically from the thread. But, because you can’t access UI elements directly, you need to use a timer to get it on the main thread. Two example projects available to look at: Examples/Desktop/UpdatingUIFromThread.

Neither one of these reponses seem to address the issue…

The OP says it works pre-ElCap, indicating to me, the ElCap has changes the message queuing system in some way.

Refresh should update the UI (from the main thread)…

Perhaps the poster doesn’t care if the main thread is blocked… sometimes the user just has to wait, in which case he wishes to update the Progress bar as part of the process.

Refresh normally says “do it now”, while Invalidate says “do it as soon as you can”

Dave, I think that if the UI updated, it’s just a consequence of the OS or REAL/Xojo being “nice”. Perhaps EC just is more hardcore. Joe, correct me if I’m wrong.

I know this has been discussed endlessly already, so I’m not asking for anything. I still can’t understand why REAL/Xojo doesn’t work more simply and intuitively (regardless of the internal explanations).

I get that TECHNICALLY the UI would refresh at the END of the function chain in Xojo (although often you catch a break, defnitely more often on Mac than Win), but that seems the very purpose of calling Refresh() -that would force the UI to update right-then-and-there dependably. Like it’s adding the Refresh() code inline to the function. Everything is on the main thread, it’s just that Refresh() essentially says “at this point of time, without going any further, the graphical codes is going to execute that updates the UI, THEN we will get on to more calculation.” If the point of a thread is a SERIAL processing of CPU use, then isn’t (or “shouldn’t”) calling Refresh() mean the above?

(Please don’t confuse any of this with updating the UI form a SEPARATE THREAD, I’m talking about how things are prioritized in the main thread.)

I just have never understood that about REAL/Xojo.

AppKit really expects the event loop to not be blocked and defers work until either the end of the current iteration or the beginning of the next one. I suspect that what’s happened here is that Core Animation layers get flushed to the window server differently in 10.11.

My point was that perhaps ElCAP doesn’t recognize “REFRESH” (or has changed the priority of the request)… and if that is the case there is little XOJO can do about it…

Remember OSX (and Windows, and Linux) are event driven processes… they are not serial execution processes. The sequence of events (from you point of view) are being interrupted by “priority” messages all the time (timers, mouse, OS level requirements).

which pretty much amount to ElCAP changing the priority… by flushing the cache… no follow on event can be higher than what it already has done. And isn’t that what the "evil, never to be used " “APP.DOEVENTS” basically did (ie. flush the event queue)

What does then mean for the REAL/Xojo user? Will a UI NEVER update until an idle point is reached in our apps? Does this mean that doing this:

For i = 0 To 800 [blah blah] ProgressBar1.Value = i / 800 Next

is completely pointless? And I take it that the only reason this worked previously is that the OS was being kind. It technically wasn’t supposed to work.

[quote=200108:@Garth Hjelte]What does then mean for the REAL/Xojo user? Will a UI NEVER update until an idle point is reached in our apps? Does this mean that doing this:

For i = 0 To 800 [blah blah] ProgressBar1.Value = i / 800 Next

is completely pointless? And I take it that the only reason this worked previously is that the OS was being kind. It technically wasn’t supposed to work.[/quote]

yes the code as you posted it completely pointless :slight_smile: and that SHOULD NOT have worked previously (without a REFRESH in the loop)

but as mentioned in the OP… adding REFRESH … SHOULD work (I’m agreeing with everything you have said so far),
but I think ElCap has changed its message handling, if this is NOT working, and if that is the case I doubt there is much XOJO can do at this point.

[quote=200108:@Garth Hjelte]What does then mean for the REAL/Xojo user? Will a UI NEVER update until an idle point is reached in our apps? Does this mean that doing this:

For i = 0 To 800 [blah blah] ProgressBar1.Value = i / 800 Next

is completely pointless? And I take it that the only reason this worked previously is that the OS was being kind. It technically wasn’t supposed to work.[/quote]

From my understanding of OS X’s graphics architecture, all drawing that ends up on screen is handled by the window server. The window server is responsible for managing and compositing surfaces from all of the applications in the current session onto the GPU using OpenGL (or Metal on 10.11).

Applications request a surface from the WindowServer for each window. All modifications to the surface are done in the application’s process. When the application is finished, the contents of the surface are flushed to the WindowServer and the new content appears on screen.

In addition, every Core Animation layer has its own surface from the WindowServer. Because these surfaces are independent of the window’s surface, the application can avoid redrawing the entire view hierarchy when the contents of a layer-backed view change. Just like the window content, a Core Animation layer’s content has to be flushed before it appears on screen.

RectControl.Refresh just calls through to NSView’s display method. It sounds like this method doesn’t force the surface to be flushed to the WindowServer for layer-backed views on 10.11, likely due to changes Apple made to unify AppKit and Core Animation’s layout and display cycles.

As a bonus, here’s what Apple has to say about synchronous updates:

[quote]When invalidating portions of your views, you should avoid using the display family of methods to force an immediate update. These methods cause the system to send a drawRect: message to the affected view (and potentially other views in the hierarchy) immediately rather than wait until the next regular update cycle. If there are several areas to update, this may result in a lot of extra work for your drawing code.

Instead, you should use the setNeedsDisplay: and setNeedsDisplayInRect: methods to mark areas as needing an update. When you call these methods, the system collects the rectangles you specify and coalesces them into a combined update region, which it then draws during the next update cycle.[/quote]

This has never worked reliably and should not be counted on, in my experience. Use a thread and yield time to the UI. Period.

Dave, sorry I meant to include the Refresh call.

Thanks Joe for the detailed explanation!

So for EC we’ll likely have to make code changes? I assume that most code that updates the UI without threading in this fashion uses Refresh(). Now will it be that if you do ANY calculation where you want to show progress, it’ll HAVE TO BE in a thread? Uisng Refresh() was good for quick and dirty things - which most things end up being. =)

[quote=200127:@Garth Hjelte]Dave, sorry I meant to include the Refresh call.

Thanks Joe for the detailed explanation!

So for EC we’ll likely have to make code changes? I assume that most code that updates the UI without threading in this fashion uses Refresh(). Now will it be that if you do ANY calculation where you want to show progress, it’ll HAVE TO BE in a thread? Uisng Refresh() was good for quick and dirty things - which most things end up being. =)[/quote]

It’s possible there are ways to force layer backed views to be flushed on 10.11, but I’ve not looked into it.

The code in question was never supposed to take that long, so it was never threaded. As things have grown, it now takes longer under some circumstances.

Sounds like the real solution is going to be to put this processing in a thread, so I guess that will have to wait until later, when I have time to deal with it. Also sounds like, for now, there really isn’t a solution if what I’m currently doing isn’t working.

While I’ve not seen this exact issue, I have a couple of situations where .invalidate doesn’t work any more with 10.11, refresh does work for me (but I’m not using it in a tight loop). From what I understand Apple wants everyone updating their apps to use layers. They make some things nicer, such as built in animation but they make complex interfaces much harder to work with.

If I were the OP, I would be looking at refactoring the code so that my application behaves as expected on the public beta. Do not expect any issues your app experiences to be resolved, this way when Apple push El Capitan to everyone your application should work as expected, if Apple make any changes or not (although we found out the hard way last year that the actual release had changes that the DPs didn’t, which broke our apps).

FWIW I was just testing the possibility to have timers fire while an event is not over, and tried App.DoEvents. It does allow the UI to update while an event is not over. ProgressBar included. Under El Capitan.

The best is to put the heavy processing in a thread and use a timer to update the bar, of course…