dabo Commit
Revision 4415
Date: 2008-08-16 10:11:22 -0700 (Sat, 16 Aug 2008)
Author: Ed
Trac: http://svn.dabodev.com/trac/dabo/changeset/4415

Changed:
U   trunk/dabo/ui/uiwx/dSpinner.py

Log:
Fixed a long-standing bug in this composite control. There were several 
situations where it appeared that the spin buttons stopped responding to 
clicks, even though the displayed value was within range. The problem turns out 
to be that while we were correctly setting the textbox's value, we were not 
setting the spinner's value; instead, it was incrementing by +1/-1 for each 
click. This commit fixes that problem by eliminating the spinner's settings 
from the mix; instead, we govern min/max/wrap through our own code.

I also fixed the problem with the collision between float and Decimal values. 
All values in the comparisons are now cast to the type of the edited value when 
there is a conflict.

Removed the conditional import on the decimal module, as we are now requiring 
Python 2.4.

Fixed the missing docstring for the class, and added some other docstrings to 
methods.

Renamed dSpinButton to _dSpinButton, as it is not meant to be a public class, 
and is not available in dabo.ui.uiwx.__init__.py.


Diff:
Modified: trunk/dabo/ui/uiwx/dSpinner.py
===================================================================
--- trunk/dabo/ui/uiwx/dSpinner.py      2008-08-14 23:11:01 UTC (rev 4414)
+++ trunk/dabo/ui/uiwx/dSpinner.py      2008-08-16 17:11:22 UTC (rev 4415)
@@ -1,9 +1,6 @@
 # -*- coding: utf-8 -*-
 import locale
-try:
-       from decimal import Decimal as decimal
-except ImportError:
-        decimal = float
+from decimal import Decimal as decimal
 import wx
 import dabo
 
@@ -18,16 +15,31 @@
 from dabo.ui import makeProxyProperty
 
 
-class dSpinButton(dcm.dDataControlMixin, wx.SpinButton):
+class _dSpinButton(dcm.dDataControlMixin, wx.SpinButton):
+       """Simple wrapper around the base wx.SpinButton."""
        def __init__(self, parent, properties=None, attProperties=None, *args, 
**kwargs):
-               self._baseClass = dSpinButton
+               self._baseClass = _dSpinButton
                preClass = wx.PreSpinButton
                kwargs["style"] = kwargs.get("style", 0) | wx.SP_ARROW_KEYS
                dcm.dDataControlMixin.__init__(self, preClass, parent, 
properties, attProperties, 
                                *args, **kwargs)
 
 
+       def __onWxSpinUp(self, evt):
+               self.raiseEvent(dEvents.SpinUp, spinType="button")
+               self.raiseEvent(dEvents.Spinner, spinType="button")
+
+
+       def __onWxSpinDown(self, evt):
+               self.raiseEvent(dEvents.SpinDown, spinType="button")
+               self.raiseEvent(dEvents.Spinner, spinType="button")
+
+
+
 class dSpinner(dabo.ui.dDataPanel):
+       """Control for allowing a user to increment a value by discreet steps 
across a range
+       of valid values.
+       """
        def __init__(self, parent, properties=None, attProperties=None, *args, 
**kwargs):
                self.__constructed = False
                self._spinWrap = False
@@ -45,7 +57,7 @@
                # Create the child controls
                self._proxy_textbox = dabo.ui.dTextBox(self, Value=val, 
Width=32, 
                                StrictNumericEntry=False, _EventTarget=self)
-               self._proxy_spinner = dSpinButton(parent=self, 
_EventTarget=self)
+               self._proxy_spinner = _dSpinButton(parent=self, 
_EventTarget=self)
                self.__constructed = True
                self.Sizer = dabo.ui.dSizer("h")
                self.Sizer.append(self._proxy_textbox, 1, valign="middle")
@@ -59,6 +71,10 @@
                self.autoBindEvents()
                ps = self._proxy_spinner
                pt = self._proxy_textbox
+               # Set an essentially infinite range. We'll handle the range 
ourselves.
+               ps.SetRange(-2**30, 2**30)
+               # We'll also control wrapping ourselves
+               self._proxy_spinner._addWindowStyleFlag(wx.SP_WRAP)
                ps.Bind(wx.EVT_SPIN_UP, self.__onWxSpinUp)
                ps.Bind(wx.EVT_SPIN_DOWN, self.__onWxSpinDown)
                ps.Bind(wx.EVT_SPIN, self._onWxHit)
@@ -72,56 +88,86 @@
                return self.__constructed
        
 
