Beispiel #1
0
class DateRangeSlider(_SliderBase):

    value = param.Tuple(default=(None, None), length=2)

    value_throttled = param.Tuple(default=None, length=2, constant=True)

    start = param.Date(default=None)

    end = param.Date(default=None)

    step = param.Number(default=1)

    _source_transforms = {'value': None, 'value_throttled': None,
                         'start': None, 'end': None, 'step': None}

    _widget_type = _BkDateRangeSlider

    def __init__(self, **params):
        if 'value' not in params:
            params['value'] = (params.get('start', self.start),
                               params.get('end', self.end))
        super(DateRangeSlider, self).__init__(**params)

    def _process_property_change(self, msg):
        msg = super(DateRangeSlider, self)._process_property_change(msg)
        if 'value' in msg:
            v1, v2 = msg['value']
            msg['value'] = (value_as_datetime(v1), value_as_datetime(v2))
        if 'value_throttled' in msg:
            v1, v2 = msg['value_throttled']
            msg['value_throttled'] = (value_as_datetime(v1), value_as_datetime(v2))
        return msg
Beispiel #2
0
class RangeXY(LinkedStream):
    """
    Axis ranges along x- and y-axis in data coordinates.
    """

    x_range = param.Tuple(default=None, length=2, constant=True, doc="""
      Range of the x-axis of a plot in data coordinates""")

    y_range = param.Tuple(default=None, length=2, constant=True, doc="""
      Range of the y-axis of a plot in data coordinates""")
Beispiel #3
0
def params_from_kwargs(**kwargs):
    """
    Utility to promote keywords with literal values to the appropriate
    parameter type with the specified default value unless the value is
    already a parameter.
    """
    params = {}
    for k, v in kwargs.items():
        kws = dict(default=v)
        if isinstance(v, param.Parameter):
            params[k] = v
        elif isinstance(v, bool):
            params[k] = param.Boolean(**kws)
        elif isinstance(v, int):
            params[k] = param.Integer(**kws)
        elif isinstance(v, float):
            params[k] = param.Number(**kws)
        elif isinstance(v, str):
            params[k] = param.String(**kws)
        elif isinstance(v, dict):
            params[k] = param.Dict(**kws)
        elif isinstance(v, tuple):
            params[k] = param.Tuple(**kws)
        elif isinstance(v, list):
            params[k] = param.List(**kws)
        elif isinstance(v, np.ndarray):
            params[k] = param.Array(**kws)
        else:
            params[k] = param.Parameter(**kws)
    return params
Beispiel #4
0
class _RangeSliderBase(_SliderBase):

    value = param.Tuple(length=2,
                        doc="""
        The selected range of the slider. Updated when a handle is dragged.""")

    value_start = param.Parameter(
        readonly=True, doc="""The lower value of the selected range.""")

    value_end = param.Parameter(
        readonly=True, doc="""The upper value of the selected range.""")

    __abstract = True

    def __init__(self, **params):
        if 'value' not in params:
            params['value'] = (params.get('start', self.start),
                               params.get('end', self.end))
        params['value_start'], params['value_end'] = params['value']
        with edit_readonly(self):
            super().__init__(**params)

    @param.depends('value', watch=True)
    def _sync_values(self):
        vs, ve = self.value
        with edit_readonly(self):
            self.param.update(value_start=vs, value_end=ve)

    def _process_property_change(self, msg):
        msg = super()._process_property_change(msg)
        if 'value' in msg:
            msg['value'] = tuple(msg['value'])
        if 'value_throttled' in msg:
            msg['value_throttled'] = tuple(msg['value_throttled'])
        return msg
Beispiel #5
0
class TestSet(param.Parameterized):

    numpy_params = ['r']
    pandas_params = ['s','t','u']
    conditionally_unsafe = ['f', 'o']

    a = param.Integer(default=5, doc='Example doc', bounds=(2,30), inclusive_bounds=(True, False))
    b = param.Number(default=4.3, allow_None=True)
    c = param.String(default='foo')
    d = param.Boolean(default=False)
    e = param.List([1,2,3], class_=int)
    f = param.List([1,2,3])
    g = param.Date(default=datetime.datetime.now())
    h = param.Tuple(default=(1,2,3), length=3)
    i = param.NumericTuple(default=(1,2,3,4))
    j = param.XYCoordinates(default=(32.1, 51.5))
    k = param.Integer(default=1)
    l = param.Range(default=(1.1,2.3), bounds=(1,3))
    m = param.String(default='baz', allow_None=True)
    n = param.ObjectSelector(default=3, objects=[3,'foo'], allow_None=False)
    o = param.ObjectSelector(default=simple_list, objects=[simple_list], allow_None=False)
    p = param.ListSelector(default=[1,4,5], objects=[1,2,3,4,5,6])
    q = param.CalendarDate(default=datetime.date.today())
    r = None if np is None else param.Array(default=ndarray)
    s = None if pd is None else param.DataFrame(default=df1, columns=2)
    t = None if pd is None else param.DataFrame(default=pd.DataFrame(
        {'A':[1,2,3], 'B':[1.1,2.2,3.3]}), columns=(1,4), rows=(2,5))
    u = None if pd is None else param.DataFrame(default=df2, columns=['A', 'B'])
    v = param.Dict({'1':2})
Beispiel #6
0
class Bounds(BaseShape):
    """
    An arbitrary axis-aligned bounding rectangle defined by the (left,
    bottom, right, top) coordinate positions.

    If supplied a single real number as input, this value will be
    treated as the radius of a square, zero-center box which will be
    used to compute the corresponding lbrt tuple.
    """

    lbrt = param.Tuple(default=(-0.5, -0.5, 0.5, 0.5),
                       doc="""
          The (left, bottom, right, top) coordinates of the bounding box.""")

    group = param.String(default='Bounds',
                         constant=True,
                         doc="The assigned group name.")

    __pos_params = ['lbrt']

    def __init__(self, lbrt, **params):
        if not isinstance(lbrt, tuple):
            lbrt = (-lbrt, -lbrt, lbrt, lbrt)

        super(Bounds, self).__init__(lbrt=lbrt, **params)
        (l, b, r, t) = self.lbrt
        xdim, ydim = self.kdims
        self.data = [
            OrderedDict([(xdim.name, np.array([l, l, r, r, l])),
                         (ydim.name, np.array([b, t, t, b, b]))])
        ]
Beispiel #7
0
class Element3D(Element2D):

    extents = param.Tuple(default=(None, None, None,
                                   None, None, None),
        doc="""Allows overriding the extents of the Element
               in 3D space defined as (xmin, ymin, zmin,
               xmax, ymax, zmax).""")
Beispiel #8
0
class Surface(Image, Element3D):
    """
    Surface Element represents a 3D surface in space.
    The data should be supplied as a dense NxM matrix.
    """

    extents = param.Tuple(default=(None, None, None, None, None, None),
                          doc="""
        Allows overriding the extents of the Element in 3D space
        defined as (xmin, ymin, zmin, xmax, ymax, zmax).""")

    kdims = param.List(default=[Dimension('x'), Dimension('y')],
                       bounds=(2, 2),
                       doc="""
        The Surface x and y dimensions of the space defined
        by the supplied extent.""")

    vdims = param.List(default=[Dimension('z')],
                       bounds=(1, 1),
                       doc="""
        The Surface height dimension.""")

    group = param.String(default='Surface', constant=True)

    def __init__(self, data, kdims=None, vdims=None, extents=None, **params):
        extents = extents if extents else (None, None, None, None, None, None)
        Image.__init__(self,
                       data,
                       kdims=kdims,
                       vdims=vdims,
                       extents=extents,
                       **params)
Beispiel #9
0
class _RangeSliderBase(_SliderBase):

    value = param.Tuple(length=2)

    value_start = param.Parameter(readonly=True)

    value_end = param.Parameter(readonly=True)

    __abstract = True

    def __init__(self, **params):
        if 'value' not in params:
            params['value'] = (params.get('start', self.start),
                               params.get('end', self.end))
        params['value_start'], params['value_end'] = params['value']
        with edit_readonly(self):
            super().__init__(**params)

    @param.depends('value', watch=True)
    def _sync_values(self):
        vs, ve = self.value
        with edit_readonly(self):
            self.param.set_param(value_start=vs, value_end=ve)

    def _process_property_change(self, msg):
        msg = super()._process_property_change(msg)
        if 'value' in msg:
            msg['value'] = tuple(msg['value'])
        if 'value_throttled' in msg:
            msg['value_throttled'] = tuple(msg['value_throttled'])
        return msg
