I have created a custom class for “automatic” buttons, based on a MacOSLib-implemented NSImageView class.
The “automation” works in several ways: I have included different images for disabled, normal and switched-on state. When the user clicks on the image, it changes according to its settings and invokes a custom event that is passed to the parent property (the window it sits on). In order to change the image, I invalidate it once it was changed.
This worked all fine, until I recently changed the communication structure of my classes to an ObserverInterface (before there were many different properties calling methods of other properties ). When the parent window, which is the observer, receives the information that some class property was changed, it sets a computed property accordingly. The latter one is computed because it automatically switches the images to the right one when the property value changes. Under some circumstances now, I get a threadaccessingUI exception when invalidate is called.
Problem is: There are no threads around this class. So I wonder if the observer interface is handled internally via threads. Should I set up a UIsafe thread which gives an invalidate?
And, while were at it: When the debug app crashes, the debugger shows me the order of method calls that lead to it. This is really nice, but much too often I click too eagerly on the crash message box which stops the debug and returns to the editor. Are those messages saved somewhere so I still can retrieve them?
What do you mean by getting the interface where? I inserted it as class interface to several classes the usual way clicking on the class and “insert interface”. The only thread I have installed in this app is on another window and not related to the buttons.
I wonder if the error was correct. I swear there is no thread, but on the crash the debugger showed me several blank properties for the window which, on the other hand, existed ?!?!?
Maybe this has to do with me mingling Xojo Windows with MacOSLibs NSWindow property. Like written elsewhere, I can work around the window.visble bug by addressing NSWindow.isvisible and NSWIndow.orderout instead of closing or hiding it. Ill have an eager eye on that and, if everything else fails, shove the invalidate into a UIsafe thread. Currently the image changed is delayed or sometimes only working when I cross the buttons position with the mouse arrow which is not whats intended.
check out the Design Patterns folder in the examples. There youll find the observer example.
I would describe it (please correct me if Im wrong) as a brillant substitute for event messaging throughout different classes. You get one instance of whatever class as the emitter the subject that can register other instances of different classes as receiver (observers).
Via the NotifyObserver method that comes with the example you send out a notification to each observer which those can handle differently via their own Update method.
Could it be the NSImageView? It sounds like the suspect execution starts in the mouse events, which in NSImageView are originally triggered by OS calls to Shared methods (which call instance methods, which raise a mouse event, which uses Observer Interface, which calls Invalidate and causes a ThreadUI exc.) I don’t know if this is possible but maybe those OS callbacks are somehow being done from another thread?
Thats an excellent hint. Yes, the debug report in debugger started with a mousedown event. I did not pay much attention to it because before inserting the observer I never had problems and my code in the buttons uses no separate threads. But somewhere the UI call must be done from a thread, it seems. Ill reinsert the invalidate and check if I can provoke a crash, tracing it down to the originator this time. And see if I get a clue from looking at the debugger
I couldnt provoke a crash yet but dug into my code. I would not be suspicious about NSImageView anymore. But I have found that inside my send routine (its an app communicating with telephones via TCPsocket or Serial. I put the send method in a thread so that the app does not hang while the socket is not ready to send) in case of an error I raise an event. This triggers a method that notifies the observers. Can it be the event is carried out on that thread and then finally tries to manipulate the UI?
Thanks, Norman! Seems you corrected me on another misunderstood concept today.
I guess to safely return a value from a thread Ill use the task class again and call the UIsafe method when I need to raise an event?
No idea about what UISafe method you’re using but a thread subclass could easily be crafted to do such a thing
It would create a timer in code, set it to a very short period
You respond to the timers Action event using AddHandler which is FOR SURE on the main thread so UI safe
And just to twist your brain a little, you can define the method that the timer uses in the Thread subclass! That keeps all your code together in one place. Code runs in the thread that called it, regardless of where it is defined. Timers are serviced by the event loop, so they always run in the main thread and therefore anything they call runs in the main thread.
And you could have MANY actions that the timer can execute using a delegate defined in the class that you set up based on some criteria through the course of running your thread and then when the timer action event is raised you invoke the delegate and get a different action based on whatever delegate was set up - yeehaw !
all kinds of options and eventually it all spins off into a very generic Task class
Now, if Tims post was intended to twist my brain, consider what your post did to it, Norman!
I made it to crash and, as it seems, I should move the send and flush into a thread safe method if my interpretation of this log is right the thread problems occurred because I write and flush from inside the thread which then transfers to the data available event of the TCPsocket subclass which triggers parsing of the received data and then, via observer notification, transfers the thread to updating my window properties. Oh boy!