Changeset 2888

Show
Ignore:
Timestamp:
03/07/07 09:35:34 (1 year ago)
Author:
uwe_grauer
Message:

merged changes from trunk into my branch

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • branches/uwe/dabo/biz/dBizobj.py

    r2856 r2888  
    393393 
    394394        self.afterCancel() 
     395         
     396     
     397    def deleteAllChildren(self, startTransaction=False): 
     398        """Delete all children associated with the current record without 
     399        deleting the current record in this bizobj. 
     400        """ 
     401        cursor = self._CurrentCursor 
     402        errMsg = self.beforeDeleteAllChildren() 
     403        if errMsg: 
     404            raise dException.BusinessRuleViolation, errMsg 
     405 
     406        if startTransaction: 
     407            cursor.beginTransaction() 
     408 
     409        try: 
     410            for child in self.__children: 
     411                child.deleteAll(startTransaction=False) 
     412            if startTransaction: 
     413                cursor.commitTransaction() 
     414            self.afterDeleteAllChildren() 
     415 
     416        except dException.DBQueryException, e: 
     417            if startTransaction: 
     418                cursor.rollbackTransaction() 
     419            raise dException.DBQueryException, e 
     420        except StandardError, e: 
     421            if startTransaction: 
     422                cursor.rollbackTransaction() 
     423            raise StandardError, e 
    395424 
    396425 
     
    444473            if startTransaction: 
    445474                cursor.rollbackTransaction() 
    446             else: 
    447                 raise dException.DBQueryException, e 
     475            raise dException.DBQueryException, e 
    448476        except StandardError, e: 
    449             cursor.rollbackTransaction() 
     477            if startTransaction: 
     478                cursor.rollbackTransaction() 
    450479            raise StandardError, e 
    451480 
     
    12911320    def prepareWhere(self, clause): 
    12921321        return self._CurrentCursor.prepareWhere(clause) 
    1293  
     1322    def getFieldClause(self): 
     1323        return self._CurrentCursor.getFieldClause() 
     1324    def getFromClause(self): 
     1325        return self._CurrentCursor.getFromClause() 
     1326    def getJoinClause(self): 
     1327        return self._CurrentCursor.getJoinClause() 
     1328    def getWhereClause(self): 
     1329        return self._CurrentCursor.getWhereClause() 
     1330    def getGroupByClause(self): 
     1331        return self._CurrentCursor.getGroupByClause() 
     1332    def getOrderByClause(self): 
     1333        return self._CurrentCursor.getOrderByClause() 
     1334    ########## END - SQL Builder interface section ############## 
    12941335 
    12951336 
     
    12991340    def beforeNew(self): return "" 
    13001341    def beforeDelete(self): return "" 
     1342    def beforeDeleteAllChildren(self): return "" 
    13011343    def beforeFirst(self): return "" 
    13021344    def beforePrior(self): return "" 
     
    13131355    def afterNew(self): pass 
    13141356    def afterDelete(self): pass 
     1357    def afterDeleteAllChildren(self): return "" 
    13151358    def afterFirst(self): pass 
    13161359    def afterPrior(self): pass 
  • branches/uwe/dabo/dColors.py

    r2497 r2888  
    1 import re 
     1import re, types 
     2 
     3class HexError(Exception): pass 
     4class InvalidCharError(HexError): pass 
     5class TypeError(HexError): pass 
     6 
     7class ColorTupleError(Exception): pass 
     8class RgbValueError(ColorTupleError): pass 
     9class LengthError(ColorTupleError): pass 
     10class IntegerTypeError(ColorTupleError): pass 
    211 
    312colorDict = {"aliceblue" : (240, 248, 255),  
     
    156165 
    157166def hexToDec(hx): 
     167    if not isinstance(hx, types.StringTypes): 
     168        raise TypeError, "Input must be a string" 
    158169    # Define a dict of char-value pairs 
    159170    hex = {"0": 0, "1": 1, "2": 2, "3": 3, "4": 4, "5": 5, "6": 6, "7": 7, "8": 8,  
     
    164175    pos = 1 
    165176    for c in rev: 
     177        if hex.get(c) == None: 
     178            raise InvalidCharError, "%s is an invalid hex character" % (c, ) 
    166179        ret += (hex[c] * pos) 
    167180        pos = pos * 16 
     
    171184def tupleToHex(t, includeHash=True): 
    172185    """Convert a color tuple into an HTML hex format.""" 
     186    if not len(t) == 3: 
     187        raise LengthError, "Color tuple needs to contain 3 elements" 
     188    for rgb in t: 
     189        if not isinstance(rgb, types.IntType): 
     190            raise IntegerTypeError, "Tuple elements should be all integers." 
     191        if not 0 <= rgb <= 255: 
     192            raise RgbValueError, "Rgb Value must be in the range 0-255" 
    173193    rx, gx, bx = hex(t[0]), hex(t[1]), hex(t[2]) 
    174194    # Each is in the format '0x00'. 
     
    176196    g = gx[2:].upper() 
    177197    b= bx[2:].upper() 
     198    if len(r) == 1: r = '0' + r 
     199    if len(g) == 1: g = '0' + g 
     200    if len(b) == 1: b = '0' + b 
    178201    ret = "" 
    179202    if includeHash: 
     
    186209    # Strip the pound sign, if any 
    187210    hx = hx.replace("#", "") 
     211    hx = hx.lstrip('0') 
     212    if len(hx) < 6: hx = '0'*(6-len(hx)) + hx 
    188213    red = hexToDec(hx[:2]) 
    189214    green = hexToDec(hx[2:4]) 
     
    217242        grps = mtch.groups() 
    218243        ret = (int(grps[0]), int(grps[1]), int(grps[2])) 
     244        for val in ret: 
     245            if not 0 <= val <= 255: 
     246                raise KeyError, "Color tuple integer must range from 0-255" 
    219247    else: 
    220248        raise KeyError, "Color '%s' is not defined." % color 
  • branches/uwe/dabo/dObject.py

    r2482 r2888  
    221221 
    222222    def _setBasePrefKey(self, val): 
     223        if not isinstance(val, types.StringTypes): 
     224            raise TypeError, 'BasePrefKey must be a string.' 
    223225        self._basePrefKey = val 
    224226        pm = self.PreferenceManager 
     
    229231 
    230232    def _getClass(self): 
    231         try: 
    232             return self.__class__ 
    233         except AttributeError: 
    234             return None 
     233        return self.__class__ 
    235234 
    236235 
     
    267266            return "?" 
    268267     
    269     def _setName(self, value): 
    270         self._name = str(value) 
     268    def _setName(self, val): 
     269        if not isinstance(val, types.StringTypes): 
     270            raise TypeError, 'Name must be a string.' 
     271        if not len(val.split()) == 1: 
     272            raise KeyError, 'Name must not contain any spaces' 
     273        self._name = val 
    271274         
    272275         
     
    293296 
    294297    def _setPreferenceManager(self, val): 
     298        if not isinstance(val, dPref): 
     299            raise TypeError, 'PreferenceManager must be a dPref object' 
    295300        self._preferenceManager = val 
    296  
    297  
    298     def _getSuperClass(self): 
    299         if self.BaseClass == self.Class: 
    300             # The superclass is lower down than Dabo, and useless to the user. 
    301             return None 
    302         else: 
    303             return self.__class__.__base__ 
    304301     
    305302 
     
    335332            _("Reference to the Preference Management object  (dPref)")) 
    336333     
    337     SuperClass = property(_getSuperClass, None, None,  
    338             _("The super class of the object. Read-only.  (class)")) 
    339  
    340334 
    341335if __name__ == "__main__": 
  • branches/uwe/dabo/lib/DesignerXmlConverter.py

    r2843 r2888  
    267267                isBorderSizer = clsname == "LayoutBorderSizer" 
    268268                ornt = "" 
     269                prnt = "" 
    269270                if not isGridSizer: 
    270                     propString = "Orientation='%s'" % self._extractKey(atts, "Orientation", "H") 
    271                 prnt = "
    272                 if isBorderSizer: 
    273                     prnt = "currParent, " 
    274                     propString = "'%s', Caption=\"%s\"" % (self._extractKey(atts, "Orientation", "H"),  
    275                            self._extractKey(atts, "Caption", "")
     271                    if isBorderSizer: 
     272                       prnt = "currParent,
     273                       propString = "'%s', Caption=\"%s\"" % (self._extractKey(atts, "Orientation", "H"),  
     274                               self._extractKey(atts, "Caption", "")) 
     275                    else: 
     276                        propString = "Orientation='%s'" % self._extractKey(atts, "Orientation", "H"
    276277                if self.CreateDesignerControls: 
    277278                    superName = clsname 
  • branches/uwe/dabo/ui/dialogs/htmlAbout.py

    r2600 r2888  
    100100    def onCopyInfo(self, evt): 
    101101        """Copy the system information to the Clipboard""" 
    102         self.Application.copyToClipboard(self.getInfoString(useHTML=False)) 
     102        info = self.getInfoString(useHTML=False) 
     103        appdoc = self.getAppSpecificString() 
     104        self.Application.copyToClipboard("\n\n".join((info, appdoc))) 
    103105 
    104106 
  • branches/uwe/dabo/ui/uiwx/__init__.py

    r2841 r2888  
    593593 
    594594 
    595 def getString(message="Please enter a string:", caption="Dabo",   defaultValue=""): 
     595def getString(message=_("Please enter a string:"), caption="Dabo",    defaultValue=""): 
    596596    """Simple dialog for returning a small bit of text from the user.""" 
    597597    dlg = wx.TextEntryDialog(_getActiveForm(), message, caption, defaultValue) 
     
    605605 
    606606 
    607 def getInt(message="Enter an integer value:", caption="Dabo",  defaultValue=0): 
     607def getInt(message=_("Enter an integer value:"), caption="Dabo", defaultValue=0): 
    608608    """Simple dialog for returning an integer value from the user.""" 
    609609    class IntDialog(dabo.ui.dOkCancelDialog): 
     
    626626    dlg.Destroy() 
    627627    return val 
     628     
     629 
     630# The next two methods prompt the user to select from a list. The first allows 
     631# a single selection, while the second allows for multiple selections. 
     632def getChoice(choices, message=None, caption=None, defaultPos=None): 
     633    """Simple dialog for presenting the user with a list of choices from which 
     634    they can select one item. 
     635    """ 
     636    return _getChoiceDialog(choices, message, caption, defaultPos, False) 
     637 
     638 
     639def getChoices(choices, message=None, caption=None, defaultPos=None): 
     640    """Simple dialog for presenting the user with a list of choices from which 
     641    they can select one or more items. Returns a tuple containing the selections. 
     642    """ 
     643    return _getChoiceDialog(choices, message, caption, defaultPos, True) 
     644 
     645 
     646def _getChoiceDialog(choices, message, caption, defaultPos, mult): 
     647    if message is None: 
     648        message = _("Please make your selection:") 
     649    if caption is None: 
     650        caption = "Dabo" 
     651    if defaultPos is None: 
     652        defaultPos = 0 
     653    if mult is None: 
     654        mult = False 
     655    class ChoiceDialog(dabo.ui.dOkCancelDialog): 
     656        def addControls(self): 
     657            self.Caption = caption 
     658            lbl = dabo.ui.dLabel(self, Caption=message) 
     659            self.lst = dabo.ui.dListBox(self, Choices=choices,  
     660                    PositionValue=defaultPos, MultipleSelect=mult,  
     661                    OnMouseLeftDoubleClick=self.onOK) 
     662            sz = self.Sizer 
     663            sz.appendSpacer(25) 
     664            sz.append(lbl, halign="center") 
     665            sz.appendSpacer(5) 
     666            sz.append(self.lst, 4, halign="center") 
     667            if mult: 
     668                hsz = dabo.ui.dSizer("h") 
     669                btnAll = dabo.ui.dButton(self, Caption=_("Select All"), 
     670                        OnHit=self.selectAll) 
     671                btnNone = dabo.ui.dButton(self, Caption=_("Unselect All"), 
     672                        OnHit=self.unselectAll) 
     673                hsz.append(btnAll) 
     674                hsz.appendSpacer(8) 
     675                hsz.append(btnNone) 
     676                sz.appendSpacer(16) 
     677                sz.append(hsz, halign="center", border=20) 
     678                sz.appendSpacer(8) 
     679                sz.append(dabo.ui.dLine(self), "x", border=44, 
     680                        borderSides=("left", "right")) 
     681            sz.appendSpacer(24) 
     682         
     683        def selectAll(self, evt): 
     684            self.lst.selectAll() 
     685         
     686        def unselectAll(self, evt): 
     687            self.lst.invertSelections() 
     688             
     689             
     690    dlg = ChoiceDialog(_getActiveForm()) 
     691    dlg.show() 
     692    if dlg.Accepted: 
     693        val = dlg.lst.StringValue 
     694    else: 
     695        val = None 
     696    dlg.release() 
     697    return val           
    628698 
    629699 
     
    781851 
    782852 
    783 def getFolder(message="Choose a folder", defaultPath="", wildcard="*"): 
     853def getFolder(message=_("Choose a folder"), defaultPath="", wildcard="*"): 
    784854    """Displays the folder selection dialog for the platform. 
    785855    Returns the path to the selected folder, or None if no selection 
  • branches/uwe/dabo/ui/uiwx/dImage.py

    r2833 r2888  
    102102            # No image to display 
    103103            self.Bitmap = wx.EmptyBitmap(1, 1) 
     104            self.Freeze() 
    104105            self.SetBitmap(self.Bitmap) 
     106            self.Thaw() 
    105107            return 
    106108 
     
    159161        self._bitmapWidth = self.Bitmap.GetWidth() 
    160162         
     163        self.Freeze() 
    161164        try: 
    162165            self.SetBitmap(self.Bitmap) 
    163166        except TypeError, e: pass 
     167        self.Thaw() 
    164168        self._inShowPic = True 
    165169        self.SetSize((origW, origH)) 
     
    289293        def afterInit(self): 
    290294            self.Caption = "dImage Demonstration" 
     295            self.mainPanel = mp = dabo.ui.dPanel(self) 
     296            self.Sizer.append1x(mp) 
     297            sz = dabo.ui.dSizer("v") 
     298            mp.Sizer = sz 
    291299            # Create a panel with horiz. and vert.  sliders 
    292             self.imgPanel = dabo.ui.dPanel(self
    293             self.VSlider = dabo.ui.dSlider(self, Orientation="V", Min=1, Max=100, 
     300            self.imgPanel = dabo.ui.dPanel(mp
     301            self.VSlider = dabo.ui.dSlider(mp, Orientation="V", Min=1, Max=100, 
    294302                Value=100, OnHit=self.onSlider) 
    295             self.HSlider = dabo.ui.dSlider(self, Orientation="H", Min=1, Max=100, 
     303            self.HSlider = dabo.ui.dSlider(mp, Orientation="H", Min=1, Max=100, 
    296304                Value=100, OnHit=self.onSlider) 
    297305             
     
    301309            hsz.appendSpacer(10) 
    302310            hsz.append(self.VSlider, 0, "x") 
    303             self.Sizer.DefaultBorder = 25 
    304             self.Sizer.DefaultBorderLeft = self.Sizer.DefaultBorderRight = True 
    305             self.Sizer.appendSpacer(25) 
    306             self.Sizer.append(hsz, 1, "x") 
    307             self.Sizer.appendSpacer(10) 
    308             self.Sizer.append(self.HSlider, 0, "x") 
    309             self.Sizer.appendSpacer(10) 
     311            sz.DefaultBorder = 25 
     312            sz.DefaultBorderLeft = sz.DefaultBorderRight = True 
     313            sz.appendSpacer(25) 
     314            sz.append(hsz, 1, "x") 
     315            sz.appendSpacer(10) 
     316            sz.append(self.HSlider, 0, "x") 
     317            sz.appendSpacer(10) 
    310318 
    311319            # Create the image control 
     
    314322            hsz = dabo.ui.dSizer("H") 
    315323            hsz.DefaultSpacing = 10 
    316             dabo.ui.dBitmapButton(self, RegID="btnRotateCW", 
     324            dabo.ui.dBitmapButton(mp, RegID="btnRotateCW", 
    317325                    Picture="rotateCW", OnHit=self.rotateCW) 
    318             dabo.ui.dBitmapButton(self, RegID="btnRotateCCW", 
     326            dabo.ui.dBitmapButton(mp, RegID="btnRotateCCW", 
    319327                    Picture="rotateCCW", OnHit=self.rotateCCW) 
    320328            hsz.append(self.btnRotateCW) 
    321329            hsz.append(self.btnRotateCCW)            
    322             self.ddScale = dabo.ui.dDropdownList(self,  
     330            self.ddScale = dabo.ui.dDropdownList(mp,  
    323331                    Choices=["Proportional", "Stretch", "Clip"], 
    324332                    DataSource = "self.Form.img", 
    325333                    DataField = "ScaleMode") 
    326334            self.ddScale.PositionValue = 0 
    327             btn = dabo.ui.dButton(self, Caption="Load Image",  
     335            btn = dabo.ui.dButton(mp, Caption="Load Image",  
    328336                    OnHit=self.onLoadImage) 
    329             btnOK = dabo.ui.dButton(self, Caption="Done", OnHit=self.close) 
     337            btnOK = dabo.ui.dButton(mp, Caption="Done", OnHit=self.close) 
    330338            hsz.append(self.ddScale, 0, "x") 
    331339            hsz.append(btn, 0, "x") 
    332340            hsz.append(btnOK, 0, "x") 
    333             self.Sizer.append(hsz, 0, alignment="right") 
    334             self.Sizer.appendSpacer(25) 
     341            sz.append(hsz, 0, alignment="right") 
     342            sz.appendSpacer(25) 
    335343             
    336344            # Set the idle update flage 
  • branches/uwe/dabo/ui/uiwx/dImageMixin.py

    r2386 r2888  
    9191            else: 
    9292                bmp = dabo.ui.strToBmp(val, self._imgScale, self._imgWd, self._imgHt) 
     93            self.Freeze() 
    9394            self.SetBitmap(bmp) 
     95            self.Thaw() 
    9496        else: 
    9597            self._properties["Picture"] = val 
  • branches/uwe/dabo/ui/uiwx/dListBox.py

    r2788 r2888  
    2828        for elem in self.GetSelections(): 
    2929            self.SetSelection(elem, False) 
     30     
     31     
     32    def selectAll(self): 
     33        if self.MultipleSelect: 
     34            for ii in xrange(self.Count): 
     35                self.SetSelection(ii) 
     36     
     37     
     38    def unselectAll(self): 
     39        self.clearSelections() 
    3040 
    3141         
     42    def invertSelections(self): 
     43        """Switch all the items from False to True, and vice-versa.""" 
     44        for ii in xrange(self.Count): 
     45            if self.IsSelected(ii): 
     46                self.Deselect(ii) 
     47            else: 
     48                self.SetSelection(ii) 
     49         
     50     
    3251    def _getMultipleSelect(self): 
    3352        return self._hasWindowStyleFlag(wx.LB_EXTENDED) 
  • branches/uwe/dabo/ui/uiwx/dListControl.py

    r2788 r2888  
    6262     
    6363     
     64    def removeColumn(self, pos=None): 
     65        """Removes the specified column, or the last column if 
     66        no column number is passed. 
     67        """ 
     68        if pos is None: 
     69            pos = self.GetColumnCount() -1 
     70        self.DeleteColumn(pos) 
     71     
     72     
    6473    def setColumns(self, colList): 
    6574        """ Accepts a list/tuple of column headings, removes any 
     
    346355             
    347356 
    348     def _getColCount(self): 
     357    def _getColumnCount(self): 
    349358        return self.GetColumnCount() 
     359 
     360    def _setColumnCount(self, val): 
     361        if self._constructed(): 
     362            cc = self.GetColumnCount() 
     363            if val < cc: 
     364                # Remove rightmost columns 
     365                while val < self.GetColumnCount(): 
     366                    self.removeColumn() 
     367            elif val > cc: 
     368                while val > self.GetColumnCount(): 
     369                    self.addColumn(_("Column %s") % self.GetColumnCount())               
     370        else: 
     371            self._properties["ColumnCount"] = val 
    350372         
    351373         
     
    452474 
    453475 
    454     ColumnCount = property(_getColCount, None, None,  
    455             _("Number of columns in the control (read-only).  (int)")
     476    ColumnCount = property(_getColumnCount, _setColumnCount, None, 
     477            _("Number of columns in the control (int)")
    456478 
    457479    Count = property(_getRowCount, None, None,  
  • branches/uwe/dabo/ui/uiwx/dPanel.py

    r2788 r2888  
    1313    def __init__(self, preClass, parent, properties=None, attProperties=None,  
    1414            *args, **kwargs): 
     15        self._minSizerWidth = 10 
     16        self._minSizerHeight = 10 
    1517        self._buffered = None 
    1618        buff = self._extractKey(attProperties, "Buffered", None) 
     
    3436            # Set the panel's minimum size back to zero. This is sometimes 
    3537            # necessary when the items in the panel have reduced in size. 
    36             self.SetMinSize((0, 0)) 
     38            self.SetMinSize((self.MinSizerWidth, self.MinSizerHeight)) 
    3739        self.Layout() 
    3840        for child in self.Children: 
     
    9698         
    9799 
     100    def _getMinSizerHeight(self): 
     101        return self._minSizerHeight 
     102 
     103    def _setMinSizerHeight(self, val): 
     104        if self._constructed(): 
     105            self._minSizerHeight = val 
     106        else: 
     107            self._properties["MinSizerHeight"] = val 
     108 
     109 
     110    def _getMinSizerWidth(self): 
     111        return self._minSizerWidth 
     112 
     113    def _setMinSizerWidth(self, val): 
     114        if self._constructed(): 
     115            self._minSizerWidth = val 
     116        else: 
     117            self._properties["MinSizerWidth"] = val 
     118 
     119 
    98120    Buffered = property(_getBuffered, _setBuffered, None, 
    99121            _("Does this panel use double-buffering to create smooth redrawing?  (bool)")) 
    100122 
     123    MinSizerHeight = property(_getMinSizerHeight, _setMinSizerHeight, None, 
     124            _("Minimum height for the panel. Default=10px  (int)")) 
     125     
     126    MinSizerWidth = property(_getMinSizerWidth, _setMinSizerWidth, None, 
     127            _("Minimum width for the panel. Default=10px  (int)")) 
     128     
     129     
    101130 
    102131 
  • branches/uwe/dabo/ui/uiwx/dSplitter.py

    r2788 r2888  
    6868        self.Sizer.append(win, 1, "expand") 
    6969        self.splitter = win 
    70         self.Layout() 
     70        self.layout() 
    7171         
    7272     
     
    107107    def __init__(self, parent, properties=None, attProperties=None, *args, **kwargs): 
    108108        self._baseClass = dSplitter 
    109         baseStyle = wx.SP_3D | wx.SP_PERMIT_UNSPLIT 
     109        baseStyle = wx.SP_3D | wx.SP_LIVE_UPDATE | wx.SP_PERMIT_UNSPLIT 
    110110        style = self._extractKey((kwargs, properties, attProperties), "style", baseStyle) 
    111111        self._createPanes = self._extractKey(attProperties, "createPanes", None) 
     
    128128             
    129129        # Default to vertical split 
    130         self._orientation = "v" 
     130        self._orientation = self._extractKey((kwargs, properties, attProperties), "Orientation", "v") 
    131131        self._sashPos = 100 
    132132        self._p1 = self._p2 = None 
     
    188188        self.Initialize(pnl) 
    189189     
     190     
     191    def layout(self): 
     192        self.Panel1.layout() 
     193        self.Panel2.layout() 
     194         
    190195     
    191196    def _onSashDClick(self, evt): 
     
    226231        else: 
    227232            self.SplitVertically(self.Panel1, self.Panel2, pos) 
    228         self.Layout() 
     233        self.layout() 
    229234     
    230235     
     
    234239            self._getSashPosition() 
    235240            self.Unsplit(win) 
    236             self.Layout() 
     241            self.layout() 
    237242             
    238243     
  • branches/uwe/dabo/ui/uiwx/dTreeView.py

    r2824 r2888  
    451451        """ 
    452452        self.lockDisplay() 
    453         ndExp = [(nd, nd.Expanded) for nd in self.nodes] 
     453        ndExp = [(nd, nd.Expanded) for nd in self.nodes 
     454 
     455                if ((not nd.IsRootNode) or self.ShowRootNode)] 
    454456        self.collapseAll() 
    455457        for nd, exp in ndExp: 
     
    460462    def getRootNode(self): 
    461463        return self._rootNode 
    462      
     464 
    463465 
    464466    def setRootNode(self, txt): 
     
    12571259 
    12581260 
    1259      
    12601261    app = dabo.dApp() 
    12611262    app.MainFormClass = TreeViewTestForm 
  • branches/uwe/dabo/ui/uiwx/uiApp.py

    r2856 r2888  
    312312                orphan.close() 
    313313            # Now close the main form. It will close any of its children. 
    314             self.dApp.MainForm.close() 
     314            mf = self.dApp.MainForm 
     315            if not mf._finito: 
     316                mf.close() 
    315317        else: 
    316318            while frms: