Changeset 4164

Show
Ignore:
Timestamp:
06/19/08 10:27:53 (5 months ago)
Author:
ed
Message:

Major re-write to the dDialog subclasses. Specifically, there is now a subclass called 'dStandardButtonDialog' that can accept one or more of the following parameters when it is created:

OK
Cancel
Yes
No
Help


It uses the wx standard button sizer methods to create the buttons following platform-specific layout guidelines. Each button has a corresponding on* (e.g., onYes, onHelp) handler which is called when the button is clicked.

There are two subclasses of dStandardButtonDialog included: dOkCancelDialog, and dYesNoDialog. dOkCancelDialog is pretty much the same as before; while dYesNoDialog is like dOkCancelDialog, except with Yes/No buttons. If you call dStandardButtonDialog without passing any parameters, a single OK button will be displayed.

I've tested this with my apps, and it seems to work seamlessly. Please consider it beta until a few more people have played around with it.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • trunk/dabo/ui/uiwx/__init__.py

    r4160 r4164  
    8080from dDropdownList import dDropdownList 
    8181from dDialog import dDialog 
     82from dDialog import dStandardButtonDialog 
    8283from dDialog import dOkCancelDialog 
     84from dDialog import dYesNoDialog 
    8385from dEditableList import dEditableList 
    8486from dEditBox import dEditBox 
  • trunk/dabo/ui/uiwx/dDialog.py

    r4028 r4164  
    204204 
    205205 
    206 class dOkCancelDialog(dDialog): 
    207     """Creates a dialog with OK/Cancel buttons and associated functionality. 
    208  
    209     Add your custom controls in the addControls() hook method, and respond to 
    210     the pressing of the Ok and Cancel buttons in the onOK() and onCancel()  
    211     event handlers. The default behavior in both cases is just to close the 
    212     form, and you can query the Accepted property to find out if the user  
    213     pressed "OK" or not. 
     206class dStandardButtonDialog(dDialog): 
     207    """Creates a dialog with standard buttons and associated functionality. You can  
     208    choose the buttons that display by passing True for any of the following  
     209    properties: 
     210     
     211        OK 
     212        Cancel 
     213        Yes 
     214        No 
     215        Help 
     216 
     217    If you don't specify buttons, only the OK will be included; if you do specify buttons, 
     218    you must specify them all; in other words, OK is only assumed if nothing is specified. 
     219    Then add your custom controls in the addControls() hook method, and respond to 
     220    the pressing of the standard buttons in the on*() handlers, where * is the name of the  
     221    associated property (e.g., onOK(), onNo(), etc.). You can query the Accepted property  
     222    to find out if the user pressed "OK" or "Yes"; if neither of these was pressed,  
     223    Accepted will be False. 
    214224    """ 
    215225    def __init__(self, parent=None, properties=None, *args, **kwargs): 
    216         super(dOkCancelDialog, self).__init__(parent=parent, properties=properties, *args, **kwargs) 
    217         self._baseClass = dOkCancelDialog 
     226        self._ok = self._extractKey((properties, kwargs), "OK") 
     227        self._cancel = self._extractKey((properties, kwargs), "Cancel") 
     228        self._yes = self._extractKey((properties, kwargs), "Yes") 
     229        self._no = self._extractKey((properties, kwargs), "No") 
     230        self._help = self._extractKey((properties, kwargs), "Help") 
     231        super(dStandardButtonDialog, self).__init__(parent=parent, properties=properties, *args, **kwargs) 
     232        self._baseClass = dStandardButtonDialog 
    218233        self._accepted = False 
    219234 
     
    226241        sz.append((0, sz.DefaultBorder)) 
    227242 
    228         # Define Ok/Cancel, and tell wx that we want stock buttons. 
    229         # We are creating them now, so that the user code can access them if needed. 
    230         self.btnOK = dabo.ui.dButton(self, id=wx.ID_OK, DefaultButton=True) 
    231         self.btnOK.bindEvent(dEvents.Hit, self.onOK) 
    232         self.btnCancel = dabo.ui.dButton(self, id=wx.ID_CANCEL, CancelButton=True) 
    233         self.btnCancel.bindEvent(dEvents.Hit, self.onCancel) 
    234          
    235         # Put the buttons in a StdDialogButtonSizer, so they get positioned/sized 
    236         # per the native platform conventions: 
    237         buttonSizer = wx.StdDialogButtonSizer() 
    238         buttonSizer.AddButton(self.btnOK) 
    239         buttonSizer.AddButton(self.btnCancel) 
    240         buttonSizer.Realize() 
    241         self._btnSizer = bsWrapper = dabo.ui.dSizer("v") 
    242         bsWrapper.append(buttonSizer, "x") 
    243      
     243        # Add the specified buttons. If none were specified, then just add OK 
     244        ok = self._ok 
     245        cancel = self._cancel 
     246        yes = self._yes 
     247        no = self._no 
     248        help = self._help 
     249        if (ok is None and cancel is None and yes is None and  
     250                no is None and help is None): 
     251            ok = True 
     252         
     253        flags = 0 
     254        if ok: 
     255            flags = flags | wx.OK 
     256        if cancel: 
     257            flags = flags | wx.CANCEL 
     258        if yes: 
     259            flags = flags | wx.YES 
     260        if no: 
     261            flags = flags | wx.NO 
     262        if help: 
     263            flags = flags | wx.HELP 
     264        if flags == 0: 
     265            # Nothing specified; default to just OK 
     266            flags = wx.OK 
     267        # Initialize the button references 
     268        self.btnOK = self.btnCancel = self.btnYes = self.btnNo = self.btnHelp = None 
     269        self.stdButtonSizer = sbs = self.CreateButtonSizer(flags) 
     270        btns = [b.GetWindow() for b in sbs.GetChildren() if b.IsWindow()] 
     271        for btn in btns: 
     272            id_ = btn.GetId() 
     273            if id_ == wx.ID_OK: 
     274                self.btnOK = btn 
     275                mthd = self._onOK 
     276            elif id_ == wx.ID_CANCEL: 
     277                self.btnCancel = btn 
     278                mthd = self._onCancel 
     279            elif id_ == wx.ID_YES: 
     280                self.btnYes = btn 
     281                mthd = self._onYes 
     282            elif id_ == wx.ID_NO: 
     283                self.btnNo = btn 
     284                mthd = self._onNo 
     285            elif id_ == wx.ID_HELP: 
     286                self.btnHelp = btn 
     287                mthd = self._onHelp 
     288            btn.Bind(wx.EVT_BUTTON, mthd) 
     289             
    244290        # Wx rearranges the order of the buttons per platform conventions, but 
    245291        # doesn't rearrange the tab order for us. So, we do it manually: 
    246292        buttons = [] 
    247         for child in buttonSizer.GetChildren(): 
     293        for child in sbs.GetChildren(): 
    248294            win = child.GetWindow() 
    249295            if win is not None: 
    250296                buttons.append(win) 
    251         buttons[1].MoveAfterInTabOrder(buttons[0]) 
     297        for pos, btn in enumerate(buttons[1:]): 
     298            btn.MoveAfterInTabOrder(buttons[pos-1]) 
     299        if cancel or no: 
     300            # The default Escape behavior destroys the dialog, so we need to replace 
     301            # this with out own. 
     302            self.SetEscapeId(wx.ID_NONE) 
     303            if cancel: 
     304                self.bindKey("esc", self._onCancel) 
     305            elif no: 
     306                self.bindKey("esc", self._onNo) 
    252307 
    253308        # Let the user add their controls 
    254         super(dOkCancelDialog, self)._addControls() 
     309        super(dStandardButtonDialog, self)._addControls() 
    255310 
    256311        # Just in case user changed Self.Sizer, update our reference: 
    257         sz = self.Sizer 
    258  
    259         if self.ButtonSizerPosition is None: 
    260             # User code didn't add it, so we must. 
    261             bs = dabo.ui.dSizer("v") 
    262             bs.append((0, sz.DefaultBorder/2)) 
    263             bs.append(self.ButtonSizer, "x") 
    264             bs.append((0, sz.DefaultBorder)) 
    265             sz.append(bs, "x") 
    266          
    267         self.layout() 
     312        bs = dabo.ui.dSizer("v") 
     313        bs.append((0, sz.DefaultBorder/2)) 
     314        bs.append(sbs, "x") 
     315        bs.append((0, sz.DefaultBorder)) 
     316        sz.append(bs, "x") 
     317 
     318    ################################################ 
     319    #  Handlers for the standard buttons 
     320    def _onOK(self, evt): 
     321        self.Accepted = True 
     322        self.onOK() 
     323        self.EndModal(kons.DLG_OK) 
     324    def _onCancel(self, evt): 
     325        self.onCancel() 
     326        self.EndModal(kons.DLG_CANCEL) 
     327    def _onYes(self, evt): 
     328        self.Accepted = True 
     329        self.onYes() 
     330        self.EndModal(kons.DLG_YES) 
     331    def _onNo(self, evt): 
     332        self.onNo() 
     333        self.EndModal(kons.DLG_NO) 
     334    def _onHelp(self, evt): 
     335        self.onHelp() 
     336    # The following are stub methods that can be overridden when needed. 
     337    def onOK(self): pass 
     338    def onCancel(self): pass 
     339    def onYes(self): pass 
     340    def onNo(self): pass 
     341    def onHelp(self): pass 
     342    ################################################ 
    268343 
    269344     
    270345    def addControls(self): 
    271         """Use this method to add controls to the dialog.  
    272  
    273         The OK/Cancel   buttons will be added after this method runs, so that they  
    274         appear at the bottom of the dialog. 
     346        """Use this method to add controls to the dialog. The standard buttons will be added  
     347        after this method runs, so that they appear at the bottom of the dialog. 
    275348        """ 
    276349        pass 
    277350     
    278      
    279     def _setEscapeBehavior(self): 
    280         """Bind/unbind the Cancel button to the escape key.""" 
    281         try: 
    282             self.btnCancel.CancelButton = self.ReleaseOnEscape 
    283             if self.ReleaseOnEscape: 
    284                 self.SetEscapeId(wx.ID_ANY) 
    285             else: 
    286                 self.SetEscapeId(wx.ID_NONE) 
    287         except AttributeError: 
    288             # Button hasn't been added yet 
    289             dabo.ui.callAfter(self._setEscapeBehavior) 
    290  
    291351     
    292352    def addControlSequence(self, seq): 
     
    318378         
    319379         
    320     def onOK(self, evt): 
    321         self.Accepted = True 
    322         self.EndModal(kons.DLG_OK) 
    323  
    324     def onCancel(self, evt): 
    325         self.Accepted = False 
    326         self.EndModal(kons.DLG_CANCEL) 
    327  
    328  
    329380    def _getAccepted(self): 
    330381        return self._accepted        
     
    335386     
    336387    def _getButtonSizer(self): 
    337         return getattr(self, "_btnSizer", None) 
    338  
    339  
    340     def _getButtonSizerPosition(self): 
    341         return self.ButtonSizer.getPositionInSizer() 
     388        return getattr(self, "stdButtonSizer", None) 
    342389 
    343390 
     
    346393 
    347394 
     395    def _getHelpButton(self): 
     396        return self.btnHelp 
     397         
     398 
    348399    def _getOKButton(self): 
    349400        return self.btnOK 
    350401         
    351402 
     403    def _getNoButton(self): 
     404        return self.btnNo 
     405         
     406 
     407    def _getYesButton(self): 
     408        return self.btnYes 
     409         
     410 
    352411    Accepted = property(_getAccepted, _setAccepted, None, 
    353412            _("Specifies whether the user accepted the dialog, or canceled.  (bool)")) 
     
    356415            _("Returns a reference to the sizer controlling the Ok/Cancel buttons.  (dSizer)")) 
    357416 
    358     ButtonSizerPosition = property(_getButtonSizerPosition, None, None, 
    359             _("""Returns the position of the Ok/Cancel buttons in the sizer.  (int)""")) 
    360  
    361417    CancelButton = property(_getCancelButton, None, None, 
    362             _("Reference to the Cancel button on the form  (dButton).")) 
    363      
    364     LastPositionInSizer = ButtonSizerPosition   ## backwards compatibility 
    365  
     418            _("Reference to the Cancel button on the form, if present  (dButton or None).")) 
     419     
     420    HelpButton = property(_getHelpButton, None, None, 
     421            _("Reference to the Help button on the form, if present  (dButton or None).")) 
     422     
     423    NoButton = property(_getNoButton, None, None, 
     424            _("Reference to the No button on the form, if present  (dButton or None).")) 
     425     
    366426    OKButton = property(_getOKButton, None, None, 
    367             _("Reference to the OK button on the form  (dButton).")) 
    368      
    369      
     427            _("Reference to the OK button on the form, if present  (dButton or None).")) 
     428     
     429    YesButton = property(_getYesButton, None, None, 
     430            _("Reference to the Yes button on the form, if present  (dButton or None).")) 
     431     
     432     
     433 
     434class dOkCancelDialog(dStandardButtonDialog): 
     435    def __init__(self, parent=None, properties=None, *args, **kwargs): 
     436        kwargs["Yes"] = kwargs["No"] = False 
     437        kwargs["OK"] = kwargs["Cancel"] = True 
     438        super(dOkCancelDialog, self).__init__(parent, properties, *args, **kwargs) 
     439        self._baseClass = dOkCancelDialog 
     440         
     441 
     442class dYesNoDialog(dStandardButtonDialog): 
     443    def __init__(self, parent=None, properties=None, *args, **kwargs): 
     444        kwargs["Yes"] = kwargs["No"] = True 
     445        kwargs["OK"] = kwargs["Cancel"] = False 
     446        super(dYesNoDialog, self).__init__(parent, properties, *args, **kwargs) 
     447        self._baseClass = dYesNoDialog 
     448 
    370449 
    371450 
     
    373452    import test 
    374453    test.Test().runTest(dDialog) 
     454    test.Test().runTest(dStandardButtonDialog) 
    375455    test.Test().runTest(dOkCancelDialog) 
     456    test.Test().runTest(dYesNoDialog)