From e9b6f2c10b90bc0a725672b823090e203b5781a0 Mon Sep 17 00:00:00 2001 From: Kyle Conroy Date: Thu, 31 Oct 2024 08:07:55 -0400 Subject: [PATCH 01/20] data menu view metadata --- .../default/plugins/data_menu/data_menu.py | 22 ++++++++++++++++++- .../default/plugins/data_menu/data_menu.vue | 2 +- jdaviz/core/template_mixin.py | 4 ++++ 3 files changed, 26 insertions(+), 2 deletions(-) diff --git a/jdaviz/configs/default/plugins/data_menu/data_menu.py b/jdaviz/configs/default/plugins/data_menu/data_menu.py index 5c4acb930b..70313e9a06 100644 --- a/jdaviz/configs/default/plugins/data_menu/data_menu.py +++ b/jdaviz/configs/default/plugins/data_menu/data_menu.py @@ -31,6 +31,7 @@ class DataMenu(TemplateMixin, LayerSelectMixin, DatasetSelectMixin): * :meth:`toggle_layer_visibility` * :meth:`create_subset` * :meth:`add_data` + * :meth:`view_metadata` """ template_file = __file__, "data_menu.vue" @@ -83,7 +84,7 @@ def data_not_in_viewer(data): @property def user_api(self): expose = ['layer', 'set_layer_visibility', 'toggle_layer_visibility', - 'create_subset', 'add_data'] + 'create_subset', 'add_data', 'view_metadata'] return UserApiWrapper(self, expose=expose) @observe('layer_items') @@ -231,3 +232,22 @@ def create_subset(self, subset_type): def vue_create_subset(self, info, *args): self.create_subset(info.get('subset_type')) # pragma: no cover + + def view_metadata(self): + """ + View metadata for the selected layer. + """ + if len(self.layer.selected) != 1: + raise ValueError("Only one layer can be selected to view metadata.") + # view metadata for the selected layer (UI only shows if a single selection AND data entry) + mp = self._viewer.jdaviz_helper.plugins.get('Metadata', None) + if mp is None: + return + try: + mp.dataset.selected = self.layer.selected[0] + except ValueError: + return + mp.open_in_tray() + + def vue_view_metadata(self, *args): + self.view_metadata() # pragma: no cover diff --git a/jdaviz/configs/default/plugins/data_menu/data_menu.vue b/jdaviz/configs/default/plugins/data_menu/data_menu.vue index 8de84bc32a..b2149af292 100644 --- a/jdaviz/configs/default/plugins/data_menu/data_menu.vue +++ b/jdaviz/configs/default/plugins/data_menu/data_menu.vue @@ -153,7 +153,7 @@ > mdi-label diff --git a/jdaviz/core/template_mixin.py b/jdaviz/core/template_mixin.py index 6e24465e06..452584c0eb 100644 --- a/jdaviz/core/template_mixin.py +++ b/jdaviz/core/template_mixin.py @@ -1538,6 +1538,7 @@ def not_trace(lyr): def _layer_to_dict(self, layer_label): is_subset = None subset_type = None + from_plugin = None colors = [] visibilities = [] linewidths = [] @@ -1549,6 +1550,8 @@ def _layer_to_dict(self, layer_label): (hasattr(layer, 'layer') and hasattr(layer.layer, 'subset_state'))) # noqa if is_subset: subset_type = get_subset_type(layer.layer) + if from_plugin is None: + from_plugin = layer.layer.data.meta.get('Plugin', None) if (getattr(viewer.state, 'color_mode', None) == 'Colormaps' and hasattr(layer.state, 'cmap')): @@ -1563,6 +1566,7 @@ def _layer_to_dict(self, layer_label): return {"label": layer_label, "is_subset": is_subset, "subset_type": subset_type, + "from_plugin": from_plugin, "icon": self.app.state.layer_icons.get(layer_label), "visible": visibilities[0] if len(list(set(visibilities))) == 1 else 'mixed', "linewidth": linewidths[0] if len(list(set(linewidths))) == 1 else 'mixed', From 8f452a59c91b0bcda62a315401baef0b1b90f45d Mon Sep 17 00:00:00 2001 From: Kyle Conroy Date: Thu, 31 Oct 2024 08:08:51 -0400 Subject: [PATCH 02/20] responsive tooltip text and button enabled --- .../default/plugins/data_menu/data_menu.py | 122 ++++++++++++++---- .../default/plugins/data_menu/data_menu.vue | 23 ++-- jdaviz/core/template_mixin.py | 2 +- 3 files changed, 110 insertions(+), 37 deletions(-) diff --git a/jdaviz/configs/default/plugins/data_menu/data_menu.py b/jdaviz/configs/default/plugins/data_menu/data_menu.py index 70313e9a06..1ec5fe78cf 100644 --- a/jdaviz/configs/default/plugins/data_menu/data_menu.py +++ b/jdaviz/configs/default/plugins/data_menu/data_menu.py @@ -1,5 +1,5 @@ from contextlib import contextmanager -from traitlets import Bool, Dict, Unicode, List, observe +from traitlets import Bool, Dict, Unicode, Integer, List, observe from jdaviz.core.template_mixin import (TemplateMixin, LayerSelectMixin, DatasetSelectMixin) from jdaviz.core.user_api import UserApiWrapper @@ -31,7 +31,7 @@ class DataMenu(TemplateMixin, LayerSelectMixin, DatasetSelectMixin): * :meth:`toggle_layer_visibility` * :meth:`create_subset` * :meth:`add_data` - * :meth:`view_metadata` + * :meth:`view_info` """ template_file = __file__, "data_menu.vue" @@ -48,6 +48,19 @@ class DataMenu(TemplateMixin, LayerSelectMixin, DatasetSelectMixin): dm_layer_selected = List().tag(sync=True) + selected_n_layers = Integer(0).tag(sync=True) + selected_n_data = Integer(0).tag(sync=True) + selected_n_subsets = Integer(0).tag(sync=True) + + info_enabled = Bool(False).tag(sync=True) + info_tooltip = Unicode().tag(sync=True) + + delete_enabled = Bool(False).tag(sync=True) + delete_tooltip = Unicode().tag(sync=True) + + subset_edit_enabled = Bool(False).tag(sync=True) + subset_edit_tooltip = Unicode().tag(sync=True) + dev_data_menu = Bool(False).tag(sync=True) def __init__(self, viewer, *args, **kwargs): @@ -84,7 +97,7 @@ def data_not_in_viewer(data): @property def user_api(self): expose = ['layer', 'set_layer_visibility', 'toggle_layer_visibility', - 'create_subset', 'add_data', 'view_metadata'] + 'create_subset', 'add_data', 'view_info'] return UserApiWrapper(self, expose=expose) @observe('layer_items') @@ -145,13 +158,62 @@ def _dm_layer_selected_changed(self, event={}): def _update_dm_layer_selected(self, event={}): if not hasattr(self, 'layer') or not self.layer.multiselect: # pragma: no cover return - if self._during_select_sync: - return - with self.during_select_sync(): - # map list of strings in self.layer.selected to indices in dm_layer_selected - layer_labels = [layer['label'] for layer in self.layer_items][::-1] - self.dm_layer_selected = [layer_labels.index(label) for label in self.layer.selected - if label in layer_labels] + if not self._during_select_sync: + with self.during_select_sync(): + # map list of strings in self.layer.selected to indices in dm_layer_selected + layer_labels = [layer['label'] for layer in self.layer_items][::-1] + self.dm_layer_selected = [layer_labels.index(label) for label in self.layer.selected + if label in layer_labels] + + # update internal counts and tooltips + self.selected_n_layers = len(self.layer.selected) + self.selected_n_subsets = len([l for l in self.layer.selected if l.startswith('Subset')]) + self.selected_n_data = self.selected_n_layers - self.selected_n_subsets + + # user-friendly representation of selection + selected_repr = "" + if self.selected_n_data: + selected_repr += f"data ({self.selected_n_data})" + if self.selected_n_subsets: + if self.selected_n_data: + selected_repr += " and" + if self.selected_n_subsets == 1: + selected_repr += f" subset ({self.selected_n_subsets})" + else: + selected_repr += f" subsets ({self.selected_n_subsets})" + + # layer info rules + if self.selected_n_layers == 1 and not self.layer_items[self.dm_layer_selected[0]].get('from_plugin', False): + self.info_enabled = True + if self.selected_n_data == 1: + self.info_tooltip = 'View metadata for selected data' + else: + self.info_tooltip = 'View subset info for selected subset' + else: + self.info_enabled = False + if self.selected_n_layers == 0: + self.info_tooltip = 'Select a layer to view info' + else: + self.info_tooltip = 'Select a single layer to view info' + + # delete layer rules + if self.selected_n_layers == 0: + self.delete_tooltip = "Select layer(s) to delete" + self.delete_enabled = False + else: + self.delete_tooltip = f"Remove selected {selected_repr}" + self.delete_enabled = True + + # subset edit rules + if self.selected_n_subsets == 1: + self.subset_edit_enabled = True + self.subset_edit_tooltip = "Edit selected subset (COMING SOON)" + else: + self.subset_edit_enabled = False + if self.selected_n_subsets == 0: + self.subset_edit_tooltip = "Select a subset to edit" + else: + self.subset_edit_tooltip = "Select a single subset to edit" def set_layer_visibility(self, layer_label, visible=True): """ @@ -233,21 +295,31 @@ def create_subset(self, subset_type): def vue_create_subset(self, info, *args): self.create_subset(info.get('subset_type')) # pragma: no cover - def view_metadata(self): + def view_info(self): """ - View metadata for the selected layer. + View info for the selected layer by opening either the metadata or subset plugin to the + selected entry. """ if len(self.layer.selected) != 1: - raise ValueError("Only one layer can be selected to view metadata.") - # view metadata for the selected layer (UI only shows if a single selection AND data entry) - mp = self._viewer.jdaviz_helper.plugins.get('Metadata', None) - if mp is None: - return - try: - mp.dataset.selected = self.layer.selected[0] - except ValueError: - return - mp.open_in_tray() - - def vue_view_metadata(self, *args): - self.view_metadata() # pragma: no cover + raise ValueError("Only one layer can be selected to view info.") + if self.layer.selected[0].startswith('Subset'): + sp = self._viewer.jdaviz_helper.plugins.get('Subset Tools', None) + if sp is None: + return + try: + sp._obj.subset_select.selected = self.layer.selected[0] + except ValueError: + return + sp.open_in_tray() + else: + mp = self._viewer.jdaviz_helper.plugins.get('Metadata', None) + if mp is None: + return + try: + mp.dataset.selected = self.layer.selected[0] + except ValueError: + return + mp.open_in_tray() + + def vue_view_info(self, *args): + self.view_info() # pragma: no cover diff --git a/jdaviz/configs/default/plugins/data_menu/data_menu.vue b/jdaviz/configs/default/plugins/data_menu/data_menu.vue index b2149af292..8b8e611ca2 100644 --- a/jdaviz/configs/default/plugins/data_menu/data_menu.vue +++ b/jdaviz/configs/default/plugins/data_menu/data_menu.vue @@ -136,35 +136,36 @@ mdi-delete + @click="view_info" + :disabled="!info_enabled" + > mdi-label Edit Subset diff --git a/jdaviz/core/template_mixin.py b/jdaviz/core/template_mixin.py index 452584c0eb..0b4cc4346f 100644 --- a/jdaviz/core/template_mixin.py +++ b/jdaviz/core/template_mixin.py @@ -1734,7 +1734,7 @@ def selected_obj(self): layers = [[layer for layer in viewer.layers if layer.layer.label in selected and self._is_valid_item(layer.layer)] - for viewer in viewers] + for viewer in viewers if viewer is not None] if not self.is_multiselect and len(layers) == 1: return layers[0] From 65249a78aa9d580fe421c62f71ea658b07c5f98f Mon Sep 17 00:00:00 2001 From: Kyle Conroy Date: Thu, 31 Oct 2024 08:09:55 -0400 Subject: [PATCH 03/20] removing subsets/data from viewer/app --- jdaviz/app.py | 3 +- jdaviz/components/data_menu_remove.vue | 55 +++++++++++++++++++ .../default/plugins/data_menu/data_menu.py | 46 +++++++++++++++- .../default/plugins/data_menu/data_menu.vue | 17 ++---- 4 files changed, 106 insertions(+), 15 deletions(-) create mode 100644 jdaviz/components/data_menu_remove.vue diff --git a/jdaviz/app.py b/jdaviz/app.py index bcc0c405b2..5260639146 100644 --- a/jdaviz/app.py +++ b/jdaviz/app.py @@ -152,7 +152,8 @@ def to_unit(self, data, cid, values, original_units, target_units): 'plugin-color-picker': 'components/plugin_color_picker.vue', 'plugin-input-header': 'components/plugin_input_header.vue', 'glue-state-sync-wrapper': 'components/glue_state_sync_wrapper.vue', - 'data-menu-add-data': 'components/data_menu_add_data.vue'} + 'data-menu-add-data': 'components/data_menu_add_data.vue', + 'data-menu-remove': 'components/data_menu_remove.vue'} _verbosity_levels = ('debug', 'info', 'warning', 'error') diff --git a/jdaviz/components/data_menu_remove.vue b/jdaviz/components/data_menu_remove.vue new file mode 100644 index 0000000000..32bd73bd05 --- /dev/null +++ b/jdaviz/components/data_menu_remove.vue @@ -0,0 +1,55 @@ + + + \ No newline at end of file diff --git a/jdaviz/configs/default/plugins/data_menu/data_menu.py b/jdaviz/configs/default/plugins/data_menu/data_menu.py index 1ec5fe78cf..481f6e1384 100644 --- a/jdaviz/configs/default/plugins/data_menu/data_menu.py +++ b/jdaviz/configs/default/plugins/data_menu/data_menu.py @@ -32,6 +32,8 @@ class DataMenu(TemplateMixin, LayerSelectMixin, DatasetSelectMixin): * :meth:`create_subset` * :meth:`add_data` * :meth:`view_info` + * :meth:`remove_from_viewer` + * :meth:`remove_from_app` """ template_file = __file__, "data_menu.vue" @@ -97,9 +99,14 @@ def data_not_in_viewer(data): @property def user_api(self): expose = ['layer', 'set_layer_visibility', 'toggle_layer_visibility', - 'create_subset', 'add_data', 'view_info'] + 'create_subset', 'add_data', 'view_info', + 'remove_from_viewer', 'remove_from_app'] return UserApiWrapper(self, expose=expose) + @property + def existing_subset_labels(self): + return [sg.label for sg in self.app.data_collection.subset_groups] + @observe('layer_items') def _update_data_not_in_viewer(self, msg): # changing the layers in the viewer needs to trigger an update to dataset_items @@ -165,9 +172,14 @@ def _update_dm_layer_selected(self, event={}): self.dm_layer_selected = [layer_labels.index(label) for label in self.layer.selected if label in layer_labels] + if event.get('name') == 'layer_items': + # don't need to make the updates below unless the selection has been changed + return + # update internal counts and tooltips self.selected_n_layers = len(self.layer.selected) - self.selected_n_subsets = len([l for l in self.layer.selected if l.startswith('Subset')]) + subset_labels = self.existing_subset_labels + self.selected_n_subsets = len([l for l in self.layer.selected if l in subset_labels]) self.selected_n_data = self.selected_n_layers - self.selected_n_subsets # user-friendly representation of selection @@ -302,7 +314,7 @@ def view_info(self): """ if len(self.layer.selected) != 1: raise ValueError("Only one layer can be selected to view info.") - if self.layer.selected[0].startswith('Subset'): + if self.layer.selected[0] in self.existing_subset_labels: sp = self._viewer.jdaviz_helper.plugins.get('Subset Tools', None) if sp is None: return @@ -323,3 +335,31 @@ def view_info(self): def vue_view_info(self, *args): self.view_info() # pragma: no cover + + def remove_from_viewer(self): + """ + Remove the selected layers from the viewer. + """ + for layer in self.layer.selected: + if layer in self.existing_subset_labels: + self.set_layer_visibility(layer, visible=False) + else: + self.app.remove_data_from_viewer(self.viewer_id, layer) + + def vue_remove_from_viewer(self, *args): + self.remove_from_viewer() # pragma: no cover + + def remove_from_app(self): + """ + Remove the selected layers from the entire app and all viewers. + """ + for layer in self.layer.selected: + if layer in self.existing_subset_labels: + subset_grp = [sg for sg in self.app.data_collection.subset_groups + if sg.label == layer][0] + self.app.data_collection.remove_subset_group(subset_grp) + else: + self.app.vue_data_item_remove({'item_name': layer}) + + def vue_remove_from_app(self, *args): + self.remove_from_app() # pragma: no cover \ No newline at end of file diff --git a/jdaviz/configs/default/plugins/data_menu/data_menu.vue b/jdaviz/configs/default/plugins/data_menu/data_menu.vue index 8b8e611ca2..7926a178f1 100644 --- a/jdaviz/configs/default/plugins/data_menu/data_menu.vue +++ b/jdaviz/configs/default/plugins/data_menu/data_menu.vue @@ -135,17 +135,12 @@ - - - mdi-delete - - + Date: Thu, 31 Oct 2024 08:10:20 -0400 Subject: [PATCH 04/20] edit subset submenu --- jdaviz/app.py | 4 +- jdaviz/components/data_menu_subset_edit.vue | 78 +++++++++++++++++++ .../data_menu_subset_edit_modify.vue | 42 ++++++++++ .../default/plugins/data_menu/data_menu.py | 4 +- .../default/plugins/data_menu/data_menu.vue | 19 ++--- 5 files changed, 131 insertions(+), 16 deletions(-) create mode 100644 jdaviz/components/data_menu_subset_edit.vue create mode 100644 jdaviz/components/data_menu_subset_edit_modify.vue diff --git a/jdaviz/app.py b/jdaviz/app.py index 5260639146..439706090d 100644 --- a/jdaviz/app.py +++ b/jdaviz/app.py @@ -153,7 +153,9 @@ def to_unit(self, data, cid, values, original_units, target_units): 'plugin-input-header': 'components/plugin_input_header.vue', 'glue-state-sync-wrapper': 'components/glue_state_sync_wrapper.vue', 'data-menu-add-data': 'components/data_menu_add_data.vue', - 'data-menu-remove': 'components/data_menu_remove.vue'} + 'data-menu-remove': 'components/data_menu_remove.vue', + 'data-menu-subset-edit': 'components/data_menu_subset_edit.vue', + 'data-menu-subset-edit-modify': 'components/data_menu_subset_edit_modify.vue'} _verbosity_levels = ('debug', 'info', 'warning', 'error') diff --git a/jdaviz/components/data_menu_subset_edit.vue b/jdaviz/components/data_menu_subset_edit.vue new file mode 100644 index 0000000000..b9b862d7d8 --- /dev/null +++ b/jdaviz/components/data_menu_subset_edit.vue @@ -0,0 +1,78 @@ + + + + + \ No newline at end of file diff --git a/jdaviz/components/data_menu_subset_edit_modify.vue b/jdaviz/components/data_menu_subset_edit_modify.vue new file mode 100644 index 0000000000..006638003f --- /dev/null +++ b/jdaviz/components/data_menu_subset_edit_modify.vue @@ -0,0 +1,42 @@ + + + + + \ No newline at end of file diff --git a/jdaviz/configs/default/plugins/data_menu/data_menu.py b/jdaviz/configs/default/plugins/data_menu/data_menu.py index 481f6e1384..6d0661a3bf 100644 --- a/jdaviz/configs/default/plugins/data_menu/data_menu.py +++ b/jdaviz/configs/default/plugins/data_menu/data_menu.py @@ -213,13 +213,13 @@ def _update_dm_layer_selected(self, event={}): self.delete_tooltip = "Select layer(s) to delete" self.delete_enabled = False else: - self.delete_tooltip = f"Remove selected {selected_repr}" + self.delete_tooltip = f"Remove selected {selected_repr}..." self.delete_enabled = True # subset edit rules if self.selected_n_subsets == 1: self.subset_edit_enabled = True - self.subset_edit_tooltip = "Edit selected subset (COMING SOON)" + self.subset_edit_tooltip = "Edit selected subset..." else: self.subset_edit_enabled = False if self.selected_n_subsets == 0: diff --git a/jdaviz/configs/default/plugins/data_menu/data_menu.vue b/jdaviz/configs/default/plugins/data_menu/data_menu.vue index 7926a178f1..8fe29849c0 100644 --- a/jdaviz/configs/default/plugins/data_menu/data_menu.vue +++ b/jdaviz/configs/default/plugins/data_menu/data_menu.vue @@ -153,19 +153,12 @@ mdi-label - - - Edit Subset - - + From 1a15cc77183984cd34dae95c79d62fe4de9f4023 Mon Sep 17 00:00:00 2001 From: Kyle Conroy Date: Wed, 30 Oct 2024 11:21:17 -0400 Subject: [PATCH 05/20] icon indication for live-plugin results --- jdaviz/app.py | 2 ++ jdaviz/components/child_layer_icon.vue | 14 ++++++++++++++ jdaviz/components/plugin_live_results_icon.vue | 14 ++++++++++++++ .../default/plugins/data_menu/data_menu.vue | 17 +++++++---------- jdaviz/core/template_mixin.py | 6 +++++- 5 files changed, 42 insertions(+), 11 deletions(-) create mode 100644 jdaviz/components/child_layer_icon.vue create mode 100644 jdaviz/components/plugin_live_results_icon.vue diff --git a/jdaviz/app.py b/jdaviz/app.py index 439706090d..1e6ee23a1e 100644 --- a/jdaviz/app.py +++ b/jdaviz/app.py @@ -133,6 +133,8 @@ def to_unit(self, data, cid, values, original_units, target_units): 'j-plugin-popout': 'components/plugin_popout.vue', 'j-multiselect-toggle': 'components/multiselect_toggle.vue', 'j-subset-icon': 'components/subset_icon.vue', + 'j-plugin-live-results-icon': 'components/plugin_live_results_icon.vue', + 'j-child-layer-icon': 'components/child_layer_icon.vue', 'plugin-previews-temp-disabled': 'components/plugin_previews_temp_disabled.vue', # noqa 'plugin-table': 'components/plugin_table.vue', 'plugin-dataset-select': 'components/plugin_dataset_select.vue', diff --git a/jdaviz/components/child_layer_icon.vue b/jdaviz/components/child_layer_icon.vue new file mode 100644 index 0000000000..4d00657341 --- /dev/null +++ b/jdaviz/components/child_layer_icon.vue @@ -0,0 +1,14 @@ + + + \ No newline at end of file diff --git a/jdaviz/components/plugin_live_results_icon.vue b/jdaviz/components/plugin_live_results_icon.vue new file mode 100644 index 0000000000..4b9e628beb --- /dev/null +++ b/jdaviz/components/plugin_live_results_icon.vue @@ -0,0 +1,14 @@ + + + \ No newline at end of file diff --git a/jdaviz/configs/default/plugins/data_menu/data_menu.vue b/jdaviz/configs/default/plugins/data_menu/data_menu.vue index 8fe29849c0..9242d1a6e6 100644 --- a/jdaviz/configs/default/plugins/data_menu/data_menu.vue +++ b/jdaviz/configs/default/plugins/data_menu/data_menu.vue @@ -46,8 +46,10 @@ /> - - {{item.label}} + + + + {{ item.label }} @@ -109,14 +111,9 @@ - - mdi-layers-outline - - {{ item.label }} + + + {{ item.label }} diff --git a/jdaviz/core/template_mixin.py b/jdaviz/core/template_mixin.py index 0b4cc4346f..08e677c3e7 100644 --- a/jdaviz/core/template_mixin.py +++ b/jdaviz/core/template_mixin.py @@ -1539,6 +1539,7 @@ def _layer_to_dict(self, layer_label): is_subset = None subset_type = None from_plugin = None + live_plugin_results = None colors = [] visibilities = [] linewidths = [] @@ -1552,6 +1553,8 @@ def _layer_to_dict(self, layer_label): subset_type = get_subset_type(layer.layer) if from_plugin is None: from_plugin = layer.layer.data.meta.get('Plugin', None) + if live_plugin_results is None: + live_plugin_results = layer.layer.data.meta.get('_update_live_plugin_results', None) is not None if (getattr(viewer.state, 'color_mode', None) == 'Colormaps' and hasattr(layer.state, 'cmap')): @@ -1566,7 +1569,8 @@ def _layer_to_dict(self, layer_label): return {"label": layer_label, "is_subset": is_subset, "subset_type": subset_type, - "from_plugin": from_plugin, + "from_plugin": from_plugin, + "live_plugin_results": live_plugin_results, "icon": self.app.state.layer_icons.get(layer_label), "visible": visibilities[0] if len(list(set(visibilities))) == 1 else 'mixed', "linewidth": linewidths[0] if len(list(set(linewidths))) == 1 else 'mixed', From eb19172c68cc5696fbd561dbc76a366a4c07a344 Mon Sep 17 00:00:00 2001 From: Kyle Conroy Date: Wed, 30 Oct 2024 12:49:36 -0400 Subject: [PATCH 06/20] improve visibility logic for "edit subset" button in footer --- jdaviz/components/data_menu_subset_edit.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jdaviz/components/data_menu_subset_edit.vue b/jdaviz/components/data_menu_subset_edit.vue index b9b862d7d8..3bc299d77d 100644 --- a/jdaviz/components/data_menu_subset_edit.vue +++ b/jdaviz/components/data_menu_subset_edit.vue @@ -1,5 +1,6 @@ - - - - - Resize in viewer - - - - From 245e335d2b32dbe22de7260ac8c3ff861c940a89 Mon Sep 17 00:00:00 2001 From: Kyle Conroy Date: Thu, 31 Oct 2024 15:34:06 -0400 Subject: [PATCH 11/20] codestyle and changelog --- CHANGES.rst | 2 +- .../default/plugins/data_menu/data_menu.py | 17 ++++++++--------- jdaviz/core/template_mixin.py | 2 +- 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index 2b1a782015..a1c278dc7a 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,7 +4,7 @@ New Features ------------ -* New design for viewer legend. [#3220, #3254, #3263] +* New design for viewer legend. [#3220, #3254, #3263, #3264] Cubeviz ^^^^^^^ diff --git a/jdaviz/configs/default/plugins/data_menu/data_menu.py b/jdaviz/configs/default/plugins/data_menu/data_menu.py index cce9f7a6e0..d9d7f53d9b 100644 --- a/jdaviz/configs/default/plugins/data_menu/data_menu.py +++ b/jdaviz/configs/default/plugins/data_menu/data_menu.py @@ -108,12 +108,11 @@ def data_not_in_viewer(data): self.viewer_icons = dict(self.app.state.viewer_icons) self.layer_icons = dict(self.app.state.layer_icons) - self.subset_edit_modes = [{'glue_name': 'replace', 'icon': read_icon(os.path.join(icon_path("glue_replace", icon_format="svg")), 'svg+xml')}, - {'glue_name': 'or', 'icon': read_icon(os.path.join(icon_path("glue_or", icon_format="svg")), 'svg+xml')}, - {'glue_name': 'and', 'icon': read_icon(os.path.join(icon_path("glue_and", icon_format="svg")), 'svg+xml')}, - {'glue_name': 'xor', 'icon': read_icon(os.path.join(icon_path("glue_xor", icon_format="svg")), 'svg+xml')}, - {'glue_name': 'andnot', 'icon': read_icon(os.path.join(icon_path("glue_andnot", icon_format="svg")), 'svg+xml')} - ] + self.subset_edit_modes = [{'glue_name': 'replace', 'icon': read_icon(os.path.join(icon_path("glue_replace", icon_format="svg")), 'svg+xml')}, # noqa + {'glue_name': 'or', 'icon': read_icon(os.path.join(icon_path("glue_or", icon_format="svg")), 'svg+xml')}, # noqa + {'glue_name': 'and', 'icon': read_icon(os.path.join(icon_path("glue_and", icon_format="svg")), 'svg+xml')}, # noqa + {'glue_name': 'xor', 'icon': read_icon(os.path.join(icon_path("glue_xor", icon_format="svg")), 'svg+xml')}, # noqa + {'glue_name': 'andnot', 'icon': read_icon(os.path.join(icon_path("glue_andnot", icon_format="svg")), 'svg+xml')}] # noqa # this currently assumes that toolbar.tools_data is set at init and does not change # if we ever support dynamic tool registration, this will need to be updated @@ -204,7 +203,7 @@ def _update_dm_layer_selected(self, event={}): # update internal counts and tooltips self.selected_n_layers = len(self.layer.selected) subset_labels = self.existing_subset_labels - self.selected_n_subsets = len([l for l in self.layer.selected if l in subset_labels]) + self.selected_n_subsets = len([lyr for lyr in self.layer.selected if lyr in subset_labels]) self.selected_n_data = self.selected_n_layers - self.selected_n_subsets # user-friendly representation of selection @@ -223,7 +222,7 @@ def _update_dm_layer_selected(self, event={}): if self.selected_n_layers == 1: if self.layer_items[self.dm_layer_selected[0]].get('from_plugin', False): self.info_enabled = False - self.info_tooltip = 'Selected data layer is a plugin product and does not have metadata' + self.info_tooltip = 'Selected data layer is a plugin product and does not have metadata' # noqa else: self.info_enabled = True if self.selected_n_data == 1: @@ -419,4 +418,4 @@ def remove_from_app(self): self.app.vue_data_item_remove({'item_name': layer}) def vue_remove_from_app(self, *args): - self.remove_from_app() # pragma: no cover \ No newline at end of file + self.remove_from_app() # pragma: no cover diff --git a/jdaviz/core/template_mixin.py b/jdaviz/core/template_mixin.py index 08e677c3e7..db5466aab6 100644 --- a/jdaviz/core/template_mixin.py +++ b/jdaviz/core/template_mixin.py @@ -1554,7 +1554,7 @@ def _layer_to_dict(self, layer_label): if from_plugin is None: from_plugin = layer.layer.data.meta.get('Plugin', None) if live_plugin_results is None: - live_plugin_results = layer.layer.data.meta.get('_update_live_plugin_results', None) is not None + live_plugin_results = layer.layer.data.meta.get('_update_live_plugin_results', None) is not None # noqa if (getattr(viewer.state, 'color_mode', None) == 'Colormaps' and hasattr(layer.state, 'cmap')): From ea756d5a067168aaf0fe50045475653646467337 Mon Sep 17 00:00:00 2001 From: Kyle Conroy Date: Fri, 1 Nov 2024 09:08:27 -0400 Subject: [PATCH 12/20] fix bug in re-parenting subsets when not linked by WCS * uncovered while writing test coverage here, but could potentially be split into its own bugfix --- jdaviz/app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jdaviz/app.py b/jdaviz/app.py index 3966ebf209..fea5d71cfa 100644 --- a/jdaviz/app.py +++ b/jdaviz/app.py @@ -2291,7 +2291,7 @@ def vue_data_item_remove(self, event): data_label = event['item_name'] data = self.data_collection[data_label] orientation_plugin = self._jdaviz_helper.plugins.get("Orientation") - if orientation_plugin is not None: + if orientation_plugin is not None and orientation_plugin.link_type == "WCS": from jdaviz.configs.imviz.plugins.orientation.orientation import base_wcs_layer_label orient = orientation_plugin.orientation.selected if orient == data_label: From bed88ebd2b8ba11e2e27850d9d55f2d7e5dee926 Mon Sep 17 00:00:00 2001 From: Kyle Conroy Date: Fri, 1 Nov 2024 09:28:10 -0400 Subject: [PATCH 13/20] simplify modify_subset method call to be consistent with others --- .../configs/default/plugins/data_menu/data_menu.py | 14 +++++++++----- .../default/plugins/data_menu/data_menu.vue | 3 +-- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/jdaviz/configs/default/plugins/data_menu/data_menu.py b/jdaviz/configs/default/plugins/data_menu/data_menu.py index d9d7f53d9b..47277a51bd 100644 --- a/jdaviz/configs/default/plugins/data_menu/data_menu.py +++ b/jdaviz/configs/default/plugins/data_menu/data_menu.py @@ -335,14 +335,12 @@ def create_subset(self, subset_type): def vue_create_subset(self, info, *args): self.create_subset(info.get('subset_type')) # pragma: no cover - def modify_subset(self, subset, combination_mode, subset_type): + def modify_subset(self, combination_mode, subset_type): """ Modify an existing subset interactively in the viewer. Parameters ---------- - subset : str - The label of the subset to modify. combination_mode : str The combination mode to apply to the subset. Must be one of 'replace', 'or', 'and', 'xor', or 'andnot'. @@ -350,6 +348,13 @@ def modify_subset(self, subset, combination_mode, subset_type): The type of subset to modify. Must be one of 'circle', 'rectangle', 'ellipse', 'annulus', 'xrange', or 'yrange'. """ + # future improvement: allow overriding layer.selected, with pre-validation + if len(self.layer.selected) != 1: + raise ValueError("Only one layer can be selected to modify subset.") + if self.layer.selected[0] not in self.existing_subset_labels: + raise ValueError("Selected layer is not a subset.") + subset = self.layer.selected[0] + # set tool first since that might default to "Create New" self._viewer.toolbar.select_tool(SUBSET_TOOL_IDS.get(subset_type, subset_type)) # set subset selection to the subset to modify @@ -359,8 +364,7 @@ def modify_subset(self, subset, combination_mode, subset_type): self.session.edit_subset_mode.mode = SUBSET_MODES.get(combination_mode) def vue_modify_subset(self, info, *args): - self.modify_subset(info.get('subset'), - info.get('combination_mode'), + self.modify_subset(info.get('combination_mode'), info.get('subset_type')) # pragma: no cover def view_info(self): diff --git a/jdaviz/configs/default/plugins/data_menu/data_menu.vue b/jdaviz/configs/default/plugins/data_menu/data_menu.vue index 263d1ea215..0e34cb3026 100644 --- a/jdaviz/configs/default/plugins/data_menu/data_menu.vue +++ b/jdaviz/configs/default/plugins/data_menu/data_menu.vue @@ -158,8 +158,7 @@ :subset_tools="subset_tools" :subset_selected="layer_selected[0]" @view-info="view_info" - @modify-subset="(combination_mode, tool) => {modify_subset({subset: layer_selected[0], - combination_mode: combination_mode, + @modify-subset="(combination_mode, tool) => {modify_subset({combination_mode: combination_mode, subset_type: tool}); data_menu_open = false}" /> From c4695771c2067834dae8564318a56aa709f1df32 Mon Sep 17 00:00:00 2001 From: Kyle Conroy Date: Fri, 1 Nov 2024 11:38:27 -0400 Subject: [PATCH 14/20] improved dark theme support --- jdaviz/components/data_menu_remove.vue | 2 +- jdaviz/components/data_menu_subset_edit.vue | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/jdaviz/components/data_menu_remove.vue b/jdaviz/components/data_menu_remove.vue index bea5e7993e..f9a377c445 100644 --- a/jdaviz/components/data_menu_remove.vue +++ b/jdaviz/components/data_menu_remove.vue @@ -15,7 +15,7 @@ v-on="on" :disabled="!delete_enabled" > - mdi-delete + mdi-delete diff --git a/jdaviz/components/data_menu_subset_edit.vue b/jdaviz/components/data_menu_subset_edit.vue index b01d6a7370..f93d05aa25 100644 --- a/jdaviz/components/data_menu_subset_edit.vue +++ b/jdaviz/components/data_menu_subset_edit.vue @@ -13,6 +13,7 @@ > - + From 2b4503f640606be090ed4a6c71c8d00c6c1cd234 Mon Sep 17 00:00:00 2001 From: Kyle Conroy Date: Fri, 1 Nov 2024 10:12:10 -0400 Subject: [PATCH 15/20] API improvements --- .../default/plugins/data_menu/data_menu.py | 60 ++++++++++--------- 1 file changed, 33 insertions(+), 27 deletions(-) diff --git a/jdaviz/configs/default/plugins/data_menu/data_menu.py b/jdaviz/configs/default/plugins/data_menu/data_menu.py index 47277a51bd..15d0bf5f7d 100644 --- a/jdaviz/configs/default/plugins/data_menu/data_menu.py +++ b/jdaviz/configs/default/plugins/data_menu/data_menu.py @@ -46,6 +46,7 @@ class DataMenu(TemplateMixin, LayerSelectMixin, DatasetSelectMixin): * :meth:`set_layer_visibility` * :meth:`toggle_layer_visibility` * :meth:`create_subset` + * :meth:`modify_subset` * :meth:`add_data` * :meth:`view_info` * :meth:`remove_from_viewer` @@ -299,32 +300,35 @@ def toggle_layer_visibility(self, layer_label): def vue_set_layer_visibility(self, info, *args): return self.set_layer_visibility(info.get('layer'), info.get('value')) # pragma: no cover - def add_data(self, data_label): + def add_data(self, *data_labels): """ Add a dataset to the viewer. Parameters ---------- - data_label : str - The label of the dataset to add to the viewer. + *data_labels : str + The label(s) of the dataset to add to the viewer. """ - if data_label not in self.dataset.choices: - raise ValueError(f"Data label '{data_label}' not able to be loaded into '{self.viewer_id}'. Must be one of: {self.dataset.choices}") # noqa - return self.app.add_data_to_viewer(self.viewer_id, data_label) + unavailable = [data_label for data_label in data_labels + if data_label not in self.dataset.choices] + if len(unavailable): + raise ValueError(f"Data labels {unavailable} not able to be loaded into '{self.viewer_id}'. Must be one of: {self.dataset.choices}") # noqa + for data_label in data_labels: + self.app.add_data_to_viewer(self.viewer_id, data_label) def vue_add_data_to_viewer(self, info, *args): self.add_data(info.get('data_label')) # pragma: no cover def create_subset(self, subset_type): """ - Create a new subset in the viewer. This sets the app-wide subset selection to 'Create New' - and selects the appropriate tool in this viewer's toolbar. + Interactively create a new subset in the viewer. This sets the app-wide subset + selection to 'Create New' and selects the appropriate tool in this viewer's toolbar. Parameters ---------- subset_type : str The type of subset to create. Must be one of 'circle', 'rectangle', 'ellipse', - 'annulus', 'xrange', or 'yrange'. + 'annulus', 'xrange', or 'yrange', and must be an available tool in this viewer. """ # clear previous selection, finalize subsets, temporarily sets default tool self._viewer.toolbar.active_tool_id = None @@ -337,7 +341,9 @@ def vue_create_subset(self, info, *args): def modify_subset(self, combination_mode, subset_type): """ - Modify an existing subset interactively in the viewer. + Interactively modify an existing subset in the viewer. This sets the app-wide subset + selection to the currently selected subset, mode to the selected combination_mode, + and selects the appropriate tool in this viewer's toolbar. Parameters ---------- @@ -346,7 +352,7 @@ def modify_subset(self, combination_mode, subset_type): 'xor', or 'andnot'. subset_type : str The type of subset to modify. Must be one of 'circle', 'rectangle', 'ellipse', - 'annulus', 'xrange', or 'yrange'. + 'annulus', 'xrange', or 'yrange', and must be an available tool in this viewer. """ # future improvement: allow overriding layer.selected, with pre-validation if len(self.layer.selected) != 1: @@ -372,25 +378,20 @@ def view_info(self): View info for the selected layer by opening either the metadata or subset plugin to the selected entry. """ + # future improvement: allow overriding layer.selected, with pre-validation if len(self.layer.selected) != 1: raise ValueError("Only one layer can be selected to view info.") if self.layer.selected[0] in self.existing_subset_labels: sp = self._viewer.jdaviz_helper.plugins.get('Subset Tools', None) - if sp is None: - return - try: - sp._obj.subset_select.selected = self.layer.selected[0] - except ValueError: - return + if sp is None: # pragma: no cover + raise ValueError("subset tools plugin not available") + sp._obj.subset_select.selected = self.layer.selected[0] sp.open_in_tray() else: mp = self._viewer.jdaviz_helper.plugins.get('Metadata', None) - if mp is None: - return - try: - mp.dataset.selected = self.layer.selected[0] - except ValueError: - return + if mp is None: # pragma: no cover + raise ValueError("metadata plugin not available") + mp.dataset.selected = self.layer.selected[0] mp.open_in_tray() def vue_view_info(self, *args): @@ -398,8 +399,11 @@ def vue_view_info(self, *args): def remove_from_viewer(self): """ - Remove the selected layers from the viewer. + Remove the selected layers from the viewer. For subset layers, this + sets the visibility of the subset layer. For data layers, + this unloads the data from the viewer, but keeps it in the application or other viewers. """ + # future improvement: allow overriding layer.selected via *args, with pre-validation for layer in self.layer.selected: if layer in self.existing_subset_labels: self.set_layer_visibility(layer, visible=False) @@ -413,11 +417,13 @@ def remove_from_app(self): """ Remove the selected layers from the entire app and all viewers. """ + # future improvement: allow overriding layer.selected via *args, with pre-validation for layer in self.layer.selected: if layer in self.existing_subset_labels: - subset_grp = [sg for sg in self.app.data_collection.subset_groups - if sg.label == layer][0] - self.app.data_collection.remove_subset_group(subset_grp) + for sg in self.app.data_collection.subset_groups: + if sg.label == layer: + self.app.data_collection.remove_subset_group(sg) + break else: self.app.vue_data_item_remove({'item_name': layer}) From 64f610d7d87628d51cfd155e3527f604fc17e43f Mon Sep 17 00:00:00 2001 From: Kyle Conroy Date: Fri, 1 Nov 2024 10:17:00 -0400 Subject: [PATCH 16/20] WIP: test coverage (known failures) --- .../configs/default/tests/test_data_menu.py | 102 +++++++++++++++++- 1 file changed, 101 insertions(+), 1 deletion(-) diff --git a/jdaviz/configs/default/tests/test_data_menu.py b/jdaviz/configs/default/tests/test_data_menu.py index af3105b42a..9316ac0cc7 100644 --- a/jdaviz/configs/default/tests/test_data_menu.py +++ b/jdaviz/configs/default/tests/test_data_menu.py @@ -1,3 +1,4 @@ +import pytest import numpy as np from specutils import SpectralRegion @@ -68,7 +69,7 @@ def test_data_menu_selection(specviz_helper, spectrum1d): assert dm.layer.selected == ['test'] -def test_data_menu_add_data(imviz_helper): +def test_data_menu_add_remove_data(imviz_helper): for i in range(3): imviz_helper.load_data(np.zeros((2, 2)) + i, data_label=f'image_{i}', show_in_viewer=False) @@ -81,6 +82,24 @@ def test_data_menu_add_data(imviz_helper): assert dm.layer.choices == ['image_0'] assert len(dm._obj.dataset.choices) == 2 + with pytest.raises(ValueError, + match="Data labels \\['dne1', 'dne2'\\] not able to be loaded into 'imviz-0'. Must be one of: \\['image_1', 'image_2'\\]"): # noqa + dm.add_data('dne1', 'dne2') + + dm.add_data('image_1', 'image_2') + assert len(dm.layer.choices) == 3 + assert len(dm._obj.dataset.choices) == 0 + + dm.layer.selected = ['image_0'] + dm.remove_from_viewer() + assert len(dm.layer.choices) == 2 + assert len(dm._obj.dataset.choices) == 1 + + dm.layer.selected = ['image_1'] + dm.remove_from_app() + assert len(dm.layer.choices) == 1 + assert len(dm._obj.dataset.choices) == 1 + def test_data_menu_create_subset(imviz_helper): imviz_helper.load_data(np.zeros((2, 2)), data_label='image', show_in_viewer=True) @@ -91,3 +110,84 @@ def test_data_menu_create_subset(imviz_helper): dm.create_subset('circle') assert imviz_helper.app.session.edit_subset_mode.edit_subset == [] assert imviz_helper.viewers['imviz-0']._obj.toolbar.active_tool_id == 'bqplot:truecircle' + + +def test_data_menu_remove_subset(specviz_helper, spectrum1d): + # load 2 data entries + specviz_helper.load_data(spectrum1d, data_label="test") + new_spec = specviz_helper.get_spectra(apply_slider_redshift=True)["test"]*0.9 + specviz_helper.load_data(new_spec, data_label="test2") + + dm = specviz_helper.viewers['spectrum-viewer']._obj.data_menu + sp = specviz_helper.plugins['Subset Tools'] + + sp._obj.import_region(SpectralRegion(6000 * spectrum1d.spectral_axis.unit, + 6100 * spectrum1d.spectral_axis.unit), + combination_mode='new') + sp._obj.import_region(SpectralRegion(6000 * spectrum1d.spectral_axis.unit, + 6100 * spectrum1d.spectral_axis.unit), + combination_mode='new') + + assert dm.layer.choices == ['test', 'test2', 'Subset 1', 'Subset 2'] + dm.layer.selected = ['Subset 1'] + dm.remove_from_viewer() + + # subset visibility is set to false, but still appears in menu (unlike removing data) + assert dm.layer.choices == ['test', 'test2', 'Subset 1', 'Subset 2'] + assert dm._obj.layer_items[2]['label'] == 'Subset 1' + # TODO: sometimes appearing as mixed right now, known bug + assert dm._obj.layer_items[2]['visible'] is not True + + # selection should not have changed by removing subset from viewer + assert dm.layer.selected == ['Subset 1'] + dm.remove_from_app() + # TODO: not quite sure why this isn't passing, seems to + # work on local tests, so may just be async? + # assert dm.layer.choices == ['test', 'test2', 'Subset 2'] + + +@pytest.mark.skip(reason="known issue") +def test_data_menu_subset_appearance(specviz_helper, spectrum1d): + # NOTE: this test is similar to above - the subset is appearing in time IF there + # are two data entries, but not in this case with just one + specviz_helper.load_data(spectrum1d, data_label="test") + + dm = specviz_helper.viewers['spectrum-viewer']._obj.data_menu + sp = specviz_helper.plugins['Subset Tools'] + + sp._obj.import_region(SpectralRegion(6000 * spectrum1d.spectral_axis.unit, + 6100 * spectrum1d.spectral_axis.unit)) + + assert dm.layer.choices == ['test', 'Subset 1'] + + +def test_data_menu_view_info(specviz_helper, spectrum1d): + # load 2 data entries + specviz_helper.load_data(spectrum1d, data_label="test") + new_spec = specviz_helper.get_spectra(apply_slider_redshift=True)["test"]*0.9 + specviz_helper.load_data(new_spec, data_label="test2") + + dm = specviz_helper.viewers['spectrum-viewer']._obj.data_menu + mp = specviz_helper.plugins['Metadata'] + sp = specviz_helper.plugins['Subset Tools'] + + sp._obj.import_region(SpectralRegion(6000 * spectrum1d.spectral_axis.unit, + 6100 * spectrum1d.spectral_axis.unit), + combination_mode='new') + sp._obj.import_region(SpectralRegion(6200 * spectrum1d.spectral_axis.unit, + 6300 * spectrum1d.spectral_axis.unit), + combination_mode='new') + + assert dm.layer.choices == ['test', 'test2', 'Subset 1', 'Subset 2'] + + dm.layer.selected = ["test2"] + dm.view_info() + assert mp.dataset.selected == "test2" + + dm.layer.selected = ["Subset 2"] + dm.view_info() + assert sp._obj.subset_select.selected == "Subset 2" + + dm.layer.selected = ["test", "test2"] + with pytest.raises(ValueError, match="Only one layer can be selected to view info"): + dm.view_info() From 55b3c023ebc96f516b697736d4872ff0dce5cbe8 Mon Sep 17 00:00:00 2001 From: Kyle Conroy Date: Fri, 1 Nov 2024 13:42:11 -0400 Subject: [PATCH 17/20] fix deprecated link_type > align_by --- jdaviz/app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jdaviz/app.py b/jdaviz/app.py index fea5d71cfa..007f907d37 100644 --- a/jdaviz/app.py +++ b/jdaviz/app.py @@ -2291,7 +2291,7 @@ def vue_data_item_remove(self, event): data_label = event['item_name'] data = self.data_collection[data_label] orientation_plugin = self._jdaviz_helper.plugins.get("Orientation") - if orientation_plugin is not None and orientation_plugin.link_type == "WCS": + if orientation_plugin is not None and orientation_plugin.align_by == "WCS": from jdaviz.configs.imviz.plugins.orientation.orientation import base_wcs_layer_label orient = orientation_plugin.orientation.selected if orient == data_label: From 33477002df5603fcd2f57354ec45ad0cc2b763c6 Mon Sep 17 00:00:00 2001 From: Kyle Conroy Date: Fri, 1 Nov 2024 14:33:13 -0400 Subject: [PATCH 18/20] prevent removing non-subset data from cubeviz & mosviz --- jdaviz/components/data_menu_remove.vue | 12 +++++++---- .../default/plugins/data_menu/data_menu.py | 20 +++++++++++++++++++ .../default/plugins/data_menu/data_menu.vue | 2 ++ 3 files changed, 30 insertions(+), 4 deletions(-) diff --git a/jdaviz/components/data_menu_remove.vue b/jdaviz/components/data_menu_remove.vue index f9a377c445..0ef267c844 100644 --- a/jdaviz/components/data_menu_remove.vue +++ b/jdaviz/components/data_menu_remove.vue @@ -34,10 +34,14 @@ - + Remove from app @@ -50,6 +54,6 @@ \ No newline at end of file diff --git a/jdaviz/configs/default/plugins/data_menu/data_menu.py b/jdaviz/configs/default/plugins/data_menu/data_menu.py index 15d0bf5f7d..a9bca2288f 100644 --- a/jdaviz/configs/default/plugins/data_menu/data_menu.py +++ b/jdaviz/configs/default/plugins/data_menu/data_menu.py @@ -76,6 +76,8 @@ class DataMenu(TemplateMixin, LayerSelectMixin, DatasetSelectMixin): delete_enabled = Bool(False).tag(sync=True) delete_tooltip = Unicode().tag(sync=True) + delete_app_enabled = Bool(False).tag(sync=True) + delete_app_tooltip = Unicode().tag(sync=True) subset_edit_enabled = Bool(False).tag(sync=True) subset_edit_tooltip = Unicode().tag(sync=True) @@ -245,6 +247,24 @@ def _update_dm_layer_selected(self, event={}): self.delete_tooltip = f"Remove selected {selected_repr}..." self.delete_enabled = True + # delete from entire app rules + delete_app_tooltip = "Remove from all viewers and application (permanent, might affect existing subsets)" # noqa + if self.app.config == 'cubeviz': + # forbid deleting non-plugin generated data + selected_items = self.layer.selected_item + for i, layer in enumerate(self.layer.selected): + if (layer not in self.existing_subset_labels + and selected_items['from_plugin'][i] is None): + self.delete_app_enabled = False + self.delete_app_tooltip = f"Cannot delete imported data from {self.app.config}" + break + else: + self.delete_app_enabled = True + self.delete_app_tooltip = delete_app_tooltip + else: + self.delete_app_enabled = True + self.delete_app_tooltip = delete_app_tooltip + # subset edit rules if self.selected_n_subsets == 1 and self.selected_n_layers == 1: self.subset_edit_enabled = True diff --git a/jdaviz/configs/default/plugins/data_menu/data_menu.vue b/jdaviz/configs/default/plugins/data_menu/data_menu.vue index 0e34cb3026..eb69083f56 100644 --- a/jdaviz/configs/default/plugins/data_menu/data_menu.vue +++ b/jdaviz/configs/default/plugins/data_menu/data_menu.vue @@ -135,6 +135,8 @@ From f1ae2bce5871d44b17be4a92b23901b549c7365d Mon Sep 17 00:00:00 2001 From: Kyle Conroy Date: Thu, 7 Nov 2024 13:05:43 -0500 Subject: [PATCH 19/20] dynamic "remove from viewer" tooltip --- jdaviz/components/data_menu_remove.vue | 6 ++++-- jdaviz/configs/default/plugins/data_menu/data_menu.py | 9 +++++++++ jdaviz/configs/default/plugins/data_menu/data_menu.vue | 1 + 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/jdaviz/components/data_menu_remove.vue b/jdaviz/components/data_menu_remove.vue index 0ef267c844..8a8e4d24b6 100644 --- a/jdaviz/components/data_menu_remove.vue +++ b/jdaviz/components/data_menu_remove.vue @@ -22,7 +22,9 @@ - + module.exports = { - props: ['delete_enabled', 'delete_tooltip', 'delete_app_enabled', 'delete_app_tooltip'], + props: ['delete_enabled', 'delete_tooltip', 'delete_viewer_tooltip', 'delete_app_enabled', 'delete_app_tooltip'], }; \ No newline at end of file diff --git a/jdaviz/configs/default/plugins/data_menu/data_menu.py b/jdaviz/configs/default/plugins/data_menu/data_menu.py index a9bca2288f..2f91c5bcbd 100644 --- a/jdaviz/configs/default/plugins/data_menu/data_menu.py +++ b/jdaviz/configs/default/plugins/data_menu/data_menu.py @@ -76,6 +76,7 @@ class DataMenu(TemplateMixin, LayerSelectMixin, DatasetSelectMixin): delete_enabled = Bool(False).tag(sync=True) delete_tooltip = Unicode().tag(sync=True) + delete_viewer_tooltip = Unicode().tag(sync=True) delete_app_enabled = Bool(False).tag(sync=True) delete_app_tooltip = Unicode().tag(sync=True) @@ -248,6 +249,14 @@ def _update_dm_layer_selected(self, event={}): self.delete_enabled = True # delete from entire app rules + subset_str = "subset" if self.selected_n_subsets == 1 else "subsets" + if self.selected_n_subsets and self.selected_n_data: + self.delete_viewer_tooltip = f"Remove selected data and hide selected {subset_str} in this viewer" # noqa + elif self.selected_n_data: + self.delete_viewer_tooltip = "Remove selected data from this viewer" + elif self.selected_n_subsets: + self.delete_viewer_tooltip = f"Hide selected {subset_str} in this viewer" + delete_app_tooltip = "Remove from all viewers and application (permanent, might affect existing subsets)" # noqa if self.app.config == 'cubeviz': # forbid deleting non-plugin generated data diff --git a/jdaviz/configs/default/plugins/data_menu/data_menu.vue b/jdaviz/configs/default/plugins/data_menu/data_menu.vue index eb69083f56..c1627a09c3 100644 --- a/jdaviz/configs/default/plugins/data_menu/data_menu.vue +++ b/jdaviz/configs/default/plugins/data_menu/data_menu.vue @@ -135,6 +135,7 @@ Date: Thu, 7 Nov 2024 13:13:49 -0500 Subject: [PATCH 20/20] avoid traceback when removing data --- jdaviz/configs/default/plugins/data_menu/data_menu.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/jdaviz/configs/default/plugins/data_menu/data_menu.py b/jdaviz/configs/default/plugins/data_menu/data_menu.py index 2f91c5bcbd..be6c68fd92 100644 --- a/jdaviz/configs/default/plugins/data_menu/data_menu.py +++ b/jdaviz/configs/default/plugins/data_menu/data_menu.py @@ -224,6 +224,11 @@ def _update_dm_layer_selected(self, event={}): # layer info rules if self.selected_n_layers == 1: + if max(self.dm_layer_selected) >= len(self.layer_items): # pragma: no cover + # can happen during state transition but should immediately be followed up + # with an update + self.info_enabled = False + self.info_tooltip = '' if self.layer_items[self.dm_layer_selected[0]].get('from_plugin', False): self.info_enabled = False self.info_tooltip = 'Selected data layer is a plugin product and does not have metadata' # noqa