dabo Commit
Revision 4386
Date: 2008-08-09 19:21:19 -0700 (Sat, 09 Aug 2008)
Author: Ed
Trac: http://svn.dabodev.com/trac/dabo/changeset/4386
Changed:
U trunk/dabo/ui/uiwx/__init__.py
D trunk/dabo/ui/uiwx/dFoldPanelBar.py
U trunk/dabo/ui/uiwx/dGrid.py
U trunk/dabo/ui/uiwx/dPageFrameMixin.py
U trunk/dabo/ui/uiwx/dPemMixin.py
A trunk/dabo/ui/uiwx/dSlidePanelControl.py
Log:
Made it simple to create custom grid editors using Dabo controls.
To create the editor, just pass the control that you want to use to
dabo.ui.makeGridEditor(). It will return a class that you can then assign to a
column's CustomEditorClass property.
Since editors by default will be sized to the grid cell they are editing, I
added two optional parameters to makeGridEditor(). You can pass 'minWidth' and
'minHeight' values; the class will then ensure that the editor is displayed
with at least those dimensions.
Renamed the dFoldPanelBar/dFoldPanel classes to dSlidePanelControl/dSlidePanel,
repectively. Added deprecation warnings if the old names are used.
Lots and lots of improvements to these classes; too many to list. I'm still not
100% satisfied with them, though, and will continue to work on them, but they
are stable and work as they should.
Diff:
Modified: trunk/dabo/ui/uiwx/__init__.py
===================================================================
--- trunk/dabo/ui/uiwx/__init__.py 2008-08-09 17:01:11 UTC (rev 4385)
+++ trunk/dabo/ui/uiwx/__init__.py 2008-08-10 02:21:19 UTC (rev 4386)
@@ -7,6 +7,7 @@
import datetime
import time
import cStringIO
+import warnings
from dabo.dLocalize import _
######################################################
@@ -88,8 +89,8 @@
from dFileDialog import dFileDialog
from dFileDialog import dFolderDialog
from dFileDialog import dSaveDialog
-from dFoldPanelBar import dFoldPanelBar
-from dFoldPanelBar import dFoldPanel
+from dSlidePanelControl import dSlidePanelControl
+from dSlidePanelControl import dSlidePanel
from dFont import dFont
from dFontDialog import dFontDialog
from dForm import dForm
@@ -155,6 +156,17 @@
from dDockForm import dDockForm
from dPageFrame import dDockTabs
+# Support the old names, but issue deprecation warnings.
+class dFoldPanelBar(dSlidePanelControl):
+ def __init__(self, *args, **kwargs):
+ warnings.warn(_("'dFoldPanelBar' is a deprecated name. Use
'dSlidePanelControl' instead"), DeprecationWarning)
+ super(dFoldPanelBar, self).__init__(*args, **kwargs)
+class dFoldPanel(dSlidePanel):
+ def __init__(self, *args, **kwargs):
+ warnings.warn(_("'dFoldPanel' is a deprecated name. Use
'dSlidePanel' instead"), DeprecationWarning)
+ super(dFoldPanel, self).__init__(*args, **kwargs)
+
+
artConstants = {}
for item in (it for it in dir(wx) if it.startswith("ART_")):
daboConstant = item[4:].lower().replace("_", "")
@@ -1172,6 +1184,110 @@
return mb
+def makeGridEditor(controlClass, minWidth=None, minHeight=None):
+ class _BaseCellEditor(wx.grid.PyGridCellEditor):
+ _controlClass = None
+ _minWidth = None
+ _minHeight = None
+
+ def Create(self, parent, id, evtHandler):
+ """Called to create the control, which must derive from
wx.Control.
+ *Must Override*
+ """
+ if not self._controlClass:
+ raise TypeError, _("Cannot create custom editor
without a control class specified.")
+ self._control = self._controlClass(parent)
+ self.SetControl(self._control)
+ if evtHandler:
+ self._control.PushEventHandler(evtHandler)
+
+ def SetSize(self, rect):
+ """Called to position/size the edit control within the
cell rectangle.
+ If you don't fill the cell (the rect) then be sure to
override
+ PaintBackground and do something meaningful there.
+ """
+ wd = rect.width + 2
+ if self._minWidth:
+ wd = max(self._minWidth, wd)
+ ht = rect.height+2
+ if self._minHeight:
+ ht = max(self._minHeight, ht)
+ self._control.SetDimensions(rect.x, rect.y, wd, ht,
wx.SIZE_ALLOW_MINUS_ONE)
+
+ def PaintBackground(self, rect, attr):
+ """Draws the part of the cell not occupied by the edit
control. The
+ base class version just fills it with background
colour from the
+ attribute. In this class the edit control fills
the whole cell so
+ don't do anything at all in order to reduce flicker.
+ """
+ pass
+
+ def BeginEdit(self, row, col, grid):
+ """Fetch the value from the table and prepare the edit
control
+ to begin editing. Set the focus to the edit control.
+ *Must Override*
+ """
+ self.startValue = grid.GetTable().GetValue(row, col)
+ self._control.Value = self.startValue
+ self._control.setFocus()
+
+ def EndEdit(self, row, col, grid):
+ """Complete the editing of the current cell. Returns
True if the value
+ has changed. If necessary, the control may be
destroyed.
+ *Must Override*
+ """
+ changed = False
+ val = self._control.Value
+ if val != self.startValue:
+ changed = True
+ grid.GetTable().SetValue(row, col, val) #
update the table
+ self.startValue = None
+ return changed
+
+ def Reset(self):
+ """Reset the value in the control back to its starting
value.
+ *Must Override*
+ """
+ self._control.Value = self.startValue
+
+ def IsAcceptedKey(self, evt):
+ """Return True to allow the given key to start editing:
the base class
+ version only checks that the event has no modifiers.
F2 is special
+ and will always start the editor.
+ """
+ return (not (evt.ControlDown() or evt.AltDown()) and
+ evt.GetKeyCode() != wx.WXK_SHIFT)
+
+ def StartingKey(self, evt):
+ """If the editor is enabled by pressing keys on the
grid, this will be
+ called to let the editor do something about that first
key if desired.
+ """
+ pass
+
+ def StartingClick(self):
+ """If the editor is enabled by clicking on the cell,
this method will be
+ called to allow the editor to simulate the click on the
control if
+ needed.
+ """
+ pass
+
+ def Destroy(self):
+ """final cleanup"""
+ self.base_Destroy()
+
+ def Clone(self):
+ """Create a new object which is the copy of this one
+ *Must Override*
+ """
+ return self.__class__
+
+ class _CustomEditor(_BaseCellEditor):
+ _controlClass = controlClass
+ _minWidth = minWidth
+ _minHeight = minHeight
+ return _CustomEditor
+
+
def browse(dataSource, parent=None, keyCaption=None, includeFields=None,
colOrder=None, colWidths=None, colTypes=None,
autoSizeCols=True):
"""Given a data source, a form with a grid containing the data
Deleted: trunk/dabo/ui/uiwx/dFoldPanelBar.py
Modified: trunk/dabo/ui/uiwx/dGrid.py
===================================================================
--- trunk/dabo/ui/uiwx/dGrid.py 2008-08-09 17:01:11 UTC (rev 4385)
+++ trunk/dabo/ui/uiwx/dGrid.py 2008-08-10 02:21:19 UTC (rev 4386)
@@ -4666,6 +4666,18 @@
col.HeaderBackColor = "orange"
col.HeaderVerticalAlignment = "Top"
col.HeaderHorizontalAlignment = "Left"
+
+ # Let's make a custom editor for the name
+ class ColoredText(dabo.ui.dTextBox):
+ def initProperties(self):
+ self.ForeColor = "blue"
+ self.FontItalic = True
+ self.FontSize = 24
+ def onKeyChar(self, evt):
+ self.ForeColor = dabo.dColors.randomColor()
+ self.FontItalic = not self.FontItalic
+ # Since we're using a big font, set a minimum height for the
editor
+ col.CustomEditorClass = dabo.ui.makeGridEditor(ColoredText,
minHeight=40)
self.addColumn(Name="Age", Order=30, DataField="age",
DataType="integer", Width=40, Caption="Age",
Modified: trunk/dabo/ui/uiwx/dPageFrameMixin.py
===================================================================
--- trunk/dabo/ui/uiwx/dPageFrameMixin.py 2008-08-09 17:01:11 UTC (rev
4385)
+++ trunk/dabo/ui/uiwx/dPageFrameMixin.py 2008-08-10 02:21:19 UTC (rev
4386)
@@ -314,6 +314,7 @@
self.DeletePage(i-1)
else:
self._properties["PageCount"] = val
+
def _getPgs(self):
## pkm: It is possible for pages to not be instances of dPage
Modified: trunk/dabo/ui/uiwx/dPemMixin.py
===================================================================
--- trunk/dabo/ui/uiwx/dPemMixin.py 2008-08-09 17:01:11 UTC (rev 4385)
+++ trunk/dabo/ui/uiwx/dPemMixin.py 2008-08-10 02:21:19 UTC (rev 4386)
@@ -123,7 +123,7 @@
properties = dictStringify(properties)
# Hacks to fix up various things:
- import dMenuBar, dMenuItem, dMenu, dFoldPanelBar, dToggleButton
+ import dMenuBar, dMenuItem, dMenu, dSlidePanelControl,
dToggleButton
if isinstance(self, dMenuItem.dMenuItem):
# Hack: wx.MenuItem doesn't take a style arg,
# and the parent arg is parentMenu.
@@ -139,8 +139,9 @@
del(self._preInitProperties["style"])
del(self._preInitProperties["id"])
del(self._preInitProperties["parent"])
- elif isinstance(self, (dFoldPanelBar.dFoldPanel,
dFoldPanelBar.dFoldPanelBar)):
- # Hack: the FoldPanel classes have no style arg.
+ elif isinstance(self, (dabo.ui.dSlidePanel,
dabo.ui.dSlidePanelControl,
+ dSlidePanelControl.dSlidePanel,
dSlidePanelControl.dSlidePanelControl)):
+ # Hack: the Slide Panel classes have no style arg.
del self._preInitProperties["style"]
# This is needed because these classes require a
'parent' param.
kwargs["parent"] = parent
Added: trunk/dabo/ui/uiwx/dSlidePanelControl.py
===================================================================
--- trunk/dabo/ui/uiwx/dSlidePanelControl.py (rev 0)
+++ trunk/dabo/ui/uiwx/dSlidePanelControl.py 2008-08-10 02:21:19 UTC (rev
4386)
@@ -0,0 +1,782 @@
+ # -*- coding: utf-8 -*-
+import wx
+import wx.lib.foldpanelbar as fpb
+import dabo
+if __name__ == "__main__":
+ dabo.ui.loadUI("wx")
+import dControlMixin as dcm
+import dabo.dEvents as dEvents
+import dabo.dColors as dColors
+from dabo.dLocalize import _
+from dabo.ui import makeDynamicProperty
+
+
+class dSlidePanel(dcm.dControlMixin, fpb.FoldPanelItem):
+ def __init__(self, parent, properties=None, attProperties=None, *args,
**kwargs):
+ self._baseClass = dSlidePanel
+ preClass = fpb.FoldPanelItem
+ self._widthAlreadySet = self._heightAlreadySet = True
+ self._border = 5
+
+ # This needs to be set *after* the panel is added to its parent
+ collapsed = self._extractKey(attProperties, "Collapsed", None)
+ if collapsed is not None:
+ collapsed = (collapsed == "True")
+ else:
+ collapsed = self._extractKey((kwargs, properties),
"Collapsed", None)
+ if collapsed is None:
+ # They might have passed it as 'Expanded'
+ collapsed = not self._extractKey((kwargs,
properties), "Expanded", True)
+
+ cbstyle = self._extractKey((kwargs, properties), "cbstyle",
None)
+ if cbstyle is None:
+ kwargs["cbstyle"] = fpb.CaptionBarStyle()
+
+ if isinstance(parent, fpb.FoldPanelBar):
+ # Items have to be added to the internal panel instead
+ self._cont = parent
+ parent = parent._foldPanel
+ else:
+ # Must have been created from the parent control
+ self._cont = parent.GetParent()
+
+ self._captionForeColor = "black"
+ self._barStyles = ("Borderless", "BorderOnly",
+ "FilledBorder", "VerticalFill",
"HorizontalFill")
+ self._barStylesLow = ("borderless", "borderonly",
+ "filledborder", "verticalfill",
"horizontalfill")
+ self._barStyleConstants = {"nostyle" : fpb.CAPTIONBAR_NOSTYLE,
+ "verticalfill" : fpb.CAPTIONBAR_GRADIENT_V,
+ "horizontalfill" : fpb.CAPTIONBAR_GRADIENT_H,
+ "borderless" : fpb.CAPTIONBAR_SINGLE,
+ "borderonly" : fpb.CAPTIONBAR_RECTANGLE,
+ "filledborder" :
fpb.CAPTIONBAR_FILLED_RECTANGLE}
+
+ dcm.dControlMixin.__init__(self, preClass, parent, properties,
attProperties, *args, **kwargs)
+
+ self._cont.appendPanel(self)
+ self._cont.RedisplayFoldPanelItems()
+ if collapsed is not None:
+ self.Collapsed = collapsed
+ # Enable detection of clicks on the caption bar
+ self._captionBar.Bind(wx.EVT_LEFT_UP, self.__onWxCaptionClick)
+ # Set up the sizer
+ self._baseSizer = sz = dabo.ui.dSizer("v")
+ self.SetSizer(sz, True)
+ sz.appendSpacer(self.CaptionHeight)
+
+
+ def GetBestSize(self):
+ ret = super(dSlidePanel, self).GetBestSize()
+ sibCount = len(self.GetParent().GetChildren())
+ prnt = self.GetParent()
+ if prnt:
+ psz = prnt.GetSize()
+ pWd, pHt = psz.GetWidth(), psz.GetHeight()
+ capHt = self.CaptionHeight * (sibCount-1)
+ if ret.GetWidth() > pWd:
+ ret.SetWidth(pWd)
+ if not self.IsExpanded():
+ ret.SetHeight(self.CaptionHeight)
+ else:
+ if self.Parent.Singleton:
+ ret.SetHeight(pHt - capHt)
+ else:
+ if ret.GetHeight() > pHt - capHt:
+ ret.SetHeight(pHt - capHt)
+ return ret
+
+
+ def onChildBorn(self, evt):
+ self._cont.lockDisplay()
+ ch = evt.child
+ self._cont.AddFoldPanelWindow(self, ch)
+ self._cont.RefreshPanelsFrom(self)
+ self._cont.unlockDisplay()
+ dabo.ui.callAfterInterval(50, self._cont.sizePanelHeights)
+
+
+ def appendSeparator(self, color=None):
+ """This draws a separator line on the panel"""
+ if color is None:
+ color = "black"
+ self.AddSeparator(self._getWxColour(color))
+
+
+ def layout(self):
+ """ Wrap the wx version of the call, if possible. """
+ self.Layout()
+ try:
+ # Call the Dabo version, if present
+ self._baseSizer.layout()
+ except AttributeError:
+ pass
+ if self.Application.Platform == "Win":
+ self.refresh()
+
+
+ def __onWxCaptionClick(self, evt):
+ self.raiseEvent(dEvents.SlidePanelCaptionClick, evt)
+
+
+ def _getBarColor1(self):
+ try:
+ ret = self._barColor1
+ except AttributeError:
+ ret = self._barColor1 =
self._captionBar.GetCaptionStyle().GetFirstColour().Get()
+ return ret
+
+ def _setBarColor1(self, val):
+ color = self._getWxColour(val)
+ self._barColor1 = val
+ style = self._captionBar.GetCaptionStyle()
+ style.SetFirstColour(color)
+ self._captionBar.SetCaptionStyle(style)
+
+
+ def _getBarColor2(self):
+ try:
+ ret = self._barColor2
+ except AttributeError:
+ ret = self._barColor2 =
self._captionBar.GetCaptionStyle().GetSecondColour().Get()
+ return ret
+
+ def _setBarColor2(self, val):
+ color = self._getWxColour(val)
+ self._barColor2 = val
+ style = self._captionBar.GetCaptionStyle()
+ style.SetSecondColour(color)
+ self._captionBar.SetCaptionStyle(style)
+
+
+ def _getBarStyle(self):
+ wxbs = self._captionBar.GetCaptionStyle()._captionStyle
+ lowerStyle = [k for k,v in self._barStyleConstants.items()
+ if v == wxbs][0]
+ return
self._barStyles[list(self._barStylesLow).index(lowerStyle)]
+
+
+ def _setBarStyle(self, val):
+ if val.lower().strip() not in self._barStylesLow:
+ dabo.errorLog.write(_("Unknown BarStyle passed: %s.
BarStyle must be one of: %s")
+ % (val, ", ".join(self._barStyles)))
+ else:
+ self._barStyle = val
+ # Apply it
+ style = self._captionBar.GetCaptionStyle()
+
style.SetCaptionStyle(self._barStyleConstants[val.lower().strip()])
+ self._captionBar.SetCaptionStyle(style)
+
+
+ def _getBorder(self):
+ return self._border
+
+ def _setBorder(self, val):
+ if self._constructed():
+ if val == self._border:
+ return
+ try:
+ bs = self._baseSizer
+ except AttributeError:
+ # Passed in params; base sizer isn't yet present
+ dabo.ui.callAfter(self._setBorder, val)
+ return
+ sz = self.Sizer
+ self._border = val
+ if sz is not None:
+ sz.setItemProp(sz, "Border", val)
+ self.layout()
+ else:
+ self._properties["Border"] = val
+
+
+ def _getCaption(self):
+ return self._captionBar._caption
+
+ def _setCaption(self, val):
+ self._captionBar._caption = val
+ self.refresh()
+
+
+ def _getCaptionForeColor(self):
+ return self._captionForeColor
+
+ def _setCaptionForeColor(self, val):
+ self._captionForeColor = val
+ style = self._captionBar.GetCaptionStyle()
+ style.SetCaptionColour(self._getWxColour(val))
+ self._captionBar.SetCaptionStyle(style)
+
+
+ def _getCaptionHeight(self):
+ return self._captionBar.GetSize()[1]
+
+
+ def _getCollapsed(self):
+ return not self.IsExpanded()
+
+ def _setCollapsed(self, val):
+ if val:
+ self._cont.collapse(self)
+ else:
+ self._cont.expand(self)
+
+
+ def _getExpanded(self):
+ return self.IsExpanded()
+
+ def _setExpanded(self, val):
+ if val:
+ self._cont.expand(self)
+ else:
+ self._cont.collapse(self)
+
+
+ def _getParent(self):
+ return self._cont
+
+
+ def _getPanelPosition(self):
+ return self._cont.Children.index(self)
+
+ def _setPanelPosition(self, val):
+ if self._constructed():
+ if val == self.PanelPosition:
+ return
+ cnt = self._cont
+ cnt._panels.remove(self)
+ cnt._panels.insert(val, self)
+ cnt.raiseEvent(dEvents.SlidePanelChange)
+ else:
+ self._properties["PanelPosition"] = val
+
+
+ def _getSizer(self):
+ sz = self._baseSizer
+ try:
+ ret = sz.Children[1].GetSizer()
+ except (IndexError, AttributeError):
+ ret = None
+ return ret
+
+ def _setSizer(self, val):
+ if self._constructed():
+ sz = self._baseSizer
+ try:
+ userSizer = sz.Children[1].GetSizer()
+ except (IndexError, AttributeError):
+ userSizer = None
+ if userSizer:
+ sz.remove(userSizer)
+ if val is not None:
+ sz.append1x(val, border=self.Border)
+ try:
+ val.Parent = self
+ except AttributeError:
+ pass
+ else:
+ self._properties["Sizer"] = val
+
+
+ BarColor1 = property(_getBarColor1, _setBarColor1, None,
+ _("Main color for the caption bar (dColor)"))
+
+ BarColor2 = property(_getBarColor2, _setBarColor2, None,
+ _("Secondary color for the caption bar. Only used in
gradients (dColor)"))
+
+ BarStyle = property(_getBarStyle, _setBarStyle, None,
+ _(""""Determines how the bar containing the caption
+ for this panel is drawn. (str)
+
+ Can be one of the following:
+ Borderless (no border, just a
plain fill color; default)
+ BorderOnly (simple border, no fill color)
+ FilledBorder (combination of the two above)
+ VerticalFill (vertical gradient
fill, using the two caption colors)
+ HorizontalFill (horizontal gradient fill,
using the two caption colors)
+ """))
+
+ Border = property(_getBorder, _setBorder, None,
+ _("Border between the contents and edges of the panel.
Default=5 (int)"))
+
+ Caption = property(_getCaption, _setCaption, None,
+ _("Caption displayed on the panel bar (str)"))
+
+ CaptionForeColor = property(_getCaptionForeColor, _setCaptionForeColor,
None,
+ _("Text color of the caption bar (str or tuple)"))
+
+ CaptionHeight = property(_getCaptionHeight, None, None,
+ _("Height of the caption bar. Read-only (int)"))
+
+ Collapsed = property(_getCollapsed, _setCollapsed, None,
+ _("Is the panel main area hidden? (bool)"))
+
+ Expanded = property(_getExpanded, _setExpanded, None,
+ _("Is the panel main area visible? (bool)"))
+
+ Parent = property(_getParent, None, None,
+ _("Reference to the containing dSlidePanelControl."))
+
+ PanelPosition = property(_getPanelPosition, _setPanelPosition, None,
+ _("Position of this panel within the parent container
(int)"))
+
+ Sizer = property(_getSizer, _setSizer, None,
+ _("The sizer for the object.") )
+
+
+ DynamicBarColor1 = makeDynamicProperty(BarColor1)
+ DynamicBarColor2 = makeDynamicProperty(BarColor2)
+ DynamicBarStyle = makeDynamicProperty(BarStyle)
+ DynamicCaption = makeDynamicProperty(Caption)
+ DynamicCaptionForeColor = makeDynamicProperty(CaptionForeColor)
+ DynamicCollapsed = makeDynamicProperty(Collapsed)
+ DynamicExpanded = makeDynamicProperty(Expanded)
+
+
+
+class dSlidePanelControl(dcm.dControlMixin, wx.lib.foldpanelbar.FoldPanelBar):
+ """Creates a control consisting of several panels that can be
+ hidden or revealed by clicking on their 'caption bar'.
+
+ This allows you to collapse each panel down to its caption bar,
+ which either remains in place or drops to the bottom.
+ """
+ def __init__(self, parent, properties=None, attProperties=None, *args,
**kwargs):
+ self._baseClass = dSlidePanelControl
+ preClass = fpb.FoldPanelBar
+ self._singleClick = False
+ self._collapseToBottom = False
+ self._singleton = False
+ self._expandContent = True
+ # Flag to indicate whether panels are being expanded
+ # or collapsed due to internal rules for Singleton format.
+ self.__inSingletonProcess = False
+ # Flag to track the currently expanded panel in Singleton
format.
+ self.__openPanel = None
+
+ dcm.dControlMixin.__init__(self, preClass, parent, properties,
attProperties, *args, **kwargs)
+
+ self._setInitialOpenPanel()
+ self.bindEvent(dEvents.SlidePanelChange,
self.__onSlidePanelChange)
+
+
+ def append(self, pnl=None, **kwargs):
+ if pnl is None:
+ # Make sure that the Caption property has been passed
+ if not "Caption" in kwargs:
+ raise ValueError, _("You must specify a Caption
when adding a panel")
+ pnl = dabo.ui.dSlidePanel(self, **kwargs)
+ elif isinstance(pnl, basestring):
+ # Just the caption; create the panel and use that
+ pnl = dabo.ui.dSlidePanel(self, Caption=pnl, **kwargs)
+ return pnl
+
+
+ def appendPanel(self, pnl):
+ # Panel is being instantiated and added as part of its
__init__().
+ pos = 0
+ if len(self._panels) > 0:
+ pos = self._panels[-1].GetItemPos() +
self._panels[-1].GetPanelLength()
+ pnl.Reposition(pos)
+ self._panels.append(pnl)
+ self.raiseEvent(dEvents.SlidePanelChange,
+ self._createCapBarEvt(pnl))
+ pnl.bindEvent(dEvents.SlidePanelCaptionClick,
+ self.onSlidePanelCaptionClick, pnl)
+ return pnl
+
+
+ def onSlidePanelCaptionClick(self, evt):
+ if self.SingleClick:
+ obj = evt.EventObject
+ obj.Expanded = not obj.Expanded
+
+
+ def _createCapBarEvt(self, pnl):
+ evt = fpb.CaptionBarEvent(fpb.wxEVT_CAPTIONBAR)
+ cap = pnl._captionBar
+ evt.SetId(cap.GetId())
+ evt.SetEventObject(cap)
+ evt.SetBar(cap)
+ return evt
+
+
+ def Collapse(self, pnl):
+ if pnl.Collapsed:
+ # nothing to do here
+ return
+ super(dSlidePanelControl, self).Collapse(pnl)
+ self.raiseEvent(dEvents.SlidePanelChange,
+ self._createCapBarEvt(pnl))
+
+
+ def Expand(self, pnl):
+ if pnl.Expanded:
+ # nothing to do here
+ return
+ super(dSlidePanelControl, self).Expand(pnl)
+ self.raiseEvent(dEvents.SlidePanelChange,
+ self._createCapBarEvt(pnl))
+
+
+ # Throw in Dabo-style wrapper names
+ expand = Expand
+ collapse = Collapse
+
+
+ def collapseAll(self):
+ for pnl in self._panels:
+ pnl.Collapsed = True
+
+
+ def expandAll(self):
+ for pnl in self._panels:
+ pnl.Expanded = True
+
+
+ def refresh(self):
+ super(dSlidePanelControl, self).refresh()
+ if self.CollapseToBottom:
+ rect = self.RepositionCollapsedToBottom()
+ vertical = self.IsVertical()
+ if vertical and rect.GetHeight() > 0 or not vertical
and rect.GetWidth() > 0:
+ self.RefreshRect(rect)
+
+
+ def layout(self):
+ """ Wrap the wx version of the call, if possible. """
+ if not self:
+ # The object may have already been released.
+ return
+ self.Layout()
+
+
+ def onResize(self, evt):
+ self.sizePanelHeights()
+
+
+ @classmethod
+ def getBasePanelClass(cls):
+ return dSlidePanel
+
+
+ def _setInitialOpenPanel(self):
+ """When self.Singleton is true, ensures that one panel is
+ open.
+ """
+ if not self.Singleton:
+ return
+ # Make sure that one panel is open. If not, open the first.
+ # If there is more than one panel open, close all but the
+ # first open panel.
+ if len(self._panels) == 0:
+ return
+ self.__inSingletonProcess = True
+ found = False
+ for pnl in self._panels:
+ if pnl.Expanded:
+ if found:
+ pnl.Expanded = False
+ else:
+ self.__openPanel = pnl
+ found = True
+ if not found:
+ self._panels[0].Expanded = True
+ self.__openPanel = self._panels[0]
+ self.__inSingletonProcess = False
+
+
+ def __onSlidePanelChange(self, evt):
+ """This ensures that one and only one panel remains expanded
+ when the control is in Singleton mode.
+ """
+ if not self.Singleton:
+ self.sizePanelHeights(force=True)
+ return
+ if self.__inSingletonProcess:
+ # The panel is changing due to this method, so ignore
+ # it to avoid infinite loops.
+ return
+ self.__inSingletonProcess = True
+ # This is in response to an external request to a panel
+ # being expanded or collapsed.
+ curr = self.__openPanel
+ try:
+ evtPanel = evt.panel
+ except AttributeError:
+ # Not fully built yet; ignore
+ return
+ isOpening = evt.expanded
+ changing = curr is not evtPanel
+ if isOpening:
+ if curr is not None:
+ if curr is not evtPanel:
+ # Close the current one
+ dabo.ui.callAfter(self.collapse, curr)
+ self.__openPanel = evtPanel
+ else:
+ # The panel is closing. If it was the current panel,
+ # keep it open.
+ if curr is None:
+ # This is the first panel being added; keep it
open
+ self.expand(evtPanel)
+ self.__openPanel = evtPanel
+ elif curr is evtPanel:
+ self.expand(curr)
+ if changing:
+ self.layout()
+ dabo.ui.callAfter(self.sizePanelHeights)
+ self.refresh()
+ self.__inSingletonProcess = False
+
+
+ def sizePanelHeights(self, force=False):
+ """Control the heights of the panels. Originally I thought we
only needed
+ this when running in Singleton mode, but now it seems better to
run this
+ in all modes.
+ """
+# - if not self.Singleton and not force:
+# - return
+ # Size the open panel to fill the space
+ top = 0
+ pnlList = self._panels[:]
+ if not pnlList:
+ # Not constructed fully
+ return
+ if self.CollapseToBottom:
+ # Sort so that the first panel is the expanded one.
+ pnlList.sort(lambda x, y: cmp(x.Collapsed, y.Collapsed))
+ fp = pnlList[0]
+ fp.Reposition(0)
+ self.RefreshPanelsFrom(fp)
+ for pnl in pnlList:
+ if not pnl.Expanded:
+ pnl.Height = pnl.CaptionHeight
+ elif self.ExpandContent:
+ # Make the panel that big, minus the height of
the captions
+ capHt = pnl.CaptionHeight * (len(self._panels)
-1)
+ pnl.Height = self.Height - capHt
+ pnl.Top = top
+ pnl.layout()
+ top += pnl.Height
+ dabo.ui.callAfter(self.layout)
+
+
+ def _getChildren(self):
+ return self._panels
+
+
+ def _getCollapseToBottom(self):
+ return bool(self._extraStyle & fpb.FPB_COLLAPSE_TO_BOTTOM)
+
+ def _setCollapseToBottom(self, val):
+ self._collapseToBottom = val
+ if val:
+ self._extraStyle = self._extraStyle |
fpb.FPB_COLLAPSE_TO_BOTTOM
+ else:
+ self._extraStyle = self._extraStyle &
~fpb.FPB_COLLAPSE_TO_BOTTOM
+ if self._panels:
+ fp = self._panels[0]
+ fp.Reposition(0)
+ self.RefreshPanelsFrom(fp)
+ self.sizePanelHeights(force=True)
+ self.layout()
+
+
+ def _getExpandContent(self):
+ return self._expandContent
+
+ def _setExpandContent(self, val):
+ if self._constructed():
+ self._expandContent = val
+ else:
+ self._properties["ExpandContent"] = val
+
+
+ def _getPanelClass(self):
+ try:
+ return self._panelClass
+ except AttributeError:
+ return dabo.ui.dSlidePanel
+
+ def _setPanelClass(self, val):
+ if self._constructed():
+ self._panelClass = val
+ else:
+ self._properties["PanelClass"] = val
+
+
+ def _getPanelCount(self):
+ return len(self.Children)
+
+ def _setPanelCount(self, val):
+ if self._constructed():
+ val = int(val)
+ if val < 0:
+ raise ValueError, _("Cannot set PanelCount to
less than zero.")
+ panelCount = len(self.Children)
+ panelClass = self.PanelClass
+
+ if val > panelCount:
+ for i in range(panelCount, val):
+ pnl = panelClass(self)
+ if not pnl.Caption:
+ pnl.Caption = _("Panel %s") %
(i+1,)
+ elif val < panelCount:
+ raise ValueError, _("Cannot reduce PanelCount.")
+ else:
+ self._properties["PanelCount"] = val
+
+
+ def _getSingleClick(self):
+ return self._singleClick
+
+ def _setSingleClick(self, val):
+ self._singleClick = val
+
+
+ def _getSingleton(self):
+ return self._singleton
+
+ def _setSingleton(self, val):
+ self._singleton = val
+ # Make sure that only one panel is open
+ self._setInitialOpenPanel()
+
+
+ Children = property(_getChildren, None, None,
+ _("List of all panels in the control (list))"))
+
+ CollapseToBottom = property(_getCollapseToBottom, _setCollapseToBottom,
None,
+ _("When True, all collapsed panels are displayed at the
bottom (bool)"))
+
+ ExpandContent = property(_getExpandContent, _setExpandContent, None,
+ _("""When True, the panels size themselves to the size
of this object.
+ Otherwise, panels only take up as much space as they
need. (default=True) (bool)"""))
+
+ PanelClass = property(_getPanelClass, _setPanelClass, None,
+ _("""Specifies the class of control to use for panels
by default. (dSlidePanel)
+ This really only applies when using the PanelCount
property to set the
+ number of panels."""))
+
+ PanelCount = property(_getPanelCount, _setPanelCount, None,
+ _("Number of child panels. (read-only) (int)"))
+
+ Panels = property(_getChildren, None, None,
+ _("List of contained panels. Same as the 'Children'
property. (read-only) (list)"))
+
+ SingleClick = property(_getSingleClick, _setSingleClick, None,
+ _("""When True, a single click on the caption bar
toggles the
+ expanded/collapsed state (bool)"""))
+
+ Singleton = property(_getSingleton, _setSingleton, None,
+ _("When True, one and only one panel at a time will be
expanded (bool)"))
+
+
+ DynamicCollapseToBottom = makeDynamicProperty(CollapseToBottom)
+ DynamicSingleClick = makeDynamicProperty(SingleClick)
+ DynamicSingleton = makeDynamicProperty(Singleton)
+
+
+if __name__ == "__main__":
+ class TestForm(dabo.ui.dForm):
+ def afterInit(self):
+ dSlidePanelControl(self, RegID="slideControl",
ExpandContent=False,
+ SingleClick=True)
+ self.Sizer.append1x(self.slideControl)
+ self.p1 = dabo.ui.dSlidePanel(self.slideControl,
Caption="First",
+ BackColor="orange")
+ self.p2 = dabo.ui.dSlidePanel(self.slideControl,
Caption="Second",
+ BarStyle="HorizontalFill",
BarColor1="lightgreen", BarColor2="ForestGreen",
+ BackColor="wheat")
+ self.p3 = dabo.ui.dSlidePanel(self.slideControl,
Caption="Third",
+ BarStyle="BorderOnly",
BackColor="powderblue", Border=33)
+
+ self.p1.Sizer = dabo.ui.dSizer("v")
+ btn = dabo.ui.dButton(self.p1, Caption="Change Bar 1
Style")
+ self.p1.Sizer.append(btn, border=25)
+ btn.bindEvent(dEvents.Hit, self.onBtn)
+
+ self.p2.Sizer = dabo.ui.dSizer("v")
+ lbl = dabo.ui.dLabel(self.p2, Caption="Tea For Two",
FontItalic=True,
+ FontSize=24)
+ self.p2.Sizer.append(lbl)
+ def collapse3(evt):
+ mc = self.slideControl
+ if mc.Singleton:
+ mc.expand(self.p2)
+ else:
+ mc.collapse(self.p3)
+ self.p3.Sizer = dabo.ui.dGridSizer(HGap=5, VGap=2,
MaxCols=2, DefaultBorder=3)
+ lbl = dabo.ui.dLabel(self.p3, Caption="Three Strikes")
+ btn = dabo.ui.dButton(self.p3, Caption="Collapse Me",
OnHit=collapse3)
+ self.p3.Sizer.appendItems((lbl, btn))
+ # Demonstrate the grid
+ self.p3.Sizer.append(dabo.ui.dLabel(self.p3,
Caption="Just"))
+ self.p3.Sizer.append(dabo.ui.dLabel(self.p3,
Caption="taking"))
+ self.p3.Sizer.append(dabo.ui.dLabel(self.p3,
Caption="up"))
+ self.p3.Sizer.append(dabo.ui.dLabel(self.p3,
Caption="space"))
+ self.p3.Sizer.append(dabo.ui.dLabel(self.p3,
Caption="in"))
+ self.p3.Sizer.append(dabo.ui.dLabel(self.p3,
Caption="the"))
+ self.p3.Sizer.append(dabo.ui.dLabel(self.p3,
Caption="Grid"))
+ self.p3.Sizer.append(dabo.ui.dLabel(self.p3,
Caption="Sizer"))
+
+ hsz = dabo.ui.dSizer("h")
+ btnCollapse = dabo.ui.dButton(self, Caption="Collapse
All")
+ btnCollapse.bindEvent(dEvents.Hit, self.onCollapseAll)
+ btnExpand = dabo.ui.dButton(self, Caption="Expand All")
+ btnExpand.bindEvent(dEvents.Hit, self.onExpandAll)
+ hsz.append(btnCollapse)
+ hsz.appendSpacer(10)
+ hsz.append(btnExpand)
+ hsz.appendSpacer(10)
+ chkSingleton = dabo.ui.dCheckBox(self,
Caption="Singleton Style",
+ DataSource="self.Form.slideControl",
DataField="Singleton")
+ chkSingle = dabo.ui.dCheckBox(self, Caption="Single
Click to Toggle",
+ DataSource="self.Form.slideControl",
DataField="SingleClick")
+ chkBottom = dabo.ui.dCheckBox(self, Caption="Collapsed
Panels To Bottom",
+ DataSource="self.Form.slideControl",
DataField="CollapseToBottom")
+ chkExpand = dabo.ui.dCheckBox(self, Caption="Expand
Content to Full Size",
+ DataSource="self.Form.slideControl",
DataField="ExpandContent")
+ self.Sizer.appendSpacer(10)
+ vsz = dabo.ui.dSizer("v")
+ vsz.append(chkSingleton)
+ vsz.append(chkSingle)
+ vsz.append(chkBottom)
+ vsz.append(chkExpand)
+ hsz.append(vsz)
+ self.Sizer.append(hsz, 0, halign="center", border=10)
+ self.layout()
+
+
+ def onBtn(self, evt):
+ import random
+ p = self.p1
+ style = random.choice(p._barStyles)
+ p.BarStyle = style
+ color1 = dabo.dColors.randomColorName()
+ color2 = dabo.dColors.randomColorName()
+ p.BarColor1 = color1
+ p.BarColor2 = color2
+ if style in ("VerticalFill", "HorizontalFill"):
+ p.Caption = "Style: %s; Colors: %s, %s" %
(style, color1, color2)
+ elif style in ("BorderOnly", ):
+ p.Caption = "Style: %s" % style
+ else:
+ p.Caption = "Style: %s; Color: %s" % (style,
color1)
+# lbl = dabo.ui.dLabel(p, Caption="Changed to %s" %
p.BarStyle,
+# FontItalic=True, FontSize=12)
+# p.Sizer.append(lbl)
+# p.layout()
+
+
+ def onCollapseAll(self, evt):
+ self.slideControl.collapseAll()
+
+ def onExpandAll(self, evt):
+ self.mainContainer.expandAll()
+
+
+ app = dabo.dApp()
+ app.MainFormClass = TestForm
+ app.start()
_______________________________________________
Post Messages to: [email protected]
Subscription Maintenance: http://leafe.com/mailman/listinfo/dabo-dev
Searchable Archives: http://leafe.com/archives/search/dabo-dev
This message: http://leafe.com/archives/byMID/%(messageid)s