UI Thread and method from a module help

Possibly tired subject, but I can’t seem to figure out how to use the threaded UI examples as the basis for creating a thread that let’s me run methods that are not in the main thread. Example: I have a method that batch uploads a large amount of records to an online database and i need it run in a thread. i want to also use the progress bar, updating it with the proper UI thread from the main thread. it works if I run the uploadData method from the UIThread, but now I’m stuck with only being able to run that one method because coded in the run event.

My question. Is there a way to run any method not in the mainThread and update the UI properly? Is it addhandler? i can’t find any clear examples.

Thomas, you can set a progress property in a Thread, and have a Timer periodically update the UI. There is War and Peace length discussion on this to be found on the NUG and in this forum, but that’s the short, easy answer.

I add a method to the thread called Action(Sender As Timer). Then in the thread I add a property called mUI As Timer. The first line of code in the thread run event is Addhandler mUI.Action, Addressof Action. Any UI updates are defined in the Action method. Then I can either let the timer run in multimode or fire it when required in single mode. Although the action method is defined in the thread it actually runs in the main thread so can update the UI without consequences.

Another thing to note is that if you are uploading the data via a tcp socket the dataavailable event is also running on the main thread, so if you are receiving any confirmation messages from the online database you can update the UI from there too.

HTH
Wayne

thanks for the help, but i can’t get my head around this. “add a method to the thread” implies i need to make a thread class with the method and assign it as a superclass to a thread control on my main window. is this correct? same with mUI timer?

Yes
You are going to create a new subclass of thread to add your own methods to

i’m getting closer. I’m using the addhandler example and modiyfing to use a thread to update UI. it seems to work well. the problem now is I have two methods in my module that I want to thread this was through the main thread.

The tricky part.

I have a method (addThread) in the main window that assigns the module method (ThreadHandler) to the run event of the the thread

mThread = New Thread
AddHandler mThread.Run, AddressOf ThreadHandler

This works fine.

million dollar question.

I want to be able to pass module methods to the addThread method with some kind of variable so I can pick any method I want from any module and assign it to the thread.

I’m trying to avoid writing a thread for for each method that needs threading.

Not sure I follow

ANY method you want to add using “AddHandler” has to have a particular signature (set of parameters that are passed to it)

The FIRST parameter will always be the item that called the method
In the case above ThreadHandler would need to have a Thread as its first parameter like

Sub ThreadHandler( caller as Thread , ... other parameters ) // but in this case there are not extra parameters
So you’d have to modify every method you want to use the way you described to have that signature
I’m not sure that’s any better

And, to be honest, AddHandler is not a “design pattern” - use it sparingly otherwise its just modern spaghetti code.

However you should realize that ANY method called from the code executed as part of the threads RUN event IS run in that thread regardless of where it actually is in the code.

So if your thread handler calls “Module1.SomeMethod” - that is run in the threads context.

sorry. i should have clarified. ThreadHandler is actually the method I want to run.

mThread = New Thread
AddHandler mThread.Run, AddressOf Method1

i’m trying to combine the UIUpdate and method thread into one process. This seems to work fine like all of the examples until you want to run some other method that’s not associated with the main window. I would like to be able to tell the thread to now run Module2 . i have tried using case seclect with a methodName string in the ThreadHandler to select which method to run. This does work, but i would rather be able to say run moduleX (moduleX not being on the main window) in a thread and update the UI progress.

This may not be the way to go, but I haven’t be able to get the UIUpdate example to work when you separate you methods from the main window.

[quote=14616:@Thomas Polson]sorry. i should have clarified. ThreadHandler is actually the method I want to run.

mThread = New Thread
AddHandler mThread.Run, AddressOf Method1

i’m trying to combine the UIUpdate and method thread into one process. This seems to work fine like all of the examples until you want to run some other method that’s not associated with the main window. I would like to be able to tell the thread to now run Module2 . i have tried using case seclect with a methodName string in the ThreadHandler to select which method to run. This does work, but i would rather be able to say run moduleX (moduleX not being on the main window) in a thread and update the UI progress.

