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

WIP: Added new info bar and use it to show the coordinate overlay #545

Closed
wants to merge 11 commits into from
16 changes: 15 additions & 1 deletion jdaviz/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
SnackbarMessage, RemoveDataMessage,
AddDataToViewerMessage, RemoveDataFromViewerMessage)
from .core.registries import (tool_registry, tray_registry, viewer_registry,
data_parser_registry)
data_parser_registry, info_registry)
from .utils import load_template

__all__ = ['Application']
Expand Down Expand Up @@ -95,6 +95,9 @@ class ApplicationState(State):
tray_items = ListCallbackProperty(
docstring="List of plugins displayed in the sidebar tray area.")

info_items = ListCallbackProperty(
docstring="List of plugins displayed in the info bar.")

stack_items = ListCallbackProperty(
docstring="Nested collection of viewers constructed to support the "
"Golden Layout viewer area.")
Expand Down Expand Up @@ -1200,10 +1203,21 @@ def compose_viewer_area(viewer_area_items):
'widget': "IPY_MODEL_" + tray_item_instance.model_id
})

for name in config.get('info', []):
info = info_registry.members.get(name)(app=self)

self.state.info_items.append({
'name': name,
'widget': "IPY_MODEL_" + info.model_id
})

self._application_handler._tools[name] = info

def _reset_state(self):
""" Resets the application state """
self.state = ApplicationState()
self.state.add_callback('stack_items', self.vue_relayout)
self._application_handler._tools = {}

