From e794369c1eab3f4a44fd82077eec1a8bca007de9 Mon Sep 17 00:00:00 2001 From: "Maarten A. Breddels" Date: Tue, 23 Nov 2021 15:21:07 +0100 Subject: [PATCH 1/2] WIP: implement log_x and log_y --- glue_jupyter/__init__.py | 9 +++- glue_jupyter/bqplot/common/viewer.py | 59 ++++++++++++++++++++++-- glue_jupyter/bqplot/tests/test_bqplot.py | 11 +++++ glue_jupyter/view.py | 5 ++ 4 files changed, 80 insertions(+), 4 deletions(-) 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..04bf58ba 100644 --- a/glue_jupyter/bqplot/common/viewer.py +++ b/glue_jupyter/bqplot/common/viewer.py @@ -25,10 +25,63 @@ class BqplotBaseView(IPyWidgetView): _default_mouse_mode_cls = ROIClickAndDrag def __init__(self, session, state=None): + super(BqplotBaseView, self).__init__(session, state=state) + 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 +112,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 +140,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) diff --git a/glue_jupyter/view.py b/glue_jupyter/view.py index 143718ac..bc2fe48e 100644 --- a/glue_jupyter/view.py +++ b/glue_jupyter/view.py @@ -36,8 +36,10 @@ def __init__(self, *args, **kwargs): self._output_widget = (None if self.session.application.get_setting('disable_output_widget') else Output()) + self.initialize_main() self.initialize_layer_options() self.initialize_toolbar() + self.create_layout() @property def toolbar_selection_tools(self): @@ -142,6 +144,9 @@ def _update_subset(self, message): def get_layer_artist(self, cls, layer=None, layer_state=None): return cls(self, self.state, layer=layer, layer_state=layer_state) + def initialize_main(self): + pass + def initialize_layer_options(self): self._layout_layer_options = LayerOptionsWidget(self) From d59857b7f8fdfb982677ef5e5944589768f00c3c Mon Sep 17 00:00:00 2001 From: "Maarten A. Breddels" Date: Tue, 14 Nov 2023 13:11:28 +0100 Subject: [PATCH 2/2] avoid unneeded changes --- glue_jupyter/bqplot/common/viewer.py | 5 +++++ glue_jupyter/view.py | 5 ----- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/glue_jupyter/bqplot/common/viewer.py b/glue_jupyter/bqplot/common/viewer.py index 04bf58ba..e519b20a 100644 --- a/glue_jupyter/bqplot/common/viewer.py +++ b/glue_jupyter/bqplot/common/viewer.py @@ -26,6 +26,11 @@ class BqplotBaseView(IPyWidgetView): 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 diff --git a/glue_jupyter/view.py b/glue_jupyter/view.py index bc2fe48e..143718ac 100644 --- a/glue_jupyter/view.py +++ b/glue_jupyter/view.py @@ -36,10 +36,8 @@ def __init__(self, *args, **kwargs): self._output_widget = (None if self.session.application.get_setting('disable_output_widget') else Output()) - self.initialize_main() self.initialize_layer_options() self.initialize_toolbar() - self.create_layout() @property def toolbar_selection_tools(self): @@ -144,9 +142,6 @@ def _update_subset(self, message): def get_layer_artist(self, cls, layer=None, layer_state=None): return cls(self, self.state, layer=layer, layer_state=layer_state) - def initialize_main(self): - pass - def initialize_layer_options(self): self._layout_layer_options = LayerOptionsWidget(self)