Beispiel #10
0
class DatetimeRangeInput(CompositeWidget):

    value = param.Tuple(default=(None, None), length=2)

    start = param.Date(default=None)

    end = param.Date(default=None)

    format = param.String(default='%Y-%m-%d %H:%M:%S', doc="""
        Datetime format used for parsing and formatting the datetime.""")

    _composite_type = Column

    def __init__(self, **params):
        self._text = StaticText(margin=(5, 0, 0, 0), style={'white-space': 'nowrap'})
        self._start = DatetimeInput(sizing_mode='stretch_width', margin=(5, 0, 0, 0))
        self._end = DatetimeInput(sizing_mode='stretch_width', margin=(5, 0, 0, 0))
        if 'value' not in params:
            params['value'] = (params['start'], params['end'])
        super().__init__(**params)
        self._msg = ''
        self._composite.extend([self._text, self._start, self._end])
        self._updating = False
        self._update_widgets()
        self._update_label()

    @param.depends('name', '_start.name', '_end.name', watch=True)
    def _update_label(self):
        self._text.value = f'{self.name}{self._start.name}{self._end.name}{self._msg}'

    @param.depends('_start.value', '_end.value', watch=True)
    def _update(self):
        if self._updating:
            return
        if (self._start.value is not None and
            self._end.value is not None and
            self._start.value > self._end.value):
            self._msg = ' (start of range must be <= end)'
            self._update_label()
            return
        elif self._msg:
            self._msg = ''
            self._update_label()
        try:
            self._updating = True
            self.value = (self._start.value, self._end.value)
        finally:
            self._updating = False

    @param.depends('value', 'start', 'end', 'name', 'format', watch=True)
    def _update_widgets(self):
        if self._updating:
            return
        try:
            self._updating = True
            self._start.param.set_param(value=self.value[0], start=self.start, end=self.end, format=self.format)
            self._end.param.set_param(value=self.value[1], start=self.start, end=self.end, format=self.format)
        finally:
            self._updating = False
class DateRangeSlider(_RangeSliderBase):

    value = param.Tuple(default=(None, None), length=2)

    value_start = param.Date(default=None, readonly=True)

    value_end = param.Date(default=None, readonly=True)

    value_throttled = param.Tuple(default=None, length=2, constant=True)

    start = param.Date(default=None)

    end = param.Date(default=None)

    step = param.Number(default=1)

    _source_transforms = {
        'value': None,
        'value_throttled': None,
        'start': None,
        'end': None,
        'step': None
    }

    _rename = {'name': 'title', 'value_start': None, 'value_end': None}

    _widget_type = _BkDateRangeSlider

    def _process_param_change(self, msg):
        msg = super()._process_param_change(msg)
        if msg.get('value') == (None, None):
            del msg['value']
        if msg.get('value_throttled') == (None, None):
            del msg['value_throttled']
        return msg

    def _process_property_change(self, msg):
        msg = super()._process_property_change(msg)
        if 'value' in msg:
            v1, v2 = msg['value']
            msg['value'] = (value_as_datetime(v1), value_as_datetime(v2))
        if 'value_throttled' in msg:
            v1, v2 = msg['value_throttled']
            msg['value_throttled'] = (value_as_datetime(v1),
                                      value_as_datetime(v2))
        return msg
Beispiel #12
0
class Element2D(Element):

    extents = param.Tuple(default=(None, None, None, None),
                          doc="""
        Allows overriding the extents of the Element in 2D space defined
        as four-tuple defining the (left, bottom, right and top) edges.""")

    __abstract = True
Beispiel #13
0
class BoundsX(LinkedStream):
    """
    A stream representing the bounds of a box selection as an
    tuple of the left and right coordinates.
    """

    boundsx = param.Tuple(default=None, constant=True, length=2,
                          allow_None=True, doc="""
        Bounds defined as (left, right) tuple.""")
Beispiel #14
0
class BoundsY(LinkedStream):
    """
    A stream representing the bounds of a box selection as an
    tuple of the bottom and top coordinates.
    """

    boundsy = param.Tuple(default=None, constant=True, length=2,
                          allow_None=True, doc="""
        Bounds defined as (bottom, top) tuple.""")
Beispiel #15
0
class Element3D(Element2D):

    extents = param.Tuple(default=(None, None, None, None, None, None),
                          doc="""
        Allows overriding the extents of the Element in 3D space
        defined as (xmin, ymin, zmin, xmax, ymax, zmax).""")

    __abstract = True

    _selection_streams = ()
 class _BigDumbParams(param.Parameterized):
     action = param.Action(default_action, allow_None=True)
     array = param.Array(np.array([1.0, 2.0]))
     boolean = param.Boolean(True, allow_None=True)
     callable = param.Callable(default_action, allow_None=True)
     class_selector = param.ClassSelector(int, is_instance=False, allow_None=True)
     color = param.Color("#FFFFFF", allow_None=True)
     composite = param.Composite(["action", "array"], allow_None=True)
     try:
         data_frame = param.DataFrame(
             pd.DataFrame({"A": 1.0, "B": np.arange(5)}), allow_None=True
         )
     except TypeError:
         data_frame = param.DataFrame(pd.DataFrame({"A": 1.0, "B": np.arange(5)}))
     date = param.Date(datetime.now(), allow_None=True)
     date_range = param.DateRange((datetime.min, datetime.max), allow_None=True)
     dict_ = param.Dict({"foo": "bar"}, allow_None=True, doc="dict means dictionary")
     dynamic = param.Dynamic(default=default_action, allow_None=True)
     file_selector = param.FileSelector(
         os.path.join(FILE_DIR_DIR, "LICENSE"),
         path=os.path.join(FILE_DIR_DIR, "*"),
         allow_None=True,
     )
     filename = param.Filename(
         os.path.join(FILE_DIR_DIR, "LICENSE"), allow_None=True
     )
     foldername = param.Foldername(os.path.join(FILE_DIR_DIR), allow_None=True)
     hook_list = param.HookList(
         [CallableObject(), CallableObject()], class_=CallableObject, allow_None=True
     )
     integer = param.Integer(10, allow_None=True)
     list_ = param.List([1, 2, 3], allow_None=True, class_=int)
     list_selector = param.ListSelector([2, 2], objects=[1, 2, 3], allow_None=True)
     magnitude = param.Magnitude(0.5, allow_None=True)
     multi_file_selector = param.MultiFileSelector(
         [],
         path=os.path.join(FILE_DIR_DIR, "*"),
         allow_None=True,
         check_on_set=True,
     )
     number = param.Number(-10.0, allow_None=True, doc="here is a number")
     numeric_tuple = param.NumericTuple((5.0, 10.0), allow_None=True)
     object_selector = param.ObjectSelector(
         False, objects={"False": False, "True": 1}, allow_None=True
     )
     path = param.Path(os.path.join(FILE_DIR_DIR, "LICENSE"), allow_None=True)
     range_ = param.Range((-1.0, 2.0), allow_None=True)
     series = param.Series(pd.Series(range(5)), allow_None=True)
     string = param.String("foo", allow_None=True, doc="this is a string")
     tuple_ = param.Tuple((3, 4, "fi"), allow_None=True)
     x_y_coordinates = param.XYCoordinates((1.0, 2.0), allow_None=True)
Beispiel #17
0
class DateRangeSlider(_SliderBase):

    value = param.Tuple(default=(None, None), length=2)

    value_throttled = param.Tuple(default=None, length=2)

    start = param.Date(default=None)

    end = param.Date(default=None)

    step = param.Number(default=1)

    _widget_type = _BkDateRangeSlider

    def _process_property_change(self, msg):
        msg = super(DateRangeSlider, self)._process_property_change(msg)
        if 'value' in msg:
            v1, v2 = msg['value']
            msg['value'] = (value_as_datetime(v1), value_as_datetime(v2))
        if 'value_throttled' in msg:
            v1, v2 = msg['value_throttled']
            msg['value_throttled'] = (value_as_datetime(v1),
                                      value_as_datetime(v2))
        return msg
