def show_indexed_timeseries_plotly(timeseries: TimeSeries,
                                   istart=None,
                                   istop=None,
                                   fig: go.FigureWidget = None,
                                   col=None,
                                   row=None,
                                   zero_start=False,
                                   xlabel='time (s)',
                                   ylabel=None,
                                   title=None,
                                   neurodata_vis_spec=None,
                                   **kwargs):
    if ylabel is None and timeseries.unit:
        ylabel = timeseries.unit

    tt = get_timeseries_tt(timeseries, istart=istart, istop=istop)
    if zero_start:
        tt = tt - tt[0]
    data, unit = get_timeseries_in_units(timeseries,
                                         istart=istart,
                                         istop=istop)

    trace_kwargs = dict()
    if col is not None or row is not None:
        trace_kwargs.update(row=row, col=col)
    fig.add_trace(x=tt, y=data, **trace_kwargs, **kwargs)
    layout_kwargs = dict(xaxis_title=xlabel)
    if ylabel is not None:
        layout_kwargs.update(yaxis_title=ylabel)
    if title is not None:
        layout_kwargs.update(title=title)

    fig.update_layout(**layout_kwargs)
示例#2
0
def show_indexed_timeseries_plotly(
    timeseries: TimeSeries,
    istart: int = 0,
    istop: int = None,
    time_window: list = None,
    trace_range: list = None,
    offsets=None,
    fig: go.FigureWidget = None,
    col=None,
    row=None,
    zero_start=False,
    scatter_kwargs: dict = None,
    figure_kwargs: dict = None,
):
    if istart != 0 or istop is not None:
        if time_window is not None:
            raise ValueError(
                "input either time window or istart/stop but not both")
        if not (0 <= istart < timeseries.data.shape[0] and
                (istop is None or 0 < istop <= timeseries.data.shape[0])):
            raise ValueError("enter correct istart/stop values")
        t_istart = istart
        t_istop = istop
    elif time_window is not None:
        t_istart = timeseries_time_to_ind(timeseries, time_window[0])
        t_istop = timeseries_time_to_ind(timeseries, time_window[1])
    else:
        t_istart = istart
        t_istop = istop
    tt = get_timeseries_tt(timeseries, istart=t_istart, istop=t_istop)
    data, unit = get_timeseries_in_units(timeseries,
                                         istart=t_istart,
                                         istop=t_istop)
    if len(data.shape) == 1:
        data = data[:, np.newaxis]
    if trace_range is not None:
        if not (0 <= trace_range[0] < data.shape[1]
                and 0 < trace_range[1] <= data.shape[1]):
            raise ValueError("enter correct trace range")
        trace_istart = trace_range[0]
        trace_istop = trace_range[1]
    else:
        trace_istart = 0
        trace_istop = data.shape[1]
    if offsets is None:
        offsets = np.zeros(trace_istop - trace_istart)
    if zero_start:
        tt = tt - tt[0]
    scatter_kwargs = dict() if scatter_kwargs is None else scatter_kwargs
    if fig is None:
        fig = go.FigureWidget(make_subplots(rows=1, cols=1))
    row = 1 if row is None else row
    col = 1 if col is None else col
    for i, trace_id in enumerate(range(trace_istart, trace_istop)):
        fig.add_trace(
            go.Scattergl(x=tt,
                         y=data[:, trace_id] + offsets[i],
                         mode="lines",
                         **scatter_kwargs),
            row=row,
            col=col,
        )
    input_figure_kwargs = dict(
        xaxis=dict(title_text="time (s)", range=[tt[0], tt[-1]]),
        yaxis=dict(title_text=unit if unit is not None else None),
        title=timeseries.name,
    )
    if figure_kwargs is None:
        figure_kwargs = dict()
    input_figure_kwargs.update(figure_kwargs)
    fig.update_xaxes(input_figure_kwargs.pop("xaxis"), row=row, col=col)
    fig.update_yaxes(input_figure_kwargs.pop("yaxis"), row=row, col=col)
    fig.update_layout(**input_figure_kwargs)
    return fig
