Thursday, October 17, 2019

#1 2019-09-13 12:59:02 pm

markconwaymunro
Member
Registered: 2017-11-28
Posts: 41
Website

Window Refresh Question

I have some intensive loops running through data and I am setting up a progress indicator and text showing the current item. However, these don't update/refresh the window. I tired numerous functions — update, display, displayIfNeeded, setNeedsDisplay, etc — and nothing seems to make the window or controls refresh. The only method I have discovered that works is if I put a .00001 or greater delay in the loop. But that seems ridiculous. Is there a better way to configure objects or code trigger so that the interface updates during a repeat loop?

Offline

 

#2 2019-09-13 06:13:25 pm

Shane Stanley
Member
From:: Australia
Registered: 2002-12-07
Posts: 5959

Re: Window Refresh Question

These days the screen is updated only at the end of each pass of the event loop. So what you need to do is tickle the event loop when you want the screen updated. You do this by effectively taking over its role. In theory, a handler like this is what you need:

Applescript:

on fordEvent()
   set theApp to current application's NSApp
   set theMode to current application's NSEventTrackingRunLoopMode
   set theMask to current application's NSAnyEventMask
   repeat
       set theEvent to (theApp's nextEventMatchingMask:theMask untilDate:(missing value) inMode:theMode dequeue:true)
       if theEvent is missing value then exit repeat
       theApp's sendEvent:theEvent
   end repeat
end fordEvent

There are two problems, though. First, NSAnyEventMask is NSUIntegerMax, which is too big to be an AS integer and too big to be represented with precision as an AS real. In practice, you can get by with a lesser mask.

The other is that, given this is likely to be called a lot, it makes more sense to define theApp etc as properties.

So:

Applescript:

property NSEventMaskDirectTouch : a reference to current application's NSEventMaskDirectTouch
property theApp : a reference to current application's NSApp
property NSEventTrackingRunLoopMode : a reference to current application's NSEventTrackingRunLoopMode

on fordEvent()
   repeat
       set theEvent to (theApp's nextEventMatchingMask:(NSEventMaskDirectTouch - 1) untilDate:(missing value) inMode:NSEventTrackingRunLoopMode dequeue:true)
       if theEvent is missing value then exit repeat
       theApp's sendEvent:theEvent
   end repeat
end fordEvent

Put calls to that in your repeat loop.


Shane Stanley <sstanley@myriad-com.com.au>
www.macosxautomation.com/applescript/apps/
latenightsw.com

Online

 

#3 2019-09-13 11:34:28 pm

technomorph
Member
Registered: 2017-12-14
Posts: 124

Re: Window Refresh Question

Shane so

Applescript:


aView’s setNeedsDisplay:true

Isn’t enough?
As I guess the view doesn’t update until the end of the loop?

Also how does the main window setting of:
“Automatically calculates event loop” toggle come into effect?
Or what does it do?   
I’ve tried turning it on and off and never really noticed any difference.

One thing I’ve been doing lately is making calls to set a new tab.
And then right after I make a call to a class function that takes a while.
The tab changes but the view doesn’t update until after the other function gets called

Offline

 

#4 2019-09-14 12:58:09 am

Shane Stanley
Member
From:: Australia
Registered: 2002-12-07
Posts: 5959

Re: Window Refresh Question

technomorph wrote:

Shane so

Applescript:


aView’s setNeedsDisplay:true

Isn’t enough?



No. All that does is mark the view as being in need of redrawing when the time comes. You use to be able to use displayIfNeeded(), but it changed a couple of versions of the OS back.
As I guess the view doesn’t update until the end of the loop?

Also how does the main window setting of:
“Automatically calculates event loop” toggle come into effect?



That's to do with the tabbing order when you tab from one field to another.

One thing I’ve been doing lately is making calls to set a new tab.
And then right after I make a call to a class function that takes a while.
The tab changes but the view doesn’t update until after the other function gets called



That's right. If you don't want the delay, call the latter part with performSelectorAfterDelay::: and a delay of 0.0. That means the second part is a new event on the run loop.


Shane Stanley <sstanley@myriad-com.com.au>
www.macosxautomation.com/applescript/apps/
latenightsw.com

Online

 

#5 2019-09-15 09:52:20 am

markconwaymunro
Member
Registered: 2017-11-28
Posts: 41
Website

Re: Window Refresh Question

To clarify, in this example:

Applescript:

property NSEventMaskDirectTouch : a reference to current application's NSEventMaskDirectTouch
property theApp : a reference to current application's NSApp
property NSEventTrackingRunLoopMode : a reference to current application's NSEventTrackingRunLoopMode

on fordEvent()
repeat
set theEvent to (theApp's nextEventMatchingMask:(NSEventMaskDirectTouch - 1) untilDate:(missing value) inMode:NSEventTrackingRunLoopMode dequeue:true)
if theEvent is missing value then exit repeat
theApp's sendEvent:theEvent
end repeat
end fordEvent

Am I calling fordEvent() in each iteration of my repeat loop, or does that show an example repeat loop into which I insert my own steps? I'm a little confused about why that has a repeat loop in it...

Offline

 

#6 2019-09-15 06:37:29 pm

Shane Stanley
Member
From:: Australia
Registered: 2002-12-07
Posts: 5959

Re: Window Refresh Question

You call it whenever you want the display to update mid-handler. So in a slow repeat loop, then every time, but in a fast loop running a lot of times you might want to use mod with your counter and call it every xth time through the loop.

The handler is doing more than forcing an update. Try running some code with a repeat loop that takes a while to run, and keep moving your mouse around vigorously while it's running. You'll see the spinning cursor. Basically, while you handler is being handled, any other events -- mouse movements and clicks, keystrokes, and more -- get added to the event queue. A spinning cursor means the queue is banked up.

And the problem is worse than a spinning cursor, because if the user typed some keystrokes, they'll happen when your handler finishes, which might not be what you want.

So what that handler is doing is getting the first event waiting in the queue and dispatching it, then repeating until the queue is empty. It avoids any bank up.

To run a long process safely you need to disable any controls you don't want to change, force an update via the handler, run the script and call the handler periodically (and because you've disabled controls any spurious clicks or keystrokes will go nowhere), and re-enable the controls at the end.


Shane Stanley <sstanley@myriad-com.com.au>
www.macosxautomation.com/applescript/apps/
latenightsw.com

Online

 

#7 2019-09-16 08:43:35 am

markconwaymunro
Member
Registered: 2017-11-28
Posts: 41
Website

Re: Window Refresh Question

That seems to be working perfectly. Thanks!

Offline

 

#8 2019-09-16 05:44:07 pm

Shane Stanley
Member
From:: Australia
Registered: 2002-12-07
Posts: 5959

Re: Window Refresh Question

FWIW, it you download my Myriad Helpers package, you can copy the two Objective-C files that implement the same thing into your code, and it will be a bit more efficient.


Shane Stanley <sstanley@myriad-com.com.au>
www.macosxautomation.com/applescript/apps/
latenightsw.com

Online

 

Board footer

Powered by FluxBB

RSS (new topics) RSS (active topics)