Search code examples
pythonqtwidgetpysideqdoublespinbox

QDoubleSpinbox with editable unit (suffix)


I am trying to make a QDoubleSpinbox that allows for entering custom metric distances with units, e.g. "3.123 mm", "2.1 um", "10.567 m".

Is there any way to convince QDoubleSpinbox to make the suffix editable?


Solution

  • I figured it out. Can't use suffix(), but there is another way. Here you go:

    import re
    from PySide.QtGui import QApplication, QDoubleSpinBox, QValidator, QAbstractSpinBox
    
    _value_split_re = re.compile(r'^([\d\.]+)\s*(\S*)$')
    def _parse_value(value):
        if not isinstance(value, str):
            return value, None
        value, unit = _value_split_re.match(value).groups()
        return float(value), unit
    
    class DistanceSpinBox(QDoubleSpinBox):
        def __init__(self, unit, allowed_units, parent=None):
            self.unit = unit
            self.allowed_units = allowed_units
            super().__init__(parent)
            self.setStepType(QAbstractSpinBox.AdaptiveDecimalStepType)
            #self.setKeyboardTracking(False)
    
        def textFromValue(self, value):
            decimals = self.decimals()
            fmt = f"{{:.{decimals}f}} {self.unit}"
            return fmt.format(value)
    
        def valueFromText(self, text):
            try:
                value, unit = _parse_value(text)
            except AttributeError:
                return .0
    
            if unit in self.allowed_units:
                self.unit = unit
                self.valueChanged.emit(value)
    
            return value
    
        def validate(self, text, pos):
            return QValidator.Acceptable, text, pos
    
    if __name__ == '__main__':
        import sys
        app = QApplication(sys.argv)
        spin_box = DistanceSpinBox('mm', ('nm', 'mm', 'cm', 'in', 'ft'))
        spin_box.show()
        sys.exit(app.exec_())
    

    Alternatively, here is a link to the full version that includes some checks.