4. Widgets

Widget is shorthand for 'window gadgets', little window things. Graphical user interfaces are build up from widgets. Widgets are provided from libraries of the operating system, which are called from idl. That means, that one and the same application may look different on different machines, as their libraries are generally not the same.

4.1. Widget types

The types of widgets provided by IDL are:

Type Description 
Base a unit that collects other widgets ordered in rows or columns 
Button A pushbutton activated by the mouse 
Slider To select a value by a sliding pointer 
Text Viewing area for text 
Draw Viewing area for graphics 
Label Static text 
List Present a list of elements to choose on with mouse 
Droplist A button like object that drops a list when activated 

The associated commands to define these widgets are called widget_<type>. The calling sequence is:

widget_id=widget_<type>(parent_id<,keywords>)

4.2. Widget Handling

To actually create, modify, delete and inquire status of a widget application on the screen, use the widget_control procedure. The systems graphical environment manager takes control over a widget application once invoked with xmanager and returns information about user actions to an event handler routine. Such events can also be traced by the widget_event procedure.

The basic structure of an IDL widget application hence is as followed:

Example:

pro wid1
 
base=widget_base(/row)
left=widget_button(base,value='Left')
middle=widget_button(base,value='Bail Out')
right=widget_button(base,value='Right')
widget_control,base,/realize
xmanager,'wid1',base
end

The event handler routine now looks like this:

pro wid1_event,event

widget_control,event.id,get_value=what
if what ne 'Bail Out' then  print, 'that was the ',what,' button' $
else  widget_control,event.top,/destroy
end

4.3. Widget types in detail

4.3.1. Base

A base widget is a rectangular area that with a collection of other widgets called 'children'. It may well contain other bases and hence establish a complex widget hierarchy. It is recommended to assign either row or column type to a base, meaning that children are placed in rows or columns. If this is not done, the only way of controlling widget positions is by explicitly using the xoffset, yoffset and xsize, ysize keywords, that define positions relative to the upper left corner of a widget in pixels. As pixel size and number vary from one machine to another, this gives unpredictable results if not handled with great care.

Generally the call to define a widget base is

widget_id=widget_base(parent, keywords)

Specifying parent make the base a sub base within another one. on the top level base it is omitted. Widgets within these base are defined by referring to widget_id as their parent.

Useful specific keywords include:
 

row, column specifies type of ordering 
align_top, align_bottom, align_right, align_left, align_center  align element of this base in the specified manner 
base_align_top, base_align_bottom, base_align_right base_align_left, base_align_center makes all children align themselves correspondingly 
exclusive 'radio buttons', only one can be pressed at a time 
nonexclusive 'radio buttons' where several can be toggled 
map, unmap visible or invisible at creation 
xsize, ysize size in pixels 

4.3.2 Buttons

Buttons are very typical widgets used to initiate certain actions. To draw a button, use

widget_id=widget_button(parent,options)

as already seen in the examples. Useful keywords:
 

frame draw a frame 
value a string or bitmap that bears the name of the button 
uvalue a variable of any type associated to the button 
xsize, ysize size in pixels 

Example: to create a compass type set of four buttons you can define a column type base which includes three levels, each row type, the first and third with one element, the second with two:

pro wid2

base=widget_base(/column,/base_align_center)
 
lev1=widget_base(base,/row)
butn=widget_button(lev1,value='N')

lev2=widget_base(base,/row)
butw=widget_button(lev2,value='W')
bute=widget_button(lev2,value='E')

lev3=widget_base(base,/row)
buts=widget_button(lev3,value='S')

widget_control,base,/realize

end

4.3.3 Draw widgets

Defines a rectangular area to receive graphical output. Can have scroll bars, if display is larger than the assigned area. Keywords:
 

colors reserve this amount of colors, has to be set at the first draw widget opened. 
frame draw a frame around the area 
uvalue a 'user value' associated with the widget 
scroll, x_scroll_size, y_scroll_size add scroll bars and define visible area 
xsize, ysize size in pixels 
 

Example

pro wid3

base=widget_base(/column) 
d1=widget_draw(base,xsize=300,ysize=200)
d2=widget_draw(base,xsize=300,ysize=200)
widget_control,base,/realize

widget_control,d1,get_value=d1index
wset, d1index
plot, sin(findgen(100)/15.)

widget_control,d2,get_value=d2index
wset, d2index
plot, cos(findgen(101)/100.*2.*!pi)