Beispiel #18
0
class Surface(Image, Element3D):
    """
    A Surface represents a regularly sampled 2D grid with associated
    values defining the height along the z-axis. The key dimensions of
    a Surface represent the 2D coordinates along the x- and y-axes
    while the value dimension declares the height at each grid
    location.

    The data of a Surface is usually defined as a 2D array of values
    and either a bounds tuple defining the extent in the 2D space or
    explicit x- and y-coordinate arrays.
    """

    extents = param.Tuple(default=(None, None, None, None, None, None),
                          doc="""
        Allows overriding the extents of the Element in 3D space
        defined as (xmin, ymin, zmin, xmax, ymax, zmax).""")

    group = param.String(default='Surface', constant=True)

    kdims = param.List(default=[Dimension('x'), Dimension('y')],
                       bounds=(2, 2),
                       doc="""
        The Surface x and y dimensions of the space defined
        by the supplied extent.""")

    vdims = param.List(default=[Dimension('z')],
                       bounds=(1, 1),
                       doc="""
        The Surface height dimension.""")

    def __init__(self, data, kdims=None, vdims=None, extents=None, **params):
        extents = extents if extents else (None, None, None, None, None, None)
        Image.__init__(self,
                       data,
                       kdims=kdims,
                       vdims=vdims,
                       extents=extents,
                       **params)

    def _get_selection_expr_for_stream_value(self, **kwargs):
        expr, bbox, _ = super(
            Surface, self)._get_selection_expr_for_stream_value(**kwargs)
        return expr, bbox, None
Beispiel #19
0
class Surface(Image, Element3D):
    """
    Surface Element represents a 3D surface in space.
    The data should be supplied as a dense NxM matrix.
    """

    extents = param.Tuple(default=(None, None, None, None, None, None),
                          doc="""Allows overriding the extents of the Element
               in 3D space defined as (xmin, ymin, zmin,
               xmax, ymax, zmax).""")

    key_dimensions = param.List(default=[Dimension('x'),
                                         Dimension('y')],
                                bounds=(2, 2),
                                doc="""
        The Surface x and y dimensions of the space defined
        by the supplied extent.""")

    value_dimensions = param.List(default=[Dimension('z')],
                                  bounds=(1, 1),
                                  doc="""
        The Surface height dimension.""")

    group = param.String(default='Surface', constant=True)

    def __init__(self, data, extents=None, **params):
        extents = extents if extents else (None, None, None, None, None, None)
        Image.__init__(self, data, extents=extents, **params)

    def range(self, dim, data_range=True):
        dim_idx = dim if isinstance(dim,
                                    int) else self.get_dimension_index(dim)
        if dim_idx in [0, 1]:
            l, b, r, t = self.bounds.lbrt()
            if dim_idx == 0:
                return (l, r)
            elif dim_idx == 1:
                return (b, t)
        return super(Image, self).range(dim, data_range=data_range)
Beispiel #20
0
class Theme(param.Parameterized):
    """The Theme model provides parameters and functionality like links to spinner images and css.

- Provide theming to your Template and Application
- implement a custom subclass Theme
"""

    spinner_static_url = param.String(
        assets.SPINNER_PANEL_STATIC_LIGHT_400_340)
    spinner_url = param.String(assets.SPINNER_PANEL_BREATH_LIGHT_400_340)
    css = param.String()
    color_cycle = param.Tuple(_COLOR_CYCLE)
    bokeh_disable_logo = param.Boolean(True)
    bokeh_theme_json = param.Dict()

    @property
    def holoviews_color_cycle(self) -> Cycle:
        """Returns the HoloViews color Cycle to be used when plotting with the Theme as the active
        Theme.

        Returns:
            Cycle: A HoloViews color Cyle.
        """
        if self.color_cycle:
            color_cycle = self.color_cycle
        else:
            color_cycle = _COLOR_CYCLE
        return Cycle(list(color_cycle))

    @property
    def bokeh_theme(self) -> BokehTheme:
        """Returns the Bokeh Theme to be used when plotting with the Theme as the active Theme.

        Returns:
            BokehTheme: A Bokeh Theme
        """
        if self.bokeh_theme_json:
            return BokehTheme(json=self.bokeh_theme_json)
        return BokehTheme(json={})
Beispiel #21
0
    def define(cls, name, **kwargs):
        """
        Utility to quickly and easily declare Stream classes. Designed
        for interactive use such as notebooks and shouldn't replace
        parameterized class definitions in source code that is imported.

        Takes a stream class name and a set of keywords where each
        keyword becomes a parameter. If the value is already a
        parameter, it is simply used otherwise the appropriate parameter
        type is inferred and declared, using the value as the default.

        Supported types: bool, int, float, str, dict, tuple and list
        """
        params = {'name': param.String(default=name)}
        for k, v in kwargs.items():
            kws = dict(default=v, constant=True)
            if isinstance(v, param.Parameter):
                params[k] = v
            elif isinstance(v, bool):
                params[k] = param.Boolean(**kws)
            elif isinstance(v, int):
                params[k] = param.Integer(**kws)
            elif isinstance(v, float):
                params[k] = param.Number(**kws)
            elif isinstance(v, str):
                params[k] = param.String(**kws)
            elif isinstance(v, dict):
                params[k] = param.Dict(**kws)
            elif isinstance(v, tuple):
                params[k] = param.Tuple(**kws)
            elif isinstance(v, list):
                params[k] = param.List(**kws)
            elif isinstance(v, np.ndarray):
                params[k] = param.Array(**kws)
            else:
                params[k] = param.Parameter(**kws)

        # Dynamic class creation using type
        return type(name, (Stream,), params)
Beispiel #22
0
    class MyParameterized(param.Parameterized):
        enable = param.Boolean(True,
                               doc="A sample Boolean parameter",
                               allow_None=True)
        what_proportion = param.Magnitude(default=0.9)
        age = param.Number(49,
                           bounds=(0, 100),
                           doc="Any Number between 0 to 100")
        how_many = param.Integer()
        favorite_quote = param.String(default="Hello, world!")

        choose_file_or_folder = param.Path(search_paths='./')
        choose_folder = param.Foldername(search_paths="./")
        choose_file = param.Filename(search_paths="./")
        select_a_file = param.FileSelector(path='./*')
        select_multiple_files = param.MultiFileSelector(path='./*')

        favorite_color = param.ObjectSelector(
            default="green", objects=["red", "yellow", "green"])
        favorite_fruit = param.Selector(default="Apple",
                                        objects=["Orange", "Apple", "Mango"])
        select_multiple = param.ListSelector(default=[3, 5],
                                             objects=[1, 2, 3, 4, 5])

        birthday = param.CalendarDate(dt.date(2017, 1, 1),
                                      bounds=(dt.date(2017, 1,
                                                      1), dt.date(2017, 2, 1)))
        appointment = param.Date(dt.datetime(2017, 1, 1),
                                 bounds=(dt.datetime(2017, 1, 1),
                                         dt.datetime(2017, 2, 1)))
        least_favorite_color = param.Color(default='#FF0000')
        dataset = param.DataFrame(pd.util.testing.makeDataFrame().iloc[:3])

        this_strange_thing = param.Tuple(default=(False, ), allow_None=True)
        some_numbers = param.NumericTuple(default=(1, 2, 3.0, 4.0))
        home_city = param.XYCoordinates(default=(-111.65, 40.23))
        bounds = param.Range(default=(-10, 10))
