diff --git a/glue_jupyter/__init__.py b/glue_jupyter/__init__.py index cf8acb99..699e1317 100755 --- a/glue_jupyter/__init__.py +++ b/glue_jupyter/__init__.py @@ -75,7 +75,7 @@ def jglue(*args, settings=None, show=False, links=None, **kwargs): return japp -def example_data_xyz(seed=42, N=500, loc=0, scale=1, label='xyz'): +def example_data_xyz(seed=42, N=500, loc=0, scale=1, label='xyz', log=False): """ Create an example dataset with three attributes x, y, and z set to random values. @@ -87,6 +87,13 @@ def example_data_xyz(seed=42, N=500, loc=0, scale=1, label='xyz'): vx = x - x.mean() vy = y - y.mean() vz = z - z.mean() + if log: + x = 10**x + y = 10**y + z = 10**z + vx = 10**vx + vy = 10**vy + vz = 10**vz speed = np.sqrt(vx**2 + vy**2 + vz**2) data_xyz = Data(x=x, y=y, z=z, vx=vx, vy=vy, vz=vz, speed=speed, label=label) return data_xyz diff --git a/glue_jupyter/bqplot/common/viewer.py b/glue_jupyter/bqplot/common/viewer.py index 92bbed58..6a9bd8be 100644 --- a/glue_jupyter/bqplot/common/viewer.py +++ b/glue_jupyter/bqplot/common/viewer.py @@ -24,10 +24,57 @@ class BqplotBaseView(IPyWidgetView): _default_mouse_mode_cls = ROIClickAndDrag def __init__(self, session, state=None): + super(BqplotBaseView, self).__init__(session, state=state) + + def initialize_figure(self): # if we allow padding, we sometimes get odd behaviour with the interacts self.scale_x = bqplot.LinearScale(min=0, max=1, allow_padding=False) - self.scale_y = bqplot.LinearScale(min=0, max=1) + + # lazily create widgets + self.scale_y_log = None + self.scale_y_linear = None + self.scale_x_log = None + self.scale_x_linear = None + + if self.state.y_log: + self.scale_y_log = bqplot.LogScale() + self.scale_y = self.scale_y_log + else: + self.scale_y_linear = bqplot.LinearScale(min=0, max=1) + self.scale_y = self.scale_y_linear + + if self.state.x_log: + self.scale_x_log = bqplot.LogScale() + self.scale_x = self.scale_x_log + else: + self.scale_x_linear = bqplot.LinearScale(min=0, max=1) + self.scale_x = self.scale_x_linear + + def update_scale_type_y(_ignore): + if self.state.y_log: + if self.scale_y_log is None: + self.scale_y_log = bqplot.LogScale() + scale = self.scale_y_log + else: + if self.scale_y_linear is None: + self.scale_y_linear = bqplot.LinearScale() + scale = self.scale_y_linear + self.scale_y = scale + self.state.add_callback('y_log', update_scale_type_y, priority=-1) + + def update_scale_type_x(_ignore): + if self.state.x_log: + if self.scale_x_log is None: + self.scale_x_log = bqplot.LogScale() + scale = self.scale_x_log + else: + if self.scale_x_linear is None: + self.scale_x_linear = bqplot.LinearScale() + scale = self.scale_x_linear + self.scale_y = scale + self.state.add_callback('x_log', update_scale_type_x, priority=-1) + self.scales = {'x': self.scale_x, 'y': self.scale_y} self.axis_x = bqplot.Axis( @@ -58,7 +105,7 @@ def __init__(self, session, state=None): self.figure.interaction = self._mouse_interact self._events_for_callback = {} - super(BqplotBaseView, self).__init__(session, state=state) + def create_layout(self): # Remove the following two lines once glue v0.16 is required - see # https://github.com/glue-viz/glue/pull/2099/files for more information. @@ -99,7 +146,7 @@ def update_axes(*ignore): on_change([(self.state, 'show_axes')])(self._sync_show_axes) - self.create_layout() + super().create_layout() def _update_bqplot_limits(self, *args): diff --git a/glue_jupyter/bqplot/tests/test_bqplot.py b/glue_jupyter/bqplot/tests/test_bqplot.py index e9f8435f..87a05880 100644 --- a/glue_jupyter/bqplot/tests/test_bqplot.py +++ b/glue_jupyter/bqplot/tests/test_bqplot.py @@ -5,6 +5,7 @@ from nbconvert.preprocessors import ExecutePreprocessor from glue.core import Data from glue.core.roi import EllipticalROI +import bqplot DATA = os.path.join(os.path.dirname(__file__), 'data') @@ -41,6 +42,17 @@ def test_histogram1d(app, dataxyz): # assert s.layers[2].hist.tolist() == [0, 1, 0, 0, 0, 0] +def test_histogram1d_log(app, dataxyz): + s = app.histogram1d(x='y', data=dataxyz) + assert s.state.y_log is False + prev_scale = s.scale_y + assert isinstance(s.scale_y, bqplot.LinearScale) + s.state.y_log = True + assert isinstance(s.scale_y, bqplot.LogScale) + s.state.y_log = False + assert s.scale_y is prev_scale + + def test_histogram1d_multiple_subsets(app, data_unlinked, datax): # Make sure that things work fine if an incompatible subset is added viewer = app.histogram1d(x='x', data=datax) diff --git a/glue_jupyter/view.py b/glue_jupyter/view.py index f4c0ea89..0541369b 100644 --- a/glue_jupyter/view.py +++ b/glue_jupyter/view.py @@ -34,9 +34,11 @@ class IPyWidgetView(Viewer): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) + self.initialize_figure() self._output_widget = Output() self.initialize_layer_options() self.initialize_toolbar() + self.create_layout() @property def toolbar_selection_tools(self):