Hi Bryan,
Thanks for the quick reply. Making my variables private and specifying subtype and view_model as done here got the code working.
This is the corrected version:
from bokeh.models.widgets import RangeSlider
from time import perf_counter
from functools import partial
class ThrottledRangeSlider(RangeSlider):
“”"
Extends the bokeh RangeSlider to provide throttling of callbacks provided to on_update
“”"
view_model = “RangeSlider”
subtype = “”
def __init__(self, doc, base_delay, *args, **kwargs):
super(ThrottledRangeSlider, self).__init__(*args, **kwargs)
self._cb_set = False
self._base_delay = base_delay
self._delay = base_delay
self._doc = doc
def on_change(self, attrname, callback):
"""
Register a server side callback to be executed when slider attributes change
"""
update_function = self._callback_wrapper(callback)
super(ThrottledRangeSlider, self).on_change(attrname, partial(self._callback_setter, update_function))
def _callback_setter(self, update_function, attrname, old, new):
if not self._cb_set:
self._doc.add_timeout_callback(partial(update_function, attrname, old), self._delay)
self._cb_set = True
def _callback_wrapper(self, callback):
"""
Wraps callback to use self.value instead of new.
This ensures the callback has the most recent slider value at execution.
Also modifies the delay between callback execution to account for time taken
performing the callback's work.
"""
def throttled_cb(*args):
start_time = perf_counter()
attr, old = args
callback(attr, old, self.value)
# Modify delay to account for time taken executing callback
self._delay = max(self._base_delay - 1000 * (perf_counter() - start_time), 50)
self._cb_set = False
return throttled_cb
<details class='elided'>
<summary title='Show trimmed content'>···</summary>
On Tuesday, October 2, 2018 at 6:35:52 PM UTC-7, Bryan Van de ven wrote:
> Hi,
>
>
>
> Bokeh Models are wrappers around a purely declarative system for exchanging typed data between Python and JS runtimes. The classes themselves are heavily instrumented and locked down in order to afford all the automatic synchronization features. If you are subclassing a Bokeh Model then you can only add new Bokeh Properties as public class attributes (i.e. "foo=Int()", etc). If you want to add "regular" instance attributes that are not Bokeh properties, they must be private, i.e. start with an underscore.
>
>
> Just as an FYI, an alternate, if somewhat clunky, approach to throttling python callbacks is given here:
>
>
>
> [https://stackoverflow.com/questions/38375961/throttling-in-bokeh-application/38379136#38379136](https://stackoverflow.com/questions/38375961/throttling-in-bokeh-application/38379136#38379136)
>
>
>
> Thanks,
>
>
>
> Bryan
>
>
> > On Oct 2, 2018, at 17:43, Matt Agee <[email protected]> wrote:
>
> >
> > Hello,
> >
> > I wanted to make a class that could be treated pretty much the same as a RangeSlider but handle throttling on the server side.
> >
> > This was my attempt:
>
> >
> > class ThrottledRangeSlider(RangeSlider):
>
> > """
>
> > Extends the bokeh RangeSlider to provide throttling of callbacks provided to on_change
>
> > """
>
> > def __init__(self, doc, base_delay, *args, **kwargs):
>
> > super(ThrottledRangeSlider, self).__init__(*args, **kwargs)
>
> >
> > self.cb_set = False
>
> > self.base_delay = base_delay
>
> > self.delay = base_delay
>
> > self.doc = doc
>
> >
> > def on_change(self, attrname, callback):
>
> > """
>
> > Register a server side callback to be executed when slider attributes change
>
> > """
>
> > update_function = self._callback_wrapper(callback)
>
> > super(ThrottledRangeSlider, self).on_change(attrname, partial(self._callback_setter, update_function))
>
> >
> > def _callback_setter(self, update_function, attrname, old, new):
>
> > if not self.cb_set:
>
> > self.doc.add_timeout_callback(partial(update_function, attrname, old), self.delay)
>
> > self.cb_set = True
>
> >
> > def _callback_wrapper(self, callback):
>
> > def throttled_cb(*args):
>
> > start_time = perf_counter()
>
> > if len(args) == 2:
>
> > attr, old = args
>
> > callback(attr, old, self.value)
>
> > else:
>
> > cb_self, attr, old = args
>
> > callback(cb_self, attr, old, self.value)
>
> > # Modify delay to account for time taken executing callback
>
> > self.delay = max(self.base_delay - 1000 * (perf_counter() - start_time), 50)
>
> > self.cb_set = False
>
> > return throttled_cb
>
> >
> >
> > I'm sure there's other bugs in there, but right now the one that's causing the code to break is setting my own attributes in __init__. This was the error message:
>
> >
> > AttributeError: unexpected attribute 'doc' to ThrottledRangeSlider, possible attributes are bar_color, callback, callback_policy, callback_throttle, css_classes, direction, disabled, end, format, height, js_event_callbacks, js_property_callbacks, name, orientation, show_value, sizing_mode, start, step, subscribed_events, tags, title, tooltips, value or width
>
> >
> > Is there some way I can attach my own server side only attributes to the slider or should I just try a different approach?
> >
> > Thanks,
> >
> > Matt
>
> >
> > --
> > You received this message because you are subscribed to the Google Groups "Bokeh Discussion - Public" group.
>
> > To unsubscribe from this group and stop receiving emails from it, send an email to [email protected].
>
> > To post to this group, send email to [email protected].
>
> > To view this discussion on the web visit [https://groups.google.com/a/continuum.io/d/msgid/bokeh/27077f54-3a27-4cc4-a554-cc31997f3782%40continuum.io](https://groups.google.com/a/continuum.io/d/msgid/bokeh/27077f54-3a27-4cc4-a554-cc31997f3782%40continuum.io).
>
> > For more options, visit [https://groups.google.com/a/continuum.io/d/optout](https://groups.google.com/a/continuum.io/d/optout).
>
>
>
</details>