;+ ; NAME: ; PROGMETER ; PURPOSE: ; A widget that displays a progress meter with a color bar ; that grows horizontally to indicate what percentage of a task has ; been completed. The percentage is also written as text. The ; window title can be set, and an optional cancel button with ; settable text may be shown. ; CATEGORY: ; OVRO SPAN UTILITY ; CALLING SEQUENCE: ; id = progmeter(/INIT,[GROUP=group][,LABEL=label]$ ; [,BUTTONTEXT=buttontext]) ; status = progmeter(id,value) ; status = progmeter(id,/DESTROY) ; INPUTS: ; id the widget ID returned by a previous, initializing ; call to PROGMETER (that used the /INIT switch). ; This input is ignored if INIT keyword is set. ; value the new value to set the widget to, ranging from ; 0 to 1 (0=0% complete, 1=100% complete). ; This input is ignored if INIT keyword is set. ; OPTIONAL (KEYWORD) INPUT PARAMETERS: ; init a keyword switch that governs which of two calling ; sequences is being used. If set, a new widget is ; created. If not set, an existing widget is to be ; updated. ; destroy a keyword switch that destroys the widget created by ; a previous call to PROGMETER (that used the /INIT switch) ; group the top base id of the calling program, so that the widget ; will be destroyed if the calling program exits. ; This input is ignored if INIT keyword is not set. ; label an optional title for the progress meter window. If ; omitted, the title "Percent Complete" is used. ; This input is ignored if INIT keyword is not set. ; buttontext ; an optional text for a button, such as a cancel button ; to interrupt the process whose progress is being shown. ; If omitted, no button is present. If set and the user ; clicks on the button an event is generated. To detect ; the event, call WIDGET_EVENT as discussed below. ; This input is ignored if INIT keyword is not set. ; colorbar an optional color for bar other than white ; ROUTINES CALLED: ; OUTPUTS: ; id the widget id of the compound widget (only when INIT ; keyword is set). ; status the status of the cancel button (only when INIT keyword ; is not set). If the cancel button has been pressed, ; status='Cancel' and if not an empty string ('') is returned. ; If an error in calling sequence occurs (ID or VALUE ; is not supplied) then status='Cancel' also. NB: If ; the widget is initialize without a button, status ; will always be an empty string ('') ; COMMENTS: ; To use the routine, call it with the /INIT switch to create the ; widget, then call it repeatedly without the /INIT switch to update ; it. When done with the widget, it should be destroyed. Here is ; an example: ; ; id = progmeter(/INIT,label='Progress Meter',button='Abort') ; val = 0 ; for i = 0, n do begin ; ; val = i/(1.0*n) ; Fraction of loop completed ; if (progmeter(id,val) eq 'Cancel') then goto,escape ; endfor ; escape: status = progmeter(id,/DESTROY) ; ; SIDE EFFECTS: ; RESTRICTIONS: ; The progress meter widget must be explicitly destroyed. ; MODIFICATION HISTORY: ; Written 28-Feb-1997 by Dale E. Gary ;- ;----------------------------------------------------------------- ; This is a helper procedure called by PROGMETER when setting the ; value of the widget. It actually draws the proportional bar and ; writes a label in XOR mode onto the bar. pro progmeter_setvalue, id, value ; Make sure value is only between 0 and 1 per = value<1>0 ; Get state variables so that draw window id is known stash = WIDGET_INFO(id,/CHILD) WIDGET_CONTROL, stash, GET_UVALUE=state, /NO_COPY ; Set the draw window as the current window, erase it and draw ; the bar using normalized coordinates wset,state.win erase ; Handle some annoying differences between UNIX and PC behavior. ; For UNIX, set font to default hardware font. I have found by ; trial and error that XOR only works for vector fonts on Win NT, ; so cannot use hardware default font on PC. Also, setting color=0 ; in XOR mode gives white on UNIX, but black on PC, so the color ; must be set differently on the two -- ARRGH. old_font = !p.font !p.font = 0 white=!d.table_size-1 if n_elements(color) eq 0 then color=white if (strlowcase(os_family()) eq 'shit') then begin ; Set graphics mode to XOR and draw bar (XOR will make it contrast with bgnd) device,set_gr=6 polyfill,[0,per,per,0], [0,0,1,1], /norm,color=0 ; Write out the percent complete in the appropriate font. I have found ; that vector fonts look lousy in XOR mode unless the font is decently ; large, so set charsize to 2. In UNIX, this is ignored because we are ; using the default hardware font. xyouts, 0.5,0.2,/norm,charsize=2,$ strcompress(fix(per*100))+'%',align=0.5,color=0 device,set_gr=3 endif else begin polyfill,[0,per,per,0], [0,0,1,1], /norm, color=white ; Write out the percent complete in the appropriate font. I have found ; that vector fonts look lousy in XOR mode unless the font is decently ; large, so set charsize to 2. In UNIX, this is ignored because we are ; using the default hardware font. if per lt 0.5 then color = white else color = 0 xyouts, 0.5,0.2,/norm,charsize=2,$ strcompress(fix(per*100))+'%',align=0.5,color=color endelse !p.font=old_font ; Resave state variables WIDGET_CONTROL,stash,SET_UVALUE=state,/NO_COPY end ;----------------------------------------------------------------- ; This is a helper function called by PROGMETER when getting the ; widget ID of the button widget. function progmeter_getvalue, id stash = WIDGET_INFO(id,/CHILD) WIDGET_CONTROL, stash, GET_UVALUE=state, /NO_COPY ; Set the return value to the current state of the button ret = state.buttonid WIDGET_CONTROL,stash,SET_UVALUE=state,/NO_COPY return,ret end ;----------------------------------------------------------------- ; This is the main routine. It consists of three separate routines, ; actually, separated by if-then clauses, one routine for each of ; the three calling sequences. If the /INIT switch is set, the ; widget is created. If neither /INIT nor /DESTROY switches are ; set, the value of the widget is updated and the button state is ; checked. If the /DESTROY switch is set, the widget is destroyed. ; function progmeter,id,value,INIT=init,DESTROY=destroy,GROUP=group,$ LABEL=label,BUTTONTEXT=buttontext,color=color,$ INPUT_TEXT=input_text ; Section of code to be run if /INIT keyword is *not* set if (not keyword_set(init)) then begin ; First verify that an ID was given if (n_elements(id) eq 0) then begin msg = ['No widget ID specified in call to PROGMETER.',$ 'Must first call PROGMETER with /INIT to obtain ID.'] xack,msg ;ans = widget_message(msg,/error) return,'Cancel' endif ; ID was given, so see if /DESTROY keyword is set. If so, destroy ; the widget and return if (keyword_set(destroy)) then begin xkill,id return,'Cancel' endif ; Neither /INIT nor /DESTROY were set, so set the value of ; the widget and check the status of the button (if any), ; then return. if (n_elements(value) eq 0) then begin msg = ['No value specified in call to PROGMETER.',$ 'Must supply a value. See documentation.'] xack,msg ;ans = widget_message(msg,/error) return,'Cancel' endif ; Set value and get button ID if not xalive(id) then return,'Cancel' xshow,id WIDGET_CONTROL,id,SET_VALUE=value WIDGET_CONTROL,id,GET_VALUE=buttonid ret = '' ; If the button ID is not zero, the button exists, so check state ; and return 'Cancel' if it has been pressed. if not xalive(buttonid) then return,'Cancel' if (buttonid ne 0L) then begin quit = WIDGET_EVENT(buttonid,/nowait) if (quit.id eq buttonid) then ret = 'Cancel' endif return,ret endif ; If /INIT keyword was not set, the program returns to caller before ; reaching this point. This is the cection of code to be run if /INIT ; keyword *is* set. ; Set some defaults if not given in calling sequence if (not keyword_set(label)) then label = 'Percent Complete' if (not keyword_set(group)) then group = 0 ; Set the base widget, specifying my own routines to be run ; if WIDGET_CONTROL,GET_VALUE or SET_VALUE are called. mk_dfont,lfont=lfont,bfont=bfont base = WIDGET_BASE(/COLUMN, TITLE=label,GROUP = group, $ FUNC_GET_VALUE='progmeter_getvalue', $ PRO_SET_VALUE ='progmeter_setvalue',tlb_frame=8) if is_string(input_text) then begin temp=widget_base(base,/row,/align_center) text=widget_text(temp,font=lfont,$ value=[' ',input_text,' '],ysize=2+n_elements(input_text),$ xsize=max(strlen(input_text))+2) endif ; Set up the draw widget and optionally the button widget temp=widget_base(base,/row,/align_center) draw = WIDGET_DRAW(temp,xsize=300,ysize=25) if (keyword_set(buttontext)) then begin ; The widget button is in its own base (bbase) so that it ; can be smaller width than full width of main base. Its ; width will be automatically adjusted to the length of the ; text string. bbase = WIDGET_BASE(base,/ROW,/ALIGN_CENTER,ysize=25) button = WIDGET_BUTTON(bbase,value=buttontext,UVALUE='BUTTON',font=bfont) endif else button = 0L ; This signifies that button is undefined ; Realize (draw) them xrealize, base,group=group,/center ; Get the window id of the draw widget WIDGET_CONTROL,draw,GET_VALUE=win ; Save some info that will be needed in the other functions state = {win:win, buttonid:button} WIDGET_CONTROL, WIDGET_INFO(base, /CHILD), SET_UVALUE=state, /NO_COPY ; The widget is created and drawn to the screen, so return the base ; ID so that the widget can be accessed by later calls to update it ; or destroy it. return,base end