Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

support exporting a plot from an unopened/inactive plugin #2934

Merged
merged 4 commits into from
Jul 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,8 @@ Bug Fixes
- Display default filepath in Export plugin, re-enable API exporting, enable relative and absolute
path exports from the UI. [#2896]

- 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 @@ -462,11 +465,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 @@ -567,7 +567,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 @@ -886,8 +887,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 @@ -903,6 +903,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 @@ -949,6 +949,7 @@
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 @@ -2632,9 +2633,7 @@
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 @@ -2747,16 +2746,14 @@
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 @@ -4685,12 +4682,14 @@
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 @@ -4715,7 +4714,6 @@
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 @@ -4741,12 +4739,19 @@
# 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))

Check warning on line 4748 in jdaviz/core/template_mixin.py

View check run for this annotation

Codecov / codecov/patch

jdaviz/core/template_mixin.py#L4748

Added line #L4748 was not covered by tests

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 @@ -4774,8 +4779,8 @@
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 @@ -4811,7 +4816,7 @@

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 @@ -4826,7 +4831,8 @@
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