class image_overlay(Operation):
    """
    Operation to build a overlay of images to a specification from a
    subset of the required elements.

    This is useful for reordering the elements of an overlay,
    duplicating layers of an overlay or creating blank image elements
    in the appropriate positions.

    For instance, image_overlay may build a three layered input
    suitable for the RGB factory operation even if supplied with one
    or two of the required channels (creating blank channels for the
    missing elements).

    Note that if there is any ambiguity regarding the match, the
    strongest match will be used. In the case of a tie in match
    strength, the first layer in the input is used. One successful
    match is always required.
    """

    output_type = Overlay

    spec = param.String(doc="""
       Specification of the output Overlay structure. For instance:

       Image.R * Image.G * Image.B

       Will ensure an overlay of this structure is created even if
       (for instance) only (Image.R * Image.B) is supplied.

       Elements in the input overlay that match are placed in the
       appropriate positions and unavailable specification elements
       are created with the specified fill group.""")

    fill = param.Number(default=0)

    default_range = param.Tuple(default=(0,1), doc="""
        The default range that will be set on the value_dimension of
        any automatically created blank image elements.""")

    group = param.String(default='Transform', doc="""
        The group assigned to the resulting overlay.""")


    @classmethod
    def _match(cls, el, spec):
        "Return the strength of the match (None if no match)"
        spec_dict = dict(zip(['type', 'group', 'label'], spec.split('.')))
        if not isinstance(el, Image) or spec_dict['type'] != 'Image':
            raise NotImplementedError("Only Image currently supported")

        sanitizers = {'group':group_sanitizer, 'label':label_sanitizer}
        strength = 1
        for key in ['group', 'label']:
            attr_value = sanitizers[key](getattr(el, key))
            if key in spec_dict:
                if spec_dict[key] != attr_value: return None
                strength += 1
        return strength


    def _match_overlay(self, raster, overlay_spec):
        """
        Given a raster or input overlay, generate a list of matched
        elements (None if no match) and corresponding tuple of match
        strength values.
        """
        ordering = [None]*len(overlay_spec) # Elements to overlay
        strengths = [0]*len(overlay_spec)   # Match strengths

        elements = raster.values() if isinstance(raster, Overlay) else [raster]

        for el in elements:
            for pos in range(len(overlay_spec)):
                strength = self._match(el, overlay_spec[pos])
                if strength is None:               continue  # No match
                elif (strength <= strengths[pos]): continue  # Weaker match
                else:                                        # Stronger match
                    ordering[pos] = el
                    strengths[pos] = strength
        return ordering, strengths


    def _process(self, raster, key=None):
        specs = tuple(el.strip() for el in self.p.spec.split('*'))
        ordering, strengths = self._match_overlay(raster, specs)
        if all(el is None for el in ordering):
            raise Exception("The image_overlay operation requires at least one match")

        completed = []
        strongest = ordering[np.argmax(strengths)]
        for el, spec in zip(ordering, specs):
            if el is None:
                spec_dict = dict(zip(['type', 'group', 'label'], spec.split('.')))
                el = Image(np.ones(strongest.data.shape) * self.p.fill,
                            group=spec_dict.get('group','Image'),
                            label=spec_dict.get('label',''))
                el.vdims[0].range = self.p.default_range
            completed.append(el)
        return np.prod(completed)
Beispiel #24
0
class Dimension(param.Parameterized):
    """
    Dimension objects are used to specify some important general
    features that may be associated with a collection of values.

    For instance, a Dimension may specify that a set of numeric values
    actually correspond to 'Height' (dimension name), in units of
    meters, and that allowed values must be floats greater than zero.

    In addition, Dimensions can be declared as cyclic, support
    categorical data using a finite set of allowed, ordered values and
    support a custom, pretty-printed representation.
    """

    name = param.String(doc="""
        Optional name associated with the Dimension. For instance,
        'height' or 'weight'.""")

    cyclic = param.Boolean(default=False,
                           doc="""
        Whether the range of this feature is cyclic such that the
        maximum allowed value (defined by the range parameter) is
        continuous with the minimum allowed value.""")

    value_format = param.Callable(default=None,
                                  doc="""
        Formatting function applied to each value before display.""")

    range = param.Tuple(default=(None, None),
                        doc="""
        Specifies the minimum and maximum allowed values for a
        Dimension. None is used to represent an unlimited bound.""")

    soft_range = param.Tuple(default=(None, None),
                             doc="""
        Specifies a minimum and maximum reference value, which
        may be overridden by the data.""")

    type = param.Parameter(default=None,
                           doc="""
        Optional type associated with the Dimension values. The type
        may be an inbuilt constructor (such as int, str, float) or a
        custom class object.""")

    unit = param.String(default=None,
                        allow_None=True,
                        doc="""
        Optional unit string associated with the Dimension. For
        instance, the string 'm' may be used represent units of meters
        and 's' to represent units of seconds.""")

    values = param.ClassSelector(class_=(str, list),
                                 default=[],
                                 doc="""
        Optional set of allowed values for the dimension that can also
        be used to retain a categorical ordering. Setting values to
        'initial' indicates that the values will be added during construction."""
                                 )

    # Defines default formatting by type
    type_formatters = {}
    unit_format = ' ({unit})'
    presets = {}  # A dictionary-like mapping name, (name,) or

    # (name, unit) to a preset Dimension object

    def __init__(self, name, **params):
        """
        Initializes the Dimension object with the given name.
        """
        if isinstance(name, Dimension):
            existing_params = dict(name.get_param_values())
        elif (name, params.get('unit', None)) in self.presets.keys():
            preset = self.presets[(str(name), str(params['unit']))]
            existing_params = dict(preset.get_param_values())
        elif name in self.presets.keys():
            existing_params = dict(self.presets[str(name)].get_param_values())
        elif (name, ) in self.presets.keys():
            existing_params = dict(
                self.presets[(str(name), )].get_param_values())
        else:
            existing_params = {'name': name}

        all_params = dict(existing_params, **params)
        name = all_params['name']
        label = name
        if isinstance(name, tuple):
            name, label = name
            all_params['name'] = name
        self.label = label

        if not isinstance(params.get('values', None), basestring):
            all_params['values'] = sorted(
                list(unique_array(params.get('values', []))))
        elif params['values'] != 'initial':
            raise Exception(
                "Values argument can only be set with the string 'initial'.")
        super(Dimension, self).__init__(**all_params)

    def __call__(self, name=None, **overrides):
        """
        Derive a new Dimension that inherits existing parameters
        except for the supplied, explicit overrides
        """
        settings = dict(self.get_param_values(onlychanged=True), **overrides)
        if name is not None: settings['name'] = name
        return self.__class__(**settings)

    @property
    def pprint_label(self):
        "The pretty-printed label string for the Dimension"
        unit = ('' if self.unit is None else type(self.unit)(
            self.unit_format).format(unit=self.unit))
        return safe_unicode(self.label) + safe_unicode(unit)

    def pprint_value(self, value):
        """
        Applies the defined formatting to the value.
        """
        own_type = type(value) if self.type is None else self.type
        formatter = (self.value_format if self.value_format else
                     self.type_formatters.get(own_type))
        if formatter:
            if callable(formatter):
                return formatter(value)
            elif isinstance(formatter, basestring):
                if isinstance(value, dt.datetime):
                    return value.strftime(formatter)
                elif isinstance(value, np.datetime64):
                    return dt64_to_dt(value).strftime(formatter)
                elif re.findall(r"\{(\w+)\}", formatter):
                    return formatter.format(value)
                else:
                    return formatter % value
        return value

    def __repr__(self):
        return self.pprint()

    def pprint_value_string(self, value):
        """
        Pretty prints the dimension name and value using the global
        title_format variable, including the unit string (if
        set). Numeric types are printed to the stated rounding level.
        """
        unit = '' if self.unit is None else ' ' + safe_unicode(self.unit)
        value = self.pprint_value(value)
        return title_format.format(name=safe_unicode(self.label),
                                   val=value,
                                   unit=unit)

    def __hash__(self):
        """
        The hash allows two Dimension objects to be compared; if the
        hashes are equal, all the parameters of the Dimensions are
        also equal.
        """
        return sum([
            hash(value) for _, value in self.get_param_values()
            if not isinstance(value, list)
        ])

    def __setstate__(self, d):
        """
        Compatibility for pickles before alias attribute was introduced.
        """
        super(Dimension, self).__setstate__(d)
        self.label = self.name

    def __str__(self):
        return self.pprint_label

    def __eq__(self, other):
        "Implements equals operator including sanitized comparison."
        dim_matches = [self.name, self.label, dimension_sanitizer(self.label)]
        if self is other:
            return True
        elif isinstance(other, Dimension):
            return bool({other.name, other.label} & set(dim_matches))
        else:
            return other in dim_matches

    def __ne__(self, other):
        "Implements not equal operator including sanitized comparison."
        return not self.__eq__(other)

    def __lt__(self, other):
        "Dimensions are sorted alphanumerically by name"
        return self.name < other.name if isinstance(
            other, Dimension) else self.name < other