-       def __onWxSpinUp(self, evt):
-               if self._spinUp():
-                       self.raiseEvent(dEvents.SpinUp, spinType="button")
-                       self.raiseEvent(dEvents.Spinner, spinType="button")
+       def _coerceTypes(self, newVal, minn, maxx, margin):
+               """Handle the problems when min/max/increment values are
+               of one type, and the edited value another.
+               """
+               typN = type(newVal)
+               # Only problem here is Decimal and float combinations
+               if typN == decimal:
+                       def toDec(val):
+                               return decimal(str(val))
+                       margin = toDec(margin)
+                       if type(maxx) == float:
+                               maxx = toDec(maxx)
+                       if type(minn) == float:
+                               minn = toDec(minn)
+               elif typN == float:
+                       if type(maxx) == decimal:
+                               maxx = float(maxx)
+                       if type(minn) == decimal:
+                               minn = float(minn)
+               return minn, maxx, margin
 
 
-       def __onWxSpinDown(self, evt):
-               if self._spinDown():
-                       self.raiseEvent(dEvents.SpinDown, spinType="button")
-                       self.raiseEvent(dEvents.Spinner, spinType="button")
-       
-       
-       def _spinUp(self):
+       def _spinUp(self, evt=None):
+               """Handles a user request to increment the value."""
                ret = True
                curr = self._proxy_textbox.Value
-               new = curr + self.Increment
-               if new <= self.Max:
-                       self._proxy_textbox.Value = new
+               newVal = curr + self.Increment
+               minn, maxx, margin = self._coerceTypes(newVal, self.Min, 
self.Max, 0.0001)
+               diff = newVal - maxx
+               if diff < margin:
+                       self._proxy_textbox.Value = newVal
                elif self._spinWrap:
-                       xs = new - self.Max
-                       self._proxy_textbox.Value = self.Min + xs
+                       self._proxy_textbox.Value = minn + diff
                else:
                        ret = False
                self._checkBounds()
                self.flushValue()
+               self.raiseEvent(dEvents.Hit, hitType="button")
                return ret
 
 
-       def _spinDown(self):
+       def _spinDown(self, evt=None):
+               """Handles a user request to decrement the value."""
                ret = True
                curr = self._proxy_textbox.Value
-               new = curr - self.Increment
-               if new >= self.Min:
-                       self._proxy_textbox.Value = new
+               newVal = curr - self.Increment
+               minn, maxx, margin = self._coerceTypes(newVal, self.Min, 
self.Max, -0.0001)
+               diff = newVal - minn
+               if diff > margin:
+                       self._proxy_textbox.Value = newVal
                elif self._spinWrap:
-                       xs = self.Min - new
-                       self._proxy_textbox.Value = self.Max - xs
+                       self._proxy_textbox.Value = maxx + diff
                else:
                        ret = False
                self._checkBounds()
                self.flushValue()
+               self.raiseEvent(dEvents.Hit, hitType="button")
                return ret
 
 
+       def __onWxSpinUp(self, evt):
+               """Respond to the wx event by raising the Dabo event."""
+               if self._spinUp():
+                       self.raiseEvent(dEvents.SpinUp, spinType="button")
+                       self.raiseEvent(dEvents.Spinner, spinType="button")
+
+
+       def __onWxSpinDown(self, evt):
+               """Respond to the wx event by raising the Dabo event."""
+               if self._spinDown():
+                       self.raiseEvent(dEvents.SpinDown, spinType="button")
+                       self.raiseEvent(dEvents.Spinner, spinType="button")
+       
+       
        def _checkBounds(self):
                """Make sure that the value is within the current Min/Max"""
                if self._proxy_textbox.Value < self.Min:
-                       self._proxy_textbox.Value = self.Min
+                       self._proxy_textbox.Value = self._proxy_spinner.Value = 
self.Min
                elif self._proxy_textbox.Value > self.Max:
-                       self._proxy_textbox.Value = self.Max
+                       self._proxy_textbox.Value = self._proxy_spinner.Value = 
self.Max
 
 
        def _onWxHit(self, evt):
@@ -138,6 +184,9 @@
 
 
        def _onChar(self, evt):
+               """Handle the case where the user presses the up/down arrows to 
+               activate the spinner.
+               """
                keys = dabo.ui.dKeys
                kc = evt.keyCode
                if kc in (keys.key_Up, keys.key_Numpad_up):
@@ -153,6 +202,9 @@
 
 
        def _onLostFocus(self, evt):
+               """We need to handle the case where the user types an invalid 
value
+               into the textbox and then tabs/clicks away.
+               """
                val = self.Value
                pt = self._proxy_textbox
                if (val > self.Max) or (val < self.Min):
@@ -219,7 +271,6 @@
        def _setMax(self, val):
                if self._constructed():
                        self._max = val
-                       self._proxy_spinner.SetRange(self.Min, val)
                        self._checkBounds()
                else:
                        self._properties["Max"] = val
@@ -231,24 +282,17 @@
        def _setMin(self, val):
                if self._constructed():
                        self._min = val
-                       self._proxy_spinner.SetRange(val, self.Max)
                        self._checkBounds()
                else:
                        self._properties["Min"] = val
 
 
        def _getSpinnerWrap(self):
-               try:
-                       return 
self._proxy_spinner._hasWindowStyleFlag(wx.SP_WRAP)
-               except AttributeError:
-                       return self._spinWrap
+               return self._spinWrap
 
        def _setSpinnerWrap(self, val):
                if self._constructed():
                        self._spinWrap = val
-                       self._proxy_spinner._delWindowStyleFlag(wx.SP_WRAP)
-                       if val:
-                               
self._proxy_spinner._addWindowStyleFlag(wx.SP_WRAP)
                else:
                        self._properties["SpinnerWrap"] = val
 




_______________________________________________
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/[EMAIL PROTECTED]

Reply via email to