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

Viewer/layer labels #1465

Merged
merged 17 commits into from
Jul 14, 2022
Merged
Show file tree
Hide file tree
Changes from 6 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 @@
New Features
------------

- Viewer/layer labels with icons that are synced app-wide. [#1465]

Cubeviz
^^^^^^^

Expand Down
32 changes: 30 additions & 2 deletions jdaviz/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@
from glue.core.link_helpers import LinkSame
from glue.plugins.wcs_autolinking.wcs_autolinking import WCSLink, IncompatibleWCS
from glue.core.message import (DataCollectionAddMessage,
DataCollectionDeleteMessage)
DataCollectionDeleteMessage,
SubsetCreateMessage,
SubsetDeleteMessage)
from glue.core.state_objects import State
from glue.core.subset import Subset, RangeSubsetState, RoiSubsetState
from glue_jupyter.app import JupyterApplication
Expand Down Expand Up @@ -127,6 +129,7 @@ class ApplicationState(State):
'tray': True,
'tab_headers': True,
},
'viewer_labels': True,
'dense_toolbar': True,
'context': {
'notebook': {
Expand All @@ -142,6 +145,9 @@ class ApplicationState(State):
'checktoradial': read_icon(os.path.join(ICON_DIR, 'checktoradial.svg'), 'svg+xml')
}, docstring="Custom application icons")

viewer_icons = DictCallbackProperty({}, docstring="Indexed icons for viewers across the app")
layer_icons = DictCallbackProperty({}, docstring="Indexed icons for layers across the app")

data_items = ListCallbackProperty(
docstring="List of data items parsed from the Glue data collection.")

Expand Down Expand Up @@ -177,6 +183,7 @@ class Application(VuetifyTemplate, HubListener):
config = Unicode("").tag(sync=True)
vdocs = Unicode("").tag(sync=True)
popout_button = Any().tag(sync=True, **widget_serialization)
viewer_labels = Bool(True).tag(sync=True)

def __init__(self, configuration=None, *args, **kwargs):
super().__init__(*args, **kwargs)
Expand Down Expand Up @@ -261,6 +268,16 @@ def __init__(self, configuration=None, *args, **kwargs):
if cur_cm not in colormaps.members:
colormaps.add(*cur_cm)

# Subscribe to messages that result in changes to the layers
self.hub.subscribe(self, AddDataMessage,
handler=self._on_layers_changed)
self.hub.subscribe(self, RemoveDataMessage,
handler=self._on_layers_changed)
self.hub.subscribe(self, SubsetCreateMessage,
handler=self._on_layers_changed)
self.hub.subscribe(self, SubsetDeleteMessage,
handler=self._on_layers_changed)

@property
def hub(self):
"""
Expand Down Expand Up @@ -351,6 +368,16 @@ def _color_to_level(color):
history=msg_level >= history_level,
popup=msg_level >= popup_level)

def _on_layers_changed(self, msg):
if hasattr(msg, 'data'):
layer_name = msg.data.label
elif hasattr(msg, 'subset'):
layer_name = msg.subset.label
else:
raise NotImplementedError(f"cannot recognize new layer from {msg}")

self.state.layer_icons.setdefault(layer_name, f"mdi-alpha-{chr(97 + len(self.state.layer_icons))}-box-outline") # noqa

def _link_new_data(self, reference_data=None, data_to_be_linked=None):
"""
When additional data is loaded, check to see if the spectral axis of
Expand Down Expand Up @@ -1429,6 +1456,8 @@ def _create_viewer_item(self, viewer, vid=None, name=None, reference=None):
# own attribute instead.
viewer._reference_id = vid # For reverse look-up

self.state.viewer_icons.setdefault(vid, f"mdi-numeric-{len(self.state.viewer_icons)+1}-circle-outline") # noqa

