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

Manual backport of #2934 #3083

Merged
merged 1 commit 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 @@ -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