Example Interactive Box
This is a tutorial for new IDL users on how
to make a simple interactive
graphics box. A number of IDL techniques are covered and also
some comments on program style.
Extra details are given with new IDL users in mind.
Here is a copy of the routine for you to try.
The routine is discussed in detail below.
What does this box routine to do?
This routine plots an elastic box on the graphics window.
The box is made and controlled using the mouse. Move the cursor to one
of the corners of the desired box, press and hold down the left mouse
button. Then while holding the left button down move the mouse
to one of the other corners of the desired box. Release the button
to lock the box in place. If the box is not what is wanted repeat
the process for a new box. Press the middle button to exit
and accept the box, or the right button to exit and reject
the box.
IDL techniques and routines illustrated
- The format of a simple procedure.
- Using parameters.
- Using keywords.
- Using the IDL mouse system variable.
- Using the IDL device procedure to set the graphics mode.
- Using repeat loops.
- Using while loops.
- The plots procedure.
- The cursor procedure.
- Relational operators.
- XOR graphics mode.
Programming style
- Neatness.
- Generous use of whitespace.
- Avoidance of goto.
- Several types of comments.
- Program header.
- Built in help.
- Nesting levels.
- Setting up statement blocks.
Program neatness makes it easier to read and understand. Use lots of
white space (spaces, tabs, blank lines). There is no penalty in execution
time since all the spaces are compiled away. The minor additional disk
space is well worthwhile.
I don't normally spend a lot of effort to
avoid gotos, especially for quick and dirty routines. But for simple cases
such as this they are easy to avoid by using the right IDL loop statement.
Remember, Whiles test at the front, Repeats test at the end of the loop.
Several types of comment statements make sense.
Section divider type comments
should stand out from the code, I add dashes or even equals for a bolder
effect, and blank lines. Detailed comments may be added to the ends of the
lines. They are much easier to read if they are lined up in a column as
far as possible (the starting column must sometimes change).
Every routine
should have a minimal header giving its name, author, and creation date.
The example here is not the standard IDL header, that may be found in the
file template.pro in the IDL distribution.
Built in help makes a routine
self documenting. Most routines never have a manual written for them and
even if they did it would soon be lost. With built in help the instructions
are always there.
Indenting the code to reflect the current nesting level
is very helpful in avoiding or finding problems. Code within Ifs or loops
should be indented to make it apparent. Ifs and Loops commonly contain
multiple statements. These are called statement blocks and are terminated
by some form of the end statement: endif, endelse, endwhile, endrep, ...
A useful trick to get the correct termination is to add it before any
of the code inside.
The Interactive Box Algorithm
The terms left mouse button and button 1 are used interchangeably.
A rough version of the algorithm:
- Wait until a mouse button is pressed.
- Save the cursor position where button 1 was pressed.
- While button 1 is held down and position changes:
- Erase old box
- Draw new box
- Save new box as old
- Repeat the above until a button other than 1 is pressed.
More details
Steps 2 and 3 above are only done if button 1 was pressed, so in the
routine these steps appear inside an if statement.
Near the front of this if statement any previous box must be
erased. This allows repeated boxes.
Before any looping or processing the mouse button flag in the
IDL system variable !mouse must be cleared. Also define an initial
previous box.
Listing of the Routine
This listing is numbered for reference. This is not the output
of the IDL .run -L executive command which does its own indenting
(very useful sometimes).
1 ;=================================================================
2 ; boxarea.pro = Select an area with a box
3 ; R. Sterner, 1995 Oct 31
4 ;=================================================================
5
6 pro boxarea, x1, y1, x2, y2, flag=flag, help=hlp
7
8 if keyword_set(hlp) then begin
9 print,' Select an area with a box.'
10 print,' boxarea, x1, y1, x2, y2'
11 print,' x1,y1 = first box point. out'
12 print,' x2,y2 = second box point. out'
13 print,' Keywords:'
14 print,' FLAG=flg Exit flag: 0=ok, 1=abort.'
15 print,' Notes: Open a box by dragging with left mouse button.'
16 print,' Repeat to get desired box.'
17 print,' Accept box with middle button.'
18 print,' Reject box with right button.'
19 print,' All coordinates are device coordinates.'
20 return
21 endif
22
23 !mouse.button = 0 ; Clear button flag.
24 device, set_graphics = 6 ; Set XOR mode.
25 x1=100 & y1=100 ; Initial old box.
26 x2=100 & y2=100
27
28 ;------ Loop until button 2 or 3 pressed ----------
29 repeat begin
30
31 ;----- Loop until any button pressed ----------
32 while !mouse.button eq 0 do cursor,/dev,xa,ya
33
34 ;----- Process button 1 hold ---------
35 if !mouse.button eq 1 then begin
36 plots,/dev,[x1,x2,x2,x1,x1],[y1,y1,y2,y2,y1] ; Erase old box.
37 x1=xa & y1=ya ; New pt 1.
38 x2=x1 & y2=y1 ; Initial pt 2.
39
40 ;------ Loop until button 1 released (drag) -------
41 repeat begin
42 cursor,/dev,/change,xb,yb ; Get new pt 2.
43 plots,/dev,[x1,x2,x2,x1,x1],[y1,y1,y2,y2,y1] ; Erase old box.
44 plots,/dev,[x1,xb,xb,x1,x1],[y1,y1,yb,yb,y1] ; Draw new box.
45 x2=xb & y2=yb ; Save new pt 2.
46 endrep until !mouse.button eq 0 ; End button 1 drag.
47
48 endif ; End process button 1.
49
50 endrep until !mouse.button ge 2 ; End wait for button 2.
51
52 device, set_graphics = 3 ; Restore plot mode.
53 flag = !mouse.button ne 2 ; Exit flag.
54
55 end
Program Notes
- Line 6: Two types of parameters
The first 4 parameters are
positional parameters.
They must be given in the same order by the calling routine as they
appear here.
The next 2 parameters are keyword parameters.
They may be given in any order in the calling routine. Keyword parameters
have the form outside = inside where outside is the
name of the parameter known to a calling routine, and inside
is the name used in the local code. They may be the same or different
as shown.
- Lines 8-21: Built in help
The printing of this help is triggered when the keyword /help
is given when calling this routine. Otherwise it is ignored.
- Lines 23-26: Set initial values
These lines set up values needed later.
The IDL system variable !mouse contains information about
the graphics cursor as controlled by the mouse. Do help,/st,!mouse
for all the details. The only field of this structure needed by this
routine is the button field which indicates which, if any,
button is currently pressed. This value is retained so must be cleared
before using. 0 means no button is pressed.
The IDL device procedure is used to set the graphics mode
to XOR. In XOR mode each bit of a pixel is XORed with the bits
of the plot color value. The details don't matter here, the interesting
property of using XOR is that plotting the same plot a second time
erases it without any damage to the original underlying image.
This is very useful for interactive type plotting. The one bad side
to XOR is that the resulting contrast is not always good. Ideally
graphics overlays should be used for such interactive graphics, but
they are not available in many graphics devices so XOR is a good
compromise.
The algorithm has no special case for the first time through. When
a new starting point is selected the old box is erased so it must exist
to avoid an error. It may be initialized to a single point without harm.
- Lines 29-50: Outer repeat loop
This loop just repeats the box drawing commands until a button
other than 1 is pressed.
- Line 32: Point 1 while loop
This one line loop just watches the cursor move around until a button
is pressed. The cursor routine is called to get the first
corner of the box. By default this routine waits for a keypress
before returning.
- Lines 35-48: Process a button 1 press
If button 1 was pressed these lines are executed.
First any previous box is erased. Since XOR mode is in effect
this is easily done just by replotting the box.
Next (lines 37-38) the new point 1 is set and point 2 is set to
the same point initially so the box erase at line 43 won't harm
anything.
- Lines 41-46: Drag to open the box
These lines are inside a repeat loop that waits for button 1
to be released.
Note the use of the /change keyword in the cursor
routine. This makes the cursor routine return if the mouse is moved
or a button was pressed. The keyword /nowait would also
work (so would /wait since a button is pressed) but would
take lots of cpu time replotting the box uselessly causing it to
flicker (on slow machines). /change uses very little
resources unless a change takes place and gives a smoother effect.
The old box is erased at line 43, the new plotted at line 44,
and the new box saved at line 45. In this section the only change is
to point 2.
- Line 52: Restore plot mode
This step is very important. Ideally the first call to the
device procedure should get and save the initial graphics
mode and this call should restore that mode. The most common
graphics mode is mode 3.
- Line 53: Setting the exit flag value
This is an example of a relational operation in IDL.
This variable !mouse.button is tested for inequality against
the value 2. The result is 1 if true or 0 if false and is returned
in the variable called flag. If button 2 was pressed then a 0
is returned, else a 1 is returned.
It is common for an error flag value of 0 to
mean no error. That way various errors may have
various non-zero values.