I know that UI tasks from a thread are bad and not supported. Using Xojo it is enforced.
Still I see the need for some UI support similar to performSelectorOnMainThread in Objective-C.
For example an application which needs to print several jobs. The job creation is performed in a thread but the print job title needs to be assigned via the window title.
It would be pretty easy to code everything in a thread and then to call something like
[quote]start performOnMainThread
self.title =foo"
end performOnMainThread [/quote]
Even when there is a speed penalty.
Comments please. Is there already a feature request?
The reason they went down the road of throwing exceptions for accessing UI outside the main thread is that doing so is a BAD idea that has unpredictable consequences. They’ve been telling us it’s a bad idea for a couple of years, and our response has often been to negotiate rather than listen. So to repeat, it’s a bad idea. Platform UI frameworks aren’t all thread safe.
The standard way of safely manipulating UI from a thread is to move the UI manipulation to a Timer. In the thread, set period of Timer to 1, mode to 1. In thread, wait for the Timer to complete its action. Keep some flag in a Timer subclass to indicate that.
I know that Thread vs. UI stuff. This is not the question and the request is not to change that.
The request is to add a feature to perform a call from a thread which is performed on the main thread (see Apples Doc for performSelectorOnMainThread).
My example above makes it really hard to use a thread and a timer properly. And therefore I request a similar feature for Xojo.
It looks like a pretty straightforward Timer implementation to me. I’ve done many just like it. 5 minutes versus waiting a few release cycles for that to be implemented, if it’s worth implementing…
[quote=6891:@Thomas Eckert]I know that Thread vs. UI stuff. This is not the question and the request is not to change that.
The request is to add a feature to perform a call from a thread which is performed on the main thread (see Apples Doc for performSelectorOnMainThread).
My example above makes it really hard to use a thread and a timer properly. And therefore I request a similar feature for Xojo.[/quote]
I have been writing ThreadSafe versions of the following controls:
PushButton
ProgressBar
Window
Label
It has been working out very nicely.
The basic outline:
Make a subclass, e.g. “cLabel”
make internal properties for all values, e.g. “private mCaption as string” which will shadow the actual ones
make computed properties to shadow the superclass ones. The Getters are trivial. The Setter need to fire a timer so the update happens later:
cLabel subclass:
Caption.Get as string
return mCaption
Caption.Set(value as string)
mCapiton = value
UIUpdateRequest
UIUpdateRequest
if mTimer = nil then
mTimer=new Timer
AddHandler mTimer.Action me.UpdateUICallback
mTimer.period = 10 // (for performance reasons, you may want to make this longer or shorter)
end if
mTimer.Mode=Timer.ModeSingle
UPdateUICallback
if me.Caption <> mCaption then
me.Caption = mCaption
end if
etc...
You can extend this technique for all the properties you want.
It’s really quite easy. So easy, in fact, that I think REAL should just modify their existing Framework to do this internally. I believe I submitted a Feature Request for this some time ago, but it was closed.
Yes. You’re right. Unfortunately is it a more tricky for my example.
A thread runs and creates a print job. Unfortunately the print job title (which is very important for me) has to be assigned via the Window title. So I can’t run async here.
Of course I know how to solve this but it is a larger re-write.
It was so easy to use just a thread. And it would easy for us via a “performOnMainThread” feature…
[quote=6906:@Michael Diehr]I have been writing ThreadSafe versions of the following controls:
PushButton
ProgressBar
Window
Label
It has been working out very nicely.
The basic outline:
Make a subclass, e.g. “cLabel”
make internal properties for all values, e.g. “private mCaption as string” which will shadow the actual ones
make computed properties to shadow the superclass ones. The Getters are trivial. The Setter need to fire a timer so the update happens later:
You can extend this technique for all the properties you want.
It’s really quite easy. So easy, in fact, that I think REAL should just modify their existing Framework to do this internally. I believe I submitted a Feature Request for this some time ago, but it was closed.[/quote]
I have no doubt your FR was closed: shadowing properties is a bad thing!
So you are preventing to do a bad thing doing another bad thing.
Thomas, and Massimo : I believe you are both wrong - this is in-essence a “write-back” cache. Since the Getter always returns the (most recent) cached value, you can always safely get the value, even in a thread.
Thomas, in your case you would do this:
cWindow
Title.Get as string
return mTitle
Caption.Set(value as string)
mTitle = value
UIUpdateRequest
Massimo: can you explain why shadowing a property is a bad thing?
For strings, it’s all of the bytes for the string and then some overhead for reference counting. While small for a label, it could get quite large for a TextArea (and it would need to be copied every time the TextArea’s text changed).
Note that I didn’t suggest this technique for TextArea Clearly, the complexity of the underlying control matters. It seems to me a gracious compromise could be reached, something like :
The following controls are known to be thread-safe:
PushButton
Label
ProgressBar
etc.
The following controls are known to be thread-UN-safe
TextArea
etc.
In my experience, having a thread-safe ProgressBar, Label, and PushButton are terribly convenient, it allows me to write code like this:
Thread1.Run
' adjust the UI
PushButtonStop.visible = true
PushButtonRun.visible = false
' do the task
for i = 1 to nmax
progressBar1.value = i
label1.caption = "Processing " + str(i) + " of " + str(nmax)
foobar()
next
' adjust the UI
label1.caption = "All done"
PushButtonStop.visible = false
PushButtonRun.visible = true
Sure, one can do this other ways, but the code then requires multiple timers and a state machine - OR the elimination of threads altogether.
This strikes me as a good third party library opportunity rather than something that should take more space in the frameworks and cloud the party line on thread safety.
I agree. A library or plugin could better handle this.
However, I think Xojo Inc. should try to offer an official workaround to this problem. Too many people need to access UI from threads and, in lack of an official library/plugin/mechanism every one is coming with his own solution which, sometimes, is worst than the problem it solves.
I’d appreciate if Xojo will provide a library or plugin for this, even if not officially supported. At least there would be one official solution.
[quote=6917:@Joe Ranieri]While I’m not Massimo, I can explain this with a simple snippet of code:
dim ctrl as cLabel = new cLabel
dim ctrl2 as Label = ctrl
ctrl2.caption = “abc”
The last line actually ends up invoking Label’s Caption setter and not cLabel’s setter.
[/quote]
But I would expect this, since you used Label instead of cLabel. Why would this be a “gotcha”? Just like most programming, you need to understand the tools to use them properly.