; docformat = 'rst' ; ; NAME: ; cgProgressBar ; ; PURPOSE: ; This program is used to draw a progress bar. ; ;******************************************************************************************; ; ; ; Copyright (c) 2012, by Fanning Software Consulting, Inc. All rights reserved. ; ; ; ; Redistribution and use in source and binary forms, with or without ; ; modification, are permitted provided that the following conditions are met: ; ; ; ; * Redistributions of source code must retain the above copyright ; ; notice, this list of conditions and the following disclaimer. ; ; * Redistributions in binary form must reproduce the above copyright ; ; notice, this list of conditions and the following disclaimer in the ; ; documentation and/or other materials provided with the distribution. ; ; * Neither the name of Fanning Software Consulting, Inc. nor the names of its ; ; contributors may be used to endorse or promote products derived from this ; ; software without specific prior written permission. ; ; ; ; THIS SOFTWARE IS PROVIDED BY FANNING SOFTWARE CONSULTING, INC. ''AS IS'' AND ANY ; ; EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES ; ; OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT ; ; SHALL FANNING SOFTWARE CONSULTING, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, ; ; INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED ; ; TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; ; ; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ; ; ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ; ; (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS ; ; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ; ;******************************************************************************************; ; ;+ ; This program is used to draw a progress bar on the display. ; ; .. image:: cgprogressbar.png ; ; The program requires the `Coyote Library ` ; to be installed on your machine. ; ; :Categories: ; Graphics ; ; :Examples: ; To display a progress bar in a program with a FOR loop:: ; ; cgProgressBar = Obj_New("CGPROGRESSBAR", /Cancel) ; cgProgressBar -> Start ; FOR j=0,9 DO BEGIN ; IF cgProgressBar -> CheckCancel() THEN BEGIN ; ok = Dialog_Message('The user cancelled operation.') ; RETURN ; ENDIF ; Wait, 0.5 ; Would probably be doing something ELSE here! ; cgProgressBar -> Update, (j+1)*10 ; ENDFOR ; cgProgressBar -> Destroy ; ; An additional example can be found my compiling the program and typing ; this command:: ; ; IDL> ProgressBar_Example ; ; :Author: ; FANNING SOFTWARE CONSULTING:: ; David W. Fanning ; 1645 Sheely Drive ; Fort Collins, CO 80526 USA ; Phone: 970-221-0438 ; E-mail: david@idlcoyote.com ; Coyote's Guide to IDL Programming: http://www.idlcoyote.com ; ; :History: ; Change History:: ; Written by: David W. Fanning, 27 September 2012. Inspired by Ronn Kling's program krProgressBar. ; ; :Copyright: ; Copyright (c) 2012, Fanning Software Consulting, Inc. ;- ; ;+ ; Checks the cancel button to see if it has been selected. ; ; :Returns: ; Returns a 1 if the user clicked the Cancel button and a 0 otherwise. ; ;+ ; The initialization routine for the cgPROGRESSBAR object class. ; ; :Keywords: ; cancelbutton: in, optional, type=boolean, default=0 ; Set this keyword if you wish to have a CANCEL button on the ; progress bar. If a CANCEL button is present, the user is reponsible ; for checking if the user has canceled while the progress bar is running. ; group_leader: in, optional, type=long ; The identifier of a group leader widget for the progress bar. If the group ; leader dies, the progress bar will be destroyed. ; nocancel: in, optional, type=boolean, default=1 ; A depreciated keyword, added for compatibility with the old Progressbar code. ; If set, sets the CancelButton keyword to 0. ; percent: in, optional, type=float, default=0.0 ; The initial percentage completion of the progress bar when it first ; appears on the display. Used only if the `Start` keyword is also set. ; text: in, optional, type=string ; The text that appears in a label widget above the progress bar. If not ; supplied, no label widget is created. ; title: in, optional, type=string ; The text that appears as the window title of the progress bar. By default, ; "Operation in Progress...". ; xoffset: in, optional, type=int ; The X offset, in pixels, from the top-left corner of the display. If ; not provided, the progress bar is centered in the window. ; xsize: in, optional, type=int, default=250 ; The X size, in pixels, of the progress bar. ; yoffset: in, optional, type=int ; The Y offset, in pixels, from the top-left corner of the display. If ; not provided, the progress bar is centered in the window. ; ysize: in, optional, type=int, default=25 ; The Y size, in pixels, of the progress bar. ;- FUNCTION cgProgressBar::INIT, $ CANCELBUTTON=cancelbutton, $ ; Set this keyword if you desire a CANCEL button on progress bar. GROUP_LEADER=group_leader, $ ; The identifier of the group leader widget. NOCANCEL=nocancel, $ ; Depreciated keyword. PERCENT=percent, $ ; Initial percent of the progress bar. (Only recognized if START used.) START=start, $ ; Set this keyword if you wish to call the START method from INIT. TEXT=text, $ ; The message text to be written over the progress bar. TITLE=title, $ ; The title of the top-level base widget. XOFFSET=xoffset, $ ; The X offset of the progress bar. XSIZE=xsize, $ ; The X size of the progress bar. YOFFSET=yoffset, $ ; The Y offset of the progress bar. YSIZE=ysize ; The Y size of the progress bar. Compile_Opt idl2 ; Standard error handling. Catch, theError IF theError NE 0 THEN BEGIN Catch, /CANCEL void = cgErrorMsg() RETURN, 0 ENDIF ; Check keywords. IF N_Elements(nocancel) NE 0 THEN cancelbutton = 1 - Keyword_Set(nocancel) SetDefaultValue, cancelbutton, 0, /Boolean SetDefaultValue, percent, 0.0 SetDefaultValue, start, 0, /Boolean SetDefaultValue, text, "" SetDefaultValue, title, "Operation in Progress..." SetDefaultValue, xsize, 250 SetDefaultValue, ysize, 25 ; Progress bar should be centered unless told otherwise. Device, Get_Screen_Size=screenSize IF screenSize[0] GT 2000 THEN screenSize[0] = screenSize[0]/2 ; Dual monitors. IF N_Elements(xoffset) EQ 0 THEN xoffset = (screenSize[0]/2 - xsize/2) IF N_Elements(yoffset) EQ 0 THEN yoffset = (screenSize[1]/2 - ysize/2) ; The progress must be between 0.0 and 100.0. percent = 0.0 > percent < 100.0 ; Create the widgets for the program. self.tlb = Widget_Base(Column=1, Group_Leader=group_leader, $ Floating=N_Elements(group_leader) GT 0, $ XOffset=xoffset, YOffset=yoffset, Title=title, $ Map=0, Base_Align_Center=1, TLB_Frame_Attr=11) IF text NE "" THEN BEGIN self.labelID = Widget_Label(self.tlb, Value=text, /Dynamic_Resize) ENDIF self.drawID = Widget_Draw(self.tlb, XSize=xsize, YSize=ysize, $ Graphics_Level=2, Renderer=1, Retain=2) IF cancelbutton THEN BEGIN self.cancelID = Widget_Button(self.tlb, Value='Cancel') ENDIF ; Create needed objects. self.view = Obj_New('IDLgrView', ViewPlane_Rect=[0,0,xsize,ysize], $ Color=[255, 255, 255]) self.model = Obj_New('IDLgrModel') self.view -> Add, self.model ; Store sizes. self.xsize = xsize self.ysize = ysize ; Create the background image for the progress bar. self.image = self -> CreateImage() self.model -> Add, self.image ; Create the initial mask for the image and update it with the percent. mask = BytArr(4, xsize, ysize) mask[3,*,*] = 255B self.mask = Obj_New('IDLgrImage', mask, Blend_Function=[3,4], /No_Copy) self -> UpdateMask, percent self.model -> Add, self.mask ; Prepare to overlay percentage as text. percentString = String(percent, Format='(I0)') + ' %' self.text = Obj_New('IDLgrText', percentString, Alignment=0.5, $ Location=[xsize/2,ysize/2,0.1], Vertical_Alignment=0.5, $ Color=[255,255,255]) self.model -> Add, self.text ; Start it up? IF start THEN self -> Start, percent RETURN, 1 END ;+ ; The clean-up routine for the object. ;- PRO cgProgressBar::CLEANUP IF Widget_Info(self.tlb, /VALID_ID) THEN Widget_Control, self.tlb, /Destroy Obj_Destroy, self.image Obj_Destroy, self.mask Obj_Destroy, self.text Obj_Destroy, self.view Obj_Destroy, self.model Obj_Destroy, self.window END ;+ ; Checks to see if a Cancel button has been clicked during the on-going operation. ; An optional response can be issued, if so. ; ; :Keywords: ; message: in, optional, type=string ; If the RESPOND keyword is set, this is the message set in the blocking ; dialog widget. By default: "Current Operation Cancelled by User". ; respond: in, optional, type=boolean, default=0 ; If this keyword is set, the program responds to a positive cancel flag ; by destroying the progress bar and setting a blocking dialog widget ; for the user. The keyword is ignored if the cancel flag is zero. ;- FUNCTION cgProgressBar::CheckCancel, $ MESSAGE=message, $ RESPOND=respond Compile_Opt idl2 ; Standard error handling. Catch, theError IF theError NE 0 THEN BEGIN Catch, /CANCEL void = cgErrorMsg() RETURN, 0 ENDIF ; Assume no cancel operation. cancelFlag = 0 ; Check for a Cancel event, if there is one. IF Widget_Info(self.cancelID, /Valid_ID) THEN BEGIN event = Widget_Event(self.cancelID, /NoWait) name = Tag_Names(event, /Structure_Name) IF name EQ 'WIDGET_BUTTON' THEN cancelFlag = 1 ENDIF ; Need a response to the cancel flag? IF cancelFlag && Keyword_Set(respond) THEN BEGIN self -> Destroy IF N_Elements(message) EQ 0 THEN message = "Current Operation Cancelled by User" void = Dialog_Message(message) ENDIF RETURN, cancelFlag END ;+ ; Create the object image for the program. Taken from Ronn Kling's ; "really cool" background image in krProgressBar. ;- FUNCTION cgProgressBar::CreateImage Compile_Opt idl2 ; Standard error handling. Catch, theError IF theError NE 0 THEN BEGIN Catch, /CANCEL void = cgErrorMsg() RETURN, -1 ENDIF ; RGB vectors set up by Ronn Kling. red = Bytscl([Intarr(193), Indgen(63)]) grn = Bytscl([Intarr(96) , Indgen(256-96)]) blu = Bytscl([Intarr(188), Intarr(256-188)+188]) data = [228,228,238,248,248,238,228,217,197,186,165,155,145,145,145, $ 145,155,166,176,186,186,186,176,166,124] IF self.ysize NE 25 THEN data = Congrid(data, self.ysize) ; Arrange the data in the form of an image. data = Rebin(Reform(data, 1, self.ysize), self.xsize, self.ysize) img = BytArr(3, self.xsize, self.ysize) img[0,*,*] = red[data] img[1,*,*] = grn[data] img[2,*,*] = blu[data] ; Create an object image. image = Obj_New('IDLgrImage', img, Interpolate=1, /No_Copy) RETURN, image END ;+ ; Destroys the progress bar. ;- PRO cgProgressBar::Destroy Compile_Opt idl2 ; Standard error handling. Catch, theError IF theError NE 0 THEN BEGIN Catch, /CANCEL void = cgErrorMsg() RETURN ENDIF IF Widget_Info(self.tlb, /VALID_ID) THEN Widget_Control, self.tlb, /DESTROY Obj_Destroy, self END ;+ ; Updates the progress bar. ; ; :Params: ; percent: in, required, type=float ; The percent the progress bar has been completed. A number between ; 0 and 100. Forced into this range. ;- PRO cgProgressBar::Update, percent Compile_Opt idl2 ; Standard error handling. Catch, theError IF theError NE 0 THEN BEGIN Catch, /CANCEL void = cgErrorMsg() RETURN ENDIF ; Nothing to do if percent equals zero. IF (N_Elements(percent) EQ 0) || (percent EQ 0.0) THEN RETURN ; Percent must be between 0 and 100. percent = 0.0 > percent < 100.0 ; Update the mask. self -> UpdateMask, percent ; Update the text object. newText = String(percent, Format='(I0)') + ' %' self.text -> SetProperty, String=newText ; Draw the progress bar. self.window -> Draw, self.view END ;+ ; Update the image mask to reflect the correct percentage. ; ; :Params: ; percent: in, required, type=float ; The percent the progress bar has been completed. A number between ; 0 and 100. Forced into this range. ;- PRO cgProgressBar::UpdateMask, percent Compile_Opt idl2 ; Standard error handling. Catch, theError IF theError NE 0 THEN BEGIN Catch, /CANCEL void = cgErrorMsg() RETURN ENDIF ; Nothing to do if percent equals zero. IF (N_Elements(percent) EQ 0) || (percent EQ 0.0) THEN RETURN ; Percent must be between 0 and 100. percent = 0.0 > percent < 100.0 ; Get the current maps self.mask -> GetProperty, Data=data ; Modify the mask by the percent needed and update the alpha channel. newMask = BytArr(self.xsize, self.ysize) + 255B x = Reverse(Bytscl(Findgen((self.xsize*(percent/100.))>1))) y = Bytarr(self.ysize) + 1B newMask[0,0] = x # y data[3, *, *] = newMask ; Set it back. self.mask -> SetProperty, Data=data END ;+ ; Starts the progress bar by realizing the progress bar on the display. ; ; :Params: ; percent: in, optional, type=float ; Starts the progress bar with this percentage completion. A number ; between 0.0 and 100.0. ;- PRO cgProgressBar::Start, percent Compile_Opt idl2 ; Standard error handling. Catch, theError IF theError NE 0 THEN BEGIN Catch, /CANCEL void = cgErrorMsg() RETURN ENDIF ; Check parameters. IF N_Elements(percent) EQ 0 THEN percent = 0.0 ; Realize the widget. Widget_Control, self.tlb, /Realize, Map=1 ; Get the window object reference number of the draw widget. Widget_Control, self.drawID, Get_Value=window self.window = window ; Draw the bar. self.window -> Draw, self.view ; Do you supply a starting percent? IF N_Elements(percent) NE 0 THEN self -> Update, (0.0 > percent < 100.0) END ;+ ; The event handler for the progress bar example program. ; ; :Params: ; event: in, required, type=structure ; The event structure. ;- PRO Progressbar_Example_Event, event ; Respond to program button events. Widget_Control, event.id, Get_Value=buttonValue CASE buttonValue OF 'Start Simple Loop': BEGIN ; Create the progress bar. progressbar = Obj_New('cgprogressbar', /Start) ; Start the loop. count = 0 FOR j=0, 1000 DO BEGIN IF j MOD 10 EQ 0 THEN BEGIN ; Update the progess bar every 100 times through loop. progressbar -> Update, (count * 1.0) count = count + 1 ENDIF Wait, 0.01 ; This is where you would do something useful. ENDFOR ; Destroy the progress bar when you are finished with it. progressbar -> Destroy ENDCASE 'Start Cancel Loop': BEGIN ; Create the progress bar. progressbar = Obj_New('cgprogressbar', /Cancel) ; Place the progress bar on the display. progressbar -> Start ; Start the loop. count = 0 FOR j=0, 1000 DO BEGIN IF j MOD 10 EQ 0 THEN BEGIN ; Update the progess bar every 100 times through loop. ; Did the user try to cancel the progress bar? IF progressbar->CheckCancel() THEN BEGIN ok = Dialog_Message('User cancelled operation.') ; Other cleanup, etc. here. progressbar -> Destroy ; Destroy the progress bar. RETURN ENDIF ; If user didn't cancel, update the progress bar. Update value ; must be between 0 and 100. progressbar -> Update, (count * 1.0) count = count + 1 ENDIF Wait, 0.01 ; This is where you would do something useful. ENDFOR ; Destroy the progress bar when you are finished with it. progressbar -> Destroy ENDCASE 'Quit': Widget_Control, event.top, /Destroy ENDCASE END ;+ ; This is an example program that illustrates how the cgProgressBar ; object can be used. ;- PRO Progressbar_Example tlb = Widget_Base(Column=1, Xoffset=200, Yoffset=200) button = Widget_Button(tlb, Value='Start Simple Loop') button = Widget_Button(tlb, Value='Start Cancel Loop') quiter = Widget_Button(tlb, Value='Quit') Widget_Control, tlb, /Realize XManager, 'progressbar_example', tlb, /No_Block END ;+ ; The class definition module for the cgPROGRESSBAR object class. ; ; :Params: ; class: out, optional, type=structure ; The object class definition as a structure. Occasionally useful. ;- PRO cgProgressBar__Define, class class = { cgPROGRESSBAR, $ INHERITS IDL_OBJECT, $ image: Obj_New(), $ mask: Obj_New(), $ text: Obj_New(), $ view: Obj_New(), $ model: Obj_New(), $ window: Obj_New(), $ xsize: 0L, $ ysize: 0L, $ tlb: 0L, $ drawID: 0L, $ cancelID: 0L, $ labelID: 0L $ } END