This may not be the way to go, but I haven’t be able to get the UIUpdate example to work when you separate you methods from the main window.[/quote]

Its actually simpler than you think
First DONT have you threads RUN event call the code that does the update directly (or via addhandler)
That code will still be running in the thread & you will still get the exception

The simplest is to create a thread subclass and add

  1. a property - UIupdateTimer as Timer

  2. a method that will handle the action event of the timer
    sub TimerHandler( t as timer)
    // here you can now call WHATEVER methods you want to update the UI
    end sub

  3. in the thread RUN event handler put
    UIUpdateTimer = new Timer

    AddHandler UIUpdateTimer.Action, AddressOf TimerHandler

    UIUpdateTimer.period = 10 // so the timer runs every 10 ms
    UIUpdateTimer.Mode = Timer.ModeMultiple

  4. at the END of the threads RUN event make sure you do
    RemoveHandler UIUpdateTimer.Action, AddressOf TimerHandler
    UIUpdateTimer = nil

Now when your thread runs it will set up a timer that is totally private to it and that timer will periodically run its action event.
And you CAN call whatever methods you want from there.

To make it so you can update ANY window I would additionally do

  1. make an interface - WindowUpdaterInterface

  2. to this interface I would add one method declaration - Update()

  3. make whatever windows need to get called from your thread implement this interface

  4. pass a reference to the Window to your thread when you construct it

    say you do this from a window
    dim athread as mythread = new mythread( self )

  5. add a method to your thread subclass - Constructor( whatWindow as WindowUpdaterInterface)

  6. hold on to that reference in your thread by adding a property to it
    whatWindowToUpdate as WindowUpdaterInterface

  7. alter the thread subclass TimerHandler as follows
    sub TimerHandler( t as timer)
    whatWindowToUpdate.Update()
    end sub

Of course you can also alter the above recommendations & such to suit your needs

first of all, thanks for taking the time. second i think i have something that going to work.

i created the thread class like you said and added method in the module

runThread(methodName As string)

Dim t As New Thread

select case methodName
case “test”
AddHandler t.Run, AddressOf test
case “other_method”
AddHandler t.Run, AddressOf other_method

etc…

end select

Window1.UIThread.Run

t.Run

it all seems to work, but i am running two threads, which i guess is not a bad thing.

two questions.
i’m not clear how implement step 4

RemoveHandler UIUpdateTimer.Action, AddressOf TimerHandler
UIUpdateTimer = nil

is the added to the thread subclass we created? how is it called?

second question.

is this a situation for the use of delegates? don’t know too much about them.

You put that at the END of the RUN event on the thread so after it’s done its work it stops running & cleans up

[quote=14656:@Thomas Polson]
is this a situation for the use of delegates? don’t know too much about them.[/quote]
AddressOf IS creating a delegate so no need to add in extra complexity unless you need it

so the run event of the thread subclass would be:

UIUpdateTimer = new Timer

AddHandler UIUpdateTimer.Action, AddressOf TimerHandler

UIUpdateTimer.period = 10 // so the timer runs every 10 ms
UIUpdateTimer.Mode = Timer.ModeMultiple

RemoveHandler UIUpdateTimer.Action, AddressOf TimerHandler
UIUpdateTimer = nil

i don’t think i’m doing it right

UIUpdateTimer = new Timer

AddHandler UIUpdateTimer.Action, AddressOf TimerHandler

UIUpdateTimer.period = 10 // so the timer runs every 10 ms
UIUpdateTimer.Mode = Timer.ModeMultiple

// RUN YOUR CODE THAT NEEDS TO BE IN THE THREAD HERE !!!
// OR NOTHING WILL HAPPEN EXECPT YOU CREATE & END THE RUN EVENT IMMEDIATELY

RemoveHandler UIUpdateTimer.Action, AddressOf TimerHandler
UIUpdateTimer = nil

