Advice on using Panel/Params with the Python Pint library (for physical quantities)

I use the python pint library to model problems with physical quantities and units e.g. width = 10cm.

I would like to create a custom parameter for a quantity with physical units and then render a helpful widget for this parameter in panel – I was hoping to get high level advice about how to proceed (and, in exchange I will report the details once I am done)

u = pint.UnitRegistry()
class Foo  (Parameterized):
    width = PhysicalQuantity(10*u.mm, bounds = (1*u.mm, 50*u.mm))

bar = Foo()
pn.Row(bar.param)

# Uses maybe one compound widget three sub-widgets: a slider, a dropdown for prefix (k, m, c, u), and length unit (meter, foot, inch, etc) 

More generally, I think I can figure out how to add a Parameter class for a custom type. Curious how to register w/ Panel which widget I would like to use for that parameter class. Also, how to combine multiple widgets into one widget.

Thanks!

1 Like

There was some discussions in this thread

2 Likes

Better late than never:

import param
import panel as pn
import pint

u = pint.UnitRegistry()
u.default_format = '~H'

class QuantityInput(pn.widgets.CompositeWidget):

    value = param.ClassSelector(pint.Quantity)

    _composite_type = pn.Row

    def __init__(self, unit_opts, number=None, **params):
        self._name = pn.widgets.StaticText(margin=(2, 2))
        self._number = pn.widgets.FloatInput(value=number, width=100, margin=(0, 2))
        self._units = pn.widgets.Select(width=100, margin=(0, 2), options=unit_opts)
        super().__init__(**params)
        self._name.value = self.name
        self._composite.extend([self._name, self._number, self._units])
        self._update()

    @param.depends('_number.value', '_units.value', watch=True)
    def _update(self):
        self.value = self._number.value * self._units.value


input_a = QuantityInput([u.mm, u.cm, u.m], name='length a', number=1.0)
input_b = QuantityInput({'inch': u.inch, 'feet': u.feet}, name='length b', number=3.0)

@pn.depends(input_a, input_b)
def area_rectangle(a_value, b_value):
    return pn.Pane(f" a x b = {(a_value * b_value).to_base_units()}", sizing_mode='stretch_width')

pn.serve(pn.Column(input_a, input_b, area_rectangle))