Why you have to update UI from a thread?

I don’t actually know why UI must be updated from timer but couldn’t the Xojo compiler do all of the work of creating a timer for you so you get the same effect but more conveniently, especially for those porting there projects from realbasic.

Thanks

Understanding the issues of threads and UI is kind of a pre-requisite for coming up with workarounds. For example, Xojo or anyone skilled enough here could write thread-safe wrappers based on CriticalSection around all the controls. If users of these wrappers use them naively, they’ll often end up with thread deadlocks that are hard to resolve. Or you could wrap all the controls with message queues. But safely reading control state from non-UI threads could yield old state.

In summary: programming with threads is difficult. Programming non-thread-safe UI toolkits with threads is really, really tricky.

Xojo could work some kind of magic, but it would be the wrong kind of magic most of the time. They have created the Task thread class that automatically links to a timer and gives you an “all in one” solution. But you still need to move your code that accesses the UI from the thread to the event the timer calls.

I like event queues for UI object that must be updated from threads… In windows one could put messages on the applications message queue and one could update the gui with those…

But I must admit this doing heavy work behind the scenes and getting the UI to work is a lot more challenging than expected.

I have a simple task to loop through all the files in a folder and update them… when necessary I need to pop up a dialog and get help from the user. One would think that the loop for each file would be handled in a thread, but then your not able to ask the user anything… I wish I could stop the thread send a message to the UI and have it pop up a dialog box and let me know when its done so the thread can move on to the next…

When you derive and class based on thread i find it unintuitive that you have to add an event handler to the window to Run the thread from… I don’t know why I wanted to implement Run in the derived class and have the owner of the instance of the subclass call run… And then the behavior is different… There is still something I don’t get about ‘the main’ thread vs the ‘other’ thread…

I wish I could just fork/exec and have two instances of the application.

[quote=47237:@Brian O’Brien]But I must admit this doing heavy work behind the scenes and getting the UI to work is a lot more challenging than expected.
[/quote]

Perhaps the most beautiful framework I’ve ever seen was that of the BeOS. The core tenet was pervasive multi-threading. Under-the-hood, it was all about messaging. But it was difficult for app developers to really get their heads around how to use it right. It felt like a small handful of us were continually answering other developers’ questions that indicated they didn’t quite get it.

As easy as Xojo makes it to work with threads, it also makes it easy to stumble over all the pitfalls of multi-threading. Not an easy problem to solve.

Have your thread start a short-period timer and then Suspend itself. 1ms is enough on the timer’s period. Have the timer display the dialog and store the results in the thread and then Resume the thread.

Interesting… Thought you might like to know this…
A thread in WindowA.MyThreadObject.Run( EventHandler) can call a method in WindowB.UpdateUI :slight_smile:

As long as WindowB.UpdateUI doesn’t actually update the UI. :slight_smile: Assuming you are using the thread correctly (your code is in the run event and you haven’t overridden the Run method), you should get an exception in the latest version of Xojo (3.3).

proc UpdateUI
listbox1.addrow(“foo”)
end sub

Perhaps it works and doesn’t complain. Still a bad idea.

I don’t know if it helps, but what I do is to create a timer subclass UITimer.

It has one property “instructions(-1) as pair”. One method, “add( command as variant, value as variant = nil )”
One event “Process( command as variant, value as variant)”.

In the method it uses the following code to store the ‘instruction’ in the queue and if the timer isn’t running to fire it.

instructions.append command : value if me.mode = 0 then me.period = 100 me.mode = 1 end if

In the “action” event.

[code] while ubound( instructions ) > -1
process( instructions( 0 ).left, instructions( 0 ).right )
instructions.remove 0
wend

me.mode = 0[/code]

So you add this subclass to the window, then use the add method to queue up what you want changed, and the Process event to actually make the change.

IMHO, I find this solution really simple and quite flexible, and it goes in line with Apple’s whole energy saving (could spawn a new thread…ha) technique, because the timer only fires when there is something for it to do.

[quote=47489:@Brian O’Brien]proc UpdateUI
listbox1.addrow(“foo”)
end sub[/quote]
That’s not supposed to work from a thread.

Thinking about Model View Controller paradigm the thread can update the model all it wants then the controller can update the view when it wants. The problem with this method is that when it comes time to update the view, I find I have to clear all the states of UI elements and start over from scratch. eg… If I had a listbox i have to delete all rows then loop and add all rows. This is because the model can go through many updates before a view update is actually performed.

Using a queue of messages between the thread and the user interface has the advantage that the user interface elements are updated in a transaction oriented fashion. Thus adding elements to listboxes can be done in row add / delete and done one at a time to avoid having to read all the elements in the model. The instruction queue is in the the controller and can update the view from it.

I wanted to have the command and parameters to the command to form the entire message set.
Sam Rowlands has a concept of passing command and parameters but can there be a concept of var args?
How do you invoke the method and pass the parameters in Process?

My threads usually communicate through interfaces that update the model and work perfectly. I liked interfaces… but I guess they don’t help when there are UI elements involved.

How do you specify that an object implements an interface(s)?

The listbox as it is implemented by Xojo can’t be used in an MVC architecture. In MVC a view never holds any data, it requests it from the controller, which then fetches the necessary rows from the model. So the model NEVER updates the view in a proper MVC architecture (AppKit works like that and also forbids updating the UI from another thread than the main one).

Instead the model notifies the controller, that a change happened in the model’s data. The next time the view draws, it automatically gets the updated data from the controller. The solution with the timer is actually not necessary, because the CellTextPaint event of the listbox can be used as replacement for the timer. So you can mimic the MVC architecture by not adding data to a listbox but just empty rows (as many as there are records in your model). Then in the CellTextPaint event you ask your controller for the data for the visible rows. This also can - implemented properly - speed up the listbox drastically.

@Eli: this sounds complex and is not necessary for Xojo. I found so far the View and Controller can be the same. If you want to ask the Model for data every CellTextPain: good luck with speed.

Beatrix:

  • it’s not complex (you already have to handle images like that with DrawPicture in the CellTextPaint event)
  • its is proper MVC like in AppKit or Qt
  • it is really faster than adding rows, especially when you have a lot of columns
  • it elegantly solves the issue of wanting to update the UI from a secondary thread

I started wondering how my WIndow2.UpdateUI method ‘EVER’ worked… yet it does…
I think i figured out how…
If window2 was created in the Run Event of the thread then it should be able to communicate with that.

Um, no. A thread cannot create a window. There’s something funky going on with your setup.

I wonder if you could use XojoScript to actually pass in strings of Xojo code and have Xojo Script execute it?

I suppose so. You could also use delegates which might be faster and simpler.