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.
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>)
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
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 |
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
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
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
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 |
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