Source code for nwbwidgets.controllers.time_window_controllers

from abc import abstractmethod

from ipywidgets import widgets, Layout, ValueWidget, link, HBox
from ipywidgets.widgets.widget_description import DescriptionWidget


[docs]class WindowController(HBox, ValueWidget, DescriptionWidget): def __init__( self, vmin, vmax, start_value=(None, None), description="window (s)", orientation="horizontal", **kwargs ): if orientation not in ("horizontal", "vertical"): ValueError("Unrecognized orientation: {}".format(orientation)) super().__init__() self.vmin = vmin self.vmax = vmax self.start_value = start_value self.description = description self.orientation = orientation if self.orientation == "horizontal": self.to_start_button = widgets.Button( description="◀◀", layout=Layout(width="65px") ) self.backwards_button = widgets.Button( description="◀", layout=Layout(width="40px") ) self.forward_button = widgets.Button( description="▶", layout=Layout(width="40px") ) self.to_end_button = widgets.Button( description="▶▶", layout=Layout(width="65px") ) else: # vertical self.to_end_button = widgets.Button( description="▲▲", layout=Layout(width="50px") ) self.forward_button = widgets.Button( description="▲", layout=Layout(width="50px") ) self.backwards_button = widgets.Button( description="▼", layout=Layout(width="50px") ) self.to_start_button = widgets.Button( description="▼▼", layout=Layout(width="50px") ) self.to_start_button.on_click(self.move_start) self.backwards_button.on_click(self.move_down) self.forward_button.on_click(self.move_up) self.to_end_button.on_click(self.move_end) # @abstractmethod # @property # def value(self): # """Must be window in seconds""" # pass
[docs] @abstractmethod def get_children(self): pass
[docs] @abstractmethod def move_up(self, change): pass
[docs] @abstractmethod def move_down(self, change): pass
[docs] @abstractmethod def move_start(self, change): pass
[docs] @abstractmethod def move_end(self, change): pass
[docs]class RangeController(WindowController): def __init__( self, vmin, vmax, start_value=None, dtype="float", description="time window (s)", orientation="horizontal", **kwargs ): super().__init__(vmin, vmax, start_value, description, orientation, **kwargs) self.dtype = dtype self.slider = self.make_range_slider(description=description, **kwargs) link((self.slider, "value"), (self, "value")) link((self.slider, "description"), (self, "description")) self.children = self.get_children()
[docs] def make_range_slider(self, **kwargs): """ Parameters ---------- kwargs: passed into RangeSlider constructor Returns ------- """ slider_kwargs = dict( value=self.start_value, min=self.vmin, max=self.vmax, continuous_update=False, readout=True, style={"description_width": "initial"}, orientation=self.orientation, ) if self.dtype == "float": slider_kwargs.update( readout_format=".1f", step=0.1, description="time window (s)", layout=Layout(width="100%"), ) slider_kwargs.update(kwargs) return widgets.FloatRangeSlider(**slider_kwargs) elif self.dtype == "int": slider_kwargs.update( description="unit window", layout=Layout(height="100%") ) slider_kwargs.update(kwargs) return widgets.IntRangeSlider(**slider_kwargs) else: raise ValueError("Unrecognized dtype: {}".format(self.dtype))
[docs] def move_up(self, change): value_range = self.value[1] - self.value[0] if self.value[1] + value_range < self.vmax: self.value = (self.value[0] + value_range, self.value[1] + value_range) else: self.move_end(change)
[docs] def move_down(self, change): value_range = self.value[1] - self.value[0] if self.value[0] - value_range > self.vmin: self.value = (self.value[0] - value_range, self.value[1] - value_range) else: self.move_start(change)
[docs] def move_start(self, change): value_range = self.value[1] - self.value[0] self.value = (self.vmin, self.vmin + value_range)
[docs] def move_end(self, change): value_range = self.value[1] - self.value[0] self.value = (self.vmax - value_range, self.vmax)
[docs] def get_children(self): if self.orientation == "horizontal": return [ self.slider, self.to_start_button, self.forward_button, self.backwards_button, self.to_end_button, ] else: return [ widgets.VBox( [ self.slider, self.to_end_button, self.forward_button, self.backwards_button, self.to_start_button, ], layout=widgets.Layout( display="flex", flex_flow="column", align_items="center" ), ) ]
[docs]class StartAndDurationController(WindowController): """ Can be used in place of the RangeController. """ DEFAULT_DURATION = 5 def __init__( self, tmax, tmin=0, start_value=None, description="start (s)", **kwargs ): """ Parameters ---------- tmax: float in seconds tmin: float in seconds start_value: (float, float) start and stop in seconds description: str kwargs: dict """ if tmin > tmax: raise ValueError( "tmax and tmin were probably entered in the wrong order. tmax should be first" ) super().__init__(tmin, tmax, start_value, description, **kwargs) self.slider = widgets.FloatSlider( value=start_value, min=tmin, max=tmax, step=0.01, description=description, continuous_update=False, orientation="horizontal", readout=True, readout_format=".2f", style={"description_width": "initial"}, layout=Layout(width="100%", min_width="250px"), ) link((self.slider, "description"), (self, "description")) if start_value is None: duration = min(self.DEFAULT_DURATION, tmax - tmin) else: duration = start_value[1] - start_value[0] self.duration = widgets.BoundedFloatText( value=duration, min=0, max=tmax - tmin, step=0.1, description="duration (s):", style={"description_width": "initial"}, layout=Layout(max_width="200px"), ) self.value = (self.slider.value, self.slider.value + self.duration.value) self.slider.observe(self.monitor_slider) self.duration.observe(self.monitor_duration) self.children = self.get_children()
[docs] def monitor_slider(self, change): if "new" in change: if isinstance(change["new"], dict): if "value" in change["new"]: value = change["new"]["value"] else: return else: value = change["new"] if self.slider.value + self.duration.value > self.vmax: self.slider.value = self.vmax - self.duration.value else: self.value = (value, value + self.duration.value)
[docs] def monitor_duration(self, change): if "new" in change: if isinstance(change["new"], dict): if "value" in change["new"]: value = change["new"]["value"] if self.slider.value + value > self.vmax: self.slider.value = self.vmax - value self.value = (self.slider.value, self.slider.value + value)
[docs] def move_up(self, change): if self.slider.value + 2 * self.duration.value < self.vmax: self.slider.value += self.duration.value else: self.move_end(change)
[docs] def move_down(self, change): if self.slider.value - self.duration.value > self.vmin: self.slider.value -= self.duration.value else: self.move_start(change)
[docs] def move_start(self, change): self.slider.value = self.vmin
[docs] def move_end(self, change): self.slider.value = self.vmax - self.duration.value
[docs] def get_children(self): if self.orientation == "horizontal": return [ self.slider, self.duration, # self.to_start_button, self.backwards_button, self.forward_button, # self.to_end_button ] else: return [ widgets.VBox( [ self.slider, self.duration, # self.to_end_button, self.forward_button, self.backwards_button, # self.to_start_button, ], layout=widgets.Layout( display="flex", flex_flow="column", align_items="center" ), ) ]