end

4.3.4 Lists

The list widget offers a list of alternatives to choose from. Keywords:
 

value string array that contains the list items. This value can not be changed later nor queried. 
xsize, ysize size in characters, ysize defines how many elements are visible in the scroll area 
uvalue a freely defined user variable 

An event handler receives the position of the selected element, but hence it can not query the value, it will not get the name of the selected element. To accomplish this, we can use the trick to associate the element list also as a the user value, which can be queried:

pro wid4_event,event
      
widget_control,event.id,get_uvalue=list
print, event.index,'. element selected, called ',list(event.index)
if event.index eq 0 then widget_control,event.top,/destroy
end

pro wid4

items=['exit','red','blue','green']
base=widget_base(/column)
list=widget_list(base,value=items,uvalue=items,ysize=2)
widget_control,base,/realize
xmanager,'wid4',base
end

4.3.5 Sliders

Sliders are used to graphically assign a numerical value to a variable.
 

minimum The minimum value for the variable, default is 0 
maximum The maximum value, default 100 
value initial value, which is then changed by actuating the slider 
suppress_value Do not show value besides slider 
vertical create a vertical instead of horizontal (default) slider 

Controlling Widgets

Let us discuss widget controls by an example. The idea of the following program is to communicate the value of a slider setting into a label widget. The slider range shall be modified by a list of possible ranges. The definition program looks like this.

PRO  wid5
COMMON wd, base, button, list, sbase, leftbut, slider, rightbut, label, scale

base=widget_base(/column,/base_align_center)

button=widget_button(base,value='Bail Out', uvalue='exit',xsize=250)

ranges=[1,10,100,1000]
tranges=['+/-1','+/-10','+/-100','+/-1000']
list=widget_list(base,value=tranges,uvalue=ranges,ysize=4)

sbase=widget_base(base,/row)
leftbut=widget_button(sbase,value='-1',uvalue='left',xsize=70)
slider=widget_slider(sbase,minimum=-1000,maximum=1000,value=0,/suppress_value,/drag)
rightbut=widget_button(sbase,value='+1',uvalue='right',xsize=70)
scale=1

label=widget_label(base,value='0.',xsize=250,frame=1)

widget_control,base,/realize
xmanager,'wid5',base
end

And here is the event handler. Observe that there are several ways to communicate between caller program and event handler, but that they can NOT exchange arguments.

PRO  wid5_event,ev
COMMON wd, base, button, list, sbase, leftbut, slider, rightbut, label, scale

type=tag_names(ev,/structure)   ; converts event to clear text

CASE type OF

'WIDGET_BUTTON': BEGIN  
        widget_control,ev.id,get_uvalue=val     ; which button was it?
        IF val eq 'exit' then begin             ; bail out
                widget_control,label,set_value='Adios Muchacho'
                widget_control,label,/realize
                wait,3
                widget_control,base,/destroy
        ENDIF ELSE BEGIN 
                print,"don't touch these buttons"  ; one of the info buttons
                widget_control,label,set_value=val+' button, no function'
                widget_control,label,/realize
        ENDELSE
        END

'WIDGET_LIST': BEGIN
        widget_control,ev.id,get_uvalue=ranges
        scale=ranges(ev.index)                  ; which element selected?
        mival=strcompress('-'+string(scale),/remove_all)
        maval=strcompress('+'+string(scale),/remove_all)
        widget_control,leftbut,set_value=mival          ; new left info button
        widget_control,rightbut,set_value=maval         ; new right info button
        widget_control,sbase,/realize
        END

'WIDGET_SLIDER': BEGIN
        widget_control,ev.id,get_value=slval            ; the slider value
        value=float(slval)/1000.*float(scale)           ; scaling
        IF scale eq 1    THEN svalue=string(value,format='(f6.3)')
        IF scale eq 10   THEN svalue=string(value,format='(f6.2)')
        IF scale eq 100  THEN svalue=string(value,format='(f6.1)')
        IF scale eq 1000 THEN svalue=string(value,format='(f6.0)')
        widget_control,label,set_value=svalue           ; communicate value
        widget_control,label,/realize
        END

ELSE:   BEGIN                           ; this should never happen
        print,'Unknown operation'
        ENDELSE
ENDCASE
      
END

Exercise: Instead of using common blocks, pass the values in the last program as user values attached  to the base window