#!/usr/bin/env python """ wizard.py This module implements a 'wizard' class for PythonCard. To see how it works, just run this module. Demo code contained herein actually defines a sample wizard, and shows it in action. """ import os, sys from PythonCardPrototype import model, res from PythonCardPrototype import dialog from pdb import set_trace as trace class wizard(model.CustomDialog): """ Defines a Wizard using hand-written rewource defs You should override the methods 'on_butPrev_mouseClick', 'on_butNext_mouseClick' and 'on_butCancel_mouseClick'. Run this module standalone to see a demo. """ def __init__(self, parent, layout, commonres, stateres, initstate): """ Wizard constructor. Arguments: - parent - 'background' instance which is launching this wizard - layout - resource layout (refer demo code below) - commonres - resources common to all 'stages' of the wizard, eg 'Next'/'Prev' buttons, heading etc. - stateres - a dict of resource definitions, keyed by each state - initstate - string - key of stateres - wizard's initial state After instantiating the wizard object, you 'launch' it with the 'showModal' method. """ model.CustomDialog.__init__(self, parent, layout) self.parent = parent self.commonResources = commonres self.stateResources = stateres self.state = None self.initstate = initstate self.setstate(initstate, 'first') def run(self): self.CenterOnScreen() self.setstate(self.initstate, 'first') self.showModal() def on_butPrev_mouseClick(self, event): print "YIKES - YOU HAVEN'T OVERRIDDEN 'on_butPrev_mouseClick'!" def on_butNext_mouseClick(self, event): print "YIKES - YOU HAVEN'T OVERRIDDEN 'on_butNext_mouseClick'!" def on_butFinish_mouseClick(self, event): self.Close() self._accepted = True def on_butCancel_mouseClick(self, event): result = dialog.messageDialog( self, 'Are you sure you want to cancel this wizard?', 'Confirm Quit', dialog.ICON_INFORMATION, dialog.BUTTON_YES_NO | dialog.BUTTON_NO_DEFAULT) if result['accepted']: self.Close() self._accepted = 0 def setstate(self, state, frame='middle'): """ Set the wizard to a new state. Remove the widgets belonging to its existing state, and add the widgets for the next state Arguments: - newstate - text string, key into the 'stateres' dict which was passed to the constructor - frame - 'first', 'middle' or 'last', dictates appearance of buttons default 'middle' """ if self.state == None: # add in common resources for k in self.commonResources.keys(): self.components[k] = self.commonResources[k] else: # remove current state's widgets for k in self.stateResources[self.state]: del self.components[k] # Add widgets for the new state newcomps = self.stateResources[state] for k in newcomps.keys(): self.components[k] = newcomps[k] self.state = state # modify 'next'/'prev' buttons if frame == 'first': # hide 'prev' button, label 'next' as 'next' #print dir(self.components['butPrev']) self.components['butPrev'].Hide() self.components['butFinish'].Hide() self.components['butNext'].Show() elif frame == 'middle': # show 'prev' and 'next' buttons, label 'next' as 'next' self.components['butPrev'].Show() self.components['butFinish'].Hide() self.components['butNext'].Show() elif frame == 'last': # show 'prev' and 'next' buttons, label 'next' as 'finish' self.components['butPrev'].Show() self.components['butFinish'].Show() self.components['butNext'].Hide() def demo(): # Run up a demo of the wizard class # Step 1: Define wizard layout wizRsrcLayout = {'type':'CustomDialog', 'name':'psstSetup', 'title':'PSST Setup Wizard', 'position':(475, 537), 'size':(433, 357), 'components': [] } # Step 2: Define wizard's common resources wizRsrcCommon = { 'stat1': {'type':'StaticText', 'name':'StaticText1', 'position':(145, 5), 'text':'Wizard Demo', }, 'butPrev' : {'type':'Button', 'name':'butPrev', 'position':(90, 305), 'size':(120, 27), 'label':'Prev', 'visible':1, }, 'butNext' : {'type':'Button', 'name':'butNext', 'position':(220, 305), 'size':(120, 27), 'label':'Next', }, 'butFinish' : {'type':'Button', 'name':'butFinish', 'position':(220, 305), 'size':(120, 27), 'label':'Finish', }, 'butCancel' : {'type':'Button', 'name':'butCancel', 'position':(370, 305), 'size':(55, 27), 'label':'Cancel', }, } # Step 3: Create layouts for each of the wizard's 'states' # layout for initial, 'start' layout wizRsrcStateStart = { 'stat1' : {'type':'StaticText', 'name':'StaticText2', 'position':(10, 10), 'text':'Stage 1', }, 'stat2' : {'type':'StaticText', 'name':'StaticText3', 'position':(140, 100), 'text':'Hi and welcome to this wizard', }, 'stat3' : {'type':'StaticText', 'name':'StaticText4', 'position':(60, 140), 'text':'Since this is the your first time running the program,', }, 'stat4' : {'type':'StaticText', 'name':'StaticText6', 'position':(115, 160), 'text':"we'll need to set up a few things.", }, 'stat5' : {'type':'StaticText', 'name':'StaticText5', 'position':(150, 205), 'text':"Click 'Next' to continue...", }, } # layout second (and final) frame wizRsrcStateStage2 = { 'stat5' : {'type':'StaticText', 'name':'StaticTextS2_1', 'position':(60, 120), 'text':'stage2 - finished', }, } # Step 4 - Stick these states into a dict wizRsrcStates = { 'start' : wizRsrcStateStart, 'stage2' : wizRsrcStateStage2 } # Step 5 - subclass the 'wizard' class class demoWizard(wizard): # override the 'nezt' button def on_butNext_mouseClick(self, event): if self.state == 'start': self.setstate('stage2', 'last') elif self.state == 'stage2': # Finish print "Wizard: finished" # override the 'prev' button def on_butPrev_mouseClick(self, event): if self.state == 'stage2': self.setstate('start', 'first') # # Done! Now create a minimal parent background window that # shows this wizard in action # demorsrc = {'stack':{'type':'Stack', 'name':'wizardDemo', 'backgrounds': [{'type':'Background', 'name':'bgMin', 'title':'wizard demo', 'size':(180, 100), 'components': [] # end components } # end background ] # end backgrounds } } class demowin(model.Background): def on_openBackground(self, event): # add button and field to this window self.components['stat'] = {'type':'StaticText', 'name':'stat', 'position':(45, 10), 'text':'Wizard Result'} self.components['fldResult'] = {'type':'TextField', 'name':'fldResult', 'position':(45,30), 'text':''} self.components['butRunWizard'] = {'type':'Button', 'name':'butRunWiz', 'position':(45,70), 'label':'Run Wizard'} # center ourself self.CenterOnScreen() # instantiate our very own wizard self.demowiz = demoWizard(self, wizRsrcLayout, wizRsrcCommon, wizRsrcStates, 'start') def on_butRunWiz_mouseClick(self, event): # Launch the wizard self.demowiz.run() print "Wizard completed" # Get the result - did user finish or cancel? fldRes = self.components['fldResult'] if self.demowiz.accepted(): fldRes.text = 'accepted' else: fldRes.text = 'declined' def on_close(self, event): # Need to do this because, when the wizard dialog gets closed with its 'Close' # method, it doesn't get fully cleaned up, which means the app hangs around # like a bad flatulence, and won't terminate without a little 'encouragement' print "Demo app closed" sys.exit(0) # # Now instantiate and run the demo app # demoapp = model.PythonCardApp(demowin, None, demorsrc) demoapp.MainLoop() if __name__ == '__main__': demo()