Beispiel #25
0
class VTKVolume(PaneBase):

    max_data_size = param.Number(default=(256**3) * 2 / 1e6,
                                 doc="""
        Maximum data size transfert allowed without subsampling""")

    origin = param.Tuple(default=None, length=3, allow_None=True)

    spacing = param.Tuple(default=(1, 1, 1),
                          length=3,
                          doc="""
        Distance between voxel in each direction""")

    render_background = param.Color(default='#52576e',
                                    doc="""
        Allows to specify the background color of the 3D rendering. The value must be specified
        as an hexadecimal color string
    """)

    colormap = param.Selector(default='erdc_rainbow_bright',
                              objects=PRESET_CMAPS,
                              doc="""
        Name of the colormap used to transform pixel value in color
    """)

    rescale = param.Boolean(default=False,
                            doc="""
        If set to True the colormap is rescale beween min and max value of the non transparent pixels
        Else the full range of the pixel values are used
    """)

    shadow = param.Boolean(default=True,
                           doc="""
        If set to False, then the mapper for the volume will not perform shading
        computations, it is the same as setting ambient=1, diffuse=0, specular=0
    """)

    sampling = param.Number(default=0.4,
                            bounds=(0, 1),
                            step=1e-2,
                            doc="""
        Parameter to adjust the distance between samples used for rendering. The lower the value is
        the more precise is the representation but it is more computationnaly intensive
    """)

    edge_gradient = param.Number(default=0.4,
                                 bounds=(0, 1),
                                 step=1e-2,
                                 doc="""
        Parameter to adjust the opacity of the volume based on the gradient between voxels
    """)

    interpolation = param.Selector(
        default='fast_linear',
        objects=['fast_linear', 'linear', 'nearest'],
        doc="""
        interpolation type for sampling a volume. `nearest` interpolation will snap to the closest voxel,
        `linear` will perform trilinear interpolation to compute a scalar value from surrounding voxels.
        `fast_linear` under WebGL 1 will perform bilinear interpolation on X and Y but use nearest
        for Z. This is slightly faster than full linear at the cost of no Z axis linear interpolation.
    """)

    ambient = param.Number(default=0.2,
                           step=1e-2,
                           doc="""
        Value to control the ambient lighting. It is the light an object gives even in the absence
        of strong light. It is constant in all directions.
    """)

    diffuse = param.Number(default=0.7,
                           step=1e-2,
                           doc="""
        Value to control the diffuse Lighting. It relies on both the light direction and the
        object surface normal.
    """)

    specular = param.Number(default=0.3,
                            step=1e-2,
                            doc="""
        Value to control specular lighting. It is the light reflects back toward the camera when hitting the
        object
    """)

    specular_power = param.Number(default=8.,
                                  doc="""
        Specular power refers to how much light is reflected in a mirror like fashion,
        rather than scattered randomly in a diffuse manner
    """)

    slice_i = param.Integer(per_instance=True,
                            doc="""
        Integer parameter to control the position of the slice normal to the X direction
    """)

    slice_j = param.Integer(per_instance=True,
                            doc="""
        Integer parameter to control the position of the slice normal to the Y direction
    """)

    slice_k = param.Integer(per_instance=True,
                            doc="""
        Integer parameter to control the position of the slice normal to the Z direction
    """)

    display_volume = param.Boolean(default=True,
                                   doc="""
        If set to True, the 3D respresentation of the volume is displayed using ray casting
    """)

    display_slices = param.Boolean(default=False,
                                   doc="""
        If set to true, the orthgonal slices in the three (X, Y, Z) directions are displayed.
        Postition of each slice can be controlled using slice_(i,j,k) parameters
    """)

    _serializers = {}

    _rename = {'max_data_size': None, 'spacing': None, 'origin': None}

    _updates = True

    def __init__(self, object=None, **params):
        super(VTKVolume, self).__init__(object, **params)
        self._sub_spacing = self.spacing
        self._volume_data = self._get_volume_data()
        if self._volume_data:
            self.param.slice_i.bounds = (0, self._volume_data['dims'][0] - 1)
            self.slice_i = (self._volume_data['dims'][0] - 1) // 2
            self.param.slice_j.bounds = (0, self._volume_data['dims'][1] - 1)
            self.slice_j = (self._volume_data['dims'][1] - 1) // 2
            self.param.slice_k.bounds = (0, self._volume_data['dims'][2] - 1)
            self.slice_k = (self._volume_data['dims'][2] - 1) // 2

    @classmethod
    def applies(cls, obj):
        if ((isinstance(obj, np.ndarray) and obj.ndim == 3)
                or any([isinstance(obj, k) for k in cls._serializers.keys()])):
            return True
        elif 'vtk' not in sys.modules:
            return False
        else:
            import vtk
            return isinstance(obj, vtk.vtkImageData)

    def _get_model(self, doc, root=None, parent=None, comm=None):
        """
        Should return the bokeh model to be rendered.
        """
        if 'panel.models.vtk' not in sys.modules:
            if isinstance(comm, JupyterComm):
                self.param.warning(
                    'VTKVolumePlot was not imported on instantiation '
                    'and may not render in a notebook. Restart '
                    'the notebook kernel and ensure you load '
                    'it as part of the extension using:'
                    '\n\npn.extension(\'vtk\')\n')
            from ...models.vtk import VTKVolumePlot
        else:
            VTKVolumePlot = getattr(sys.modules['panel.models.vtk'],
                                    'VTKVolumePlot')

        props = self._process_param_change(self._init_properties())
        volume_data = self._volume_data

        model = VTKVolumePlot(data=volume_data, **props)
        if root is None:
            root = model
        self._link_props(model, ['colormap'], doc, root, comm)
        self._models[root.ref['id']] = (model, parent)
        return model

    def _update_object(self, ref, doc, root, parent, comm):
        self._legend = None
        super(VTKVolume, self)._update_object(ref, doc, root, parent, comm)

    def _init_properties(self):
        return {
            k: v
            for k, v in self.param.get_param_values()
            if v is not None and k not in
            ['default_layout', 'object', 'max_data_size', 'spacing', 'origin']
        }

    def _update(self, model):
        self._volume_data = self._get_volume_data()
        if self._volume_data:
            self.param.slice_i.bounds = (0, self._volume_data['dims'][0] - 1)
            self.slice_i = (self._volume_data['dims'][0] - 1) // 2
            self.param.slice_j.bounds = (0, self._volume_data['dims'][1] - 1)
            self.slice_j = (self._volume_data['dims'][1] - 1) // 2
            self.param.slice_k.bounds = (0, self._volume_data['dims'][2] - 1)
            self.slice_k = (self._volume_data['dims'][2] - 1) // 2
        model.data = self._volume_data

    @classmethod
    def register_serializer(cls, class_type, serializer):
        """
        Register a seriliazer for a given type of class.
        A serializer is a function which take an instance of `class_type`
        (like a vtk.vtkImageData) as input and return a numpy array of the data
        """
        cls._serializers.update({class_type: serializer})

    def _volume_from_array(self, sub_array):
        return dict(
            buffer=base64encode(
                sub_array.ravel(
                    order='F' if sub_array.flags['F_CONTIGUOUS'] else 'C')),
            dims=sub_array.shape
            if sub_array.flags['F_CONTIGUOUS'] else sub_array.shape[::-1],
            spacing=self._sub_spacing
            if sub_array.flags['F_CONTIGUOUS'] else self._sub_spacing[::-1],
            origin=self.origin,
            data_range=(sub_array.min(), sub_array.max()),
            dtype=sub_array.dtype.name)

    def _get_volume_data(self):
        if self.object is None:
            return None
        elif isinstance(self.object, np.ndarray):
            return self._volume_from_array(self._subsample_array(self.object))
        else:
            available_serializer = [
                v for k, v in self._serializers.items()
                if isinstance(self.object, k)
            ]
            if not available_serializer:
                import vtk
                from vtk.util import numpy_support

                def volume_serializer(imageData):
                    array = numpy_support.vtk_to_numpy(
                        imageData.GetPointData().GetScalars())
                    dims = imageData.GetDimensions()[::-1]
                    self.spacing = imageData.GetSpacing()[::-1]
                    self.origin = imageData.GetOrigin()
                    return self._volume_from_array(
                        self._subsample_array(array.reshape(dims, order='C')))

                self.register_serializer(vtk.vtkImageData, volume_serializer)
                serializer = volume_serializer
            else:
                serializer = available_serializer[0]
            return serializer(self.object)

    def _subsample_array(self, array):
        original_shape = array.shape
        spacing = self.spacing
        extent = tuple(
            (o_s - 1) * s for o_s, s in zip(original_shape, spacing))
        dim_ratio = np.cbrt(
            (np.prod(original_shape) / 1e6) / self.max_data_size)
        max_shape = tuple(int(o_s / dim_ratio) for o_s in original_shape)
        dowsnscale_factor = [
            max(o_s, m_s) / m_s for m_s, o_s in zip(max_shape, original_shape)
        ]

        if any([d_f > 1 for d_f in dowsnscale_factor]):
            try:
                import scipy.ndimage as nd
                sub_array = nd.interpolation.zoom(
                    array,
                    zoom=[1 / d_f for d_f in dowsnscale_factor],
                    order=0)
            except ImportError:
                sub_array = array[::int(np.ceil(dowsnscale_factor[0])), ::int(
                    np.ceil(dowsnscale_factor[1])
                ), ::int(np.ceil(dowsnscale_factor[2]))]
            self._sub_spacing = tuple(e / (s - 1)
                                      for e, s in zip(extent, sub_array.shape))
        else:
            sub_array = array
            self._sub_spacing = self.spacing
        return sub_array