return {
'id': vid,
'name': name or vid,
Expand All @@ -1438,7 +1467,6 @@ def _create_viewer_item(self, viewer, vid=None, name=None, reference=None):
'tools_open': False,
'layer_options': "IPY_MODEL_" + viewer.layer_options.model_id,
'viewer_options': "IPY_MODEL_" + viewer.viewer_options.model_id,
'layer_viewer_open': False,
'selected_data_items': {},
'config': self.config, # give viewer access to app config/layout
'data_open': False,
Expand Down
5 changes: 4 additions & 1 deletion jdaviz/app.vue
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,8 @@
:data_items="state.data_items"
:app_settings="state.settings"
:icons="state.icons"
:viewer_icons="state.viewer_icons"
:layer_icons="state.layer_icons"
@resize="relayout"
:closefn="destroy_viewer_item"
@data-item-selected="data_item_selected($event)"
Expand Down Expand Up @@ -342,8 +344,9 @@ a:active {
filter: invert(1) saturate(1) brightness(100);
}

.invert-if-dark.theme--dark {
.invert, .invert-if-dark.theme--dark {
filter: invert(1) saturate(1) brightness(100);
color: white;
}

.jdaviz-nested-toolbar .v-btn {
Expand Down
2 changes: 2 additions & 0 deletions jdaviz/components/plugin_layer_select.vue
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
</span>
</v-chip>
<span v-else>
<v-icon style='margin-right: 2px'>{{ data.item.icon }}</v-icon>
{{ data.item.label }}
</span>
</div>
Expand All @@ -49,6 +50,7 @@
<template slot="item" slot-scope="data">
<div class="single-line">
<span>
<v-icon style='margin-left: -2px; margin-right: 2px'>{{ data.item.icon }}</v-icon>
{{ data.item.label }}
</span>
</div>
Expand Down
4 changes: 3 additions & 1 deletion jdaviz/components/plugin_viewer_select.vue
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,11 @@
<v-chip v-if="multiselect" style="width: calc(100% - 20px)">
<span>
<v-icon style='margin-left: -10px; margin-right: 2px'>{{ data.item.icon }}</v-icon>
{{ data.item.label.split("-viewer")[0] }}
{{ data.item.label }}
</span>
</v-chip>
<span v-else>
<v-icon style='margin-right: 2px'>{{ data.item.icon }}</v-icon>
{{ data.item.label }}
</span>
</div>
Expand All @@ -49,6 +50,7 @@
<template slot="item" slot-scope="data">
<div class="single-line">
<span>
<v-icon style='margin-left: -2px; margin-right: 2px'>{{ data.item.icon }}</v-icon>
{{ data.item.label }}
</span>
</div>
Expand Down
15 changes: 14 additions & 1 deletion jdaviz/configs/default/plugins/plot_options/plot_options.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import os

from traitlets import Any, Dict, Float, Bool, Int, List, Unicode
from echo import add_callback
from traitlets import Any, Dict, Float, Bool, Int, List, Unicode, observe

from glue.viewers.profile.state import ProfileViewerState, ProfileLayerState
from glue.viewers.image.state import ImageSubsetLayerState
Expand Down Expand Up @@ -107,6 +108,8 @@ class PlotOptions(TemplateMixin):
icon_radialtocheck = Unicode(read_icon(os.path.join(ICON_DIR, 'radialtocheck.svg'), 'svg+xml')).tag(sync=True) # noqa
icon_checktoradial = Unicode(read_icon(os.path.join(ICON_DIR, 'checktoradial.svg'), 'svg+xml')).tag(sync=True) # noqa

setting_show_viewer_labels = Bool(True).tag(sync=True)

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.viewer = ViewerSelect(self, 'viewer_items', 'viewer_selected', 'multiselect')
Expand Down Expand Up @@ -197,6 +200,16 @@ def is_not_subset(state):
# zoom limits
# display_units

self.setting_show_viewer_labels = self.app.state.settings['viewer_labels']
add_callback(self.app.state, 'settings', self._on_app_settings_changed)

@observe('setting_show_viewer_labels')
def _on_show_viewer_labels_changed(self, event):
self.app.state.settings['viewer_labels'] = event['new']

def _on_app_settings_changed(self, value):
self.setting_show_viewer_labels = value['viewer_labels']

def vue_unmix_state(self, name):
sync_state = getattr(self, name)
sync_state.unmix_state()
Expand Down
20 changes: 20 additions & 0 deletions jdaviz/configs/default/plugins/plot_options/plot_options.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,26 @@
:link="'https://jdaviz.readthedocs.io/en/'+vdocs+'/'+config+'/plugins.html#plot-options'"
:popout_button="popout_button">

<v-row>
<v-expansion-panels popout>
<v-expansion-panel>
<v-expansion-panel-header v-slot="{ open }">
<span style="padding: 6px">Settings</span>
</v-expansion-panel-header>
<v-expansion-panel-content>
<v-row>
<v-switch
v-model="setting_show_viewer_labels"
label="Show labels in viewers"
hint="Whether to show viewer/layer labels on each viewer"
persistent-hint
></v-switch>
</v-row>
</v-expansion-panel-content>
</v-expansion-panel>
</v-expansion-panels>
</v-row>

<v-row>
<div style="width: calc(100% - 32px)">
</div>
Expand Down
64 changes: 62 additions & 2 deletions jdaviz/container.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
:data_items="data_items"
:app_settings="app_settings"
:icons="icons"
:viewer_icons="viewer_icons"
:layer_icons="layer_icons"
@resize="$emit('resize')"
:closefn="closefn"
@data-item-selected="$emit('data-item-selected', $event)"
Expand Down Expand Up @@ -55,17 +57,65 @@

</div>

<v-card tile flat style="flex: 1; margin-top: -2px; overflow-y: auto;">
<v-card tile flat style="flex: 1; margin-top: -2px; overflow-y: hidden;">
<div v-if="app_settings.viewer_labels" class='viewer-label-container'>
<div :class="viewer.config==='imviz' ? 'viewer-label viewer-label-imviz invert-if-dark' : 'viewer-label invert-if-dark'">
<j-tooltip span_style="white-space: nowrap">
<v-icon :class="viewer.config==='imviz' ? 'invert' : 'invert-if-dark'" style="float: right">{{viewer_icons[[viewer.id]]}}</v-icon>
kecnry marked this conversation as resolved.
Show resolved Hide resolved
</j-tooltip>
<span :class="viewer.config==='imviz' ? 'invert' : 'invert-if-dark'" style="margin-left: 24px; margin-right: 32px; line-height: 24px">{{viewer.reference || viewer.id}}</span>
</div>

<div v-for="(icon, layer_name) in layer_icons" :class="viewer.config==='imviz' ? 'viewer-label viewer-label-imviz invert-if-dark' : 'viewer-label invert-if-dark'">
<div v-if="layer_in_viewer(viewer, data_items, layer_name)">
<j-tooltip span_style="white-space: nowrap">
<v-icon :class="viewer.config==='imviz' ? 'invert' : 'invert-if-dark'" style="float: right">{{icon}}</v-icon>
</j-tooltip>
<span :class="viewer.config==='imviz' ? 'invert' : 'invert-if-dark'" style="margin-left: 24px; margin-right: 32px; line-height: 24px">{{layer_name}}</span>
</div>
</div>
</div>

<jupyter-widget :widget="viewer.widget" style="width: 100%; height: 100%"></jupyter-widget>
</v-card>
</gl-component>
</component>
</template>

<style>
.viewer-label-container {
position: absolute;
right: 0;
z-index: 1;
width: 24px;
}
.viewer-label {
display: block;
float: right;
background-color: white;
width: 24px;
overflow: hidden;
white-space: nowrap;
/*cursor: pointer;*/
}
.viewer-label-imviz {
background-color: #393939c2;
}
.viewer-label:hover {
background-color: #e5e5e5;
width: auto;
border-bottom-left-radius: 4px;
border-top-left-radius: 4px;
}
.viewer-label-imviz:hover {
background-color: #777777c2;
}
</style>

<script>
module.exports = {
name: "g-viewer-tab",
props: ["stack", "data_items", "closefn", "app_settings", "icons"],
props: ["stack", "data_items", "closefn", "app_settings", "icons", "viewer_icons", "layer_icons"],
created() {
this.$parent.childMe = () => {
return this.$children[0];
Expand All @@ -80,6 +130,16 @@ module.exports = {
* between a user closing a tab or a re-render. However, when the user closes a tab, the
* source of the event is a vue component. We can use that distinction as a close signal. */
source.$root && this.closefn(viewerId);
},
layer_in_viewer(viewer, data_items, layer_name) {
for (data_item of data_items) {
if (data_item['name'] == layer_name) {
// determine if this layer is LOADED (not necessarily visible) in the selected viewer
return (Object.keys(viewer.selected_data_items).indexOf(data_item['id']) !== -1 && viewer.selected_data_items[data_item['id']] !== 'hidden')
}
}
// then layer_name is likely of a subset, so we'll only include for spectrum-viewers
return viewer.reference === 'spectrum-viewer';
}
},
computed: {
Expand Down
25 changes: 14 additions & 11 deletions jdaviz/core/template_mixin.py
Original file line number Diff line number Diff line change
Expand Up @@ -168,20 +168,21 @@ def hub(self):

@property
def viewer_dicts(self):
def _dict_from_viewer(viewer, viewer_item, index=None):
d = {'viewer': viewer, 'id': viewer_item['id']}
def _dict_from_viewer(viewer, viewer_item):
d = {'viewer': viewer,
'id': viewer_item['id'],
'icon': self.app.state.viewer_icons.get(viewer_item['id'])}
if viewer_item.get('reference') is not None:
d['reference'] = viewer_item['reference']
d['label'] = viewer_item['reference']
else:
d['reference'] = None
d['label'] = viewer_item['id']
if index is not None:
d['icon'] = f"mdi-numeric-{index+1}-circle-outline"

return d

return [_dict_from_viewer(viewer, self.app._viewer_item_by_id(vid), index)
for index, (vid, viewer) in enumerate(self.app._viewer_store.items())
return [_dict_from_viewer(viewer, self.app._viewer_item_by_id(vid))
for vid, viewer in self.app._viewer_store.items()
if viewer.__class__.__name__ != 'MosvizTableViewer']

@cached_property
Expand Down Expand Up @@ -453,10 +454,10 @@ def _get_viewer(self, viewer):
except TypeError:
return self.app.get_viewer_by_id(viewer)

def _layer_to_dict(self, layer, index=None):
d = {"label": layer.layer.label, "color": layer.state.color}
if index is not None:
d['icon'] = f"mdi-alpha-{chr(97 + index)}-box-outline"
def _layer_to_dict(self, layer):
d = {"label": layer.layer.label,
"color": layer.state.color,
"icon": self.app.state.layer_icons.get(layer.layer.label)}
return d

def _on_viewer_changed(self, msg=None):
Expand Down Expand Up @@ -495,7 +496,7 @@ def _on_layers_changed(self, msg=None):
_, inds = np.unique(layer_labels, return_index=True)
layers = [layers[i] for i in inds]

self.items = manual_items + [self._layer_to_dict(layer, index) for index, layer in enumerate(layers)] # noqa
self.items = manual_items + [self._layer_to_dict(layer) for layer in layers]
self._apply_default_selection()

@cached_property
Expand Down Expand Up @@ -1438,6 +1439,8 @@ def _state_item(item):
for selected_layer_label in layer_labels:
layer_states.append([])
for viewer in viewers:
if viewer is None:
continue
for layer in viewer.layers:
if layer.layer.label == selected_layer_label:
layer_states[-1].append(layer.state)
Expand Down