diff --git a/glue_jupyter/__init__.py b/glue_jupyter/__init__.py index 25f19039..cdaf6acc 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 410555e5..e519b20a 100644 --- a/glue_jupyter/bqplot/common/viewer.py +++ b/glue_jupyter/bqplot/common/viewer.py @@ -25,10 +25,68 @@ class BqplotBaseView(IPyWidgetView): _default_mouse_mode_cls = ROIClickAndDrag def __init__(self, session, state=None): + super(BqplotBaseView, self).__init__(session, state=state) + self.create_layout() + + def initialize_layer_options(self): + self.initialize_main() + super().initialize_layer_options() + def initialize_main(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 = bqplot.LogScale() + else: + self.scale_y = bqplot.LinearScale(min=0, max=1) + + if self.state.x_log: + self.scale_x = bqplot.LogScale() + else: + self.scale_x = bqplot.LinearScale(min=0, max=1) + + def update_scale_type_y(_ignore): + prev = self.scale_y + if self.state.y_log: + self.scale_y = bqplot.LogScale() + else: + self.scale_y = bqplot.LinearScale() + self._mouse_interact.y_scale = self.scale_y + prev.close() + prev.unobserve(self.update_glue_scales, names=['min', 'max']) + self.scale_x.observe(self.update_glue_scales, names=['min', 'max']) + update_scales() + self.state.add_callback('y_log', update_scale_type_y, priority=-1) + + def update_scale_type_x(_ignore): + prev = self.scale_x + if self.state.x_log: + self.scale_x = bqplot.LogScale() + else: + self.scale_x_linear = bqplot.LinearScale() + scale = self.scale_x_linear + self.scale_y = scale + prev.unobserve(self.update_glue_scales, names=['min', 'max']) + self.scale_x.observe(self.update_glue_scales, names=['min', 'max']) + update_scales() + self.state.add_callback('x_log', update_scale_type_x, priority=-1) + + def update_scales(): + self.scales = {'x': self.scale_x, 'y': self.scale_y} + self.axis_x.scale = self.scale_x + self.axis_y.scale = self.scale_y + self.figure.axes = [self.axis_x, self.axis_y] + self.figure.scale_x = self.scale_x + self.figure.scale_y = self.scale_y + self._mouse_interact.x_scale = self.scale_x + self._mouse_interact.y_scale = self.scale_y self.scales = {'x': self.scale_x, 'y': self.scale_y} self.axis_x = bqplot.Axis( @@ -59,7 +117,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. @@ -87,7 +145,7 @@ def __init__(self, session, state=None): on_change([(self.state, 'show_axes')])(self._sync_show_axes) - self.create_layout() + super().create_layout() def update_x_axislabel(self, *event): self.axis_x.label = self.state.x_axislabel diff --git a/glue_jupyter/bqplot/tests/test_bqplot.py b/glue_jupyter/bqplot/tests/test_bqplot.py index d282df53..da3f3b9a 100644 --- a/glue_jupyter/bqplot/tests/test_bqplot.py +++ b/glue_jupyter/bqplot/tests/test_bqplot.py @@ -7,6 +7,7 @@ from glue.core import Data from glue.core.roi import CircularAnnulusROI, EllipticalROI from ..common.tools import TrueCircularROI +import bqplot DATA = os.path.join(os.path.dirname(__file__), 'data') @@ -43,6 +44,16 @@ 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 + 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 isinstance(s.scale_y, bqplot.LinearScale) + + 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)