This program is an example of using timer events to do a task in the background while allowing the the user interface to still respond to events. This technique requires the background task to be split into parts which are short enough for each part to complete quickly enough for a user to not notice a delay if he begins to interact with the interface. In other words, if each part of the task takes 0.1 seconds, then there is a potential delay of 0.1 seconds before the user interface will respond.

mg_timer_demo screenshot

Here are the source and docs for the demo.

I’m going to use the normal “pstate” technique for passing data to my event handlers and standard techniques for manipulating object graphics interactively without explaining them. I’m only going to talk about the “new” part: timer events to handle a background task.

First, we need a widget that won’t generate events normally. We could use one we already have, but I usually create one specifically to do the timer events. Since bases don’t appear in the interface, I often use something like:

timer = widget_base(toolbar, uname='timer')

Next, we’ll need a few fields in our state structure to store information related to the timer events.

state = { oview: oview, $
          owindow: owindow, $
          otrack: otrack, $
          time: 0.1, $
          t: 0L, $
          stop: 1B $
        }

The time field holds the time value between timer events. Make sure this is long enough to accomplish a step in your task. The t field is a counter. We’ll rotate the surface one degree each time the timer goes off and stop after 360 rotations; using the counter to mark our position. The stop field indicates if the “stop” button has been hit (the initial condition is as if the “stop” button was just hit).

The event handler code for the timer event is fairly straightforward. Here is the case for timer events:

'timer' : begin
    if ((*pstate).stop) then return
    if (++(*pstate).t gt 360) then return
    omodel = (*pstate).oview->getByName('model')
    omodel->rotate, [0, 1, 0], 1
    (*pstate).owindow->draw, (*pstate).oview
    widget_control, event.id, timer=(*pstate).time
  end

If the stop field is set, then we don’t do anything. If the counter (which we increment) has gone past 360 we also don’t do anything. Otherwise, rotate the model by a degree around the y-axis. Finally, set the timer to go off again in (*pstate).time seconds.

Another choice for handling background tasks is to use the new IDL_IDLBridge class. With the IDL_IDLBridge class technique, the task would not have to be broken down into small subtasks, but there are more difficulties with overhead and passing data. This will be the subject of a future article.