diff --git a/glue_jupyter/bqplot/common/reactviewer.py b/glue_jupyter/bqplot/common/reactviewer.py index 24605b05..b31ffa24 100644 --- a/glue_jupyter/bqplot/common/reactviewer.py +++ b/glue_jupyter/bqplot/common/reactviewer.py @@ -24,9 +24,9 @@ def create_scale(viewer_state, name): v_min, set_v_min = use_echo_state(viewer_state, f"{name}_min") v_max, set_v_max = use_echo_state(viewer_state, f"{name}_max") if is_log: - scale = bq.LogScale(min=v_min, max=v_max, allow_padding=False) + scale = bq.LogScale(min=v_min, max=v_max, allow_padding=False).shared() else: - scale = bq.LinearScale(min=v_min, max=v_max, allow_padding=False) + scale = bq.LinearScale(min=v_min, max=v_max, allow_padding=False).shared() return scale @@ -76,7 +76,6 @@ def Figure( ] show_axes, _ = use_echo_state(viewer_state, "show_axes") - # print(viewer_state) axis_x = bq.Axis(scale=scale_x, grid_lines="none", label=label_x, visible=show_axes) axis_y = bq.Axis( scale=scale_y, @@ -118,7 +117,7 @@ def initialize_figure(self): self.figure_el = Figure( self, self.state, is2d=self.is2d, components=self.components ) - self._figure: bqplot.Figure = react.render_fixed(self.figure_el)[0] + self._figure: bqplot.Figure = react.render_fixed(self.figure_el, handle_error=False)[0] self.scale_x = self._figure.scale_x self.scale_y = self._figure.scale_y self.scales = {"x": self.scale_x, "y": self.scale_y} @@ -226,4 +225,4 @@ def _callback_key(self, callback): return (callback.__func__, (callback.__self__,)) elif isinstance(callback, partial): return (callback.func, callback.args) - return callback \ No newline at end of file + return callback diff --git a/glue_jupyter/bqplot/histogram/__init__.py b/glue_jupyter/bqplot/histogram/__init__.py index 10684448..f2efc8a1 100644 --- a/glue_jupyter/bqplot/histogram/__init__.py +++ b/glue_jupyter/bqplot/histogram/__init__.py @@ -1 +1,2 @@ -from .reacthistogram import * # noqa +from .layer_artist import * # noqa +from .viewer import * # noqa diff --git a/glue_jupyter/bqplot/histogram/reacthistogram.py b/glue_jupyter/bqplot/histogram/reacthistogram.py deleted file mode 100644 index e61612fe..00000000 --- a/glue_jupyter/bqplot/histogram/reacthistogram.py +++ /dev/null @@ -1,173 +0,0 @@ -from glue.core.subset import roi_to_subset_state -from glue.core.roi import RangeROI - -from glue.viewers.histogram.state import HistogramLayerState -from glue.viewers.histogram.state import HistogramViewerState -from glue_jupyter.common.state_widgets.layer_histogram import HistogramLayerStateWidget -from glue_jupyter.common.state_widgets.viewer_histogram import ( - HistogramViewerStateWidget, -) - -from glue.core.exceptions import IncompatibleAttribute -from glue.utils import color2hex - - -from ..common.reactviewer import BqplotBaseViewReact -from ...common.hooks import use_echo_state - -import react_ipywidgets as react -import react_ipywidgets.bqplot as bq -import react_ipywidgets.logging as logging - - -import numpy as np - - -def calculate_bins( - viewer_state: HistogramViewerState, layer_state: HistogramLayerState -): - use_echo_state(viewer_state, "hist_x_max") - use_echo_state(viewer_state, "hist_x_min") - use_echo_state(viewer_state, "hist_n_bin") - use_echo_state(viewer_state, "cumulative") - use_echo_state(viewer_state, "normalize") - disabled, set_disabled = use_echo_state(layer_state, "disabled") - disabled_reason, set_disabled_reason = use_echo_state( - layer_state, "disabled_reason" - ) - # layer_state.reset_cache() - - try: - bins, hist_unscaled = layer_state.histogram - set_disabled(False) - except IncompatibleAttribute: - bins = None - set_disabled(True) - set_disabled_reason("Could not compute histogram") - - if bins is None: - return # can happen when the subset is empty - - if bins.size == 0 or hist_unscaled.sum() == 0: - return - - hist = hist_unscaled.astype(np.float) - dx = bins[1] - bins[0] - - if viewer_state.cumulative: - hist = hist.cumsum() - if viewer_state.normalize: - hist /= hist.max() - elif viewer_state.normalize: - hist /= hist.sum() * dx - - # TODO this won't work for log ... - centers = (bins[:-1] + bins[1:]) / 2 - assert len(centers) == len(hist) - x = centers - y = hist - - # We have to do the following to make sure that we reset the y_max as - # needed. We can't simply reset based on the maximum for this layer - # because other layers might have other values, and we also can't do: - # - # viewer_state.y_max = max(viewer_state.y_max, result[0].max()) - # - # because this would never allow y_max to get smaller. - - layer_state._y_max = hist.max() - if viewer_state.y_log: - layer_state._y_max *= 2 - else: - layer_state._y_max *= 1.2 - - if viewer_state.y_log: - layer_state._y_min = hist[hist > 0].min() / 10 - else: - layer_state._y_min = 0 - - largest_y_max = max(getattr(layer, "_y_max", 0) for layer in viewer_state.layers) - if largest_y_max != viewer_state.y_max: - viewer_state.y_max = largest_y_max - - smallest_y_min = min( - getattr(layer, "_y_min", np.inf) for layer in viewer_state.layers - ) - if smallest_y_min != viewer_state.y_min: - viewer_state.y_min = smallest_y_min - - return x, y - - -@react.component -def Histogram( - scale_x, - scale_y, - viewer_state: HistogramViewerState, - layer_state: HistogramLayerState, -): - visible, set_visible = use_echo_state(layer_state, "visible") - color, set_color = use_echo_state(layer_state, "color") - alpha, set_color = use_echo_state(layer_state, "alpha") - scales = dict(x=scale_x, y=scale_y) - bins = calculate_bins(viewer_state, layer_state) - if bins is None: - x = y = [0, 1] # dummy data, in case we cannot bin - else: - x, y = bins - - bars = bq.Bars(scales=scales, x=x, y=y, colors=[color2hex(color)], visible=visible, opacities=[alpha]) - return bars - - -from glue.viewers.common.layer_artist import LayerArtist - - -class HistogramLayerArtist(LayerArtist): - - _layer_state_cls = HistogramLayerState - # component = Histogram - - def __init__(self, view, viewer_state, layer_state=None, layer=None): - - super(HistogramLayerArtist, self).__init__( - viewer_state, layer_state=layer_state, layer=layer - ) - - -class BqplotHistogramView(BqplotBaseViewReact): - allow_duplicate_data = False - allow_duplicate_subset = False - large_data_size = 1e5 - is2d = False - - _state_cls = HistogramViewerState - _options_cls = HistogramViewerStateWidget - _data_artist_cls = HistogramLayerArtist - _subset_artist_cls = HistogramLayerArtist - _layer_style_widget_cls = HistogramLayerStateWidget - - tools = ["bqplot:home", "bqplot:panzoom", "bqplot:xrange"] - - components = {HistogramLayerState: Histogram} - - def _roi_to_subset_state(self, roi): - # TODO: copy paste from glue/viewers/histogram/qt/data_viewer.py - # TODO Does subset get applied to all data or just visible data? - - bins = self.state.bins - - x = roi.to_polygon()[0] - lo, hi = min(x), max(x) - - if lo >= bins.min(): - lo = bins[bins <= lo].max() - if hi <= bins.max(): - hi = bins[bins >= hi].min() - - roi_new = RangeROI(min=lo, max=hi, orientation="x") - - return roi_to_subset_state(roi_new, x_att=self.state.x_att) - - def redraw(self): - pass # i don't think we need to do anything \ No newline at end of file diff --git a/glue_jupyter/common/hooks.py b/glue_jupyter/common/hooks.py index 1c4a6b52..a61be530 100644 --- a/glue_jupyter/common/hooks.py +++ b/glue_jupyter/common/hooks.py @@ -15,7 +15,7 @@ def cleanup(): state.add_callback(name, handler) return cleanup - react.use_side_effect(add_event_handler) + react.use_effect(add_event_handler) def set_value_sync(new_value): setattr( @@ -40,4 +40,4 @@ def cleanup(): viewer._layer_artist_container.on_changed(handler) return cleanup - react.use_side_effect(hookup) + react.use_effect(hookup) diff --git a/setup.cfg b/setup.cfg index 08fd9daf..ab029594 100644 --- a/setup.cfg +++ b/setup.cfg @@ -26,7 +26,7 @@ install_requires = bqplot-image-gl>=1.4.3 bqplot>=0.12.17 scikit-image - react-ipywidgets + react-ipywidgets>=0.11.0 [options.extras_require] test =