I think I need a Mac OS helper app to manage drawing a display window with reliable timing. Back when threads could be used to run precisely timed graphics tasks, I had no problems. Now under Cocoa, threads cannot be used to draw graphics, and the Task object is an unreliable replacement. The window is display only, meaning that there is no user interaction with the window.
So I need a helper app, but one that will create and manage drawing a window with regular and reliable refreshes of text and graphics (i.e. not video). I’d prefer that it not have an icon in the dock, as with a desktop app. I’ll communicate with it using an IPC socket.
Any suggestions would be appreciated.
So dont use the TASK object
The ONLY thing that really is required is a timer and you can create one purely in code on the thread
When you thread starts create it with New Timer
Add a method to your thread that is going to handle the Timers action event using addhandler
Set its period really short (like 0 or 1 msec)
In THAT method (and only that method or ones it calls directly) you can be sure you are running on the main thread and now you can draw to the UI safely
And it will get time VERY fast since the timer period is so short
LSUIElement (String - OS X) specifies whether the app runs as an agent app. If this key is set to 1, Launch Services runs the app as an agent app. Agent apps do not appear in the Dock or in the Force Quit window. Although they typically run as background apps, they can come to the foreground to present a user interface if desired. A click on a window belonging to an agent app brings that app forward to handle events.
The Dock and loginwindow are two apps that run as agent apps.
"[/quote]
If you think about it, using a timer and a series of properties to pass data between the thread and the window is not very different from having a separate helper program and pass data through IPC, but it is much simpler. You may want to try the all-in-one before embarking into the helper app.
See LSUIElement[/quote]
Thanks Michel. That’s exactly what I was looking for, but I think Norm has shown me a way to do what I need to do within my main app, so I’m going to say he answered it.
[quote=219553:@Norman Palardy]So dont use the TASK object
The ONLY thing that really is required is a timer and you can create one purely in code on the thread
When you thread starts create it with New Timer
Add a method to your thread that is going to handle the Timers action event using addhandler
Set its period really short (like 0 or 1 msec)
In THAT method (and only that method or ones it calls directly) you can be sure you are running on the main thread and now you can draw to the UI safely
And it will get time VERY fast since the timer period is so short[/quote]
I had to read this a couple of times. I have not used addHandler before. So in the Open event of my display window I’d have “AddHandler WindowTimer.Action, AddressOf MyWindowUpdater”
…and MyWindowUpdater is a method in my thread? And so then the code in MyWindowUpdater can touch the UI, but it does execute in a separate thread? scratches head
Thanks!
Just because a method is defined in a thread doesn’t mean that it runs in the thread context. It runs in the context of whatever called it. And just because a method is defined outside the thread doesn’t mean that it won’t run in the thread, if the thread’s Run event calls it.
Everything called by the Timer runs in the main (UI) thread, regardless of where it is defined.
Everything called by the Thread’s Run event runs in the thread, regardless of where it is defined.
[quote=219740:@Tim Hare]Just because a method is defined in a thread doesn’t mean that it runs in the thread context. It runs in the context of whatever called it. And just because a method is defined outside the thread doesn’t mean that it won’t run in the thread, if the thread’s Run event calls it.
Everything called by the Timer runs in the main (UI) thread, regardless of where it is defined.
Everything called by the Thread’s Run event runs in the thread, regardless of where it is defined.[/quote]
That’s my understanding. And so I must have misunderstood the approach that Norm is suggesting…?
[quote=219735:@Tod Nixon]I had to read this a couple of times. I have not used addHandler before. So in the Open event of my display window I’d have “AddHandler WindowTimer.Action, AddressOf MyWindowUpdater”
…and MyWindowUpdater is a method in my thread? And so then the code in MyWindowUpdater can touch the UI, but it does execute in a separate thread? scratches head
Thanks![/quote]
The key here is that the TIMERS action event is trigger BY the Xojo RUNTIME and NOT by the thread
This means its on the main thread so you can update UI from it
BUT since the method is defined IN the thread it has access to everything in the thread (properties, etc)
SO you end up being able to have the thread do work and have a way to move that work to the UI without having to try & manipulate the UI from within the thread itself
The method you define in your thread subclass (you will need to create a subclass just for this) and set up using AddHandler should have the signature
Sub myWindowUpdated(whichTimer as Timer)
as the first param is always the object making the call
You’d set it up like
AddHandler WindowTimer.Action, AddressOf MyWindowUpdater
And when you END the thread run even make sure you call RemoveHandler which is nearly identical to add handler
[quote=219802:@Norman Palardy]The key here is that the TIMERS action event is trigger BY the Xojo RUNTIME and NOT by the thread
This means its on the main thread so you can update UI from it
BUT since the method is defined IN the thread it has access to everything in the thread (properties, etc)
SO you end up being able to have the thread do work and have a way to move that work to the UI without having to try & manipulate the UI from within the thread itself
The method you define in your thread subclass (you will need to create a subclass just for this) and set up using AddHandler should have the signature
Sub myWindowUpdated(whichTimer as Timer)
as the first param is always the object making the call
You’d set it up like
AddHandler WindowTimer.Action, AddressOf MyWindowUpdater
And when you END the thread run even make sure you call RemoveHandler which is nearly identical to add handler[/quote]
Thanks for the clarification. I think I did try a variation on this approach and found that the timer in the main thread can be delayed arbitrarily, much as the timers in the Task object. That produces stutters in an otherwise smooth animation (not really animation, but writing text in shades of gray to produce a fade up/fade down effect).
Again, this was some time ago when I started working with Cocoa. But I think I may have had too much code in the timer’s action. Your approach keeps the thread, and the only thing the timer will do is draw a stringShapeGroup (created by the thread) to the window.