示例#3
0
class PapayaConfigWidget(VBox):
    """A widget that displays widgets to adjust NLPapayaViewer image parameters."""

    lut_options = [
        "Grayscale",
        "Red Overlay",
        "Green Overlay",
        "Blue Overlay",
        "Gold",
        "Spectrum",
        "Overlay (Positives)",
        "Overlay (Negatives)",
    ]

    def __init__(self, viewer, *args, **kwargs):
        """
        Parameters
        ----------
        viewer: NlPapayaViewer
            associated viewer.
        """
        super().__init__(*args, **kwargs)

        self._viewer = viewer
        self._init_widgets()

        self.children = [
            VBox([
                VBox(
                    [self._hist],
                    layout=Layout(
                        height="auto",
                        margin="0px 0px 0px 0px",
                        padding="5px 5px 5px 5px",
                    ),
                ),
                VBox(
                    [
                        self._alpha,
                        self._lut,
                        self._nlut,
                        self._min,
                        self._minp,
                        self._max,
                        self._maxp,
                        self._sym,
                    ],
                    layout=Layout(width="230px"),
                ),
            ])
        ]

    def _init_widgets(self):
        """Initializes all configuration widgets. Possible image config parameters are:"""
        layout = Layout(width="200px", max_width="200px")

        self._alpha = FloatSlider(
            value=1,
            min=0,
            max=1.0,
            step=0.1,
            description="alpha:",
            description_tooltip="Overlay image alpha level (0 to 1).",
            disabled=False,
            continuous_update=True,
            orientation="horizontal",
            readout=True,
            readout_format=".1f",
            layout=layout,
        )

        self._lut = Dropdown(
            options=PapayaConfigWidget.lut_options,
            value="Red Overlay",
            description="lut:",
            description_tooltip="The color table name.",
            layout=layout,
        )

        self._nlut = Dropdown(
            options=PapayaConfigWidget.lut_options,
            value="Red Overlay",
            description="negative-lut:",
            description_tooltip=
            "The color table name used by the negative side of the parametric pair.",
            layout=layout,
        )

        self._min = FloatText(
            value=None,
            description="min:",
            description_tooltip="The display range minimum.",
            step=0.01,
            continuous_update=True,
            disabled=False,
            layout=layout,
        )

        self._minp = BoundedFloatText(
            value=None,
            min=0,
            max=100,
            step=1,
            continuous_update=True,
            description="min %:",
            description_tooltip=
            "The display range minimum as a percentage of image max.",
            disabled=False,
            layout=layout,
        )

        self._max = FloatText(
            value=None,
            description="max:",
            description_tooltip="The display range maximum.",
            step=0.01,
            continuous_update=True,
            disabled=False,
            layout=layout,
        )

        self._maxp = BoundedFloatText(
            value=None,
            min=0,
            max=100,
            step=1,
            continuous_update=True,
            description="max %:",
            description_tooltip=
            "The display range minimum as a percentage of image max.",
            disabled=False,
            layout=layout,
        )

        self._sym = Checkbox(
            value=False,
            description="symmetric",
            description_tooltip=
            "When selected, sets the negative range of a parametric pair to the same size as the positive range.",
            disabled=False,
            layout=layout,
        )

        # figure to display histogram of image data
        fig = Figure()
        fig.update_layout(
            height=300,
            margin=dict(l=15, t=15, b=15, r=15, pad=4),
            showlegend=True,
            legend_orientation="h",
        )

        self._hist = FigureWidget(fig)
        self._hist.add_trace(
            Histogram(x=[], name="All image data", visible="legendonly"))
        self._hist.add_trace(Histogram(x=[], name="Image data without 0s"))

        self._handlers = defaultdict()

    def _set_values(self, config, range, data):
        """Sets config values from the specified `config` and creates histogram for `data`.

        Parameters
        ----------
        config : dict
            configuration parameters for the image. Possible keywords are:
            alpha : int
                the overlay image alpha level (0 to 1).
            lut : str
                the color table name.
            negative_lut : str
                the color table name used by the negative side of the parametric pair.
            max : int
                the display range maximum.
            maxPercent : int
                the display range maximum as a percentage of image max.
            min : int
                the display range minimum.
            minPercent : int
                the display range minimum as a percentage of image min.
           symmetric : bool
                if true, sets the negative range of a parametric pair to the same size as the positive range.
        range: float
            range of image values.
        data: []
           flattened image data.
        """
        self._alpha.value = config.get("alpha", 1)
        self._lut.value = config.get("lut", PapayaConfigWidget.lut_options[1])
        self._nlut.value = config.get("negative_lut",
                                      PapayaConfigWidget.lut_options[1])
        self._min.value = config.get("min", 0)
        self._minp.value = self._get_per_from_value(range,
                                                    config.get("min", 0))
        self._max.value = config.get("max", 0.1)
        self._maxp.value = self._get_per_from_value(range,
                                                    config.get("max", 0.1))
        self._sym.value = config.get("symmetric", "false") == "true"

        # set histogram data
        self._hist.data[0].x = data
        # leave out 0 values
        self._hist.data[1].x = [] if (data == []
                                      or data is None) else data[data != 0]

    def _add_handlers(self, image):
        """Add config widget event handlers to change the config values for the specified `image`.

        Parameters
        ----------
        image: neurolang_ipywidgets.PapayaImage
            image whose config values will be viewed/modified using this config widget.
        """

        # Dropdown does not support resetting event handlers after Dropdown.unobserve_all is called
        # So handlers are stored to be removed individually
        # github issue https://github.com/jupyter-widgets/ipywidgets/issues/1868

        self._handlers["alpha"] = partial(self._config_changed,
                                          image=image,
                                          name="alpha")
        self._handlers["lut"] = partial(self._config_changed,
                                        image=image,
                                        name="lut")
        self._handlers["nlut"] = partial(self._config_changed,
                                         image=image,
                                         name="negative_lut")
        self._handlers["min"] = partial(self._config_changed,
                                        image=image,
                                        name="min")
        self._handlers["minp"] = partial(self._set_min_max,
                                         image=image,
                                         name="minPercent")
        self._handlers["max"] = partial(self._config_changed,
                                        image=image,
                                        name="max")
        self._handlers["maxp"] = partial(self._set_min_max,
                                         image=image,
                                         name="maxPercent")
        self._handlers["sym"] = partial(self._config_bool_changed,
                                        image=image,
                                        name="symmetric")

        self._alpha.observe(self._handlers["alpha"], names="value")

        self._lut.observe(self._handlers["lut"], names="value")

        self._nlut.observe(self._handlers["nlut"], names="value")

        self._min.observe(self._handlers["min"], names="value")

        self._minp.observe(self._handlers["minp"], names="value")

        self._max.observe(self._handlers["max"], names="value")

        self._maxp.observe(self._handlers["maxp"], names="value")

        self._sym.observe(self._handlers["sym"], names="value")

    def _remove_handlers(self):
        """Removes all event handlers set for the config widgets."""
        if len(self._handlers):
            self._alpha.unobserve(self._handlers["alpha"], names="value")
            self._lut.unobserve(self._handlers["lut"], names="value")
            self._nlut.unobserve(self._handlers["nlut"], names="value")
            self._min.unobserve(self._handlers["min"], names="value")
            self._minp.unobserve(self._handlers["minp"], names="value")
            self._max.unobserve(self._handlers["max"], names="value")
            self._maxp.unobserve(self._handlers["maxp"], names="value")
            self._sym.unobserve(self._handlers["sym"], names="value")

            self._handlers = defaultdict()

    @debounce(0.5)
    def _config_changed(self, change, image, name):
        if name == "min":
            self._minp.unobserve(self._handlers["minp"], names="value")
            self._minp.value = self._get_per_from_value(
                image.range, change.new)
            self._minp.observe(self._handlers["minp"], names="value")
        elif name == "max":
            self._maxp.unobserve(self._handlers["maxp"], names="value")
            self._maxp.value = self._get_per_from_value(
                image.range, change.new)
            self._maxp.observe(self._handlers["maxp"], names="value")

        self._set_config(image, name, change.new)

    @debounce(0.5)
    def _set_min_max(self, change, image, name):
        if name == "minPercent":
            self._min.unobserve(self._handlers["min"], names="value")
            self._min.value = self._get_value_from_per(image.range, change.new)
            self._set_config(image, "min", self._min.value)
            self._min.observe(self._handlers["min"], names="value")
        elif name == "maxPercent":
            self._max.unobserve(self._handlers["max"], names="value")
            self._max.value = self._get_value_from_per(image.range, change.new)
            self._set_config(image, "max", self._max.value)
            self._max.observe(self._handlers["max"], names="value")

    def _config_bool_changed(self, change, image, name):
        value = "false"
        if change.new:
            value = "true"
        self._set_config(image, name, value)

    def _set_config(self, image, key, value):
        image.config[key] = value
        self._viewer.set_images()

    def _get_per_from_value(self, range, value):
        return round(value * 100 / range, 0)

    def _get_value_from_per(self, range, per):
        return round(per * range / 100, 2)

    def set_image(self, image):
        """Sets the image whose config values will be viewed/modified using this config widget.
        If image is `None`, all config values are reset.

        Parameters
        ----------
        image: neurolang_ipywidgets.PapayaImage
            image whose config values will be viewed/modified using this config widget.
        """
        if image:
            self._remove_handlers()
            self._set_values(image.config, image.range,
                             image.image.get_fdata().flatten())
            self._add_handlers(image)
        else:
            self.reset()

    def reset(self):
        """Resets values for all config widgets."""
        self._remove_handlers()
        self._set_values({}, 100, [])
        self.layout.visibility = "hidden"