Attached is the patch I mentioned to add a dropdown combo box containing the distance measures. The patch seems to work - doing things like entering "25cm" will correctly parse out the units and update the combobox and lineedit. The major piece of wierdness is that selecting "fraction" in the dropdown with a number > 1 will actually choose "pts" (this is what the code in utilfuncs.py actually does, but it doesn't seem like great UI).

_However_

The patch is kinda ugly, for example there's some quite involved logic to determine what happens if you enter a value without units. I'm not quite sure what the best way to make it prettier is. Suggestions?
Index: setting/controls.py
===================================================================
RCS file: /cvs/veusz/veusz/setting/controls.py,v
retrieving revision 1.6
diff -u -r1.6 controls.py
--- setting/controls.py	25 Mar 2005 18:11:38 -0000	1.6
+++ setting/controls.py	19 Apr 2005 20:24:48 -0000
@@ -24,6 +24,7 @@
 
 import qt
 import setting
+import utils
 
 class SettingEdit(qt.QLineEdit):
     """Main control for editing settings which are text."""
@@ -70,7 +71,88 @@
     def onModified(self, mod):
         """called when the setting is changed remotely"""
         self.setText( self.setting.toText() )
+
+class DistanceSettingEdit(qt.QHBox):
+    def __init__(self, setting, parent):
+        qt.QWidget.__init__(self,parent)
+
+        self.setting=setting
+        self.bgcolour = self.paletteBackgroundColor()
+
+        self.box = qt.QHBox(self)
+
+        self.valueBox = qt.QLineEdit(self,"valueBox")
+        
+        self.unitsCombo = qt.QComboBox(0,self,"unitsCombo")
+                     
+        self.unitsCombo.setEditable(False)
+        self.unitsCombo.clear()
+
+        units = utils.getDistUnitNames()
+        del units[units.index("ratio")]
+
+        for item in units:
+            self.unitsCombo.insertItem(item)
+
+        self.connect(self.unitsCombo, qt.SIGNAL('activated(const QString&)'),
+                     self.validateAndSet)
+
+        self.setting.setOnModified(self.onModified)
+
+        self.connect(self.valueBox, qt.SIGNAL('lostFocus()'),
+                     self.validateAndSet)
+        self.connect(self.valueBox, qt.SIGNAL('returnPressed()'),
+                     self.validateAndSet)
+        self.updateFromString(setting.toText(), True)
+
+    def updateFromString(self, string, noUnitsIsRatio=False):
+        """Take a string and set the values of the controls appropriately"""
+        value,unit = utils.splitDist(string)
+        self.valueBox.setText(value)
+        #If the value returned is ratio, there are three possibilites
+        # - we are being called from the lineEdit being modified without
+        #   a specific unit-  in this case we want to keep the current unit
+        # - a dimenionless unit < 1 is specified in the lineEdit- this
+        #   indicates a decimal fraction of the maximum allowed size
+        # - a dimenionless unit > 1 is specified in the lineEdit- this converts
+        #   the unit to points
+        if unit=="ratio":
+            if noUnitsIsRatio:
+                if float(value) > 1:
+                    unit = "pt"
+                else:
+                    unit = "fraction"
+                self.unitsCombo.setCurrentText(unit)
+        else:
+            self.unitsCombo.setCurrentText(unit)
+
+    def done(self):
+        """Delete modification notification."""
+        self.setting.removeOnModified(self.onModified)
+
+    def validateAndSet(self):
+        """Check the text is a valid setting and update it."""
+        self.updateFromString(str(self.valueBox.text()))
+
+        if not(self.unitsCombo.currentText() == "fraction"):
+            text = str(self.valueBox.text()) +  str(self.unitsCombo.currentText())
+        else:
+            text = str(self.valueBox.text())
+        try:
+            val = self.setting.fromText(text)
+            self.valueBox.setPaletteBackgroundColor(self.bgcolour)
+            
+            # value has changed
+            if self.setting.get() != val:
+                self.setting.set(val)
+
+        except setting.InvalidType:
+            self.valueBox.setPaletteBackgroundColor(qt.QColor('red'))
+
+    def onModified(self, mod):
+        self.updateFromString(self.setting.toText(), True)
         
+
 class BoolSettingEdit(qt.QCheckBox):
     """A check box for changing a bool setting."""
     
Index: setting/setting.py
===================================================================
RCS file: /cvs/veusz/veusz/setting/setting.py,v
retrieving revision 1.10
diff -u -r1.10 setting.py
--- setting/setting.py	9 Apr 2005 22:20:02 -0000	1.10
+++ setting/setting.py	19 Apr 2005 20:24:48 -0000
@@ -364,7 +364,7 @@
             raise InvalidType
         
     def makeControl(self, *args):
-        return controls.SettingEdit(self, *args)
+        return controls.DistanceSettingEdit(self, *args)
 
 class Choice(Setting):
     """One out of a list of strings."""
Index: utils/utilfuncs.py
===================================================================
RCS file: /cvs/veusz/veusz/utils/utilfuncs.py,v
retrieving revision 1.14
diff -u -r1.14 utilfuncs.py
--- utils/utilfuncs.py	2 Apr 2005 16:55:32 -0000	1.14
+++ utils/utilfuncs.py	19 Apr 2005 20:24:50 -0000
@@ -313,31 +313,51 @@
 # the recipient function takes regexp match, painter and maximum size of frac
 
 _distRegexp=[ (re.compile('^([0-9\.]+) *%$'),
-               _distPerc),
+               _distPerc, "%"),
               (re.compile('^([0-9\.]+) */ *([0-9\.]+)$'),
-               _distFrac),
+               _distFrac, "fraction"),
               (re.compile('^([0-9\.]+) *pt$'),
-               lambda match, painter, t: _distPhys(match, painter, 1.)),
+               lambda match, painter, t: _distPhys(match, painter, 1.), "pt"),
               (re.compile('^([0-9\.]+) *cm$'),
-               lambda match, painter, t: _distPhys(match, painter, 28.452756)),
+               lambda match, painter, t: _distPhys(match, painter, 28.452756), "cm"),
               (re.compile('^([0-9\.]+) *mm$'),
-               lambda match, painter, t: _distPhys(match, painter, 2.8452756)),
+               lambda match, painter, t: _distPhys(match, painter, 2.8452756), "mm"),
               (re.compile('^([0-9\.]+) *(inch|in|")$'),
-               lambda match, painter, t: _distPhys(match, painter, 72.27)),
+               lambda match, painter, t: _distPhys(match, painter, 72.27), "in"),
               (re.compile('^([0-9\.]+)$'),
-               _distRatio)
+               _distRatio, "ratio")
               ]
 
 def isDist(dist):
     """Is the text a valid distance measure?"""
 
     dist = dist.strip()
-    for reg, fn in _distRegexp:
+    for reg, fn, name in _distRegexp:
         if reg.match(dist) != None:
             return True
 
     return False
 
+def getDistUnitNames():
+    return [name for reg, fn, name in _distRegexp]
+
+def splitDist(dist):
+    """Split a string containing a value+units into a magnitude,unit tuple
+    Note: a return value of "Ratio" indicates no specific unit 
+    """
+    dist=dist.strip()
+    for reg, fn, unit in _distRegexp:
+        match= reg.match(dist)
+        if match != None:
+            if unit == "fraction":
+                value  =  dist
+            else:
+                value = match.group(1)
+            return value,unit
+    #XXX - do we  want to raise an error here?
+    return False
+            
+               
 def cnvtDist(dist, painter):
     '''Convert a distance to plotter units.
 
@@ -361,7 +381,7 @@
     dist = dist.strip()
 
     # compare string against each regexp
-    for reg, fn in _distRegexp:
+    for reg, fn, name in _distRegexp:
         m = reg.match(dist)
 
         # if there's a match, then call the appropriate conversion fn

Répondre à