holoviz-topics / EarthSim

Tools for working with and visualizing environmental simulations.
https://earthsim.holoviz.org
BSD 3-Clause "New" or "Revised" License
65 stars 21 forks source link

parambokeh implementation of param.DateRange #187

Closed kcpevey closed 5 years ago

kcpevey commented 6 years ago

I'm trying to use the parambokeh widget for param.DateRange. From the docs, it looks like I need to feed param.DateRange a datetime tuple, but when I try instantiate with a default tuple of datetime objs, I get an error:

import param
from datetime import datetime, timedelta

dt = datetime.now()
dt2 = dt + timedelta(hours=5)

class foo(param.Parameterized):
    time_range = param.DateRange(default=(dt, dt2))

parambokeh.Widgets(foo)

Results in this error:

---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-8-5df000a75230> in <module>()
----> 1 parambokeh.Widgets(foo)

C:\ProgramData\Anaconda3\envs\earthsimAUG\lib\site-packages\param\parameterized.py in __new__(class_, *args, **params)
   2047         inst = class_.instance()
   2048         inst.param._set_name(class_.__name__)
-> 2049         return inst.__call__(*args,**params)
   2050 
   2051     def __call__(self,*args,**kw):

C:\ProgramData\Anaconda3\envs\earthsimAUG\lib\site-packages\parambokeh\__init__.py in __call__(self, parameterized, doc, plots, **params)
    232 
    233         # Initialize widgets and populate container
--> 234         widgets, views = self.widgets()
    235         plots = views + plots
    236         widget_box.children = widgets

C:\ProgramData\Anaconda3\envs\earthsimAUG\lib\site-packages\parambokeh\__init__.py in widgets(self)
    495 
    496         if self.p.show_labels:
--> 497             widgets += [self.widget(pname) for pname in ordered_params]
    498         else:
    499             widgets += [self.widget(pname) for pname in ordered_params]

C:\ProgramData\Anaconda3\envs\earthsimAUG\lib\site-packages\parambokeh\__init__.py in <listcomp>(.0)
    495 
    496         if self.p.show_labels:
--> 497             widgets += [self.widget(pname) for pname in ordered_params]
    498         else:
    499             widgets += [self.widget(pname) for pname in ordered_params]

C:\ProgramData\Anaconda3\envs\earthsimAUG\lib\site-packages\parambokeh\__init__.py in widget(self, param_name)
    459         """Get widget for param_name"""
    460         if param_name not in self._widgets:
--> 461             self._widgets[param_name] = self._make_widget(param_name)
    462         return self._widgets[param_name]
    463 

C:\ProgramData\Anaconda3\envs\earthsimAUG\lib\site-packages\parambokeh\__init__.py in _make_widget(self, p_name)
    403             kw['start'], kw['end'] = p_obj.get_soft_bounds()
    404 
--> 405         w = widget_class(**kw)
    406 
    407         if hasattr(p_obj, 'callbacks') and value is not None:

C:\ProgramData\Anaconda3\envs\earthsimAUG\lib\site-packages\parambokeh\widgets.py in RangeWidget(*args, **kw)
     92     if isinstance(kw['start'], int) and isinstance(kw['end'], int):
     93         kw['step'] = 1
---> 94     return RangeSlider(*args, **kw)
     95 
     96 def PlotWidget(*args, **kw):

C:\ProgramData\Anaconda3\envs\earthsimAUG\lib\site-packages\bokeh\model.py in __init__(self, **kwargs)
    217         self._id = kwargs.pop("id", make_id())
    218         self._document = None
--> 219         super(Model, self).__init__(**kwargs)
    220         default_theme.apply_to_model(self)
    221 

C:\ProgramData\Anaconda3\envs\earthsimAUG\lib\site-packages\bokeh\core\has_props.py in __init__(self, **properties)
    234 
    235         for name, value in properties.items():
--> 236             setattr(self, name, value)
    237 
    238     def __setattr__(self, name, value):

C:\ProgramData\Anaconda3\envs\earthsimAUG\lib\site-packages\bokeh\core\has_props.py in __setattr__(self, name, value)
    261 
    262         if name in props or (descriptor is not None and descriptor.fset is not None):
--> 263             super(HasProps, self).__setattr__(name, value)
    264         else:
    265             matches, text = difflib.get_close_matches(name.lower(), props), "similar"

C:\ProgramData\Anaconda3\envs\earthsimAUG\lib\site-packages\bokeh\core\property\descriptors.py in __set__(self, obj, value, setter)
    501             raise RuntimeError("%s.%s is a readonly property" % (obj.__class__.__name__, self.name))
    502 
--> 503         self._internal_set(obj, value, setter=setter)
    504 
    505     def __delete__(self, obj):

C:\ProgramData\Anaconda3\envs\earthsimAUG\lib\site-packages\bokeh\core\property\descriptors.py in _internal_set(self, obj, value, hint, setter)
    722 
    723         '''
--> 724         value = self.property.prepare_value(obj, self.name, value)
    725 
    726         old = self.__get__(obj, obj.__class__)

C:\ProgramData\Anaconda3\envs\earthsimAUG\lib\site-packages\bokeh\core\property\bases.py in prepare_value(self, obj_or_cls, name, value)
    283                     break
    284             else:
--> 285                 raise e
    286         else:
    287             value = self.transform(value)

C:\ProgramData\Anaconda3\envs\earthsimAUG\lib\site-packages\bokeh\core\property\bases.py in prepare_value(self, obj_or_cls, name, value)
    276     def prepare_value(self, obj_or_cls, name, value):
    277         try:
--> 278             self.validate(value)
    279         except ValueError as e:
    280             for tp, converter in self.alternatives:

C:\ProgramData\Anaconda3\envs\earthsimAUG\lib\site-packages\bokeh\core\properties.py in validate(self, value, detail)
   1435                     all(type_param.is_valid(item) for type_param, item in zip(self.type_params, value))):
   1436                 msg = "" if not detail else "expected an element of %s, got %r" % (self, value)
-> 1437                 raise ValueError(msg)
   1438 
   1439     def _sphinx_type(self):

ValueError: expected an element of Tuple(Float, Float), got (datetime.datetime(2018, 8, 15, 10, 34, 58, 200529), datetime.datetime(2018, 8, 15, 15, 34, 58, 200529))

If I instantiate the param.DateRange with default=None, then set (dt, dt2) into the object as a tuple, that won't throw an error, but I also don't see a widget.

DateRange is inheriting from Range which might be where the Tuple(Float, Float) req is coming from? If so, that seems like a bug.

kcpevey commented 6 years ago

I should also note that instantiating with default=None, i.e. time_range = param.DateRange(default=None) will cause NONE of the widgets in the class to be visualized with parambokeh. Even if I have other valid widgets in the same class.

jbednar commented 6 years ago

Interesting. I think ParamNB knows about DateRange, but ParamBokeh does not. Usually, what will happen in a case like that is that ParamBokeh will simply display a text widget, which works ok until there is proper support. In this case, yes, Range is supported, and DateRange is a type of Range so it's falling back to that. https://github.com/ioam/parambokeh/pull/72 should force it back to a TextWidget, though that's not actually editable. @ceball may be able to do a better fix...

kcpevey commented 5 years ago

closing since panel has superseded parambokeh