def get_configuration(self, path=None, section=None):
""" Returns a copy of the application configuration
Expand Down
17 changes: 13 additions & 4 deletions jdaviz/app.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<template>
<v-app id="web-app">
<v-app id="web-app" class="jdaviz">
<v-app-bar color="primary" dark dense flat app absolute clipped-right>
<jupyter-widget :widget="item.widget" v-for="item in state.tool_items" :key="item.name"></jupyter-widget>
<v-spacer></v-spacer>
Expand All @@ -10,15 +10,19 @@
</v-btn>
</v-toolbar-items>
</v-app-bar>

<v-content
:style="checkNotebookContext() ? 'height: ' + state.settings.context.notebook.max_height + '; border: solid 1px #e5e5e5;' : ''"
:style="checkNotebookContext() ? 'height: ' + state.settings.context.notebook.max_height + '; border: solid 1px #e5e5e5;' : `max-height: calc(100% - 48px${state.info_items && state.info_items.length > 0 ? ' - var(--info-bar-height)' : ''})`"
>
<v-row style="height: var(--info-bar-height); background: #325e74;" v-if="state.info_items.length > 0">
<v-col v-for="item in state.info_items" :key="item.name">
<jupyter-widget :widget="item.widget"></jupyter-widget>
</v-col>
</v-row>
<v-container class="fill-height pa-0" fluid>
<splitpanes @resize="relayout">
<pane size="75">
<golden-layout
:style="checkNotebookContext() ? 'height: 100%;' : 'height: calc(100vh - 48px)'"
style="height: 100%;"
:has-headers="state.settings.visible.tab_headers"
>
<gl-row :closable="false">
Expand Down Expand Up @@ -112,6 +116,10 @@ export default {

<style id="web-app">

.jdaviz {
--info-bar-height: 40px;
}

/* fix for loading overlay z-index */
div.output_wrapper {
z-index: auto;
Expand Down Expand Up @@ -192,4 +200,5 @@ div.output_wrapper {
padding: 0px;
margin: 0px;
}

</style>
2 changes: 2 additions & 0 deletions jdaviz/configs/imviz/imviz.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ toolbar:
- g-data-tools
- g-subset-tools
- g-image-viewer-creator
info:
- g-coords-info
viewer_area:
- container: col
children:
Expand Down
3 changes: 2 additions & 1 deletion jdaviz/configs/imviz/plugins/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from .tools import * # noqa
from .viewers import * # noqa
from .image_viewer_creator import * # noqa
from .image_viewer_creator import * # noqa
from .coords_info import * # noqa
1 change: 1 addition & 0 deletions jdaviz/configs/imviz/plugins/coords_info/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from .coords_info import * # noqa
13 changes: 13 additions & 0 deletions jdaviz/configs/imviz/plugins/coords_info/coords_info.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
from traitlets import Unicode

from jdaviz.core.registries import info_registry
from jdaviz.core.template_mixin import TemplateMixin
from jdaviz.utils import load_template

__all__ = ['CoordsInfo']


@info_registry('g-coords-info')
class CoordsInfo(TemplateMixin):
template = load_template("coords_info.vue", __file__).tag(sync=True)
text = Unicode("").tag(sync=True)
5 changes: 5 additions & 0 deletions jdaviz/configs/imviz/plugins/coords_info/coords_info.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<template>
<div align='right' style='padding-right: 10px;'>
<span style="color: #ffffff">{{ text }}</span>
</div>
</template>
23 changes: 13 additions & 10 deletions jdaviz/configs/imviz/plugins/viewers.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@
from glue.core import BaseData
from glue_jupyter.bqplot.image import BqplotImageView

from bqplot import Label

from jdaviz.core.registries import viewer_registry

__all__ = ['ImvizImageView']
Expand All @@ -21,9 +19,7 @@ def __init__(self, *args, **kwargs):

super().__init__(*args, **kwargs)

self.label_mouseover = Label(x=[0.05], y=[0.05], text=[''],
default_size=12, colors=['orange'])
self.figure.marks = self.figure.marks + [self.label_mouseover]
self.label_mouseover = None

self.add_event_callback(self.on_mouse_or_key_event, events=['mousemove', 'keydown'])

Expand All @@ -34,6 +30,12 @@ def on_mouse_or_key_event(self, data):
if len(self.state.layers) == 0:
return

if self.label_mouseover is None:
if 'g-coords-info' in self.session.application._tools:
self.label_mouseover = self.session.application._tools['g-coords-info']
else:
return

if data['event'] == 'mousemove':

# Display the current cursor coordinates (both pixel and world) as
Expand All @@ -44,7 +46,7 @@ def on_mouse_or_key_event(self, data):
x = data['domain']['x']
y = data['domain']['y']

overlay = f'x={x:.1f} y={y:.1f}'
overlay = f'x={x:5.1f} y={y:5.1f}'

image = self.state.layers[0].layer

Expand All @@ -53,15 +55,16 @@ def on_mouse_or_key_event(self, data):
# Convert these to a SkyCoord via WCS - note that for other datasets
# we aren't actually guaranteed to get a SkyCoord out, just for images
# with valid celestial WCS
celestial_coordinates = image.coords.pixel_to_world(x, y).icrs.to_string('hmsdms')
overlay += f' ICRS={celestial_coordinates}'
celestial_coordinates = (image.coords.pixel_to_world(x, y).icrs
.to_string('hmsdms', precision=4, pad=True))
overlay += f' ICRS={celestial_coordinates:32s}'

# Extract data values at this position
if x > -0.5 and y > -0.5 and x < image.shape[1] - 0.5 and y < image.shape[0] - 0.5:
value = image.get_data(image.main_components[0])[int(round(y)), int(round(x))]
overlay += f' data={value:.2g}'
overlay += f' data={value:10.2g}'

self.label_mouseover.text = [overlay]
self.label_mouseover.text = overlay

elif data['event'] == 'mouseleave' or data['event'] == 'mouseenter':

Expand Down
25 changes: 23 additions & 2 deletions jdaviz/core/registries.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@
from ipyvuetify import VuetifyTemplate
from ipywidgets import Widget


__all__ = ['viewer_registry', 'tray_registry', 'tool_registry',
'data_parser_registry', 'ViewerRegistry', 'TrayRegistry',
'ToolRegistry', 'MenuRegistry', 'DataParserRegistry']
'ToolRegistry', 'MenuRegistry', 'DataParserRegistry',
'info_registry', 'InfoRegistry']


def convert(name):
Expand Down Expand Up @@ -163,6 +163,26 @@ def decorator(cls):
return decorator


class InfoRegistry(UniqueDictRegistry):
"""
Registry containing references to plugins which will populate the
info bar.
"""
def __call__(self, name=None):
def decorator(cls):
# The class must inherit from `Widget` in order to be
# ingestible by the component initialization.
if not issubclass(cls, Widget):
raise ValueError(
f"Unrecognized superclass for `{cls.__name__}`. All "
f"registered tools must inherit from "
f"`ipywidgets.Widget`.")

self.add(name, cls)
return cls
return decorator


class DataParserRegistry(UniqueDictRegistry):
"""
Registry containing parsing functions for attempting to auto-populate the
Expand All @@ -179,4 +199,5 @@ def decorator(func):
tray_registry = TrayRegistry()
tool_registry = ToolRegistry()
menu_registry = MenuRegistry()
info_registry = InfoRegistry()
data_parser_registry = DataParserRegistry()
60 changes: 53 additions & 7 deletions notebooks/ImvizExample.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
},
{
"cell_type": "code",
"execution_count": null,
"execution_count": 1,
"id": "handed-chicago",
"metadata": {},
"outputs": [],
Expand All @@ -29,10 +29,21 @@
},
{
"cell_type": "code",
"execution_count": null,
"execution_count": 2,
"id": "caroline-enclosure",
"metadata": {},
"outputs": [],
"outputs": [
{
"data": {
"text/plain": [
"('msx_e.fits', <http.client.HTTPMessage at 0x7f8d99980520>)"
]
},
"execution_count": 2,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"from urllib.request import urlretrieve\n",
"\n",
Expand All @@ -50,12 +61,35 @@
},
{
"cell_type": "code",
"execution_count": null,
"execution_count": 3,
"id": "covered-bikini",
"metadata": {
"scrolled": false
},
"outputs": [],
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"WARNING: Could not load OpenGL library.\n",
"WARNING:vispy:Could not load OpenGL library.\n"
]
},
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "88a404f3602e4049a295b64f51f87d6d",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"Application(components={'g-viewer-tab': '<template>\\n <component :is=\"stack.container\">\\n <g-viewer-tab\\n …"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"from jdaviz import Imviz\n",
"import matplotlib.pyplot as plt\n",
Expand All @@ -81,10 +115,22 @@
},
{
"cell_type": "code",
"execution_count": null,
"execution_count": 4,
"id": "sonic-cuisine",
"metadata": {},
"outputs": [],
"outputs": [
{
"ename": "IndexError",
"evalue": "list index out of range",
"output_type": "error",
"traceback": [
"\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
"\u001b[0;31mIndexError\u001b[0m Traceback (most recent call last)",
"\u001b[0;32m<ipython-input-4-0346e107b656>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mviewer\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mstate\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mlayers\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mstretch\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m'sqrt'\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
"\u001b[0;31mIndexError\u001b[0m: list index out of range"
]
}
],
"source": [
"viewer.state.layers[0].stretch = 'sqrt'"
]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ <h1>{{ loading_text }}</h1>
</v-slide-y-transition>
<v-slide-y-transition>
<template v-show="!loading">
<jupyter-widget-mount-point mount-id="content">
<jupyter-widget-mount-point style="max-height: 100vh" mount-id="content">
</jupyter-widget-mount-point>
</template>
</v-slide-y-transition>
Expand Down