Beispiel #26
0
class LoadingStyler(param.Parameterized):
    """A utility that can be used to select and style the loading spinner"""

    spinner = param.ObjectSelector(default=DEFAULT_URL,
                                   objects=config.SPINNERS,
                                   doc="The loading spinner to use")
    spinner_height = param.Integer(50, bounds=(1, 100))
    background_rgb = param.Tuple((255, 255, 255))
    background_alpha = param.Number(0.5,
                                    bounds=(0.0, 1.0),
                                    step=0.01,
                                    doc="The background alpha")
    color = param.Color(config.DEFAULT_COLOR)
    style = param.String("",
                         doc="The CSS Style applied to the loading spinner")

    settings_panel = param.Parameter(
        doc="A panel containing the settings of the LoadingStyler")
    style_panel = param.Parameter(
        doc="An 'invisible' HTML pane containing the css style")

    def __init__(self, **params):
        super().__init__(**params)

        self.settings_panel = pn.Param(
            self,
            parameters=[
                "spinner",
                "spinner_height",
                "background_alpha",
                "color",
                "style",
            ],
            widgets={
                "style": {
                    "type": pn.widgets.TextAreaInput,
                    "sizing_mode": "stretch_both",
                    "disabled": True,
                }
            },
        )

        self.style_panel = pn.pane.HTML(sizing_mode="fixed",
                                        width=0,
                                        height=0,
                                        margin=0)
        self._toggle_color()
        self._update_style()

    @property
    def _spinner_url(self):
        spinner = self.spinner
        if callable(spinner):
            return spinner(self.color)  # pylint: disable=not-callable
        return spinner

    @param.depends("spinner", watch=True)
    def _toggle_color(self):
        color_picker: pn.widgets.ColorPicker = [
            widget for widget in self.settings_panel
            if isinstance(widget, pn.widgets.ColorPicker)
        ][0]
        color_picker.disabled = not callable(self.spinner)

    @param.depends("spinner",
                   "spinner_height",
                   "color",
                   "background_rgb",
                   "background_alpha",
                   watch=True)
    def _update_style(self):
        self.style = f"""
.bk.pn-loading:before {{
background-image: url('{self._spinner_url}');
background-size: auto {self.spinner_height}%;
background-color: rgb({self.background_rgb[0]},{self.background_rgb[1]},{self.background_rgb[2]},{self.background_alpha});
}}"""

    @param.depends("style", watch=True)
    def _update_loading_spinner_css(self):
        self.style_panel.object = f"""<style>{self.style}</style>"""
Beispiel #27
0
class Dimension(param.Parameterized):
    """
    Dimension objects are used to specify some important general
    features that may be associated with a collection of values.

    For instance, a Dimension may specify that a set of numeric values
    actually correspond to 'Height' (dimension name), in units of
    meters, with a descriptive label 'Height of adult males'.

    All dimensions object have a name that identifies them and a label
    containing a suitable description. If the label is not explicitly
    specified it matches the name.

    These two parameters define the core identity of the dimension
    object and must match if two dimension objects are to be considered
    equivalent. All other parameters are considered optional metadata
    and are not used when testing for equality.

    Unlike all the other parameters, these core parameters can be used
    to construct a Dimension object from a tuple. This format is
    sufficient to define an identical Dimension:

    Dimension('a', label='Dimension A') == Dimension(('a', 'Dimension A'))

    Everything else about a dimension is considered to reflect
    non-semantic preferences. Examples include the default value (which
    may be used in a visualization to set an initial slider position),
    how the value is to rendered as text (which may be used to specify
    the printed floating point precision) or a suitable range of values
    to consider for a particular analysis.

    Units
    -----

    Full unit support with automated conversions are on the HoloViews
    roadmap. Once rich unit objects are supported, the unit (or more
    specifically the type of unit) will be part of the core dimension
    specification used to establish equality.

    Until this feature is implemented, there are two auxillary
    parameters that hold some partial information about the unit: the
    name of the unit and whether or not it is cyclic. The name of the
    unit is used as part of the pretty-printed representation and
    knowing whether it is cyclic is important for certain operations.
    """

    name = param.String(doc="""
       Short name associated with the Dimension, such as 'height' or
       'weight'. Valid Python identifiers make good names, because they
       can be used conveniently as a keyword in many contexts.""")

    label = param.String(default=None,
                         doc="""
        Unrestricted label used to describe the dimension. A label
        should succinctly describe the dimension and may contain any
        characters, including Unicode and LaTeX expression.""")

    cyclic = param.Boolean(default=False,
                           doc="""
        Whether the range of this feature is cyclic such that the
        maximum allowed value (defined by the range parameter) is
        continuous with the minimum allowed value.""")

    value_format = param.Callable(default=None,
                                  doc="""
        Formatting function applied to each value before display.""")

    range = param.Tuple(default=(None, None),
                        doc="""
        Specifies the minimum and maximum allowed values for a
        Dimension. None is used to represent an unlimited bound.""")

    soft_range = param.Tuple(default=(None, None),
                             doc="""
        Specifies a minimum and maximum reference value, which
        may be overridden by the data.""")

    type = param.Parameter(default=None,
                           doc="""
        Optional type associated with the Dimension values. The type
        may be an inbuilt constructor (such as int, str, float) or a
        custom class object.""")

    step = param.Number(default=None,
                        doc="""
        Optional floating point step specifying how frequently the
        underlying space should be sampled. May be used to define a
        discrete sampling of over the range.""")

    unit = param.String(default=None,
                        allow_None=True,
                        doc="""
        Optional unit string associated with the Dimension. For
        instance, the string 'm' may be used represent units of meters
        and 's' to represent units of seconds.""")

    values = param.List(default=[],
                        doc="""
        Optional specification of the allowed value set for the
        dimension that may also be used to retain a categorical
        ordering.""")

    # Defines default formatting by type
    type_formatters = {}
    unit_format = ' ({unit})'
    presets = {}  # A dictionary-like mapping name, (name,) or

    # (name, unit) to a preset Dimension object

    def __init__(self, spec, **params):
        """
        Initializes the Dimension object with the given name.
        """
        if 'name' in params:
            raise KeyError(
                'Dimension name must only be passed as the positional argument'
            )

        if isinstance(spec, Dimension):
            existing_params = dict(spec.get_param_values())
        elif (spec, params.get('unit', None)) in self.presets.keys():
            preset = self.presets[(str(spec), str(params['unit']))]
            existing_params = dict(preset.get_param_values())
        elif spec in self.presets:
            existing_params = dict(self.presets[spec].get_param_values())
        elif (spec, ) in self.presets:
            existing_params = dict(self.presets[(spec, )].get_param_values())
        else:
            existing_params = {}

        all_params = dict(existing_params, **params)
        if isinstance(spec, tuple):
            name, label = spec
            all_params['name'] = name
            all_params['label'] = label
            if 'label' in params and (label != params['label']):
                if params['label'] != label:
                    self.warning(
                        'Using label as supplied by keyword ({!r}), ignoring '
                        'tuple value {!r}'.format(params['label'], label))
                all_params['label'] = params['label']
        elif isinstance(spec, basestring):
            all_params['name'] = spec
            all_params['label'] = params.get('label', spec)

        if all_params['name'] == '':
            raise ValueError('Dimension name cannot be the empty string')
        if all_params['label'] in ['', None]:
            raise ValueError(
                'Dimension label cannot be None or the empty string')

        values = params.get('values', [])
        if isinstance(values, basestring) and values == 'initial':
            self.warning(
                "The 'initial' string for dimension values is no longer supported."
            )
            values = []

        all_params['values'] = list(unique_array(values))
        super(Dimension, self).__init__(**all_params)

    @property
    def spec(self):
        "Returns the corresponding tuple specification"
        return (self.name, self.label)

    def __call__(self, spec=None, **overrides):
        "Aliased to clone method. To be deprecated in 2.0"
        return self.clone(spec=spec, **overrides)

    def clone(self, spec=None, **overrides):
        """
        Derive a new Dimension that inherits existing parameters
        except for the supplied, explicit overrides
        """
        settings = dict(self.get_param_values(onlychanged=True), **overrides)

        if spec is None:
            spec = (self.name, overrides.get('label', self.label))
        if 'label' in overrides and isinstance(spec, basestring):
            spec = (spec, overrides['label'])
        elif 'label' in overrides and isinstance(spec, tuple):
            if overrides['label'] != spec[1]:
                self.warning(
                    'Using label as supplied by keyword ({!r}), ignoring '
                    'tuple value {!r}'.format(overrides['label'], spec[1]))
            spec = (spec[0], overrides['label'])

        return self.__class__(
            spec, **{
                k: v
                for k, v in settings.items() if k not in ['name', 'label']
            })

    def __hash__(self):
        """
        The hash allows Dimension objects to be used as dictionary keys in Python 3.
        """
        return hash(self.spec)

    def __setstate__(self, d):
        """
        Compatibility for pickles before alias attribute was introduced.
        """
        super(Dimension, self).__setstate__(d)
        self.label = self.name

    def __eq__(self, other):
        "Implements equals operator including sanitized comparison."

        if isinstance(other, Dimension):
            return self.spec == other.spec

        # For comparison to strings. Name may be sanitized.
        return other in [self.name, self.label, dimension_sanitizer(self.name)]

    def __ne__(self, other):
        "Implements not equal operator including sanitized comparison."
        return not self.__eq__(other)

    def __lt__(self, other):
        "Dimensions are sorted alphanumerically by name"
        return self.name < other.name if isinstance(
            other, Dimension) else self.name < other

    def __str__(self):
        return self.name

    def __repr__(self):
        return self.pprint()

    @property
    def pprint_label(self):
        "The pretty-printed label string for the Dimension"
        unit = ('' if self.unit is None else type(self.unit)(
            self.unit_format).format(unit=self.unit))
        return bytes_to_unicode(self.label) + bytes_to_unicode(unit)

    def pprint(self):
        changed = dict(self.get_param_values(onlychanged=True))
        if len(set([changed.get(k, k) for k in ['name', 'label']])) == 1:
            return 'Dimension({spec})'.format(spec=repr(self.name))

        ordering = sorted(sorted(changed.keys()),
                          key=lambda k: (-float('inf')
                                         if self.params(k).precedence is None
                                         else self.params(k).precedence))
        kws = ", ".join('%s=%r' % (k, changed[k]) for k in ordering
                        if k != 'name')
        return 'Dimension({spec}, {kws})'.format(spec=repr(self.name), kws=kws)

    def pprint_value(self, value):
        """
        Applies the defined formatting to the value.
        """
        own_type = type(value) if self.type is None else self.type
        formatter = (self.value_format if self.value_format else
                     self.type_formatters.get(own_type))
        if formatter:
            if callable(formatter):
                return formatter(value)
            elif isinstance(formatter, basestring):
                if isinstance(value, dt.datetime):
                    return value.strftime(formatter)
                elif isinstance(value, np.datetime64):
                    return dt64_to_dt(value).strftime(formatter)
                elif re.findall(r"\{(\w+)\}", formatter):
                    return formatter.format(value)
                else:
                    return formatter % value
        return unicode(bytes_to_unicode(value))

    def pprint_value_string(self, value):
        """
        Pretty prints the dimension name and value using the global
        title_format variable, including the unit string (if
        set). Numeric types are printed to the stated rounding level.
        """
        unit = '' if self.unit is None else ' ' + bytes_to_unicode(self.unit)
        value = self.pprint_value(value)
        return title_format.format(name=bytes_to_unicode(self.label),
                                   val=value,
                                   unit=unit)
