Task integration in a project

Hi,
I have a Xojo project that has gone through many iterations, going all the way back to Real Basic in 1999. Recently, I tried building the program for Windows and was initially very pleased at how easy this seemed. But, then I realized that the Windows version wasn’t updating a list box in the user interface, something that had never been a problem on the Mac. Following various leads, I have been trying to incorporate a task to alleviate this problem. But, I have reached a point where I think that I need some help.

The major function of this program is to record readings from an instrument at regular time intervals (on the order of seconds), via a serial connection. As each measurement is made, it is entered into a listbox, and a progress bar is updated.

I have incorporated a task as a control in the window that contains the listbox, as well as a button that starts the run. And, I have managed to use the UpdateUI event to update the listbox. From initial experiments, this does seem to resolve the problems that I was seeing on Windows 10.

What I haven’t been able to figure out, however, is how to exit gracefully from the task and return control to the main thread of the program. Following the run, the window containing the listbox needs to be updated, and a graph is made from the contents of the listbox, using DataPlotClasses, from Roger Meier.

In the action for the button that starts the process, I have:
setUpRun// a method that sets things up for the run
kineticsTask.Run

Everything works with this, but nothing happens after the task completes, which makes sense. If I add a call to another method, like this:
setUpRun// a method that sets things up for the run
kineticsTask.Run
endRun// a method that should up date the window and call the plotting routine
Then, endRun starts as soon as kineticsTask.Run starts, which also makes sense, since the task, by definition, has started in another thread.

I thought that something like this might work:
setUpRun
kineticsTask.Run
while kineticsTask.state <> kineticsTask.Running
wend
while kineticsTask.state = kineticsTask.Running
wend
endRun
But, this appears to block the task from updating the user interface until the task is done, which is the problem that I was trying to solve in the first place!

Also, the data in the listbox does not seem to be available to the window in which the plot is to be made, though this may be unrelated to the use of the task.

Any help would be greatly appreciated!

Thanks,
David

have you been able to debug the program, trace it to see where it goes, and where it does not go (and should ?)

Make a work queue for your task. The task should live in a suspended state, but if you queue a work item for it and then resume it, the task should dequeue items, process them, and suspend itself when the queue is empty.

Part of the initialisation of your app will be to start the task, which should then suspend itself waiting for work.

Tim,
Thanks for your comments. I’m afraid, though, that I don’t quite understand what you are suggesting, especially with regard to making a work queue. During a given session with the app, the task will be run multiple times, from fresh windows. ’

At present, I have the task as a control in the window. I have tried placing
kineticsTask.Run
kineticsTask.Suspend

in the open event for the window, and then
kineticsTask.Resume

In the button used to start the process. But, this seems not to start the task.

As far as I can tell, there isn’t a que object defined in Xojo, so I presume that you are referring to something generic, and I have a general sense of what a you mean. (I did live in Britain for a few years!) . How do I set up a queue that will start the task, wait for the task to complete, and then carry on?

Thanks,
David

I’ve not been able to work on this for a few weeks, but after returning to it, I find myself still stuck in the same place. Any additional suggestions would be greatly appreciated.

To briefly summarize what I am trying to do:

My program collects data at time intervals and fills a listbox with the results. After collecting the data, there are further calls to the user interface, including changes to the window containing the listbox and creation of a plot from the contents of the listbox, using DataPlotClasses, from Roger Meier.

On Windows, the listbox isn’t updated as the data are collected, and I have been trying to use a task to keep the interface responsive.

The problem is that I only really want to put the data collections (with listbox updates) under the control of the task. Once that task is done, I want the program to move on automatically to the other user interface changes, without going through all of the extra manipulations required to control the interface from a task.

Tim has suggested setting up a queue, but I’m afraid that I don’t know how to do that.

Thanks,
David

The traditional method would be to install a timer whose Action event handler simply calls the appropriate method and have the thread set the timer to single mode just before it quits.

More elegant IMHO is the usage of Xojo.core.timer.Calllater (a delay of 0 is enough) where you could also forward parameters to the method or choose the appropriate methods from a variety without the need for several timers.

Any way, during thread action the main thread basically idles this way and can update the listbox, and then it can take control again and start further processing.

[quote=427021:@David Goldenberg]Tim,
Thanks for your comments. I’m afraid, though, that I don’t quite understand what you are suggesting, especially with regard to making a work queue. During a given session with the app, the task will be run multiple times, from fresh windows. ’

At present, I have the task as a control in the window. I have tried placing
kineticsTask.Run
kineticsTask.Suspend

in the open event for the window, and then
kineticsTask.Resume

In the button used to start the process. But, this seems not to start the task.

As far as I can tell, there isn’t a que object defined in Xojo, so I presume that you are referring to something generic, and I have a general sense of what a you mean. (I did live in Britain for a few years!) . How do I set up a queue that will start the task, wait for the task to complete, and then carry on?[/quote]

Make yourself a class (myworkitem, say) which has properties that the task will need in order to process one workitem. If for example your task will want to process different types of work item, then you might want to have a property called “command”, integer, to distinguish them. Then make an empty array of these items as a property of your task; this is your work queue.

In the task

The general task flow is then:

[code]Dim wsr as myworkitem

// Enter main work loop

while (true)

while (workitems.Ubound>-1) // There are requests pending

wsr = workitems(0) // Get the next request
workitems.Remove (0) // Remove it from the queue

select case wsr.command

case 1
// code for first command type

case 2
// code for second command type

// other cases here, as required

end select

wend // of loop looking for work

wsr = Nil // Free up the work item
task.suspend () // Task suspends itself as it has no work

wend // of main work loop
[/code]

Elsewhere in the app

When you want the task to do something, create and queue a work item, then resume the task:

[code]Dim wsr as myworkitem

wsr = new myworkitem
wsr.command = 1 // A type 1 request, for example
// initialise other properties in this workitem as required
workitems.Append (wsr) // Add this workitem to the end of the queue
app.mytask.resume () // Resume the task that will process this queue
[/code]

This code can be in another thread or even the main thread, but you can’t (in any useful way), “pause” the main thread until your task has finished its work (not without hanging the UI, anyway). If you want another thread to create the work item and then pause while the worker thread does the work item and then resume, you’d have to pass (in the work item), the id of that thread to the worker thread, which would need to resume your other thread when it had finished.

By the way, the word “queue” in this context is not a specifically British term, it’s more a computer-science term, see:

https://en.wikipedia.org/wiki/Message_queue

Ulrich and Tim,
Thanks very much for your detailed replies. This is definitely getting me into deeper waters than I am used to! So, I will have to do some more reading and thinking. I found this on the web, which looks like it will give me some guidance to follow up with Ulrich’s suggestion:
https://scispec.ca/index.php/books/38-threads-and-timers

I will, no doubt, be back with more questions!
David

Problem solved!

Thanks again to those who replied earlier. After more stumbling around, I finally realized that there was a much simpler solution to my original problem: All that was needed was a timer to control the measurements and table update, without a new thread or task.
Previously, I had been timing the measurements in the most rudimentary way: using ticks and a while loop. With a timer, everything is more responsive, and, as a secondary benefit, the code is much simpler and cleaner.

One thing that didn’t work with the timer is the UserCancelled event. I thought that if I checked for this in the timer action, I would be able to stop the timer and exit the process. But, that didn’t seem to work. I was able to do the same thing, though, by checking for the appropriate key presses. Is there an obvious reason that UserCancelled doesn’t work in a timer?

Thanks,
David