thanks, but this is where i get crossed up. if I run my code here in the run event like you say, i can only run one thing like a routine to copy all files on a folder. i would have to write another class if i want to run a routine to batch upload to a database and another if i want to convert a bunch of image files. etc.

[quote=14689:@Norman Palardy]// RUN YOUR CODE THAT NEEDS TO BE IN THE THREAD HERE !!!
// OR NOTHING WILL HAPPEN EXECPT YOU CREATE & END THE RUN EVENT IMMEDIATELY
[/quote]

Could easily be myMethod which is located in a module. Because it’s called from the thread it will be running in that thread.

thank, but get where you run the code. the problem is i want to choose what code to run at runtime.

You’re going to need to understand either delegates so you can define one & create it at runtime and pass that to your thread based on some criteria or interfaces which can allow you to do something similar.

Or better yet both

thanks. i was coming to that conclusion.

[quote=14620:@Norman Palardy]Its actually simpler than you think
First DONT have you threads RUN event call the code that does the update directly (or via addhandler)
That code will still be running in the thread & you will still get the exception

The simplest is to create a thread subclass and add

  1. a property - UIupdateTimer as Timer

  2. a method that will handle the action event of the timer
    sub TimerHandler( t as timer)
    // here you can now call WHATEVER methods you want to update the UI
    end sub

  3. in the thread RUN event handler put
    UIUpdateTimer = new Timer

    AddHandler UIUpdateTimer.Action, AddressOf TimerHandler

    UIUpdateTimer.period = 10 // so the timer runs every 10 ms
    UIUpdateTimer.Mode = Timer.ModeMultiple

  4. at the END of the threads RUN event make sure you do
    RemoveHandler UIUpdateTimer.Action, AddressOf TimerHandler
    UIUpdateTimer = nil

Now when your thread runs it will set up a timer that is totally private to it and that timer will periodically run its action event.
And you CAN call whatever methods you want from there.

To make it so you can update ANY window I would additionally do

  1. make an interface - WindowUpdaterInterface

  2. to this interface I would add one method declaration - Update()

  3. make whatever windows need to get called from your thread implement this interface

  4. pass a reference to the Window to your thread when you construct it

    say you do this from a window
    dim athread as mythread = new mythread( self )

  5. add a method to your thread subclass - Constructor( whatWindow as WindowUpdaterInterface)

  6. hold on to that reference in your thread by adding a property to it
    whatWindowToUpdate as WindowUpdaterInterface

  7. alter the thread subclass TimerHandler as follows
    sub TimerHandler( t as timer)
    whatWindowToUpdate.Update()
    end sub

Of course you can also alter the above recommendations & such to suit your needs[/quote]

Thanks for this example code. I’ve got it working, but I’ve noticed that that the Thread’s ‘UIUpdateTimer’ continues to fire even when I kill and set the thread variable to nil.

Shouldn’t the thread’s timer stop and get destroyed when the thread variable is set to nil?

From what I’m seeing in Xojo 2013r4.1:

When I set the thread variable to nil the ‘Destructor’ method that I’ve created for the subclass doesn’t fire, which seems wrong. Shouldn’t it fire when the thread object is set to nil?

The temporary fix that I’ve had to do was create a new method named ‘CleanUpAndKill’ that does this:

// --------

if ( UIUpdateTimer <> nil ) then

UIUpdateTimer.Mode = Timer.ModeOff

RemoveHandler UIUpdateTimer.Action, AddressOf TimerHandler
UIUpdateTimer = nil

end if

me.Kill

// --------

Then I call this thread method when/if I need to stop the thread immediately. But this seems like a work around.

Maybe I’m missing something? Or maybe there’s a bug in Xojo?

[quote=55704:@Justin Elliott]
Maybe I’m missing something? Or maybe there’s a bug in Xojo?[/quote]

reference cycles :stuck_out_tongue:
your thread has a reference to the timer & the timer has a reference to the thread (via the delegate created using AddHandler)