Beispiel #28
0
class EditableRangeSlider(CompositeWidget, _SliderBase):
    """
    The EditableRangeSlider widget allows selecting a floating-point
    range using a slider with two handles and for more precise control
    also offers a set of number input boxes.

    Reference: https://panel.holoviz.org/reference/widgets/EditableRangeSlider.html

    :Example:

    >>> EditableRangeSlider(
    ...      value=(1.0, 1.5), start=0.0, end=2.0, step=0.25, name="A tuple of floats"
    ... )
    """

    value = param.Range(
        default=(0, 1),
        doc="Current range value. Updated when a handle is dragged")

    value_throttled = param.Range(default=None,
                                  constant=True,
                                  doc="""
        The value of the slider. Updated when the handle is released.""")

    start = param.Number(default=0., doc="Lower bound of the range.")

    end = param.Number(default=1., doc="Upper bound of the range.")

    step = param.Number(default=0.1, doc="Slider and number input step.")

    editable = param.Tuple(default=(True, True),
                           doc="""
        Whether the lower and upper values are editable.""")

    format = param.ClassSelector(default='0.0[0000]',
                                 class_=(
                                     str,
                                     TickFormatter,
                                 ),
                                 doc="""
        Allows defining a custom format string or bokeh TickFormatter.""")

    show_value = param.Boolean(default=False,
                               readonly=True,
                               precedence=-1,
                               doc="""
        Whether to show the widget value.""")

    _composite_type = Column

    def __init__(self, **params):
        if not 'width' in params and not 'sizing_mode' in params:
            params['width'] = 300
        super().__init__(**params)
        self._label = StaticText(margin=0, align='end')
        self._slider = RangeSlider(margin=(0, 0, 5, 0), show_value=False)
        self._slider.param.watch(self._sync_value, 'value')
        self._slider.param.watch(self._sync_value, 'value_throttled')
        self._start_edit = FloatInput(min_width=50,
                                      margin=0,
                                      format=self.format,
                                      css_classes=['slider-edit'])
        self._end_edit = FloatInput(min_width=50,
                                    margin=(0, 0, 0, 10),
                                    format=self.format,
                                    css_classes=['slider-edit'])
        self._start_edit.param.watch(self._sync_start_value, 'value')
        self._start_edit.param.watch(self._sync_start_value, 'value_throttled')
        self._end_edit.param.watch(self._sync_end_value, 'value')
        self._end_edit.param.watch(self._sync_end_value, 'value_throttled')

        sep = StaticText(value='...', margin=(0, 2, 0, 2), align='end')
        edit = Row(self._label,
                   self._start_edit,
                   sep,
                   self._end_edit,
                   sizing_mode='stretch_width',
                   margin=0)
        self._composite.extend([edit, self._slider])
        self._slider.jscallback(args={
            'start': self._start_edit,
            'end': self._end_edit
        },
                                value="""
        let [min, max] = cb_obj.value
        start.value = min
        end.value = max
        """)
        self._start_edit.jscallback(args={'slider': self._slider},
                                    value="""
        if (cb_obj.value < slider.start) {
          slider.start = cb_obj.value
        } else if (cb_obj.value > slider.end) {
          slider.end = cb_obj.value
        }
        """)
        self._end_edit.jscallback(args={'slider': self._slider},
                                  value="""
        if (cb_obj.value < slider.start) {
          slider.start = cb_obj.value
        } else if (cb_obj.value > slider.end) {
          slider.end = cb_obj.value
        }
        """)
        self._update_editable()
        self._update_layout()
        self._update_name()
        self._update_slider()
        self._update_value()

    @param.depends('editable', watch=True)
    def _update_editable(self):
        self._start_edit.disabled = not self.editable[0]
        self._end_edit.disabled = not self.editable[1]

    @param.depends('name', watch=True)
    def _update_name(self):
        if self.name:
            label = f'{self.name}:'
            margin = (0, 10, 0, 0)
        else:
            label = ''
            margin = (0, 0, 0, 0)
        self._label.param.update(**{'margin': margin, 'value': label})

    @param.depends('width', 'height', 'sizing_mode', watch=True)
    def _update_layout(self):
        self._start_edit.sizing_mode = self.sizing_mode
        self._end_edit.sizing_mode = self.sizing_mode
        if self.sizing_mode not in ('stretch_width', 'stretch_both'):
            w = (self.width or 300) // 4
            self._start_edit.width = w
            self._end_edit.width = w

    @param.depends('start',
                   'end',
                   'step',
                   'bar_color',
                   'direction',
                   'show_value',
                   'tooltips',
                   'name',
                   'format',
                   watch=True)
    def _update_slider(self):
        self._slider.param.update(
            **{
                'format': self.format,
                'start': self.start,
                'end': self.end,
                'step': self.step,
                'bar_color': self.bar_color,
                'direction': self.direction,
                'show_value': self.show_value,
                'tooltips': self.tooltips,
            })
        self._start_edit.step = self.step
        self._end_edit.step = self.step

    @param.depends('value', watch=True)
    def _update_value(self):
        self._slider.value = self.value
        self._start_edit.value = self.value[0]
        self._end_edit.value = self.value[1]

    def _sync_value(self, event):
        with param.edit_constant(self):
            self.param.update(**{event.name: event.new})

    def _sync_start_value(self, event):
        if event.name == 'value':
            end = self.value[1] if self.value else self.end
        else:
            end = self.value_throttled[1] if self.value_throttled else self.end
        with param.edit_constant(self):
            self.param.update(**{event.name: (event.new, end)})

    def _sync_end_value(self, event):
        if event.name == 'value':
            start = self.value[0] if self.value else self.start
        else:
            start = self.value_throttled[
                0] if self.value_throttled else self.start
        with param.edit_constant(self):
            self.param.update(**{event.name: (start, event.new)})
