Skip to content

Commit

Permalink
support exporting a plot from an unopened/inactive plugin (spaceteles…
Browse files Browse the repository at this point in the history
…cope#2934)

* support exporting a plot from an unopened/inactive plugin
* prevent clearing plot selection (under most conditions)
  • Loading branch information
kecnry committed Jul 11, 2024
1 parent 06b3be2 commit 4e8c1b6
Show file tree
Hide file tree
Showing 4 changed files with 46 additions and 22 deletions.
2 changes: 2 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
Bug Fixes
---------

- Fixes exporting the stretch histogram from Plot Options before the Plot Options plugin is ever opened. [#2934]

Cubeviz
^^^^^^^

Expand Down
18 changes: 13 additions & 5 deletions jdaviz/configs/default/plugins/export/export.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,9 @@ class Export(PluginTemplateMixin, ViewerSelectMixin, SubsetSelectMixin,
dataset_format_items = List().tag(sync=True)
dataset_format_selected = Unicode().tag(sync=True)

# copy of widget of the selected plugin_plot in case the parent plugin is not opened
plugin_plot_selected_widget = Unicode().tag(sync=True)

plugin_plot_format_items = List().tag(sync=True)
plugin_plot_format_selected = Unicode().tag(sync=True)

Expand Down Expand Up @@ -439,11 +442,16 @@ def export(self, filename=None, show_dialog=None, overwrite=False,
else:
filename = None

with plot._plugin.as_active():
# NOTE: could still take some time for the plot itself to update,
# for now we'll hardcode a short amount of time for the plot to render any updates
time.sleep(0.2)
self.save_figure(plot, filename, filetype, show_dialog=show_dialog)
if not plot._plugin.is_active:
# force an update to the plot. This requires the plot to have set
# update_callback when instantiated
plot._update()

# create a copy of the widget shown off screen to enable rendering
# in case one was never created in the parent plugin
self.plugin_plot_selected_widget = f'IPY_MODEL_{plot.model_id}'

self.save_figure(plot, filename, filetype, show_dialog=show_dialog)

elif len(self.plugin_table.selected):
filetype = self.plugin_table_format.selected
Expand Down
14 changes: 11 additions & 3 deletions jdaviz/configs/default/plugins/plot_options/plot_options.py
Original file line number Diff line number Diff line change
Expand Up @@ -573,7 +573,8 @@ def state_attr_for_line_visible(state):
'stretch_params_value', 'stretch_params_sync',
state_filter=is_image)

self.stretch_histogram = Plot(self, name='stretch_hist', viewer_type='histogram')
self.stretch_histogram = Plot(self, name='stretch_hist', viewer_type='histogram',
update_callback=self._update_stretch_histogram)
# Add the stretch bounds tool to the default Plot viewer.
self.stretch_histogram.tools_nested.append(["jdaviz:stretch_bounds"])
self.stretch_histogram._initialize_toolbar(["jdaviz:stretch_bounds"])
Expand Down Expand Up @@ -892,8 +893,7 @@ def _update_stretch_hist_sync(self, msg={}):
@observe('is_active', 'layer_selected', 'viewer_selected',
'stretch_hist_zoom_limits')
@skip_if_no_updates_since_last_active()
@with_spinner('stretch_hist_spinner')
def _update_stretch_histogram(self, msg={}):
def _request_update_stretch_histogram(self, msg={}):
if not hasattr(self, 'viewer'): # pragma: no cover
# plugin hasn't been fully initialized yet
return
Expand All @@ -909,6 +909,14 @@ def _update_stretch_histogram(self, msg={}):
# its type
msg = {}

# NOTE: this method is separate from _update_stretch_histogram so that
# _update_stretch_histogram can be called manually (or from the
# update_callback on the Plot object itself) without going through
# the skip_if_no_updates_since_last_active check
self._update_stretch_histogram(msg)

@with_spinner('stretch_hist_spinner')
def _update_stretch_histogram(self, msg={}):
if not self.stretch_function_sync.get('in_subscribed_states'): # pragma: no cover
# no (image) viewer with stretch function options
return
Expand Down
34 changes: 20 additions & 14 deletions jdaviz/core/template_mixin.py
Original file line number Diff line number Diff line change
Expand Up @@ -919,6 +919,7 @@ def _apply_default_selection(self, skip_if_current_valid=True):
self.selected = self._default_text if self._default_text else default_empty
else:
self.selected = default_empty
self._clear_cache(*self._cached_properties)

def _is_valid_item(self, item, filter_callables={}):
for valid_filter in self.filters:
Expand Down Expand Up @@ -2528,9 +2529,7 @@ def _on_tables_changed(self, *args):
manual_items = [{'label': label} for label in self.manual_options]
self.items = manual_items + [{'label': k} for k, v in self.plugin.app._plugin_tables.items()
if self._is_valid_item(v._obj)]
self._apply_default_selection()
# future improvement: only clear cache if the selected data entry was changed?
self._clear_cache(*self._cached_properties)
self._apply_default_selection(skip_if_current_valid=True)

@cached_property
def selected_obj(self):
Expand Down Expand Up @@ -2643,16 +2642,14 @@ def _on_plots_changed(self, *args):
manual_items = [{'label': label} for label in self.manual_options]
self.items = manual_items + [{'label': k} for k, v in self.plugin.app._plugin_plots.items()
if self._is_valid_item(v._obj)]
self._apply_default_selection()
# future improvement: only clear cache if the selected data entry was changed?
self._apply_default_selection(skip_if_current_valid=True)
self._clear_cache(*self._cached_properties)

@cached_property
def selected_obj(self):
return self.plugin.app._jdaviz_helper.plugin_plots.get(self.selected)

def _is_valid_item(self, plot):

def not_empty_plot(plot):
# checks plot.figure.marks to determine if figure is of an empty plot
# not sure if this is a foolproof way to do this?
Expand Down Expand Up @@ -4562,12 +4559,14 @@ class Plot(PluginSubcomponent):
figure = Any().tag(sync=True, **widget_serialization)
toolbar = Any().tag(sync=True, **widget_serialization)

def __init__(self, plugin, name='plot', viewer_type='scatter', app=None, *args, **kwargs):
def __init__(self, plugin, name='plot', viewer_type='scatter', update_callback=None,
app=None, *args, **kwargs):
super().__init__(plugin, 'Plot', *args, **kwargs)
if app is None:
app = jglue()

self._app = app
self._update_callback = update_callback
self._plugin = plugin
self._plot_name = name
self.viewer = app.new_data_viewer(viewer_type, show=False)
Expand All @@ -4592,7 +4591,6 @@ def __init__(self, plugin, name='plot', viewer_type='scatter', app=None, *args,
self._initialize_toolbar()

plugin.session.hub.broadcast(PluginPlotAddedMessage(sender=self))
plugin.session.hub.broadcast(PluginPlotModifiedMessage(sender=self))

def _initialize_toolbar(self, default_tool_priority=[]):
self.toolbar = NestedJupyterToolbar(self.viewer, self.tools_nested, default_tool_priority)
Expand All @@ -4618,12 +4616,19 @@ def _check_valid_components(self, **kwargs):
# https://github.com/astrofrog/fast-histogram/issues/60
raise ValueError("histogram requires data entries with length > 1")

def _remove_data(self, label):
def _remove_data(self, label, broadcast=True):
dc_entry = self.app.data_collection[label]
self.viewer.remove_data(dc_entry)
self.app.data_collection.remove(dc_entry)

self._plugin.session.hub.broadcast(PluginPlotModifiedMessage(sender=self))
if broadcast:
self._plugin.session.hub.broadcast(PluginPlotModifiedMessage(sender=self))

def _update(self):
# call the update callback, if it exists, on the parent plugin.
# This is useful for updating the plot when a plugin is inactive.
if self._update_callback is not None:
self._update_callback()

def _update_data(self, label, reset_lims=False, **kwargs):
self._check_valid_components(**kwargs)
Expand Down Expand Up @@ -4651,8 +4656,8 @@ def _update_data(self, label, reset_lims=False, **kwargs):
style_state = self.layers[label].state.as_dict()
else:
style_state = {}
self._remove_data(label)
self._add_data(label, **kwargs)
self._remove_data(label, broadcast=False)
self._add_data(label, broadcast=False, **kwargs)
self.update_style(label, **style_state)
if reset_lims:
self.viewer.state.reset_limits()
Expand Down Expand Up @@ -4688,7 +4693,7 @@ def update_style(self, label, **kwargs):

self._plugin.session.hub.broadcast(PluginPlotModifiedMessage(sender=self))

def _add_data(self, label, **kwargs):
def _add_data(self, label, broadcast=True, **kwargs):
self._check_valid_components(**kwargs)
data = Data(label=label, **kwargs)
dc = self.app.data_collection
Expand All @@ -4703,7 +4708,8 @@ def _add_data(self, label, **kwargs):
dc.add_link(links)
self.viewer.add_data(dc_entry)

self._plugin.session.hub.broadcast(PluginPlotModifiedMessage(sender=self))
if broadcast:
self._plugin.session.hub.broadcast(PluginPlotModifiedMessage(sender=self))

def _refresh_marks(self):
# ensure all marks are drawn
Expand Down

0 comments on commit 4e8c1b6

Please sign in to comment.