Beispiel #29
0
class DateRangeSlider(_RangeSliderBase):
    """
    The DateRangeSlider widget allows selecting a date range using a
    slider with two handles. Supports datetime.datetime, datetime.data
    and np.datetime64 ranges.

    Reference: https://panel.holoviz.org/reference/widgets/DateRangeSlider.html

    :Example:

    >>> import datetime as dt
    >>> DateRangeSlider(
    ...     value=(dt.datetime(2025, 1, 9), dt.datetime(2025, 1, 16)),
    ...     start=dt.datetime(2025, 1, 1),
    ...     end=dt.datetime(2025, 1, 31),
    ...     step=2,
    ...     name="A tuple of datetimes"
    ... )
    """

    value = param.Tuple(
        default=(None, None),
        length=2,
        doc=
        """The selected range as a tuple of values. Updated when one of the handles is
        dragged. Supports datetime.datetime, datetime.date, and np.datetime64 ranges."""
    )

    value_start = param.Date(default=None,
                             readonly=True,
                             doc="""
        The lower value of the selected range.""")

    value_end = param.Date(default=None,
                           readonly=True,
                           doc="""
        The upper value of the selected range.""")

    value_throttled = param.Tuple(default=None,
                                  length=2,
                                  constant=True,
                                  doc="""
        The selected range as a tuple of values. Updated one of the handles is released. Supports 
        datetime.datetime, datetime.date and np.datetime64 ranges""")

    start = param.Date(default=None, doc="""
        The lower bound.""")

    end = param.Date(default=None, doc="""
        The upper bound.""")

    step = param.Number(default=1,
                        doc="""
        The step size. Default is 1 (day).""")

    _source_transforms = {
        'value': None,
        'value_throttled': None,
        'start': None,
        'end': None,
        'step': None
    }

    _rename = {'name': 'title', 'value_start': None, 'value_end': None}

    _widget_type = _BkDateRangeSlider

    def _process_param_change(self, msg):
        msg = super()._process_param_change(msg)
        if msg.get('value') == (None, None):
            del msg['value']
        elif 'value' in msg:
            v1, v2 = msg['value']
            if isinstance(v1, dt.datetime):
                v1 = datetime_as_utctimestamp(v1)
            if isinstance(v2, dt.datetime):
                v2 = datetime_as_utctimestamp(v2)
            msg['value'] = (v1, v2)
        if msg.get('value_throttled') == (None, None):
            del msg['value_throttled']
        return msg

    def _process_property_change(self, msg):
        msg = super()._process_property_change(msg)
        if 'value' in msg:
            v1, v2 = msg['value']
            msg['value'] = (value_as_datetime(v1), value_as_datetime(v2))
        if 'value_throttled' in msg:
            v1, v2 = msg['value_throttled']
            msg['value_throttled'] = (value_as_datetime(v1),
                                      value_as_datetime(v2))
        return msg
Beispiel #30
0
class EditableRangeSlider(CompositeWidget, _SliderBase):
    """
    The EditableRangeSlider extends the RangeSlider by adding text
    input fields to manually edit the range and potentially override
    the bounds.
    """

    editable = param.Tuple(default=(True, True),
                           doc="""
        Whether the lower and upper values are editable.""")

    end = param.Number(default=1., doc="Upper bound of the range.")

    format = param.ClassSelector(default='0.0[0000]',
                                 class_=string_types + (TickFormatter, ),
                                 doc="""
        Allows defining a custom format string or bokeh TickFormatter.""")

    show_value = param.Boolean(default=False,
                               readonly=True,
                               precedence=-1,
                               doc="""
        Whether to show the widget value.""")

    start = param.Number(default=0., doc="Lower bound of the range.")

    step = param.Number(default=0.1, doc="Slider and number input step.")

    value = param.Range(default=(0, 1), doc="Current range value.")

    value_throttled = param.Range(default=None, constant=True)

    _composite_type = Column

    def __init__(self, **params):
        if not 'width' in params and not 'sizing_mode' in params:
            params['width'] = 300
        super().__init__(**params)
        self._label = StaticText(margin=0, align='end')
        self._slider = RangeSlider(margin=(0, 0, 5, 0), show_value=False)
        self._slider.param.watch(self._sync_value, 'value')
        self._slider.param.watch(self._sync_value, 'value_throttled')
        self._start_edit = FloatInput(min_width=50,
                                      margin=0,
                                      format=self.format,
                                      css_classes=['slider-edit'])
        self._end_edit = FloatInput(min_width=50,
                                    margin=(0, 0, 0, 10),
                                    format=self.format,
                                    css_classes=['slider-edit'])
        self._start_edit.param.watch(self._sync_start_value, 'value_throttled')
        self._end_edit.param.watch(self._sync_end_value, 'value_throttled')

        sep = StaticText(value='...', margin=(0, 2, 0, 2), align='end')
        edit = Row(self._label,
                   self._start_edit,
                   sep,
                   self._end_edit,
                   sizing_mode='stretch_width',
                   margin=0)
        self._composite.extend([edit, self._slider])
        self._slider.jscallback(args={
            'start': self._start_edit,
            'end': self._end_edit
        },
                                value="""
        let [min, max] = cb_obj.value
        start.value = min
        end.value = max
        """)
        self._start_edit.jscallback(args={'slider': self._slider},
                                    value="""
        if (cb_obj.value < slider.start) {
          slider.start = cb_obj.value
        } else if (cb_obj.value > slider.end) {
          slider.end = cb_obj.value
        }
        slider.value = [cb_obj.value, slider.value[1]]
        """)
        self._end_edit.jscallback(args={'slider': self._slider},
                                  value="""
        if (cb_obj.value < slider.start) {
          slider.start = cb_obj.value
        } else if (cb_obj.value > slider.end) {
          slider.end = cb_obj.value
        }
        slider.value = [slider.value[0], cb_obj.value]
        """)
        self._update_editable()
        self._update_layout()
        self._update_name()
        self._update_slider()
        self._update_value()

    @param.depends('editable', watch=True)
    def _update_editable(self):
        self._start_edit.disabled = not self.editable[0]
        self._end_edit.disabled = not self.editable[1]

    @param.depends('name', watch=True)
    def _update_name(self):
        if self.name:
            label = f'{self.name}:'
            margin = (0, 10, 0, 0)
        else:
            label = ''
            margin = (0, 0, 0, 0)
        self._label.param.set_param(**{'margin': margin, 'value': label})

    @param.depends('width', 'height', 'sizing_mode', watch=True)
    def _update_layout(self):
        self._start_edit.sizing_mode = self.sizing_mode
        self._end_edit.sizing_mode = self.sizing_mode
        if self.sizing_mode not in ('stretch_width', 'stretch_both'):
            w = (self.width or 300) // 4
            self._start_edit.width = w
            self._end_edit.width = w

    @param.depends('start',
                   'end',
                   'step',
                   'bar_color',
                   'direction',
                   'show_value',
                   'tooltips',
                   'name',
                   'format',
                   watch=True)
    def _update_slider(self):
        self._slider.param.set_param(
            **{
                'format': self.format,
                'start': self.start,
                'end': self.end,
                'step': self.step,
                'bar_color': self.bar_color,
                'direction': self.direction,
                'show_value': self.show_value,
                'tooltips': self.tooltips,
            })
        self._start_edit.step = self.step
        self._end_edit.step = self.step

    @param.depends('value', watch=True)
    def _update_value(self):
        self._slider.value = self.value
        self._start_edit.value = self.value[0]
        self._end_edit.value = self.value[1]

    def _sync_value(self, event):
        with param.edit_constant(self):
            self.param.set_param(**{event.name: event.new})

    def _sync_start_value(self, event):
        with param.edit_constant(self):
            self.param.set_param(
                **{event.name: (event.new, self.value_throttled[1])})

    def _sync_end_value(self, event):
        with param.edit_constant(self):
            self.param.set_param(
                **{event.name: (self.value_throttled[0], event.new)})