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

Initial draft of interface definition #142

Draft
wants to merge 50 commits into
base: main
Choose a base branch
from
Draft
Changes from 1 commit
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
4b18213
Initial draft on interface definition
mwcraig Jul 2, 2021
9ee09df
Minimal change implementation of concrete ginga viewer
mwcraig Jul 12, 2021
fead302
Partial implementation of bqplot astro widget
mwcraig Jul 16, 2021
ccd5c39
Update astrowidgets/bqplot.py
mwcraig Jul 26, 2021
f9649c0
Set default cuts and auto-scale image
mwcraig Jul 28, 2021
6e73ade
Add basic cursor display
mwcraig Jul 28, 2021
9ba1839
Do not reset view when cuts change
mwcraig Jul 28, 2021
9e6e037
Make pixel index correspond to center of pixel in viewer
mwcraig Jul 28, 2021
0fbb617
PL ginga changes
mwcraig Aug 27, 2021
16d348e
Move ginga implementations back to ginga class
mwcraig Aug 27, 2021
a616101
Factor ginga-specific code out of base test class
mwcraig Aug 27, 2021
aab7edd
Add ginga viewer test
mwcraig Aug 27, 2021
518c908
Add bqplot viewer test
mwcraig Aug 27, 2021
7cc8a0c
Remove ginga-specific test code
mwcraig Aug 27, 2021
c2fc5b7
Make warning message checks more flexible
mwcraig Aug 27, 2021
88a2666
Bring bqplot closer to passing API tests
mwcraig Aug 27, 2021
9d08403
New offset_by with new API.
pllim Jul 1, 2021
819c115
Remove incorrect creation of WCS
mwcraig Aug 30, 2021
9ebc27f
Ensure save only overwrites if user wants that
mwcraig Aug 30, 2021
a57ed37
Change assertion to ValueError to match API
mwcraig Aug 30, 2021
14662e8
Do not alert user that scroll_pan actually doesn't work
mwcraig Aug 30, 2021
feda94b
Handle several marker-related settings
mwcraig Aug 30, 2021
efdf200
Add a few more API attributes
mwcraig Aug 30, 2021
4b206c1
Fix bug in error messages
mwcraig Aug 30, 2021
657f294
Add several methods for implementing markers
mwcraig Aug 30, 2021
4bdd27d
Implement offset_by
mwcraig Aug 30, 2021
e228174
Rewrite test classes to use fixtures instead of setup_class
mwcraig Aug 30, 2021
1e9962e
DOC: Update user-facing doc.
pllim Feb 19, 2021
33799e3
DOC: Fix inheritance diagram for Ginga API.
pllim Feb 22, 2021
bedd9f6
Allow the image widget to detect GUI zoom changes
mwcraig Sep 12, 2021
c8cd3cf
Add click_center functionality
mwcraig Sep 17, 2021
da5ac8e
Respond to changes in cursor value
mwcraig Sep 17, 2021
5c47658
Fix bug in setting up stretch
mwcraig Sep 17, 2021
0c5048a
Fix errors in marking setup
mwcraig Sep 17, 2021
a7e9384
Handle case when user does not specify marker name in add_markers
mwcraig Sep 17, 2021
584ebf5
Add click-to-mark functionality
mwcraig Sep 22, 2021
3de921a
WIP bqplot widget notebook
mwcraig Oct 27, 2021
838d027
Propogate marker API changes
mwcraig May 4, 2023
da4fd3f
Use proper numpy type names
mwcraig May 4, 2023
a196d29
Finish most of API implementation
mwcraig May 4, 2023
bf3f0bb
Clean up tests
mwcraig May 4, 2023
5fd58a3
Remove cell outputs in sample notebook
mwcraig May 4, 2023
9b747ff
Add traitlet/widget related warnings to ignore list
mwcraig May 4, 2023
10abb5e
Restore a couple of constants that got los in the refactor
mwcraig May 4, 2023
92c2eb5
Remove base class tests
mwcraig May 4, 2023
2f3bd38
Remove pixel offset from ginga widget
mwcraig May 5, 2023
cccd747
Update marker method names in test
mwcraig May 5, 2023
8bc7c3d
Replace cut test with something sensible
mwcraig May 5, 2023
5be93fb
Update more marker tests
mwcraig May 5, 2023
82a7844
Add widget arguments to ginga
mwcraig May 5, 2023
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
Prev Previous commit
Next Next commit
Allow the image widget to detect GUI zoom changes
mwcraig committed May 4, 2023
commit bedd9f62628772a5bcc8778527dc5a8f47f40a8c
74 changes: 72 additions & 2 deletions astrowidgets/bqplot.py
Original file line number Diff line number Diff line change
@@ -68,6 +68,8 @@ def __init__(self, image_data=None,
'image': ColorScale(max=1, min=0,
scheme='Greys')}

self._image_shape = None

self._scatter_marks = {}

self._figure = Figure(scales=self._scales, axes=[axis_x, axis_y],
@@ -238,6 +240,24 @@ def set_zoom_level(self, zoom_level):
self.set_size(new_width, 'x')
self._set_scale_aspect_ratio_to_match_viewer('y')

def get_zoom_level(self):
"""
Get the zoom level of the current view, if such a view has been set.

A zoom level of 1 means 1 pixel in the image is 1 pixel in the viewer,
i.e. the scale width in the horizontal direction matches the width in
pixels of the figure.
"""
if self._image_shape is None:
return None

# The width is used here but the height could be used instead
# and the result would be the same since the pixels are square.
figure_width = float(self._figure.layout.width[:-2])
scale_width = self.scale_widths[1]

return figure_width / scale_width

def plot_named_markers(self, x, y, mark_id, color='yellow',
size=100, style='circle'):
scale_dict = dict(x=self._scales['x'], y=self._scales['y'])
@@ -394,12 +414,21 @@ def __init__(self, *args, image_width=500, image_height=500):
self._wcs = None
self._is_marking = False

# Use this to manage whether or not to send changes in zoom level
# to the viewer.
self._zoom_source_is_gui = False
# Use this to let the method monitoring changes coming from the
# image know that the ImageWidget itself is in the process of
# updating the zoom.
self._updating_zoom = False

self.marker = {'color': 'red', 'radius': 20, 'type': 'square'}
self.cuts = apviz.AsymmetricPercentileInterval(1, 99)

self._cursor = ipw.HTML('Coordinates show up here')

self._init_mouse_callbacks()
self._init_watch_image_changes()
self.children = [self._astro_im, self._cursor]

def _init_mouse_callbacks(self):
@@ -448,6 +477,39 @@ def _mouse_move(self, event_data):
ra_dec = ''
self._cursor.value = ', '.join([pixel_location, ra_dec, value])

def _init_watch_image_changes(self):
"""
Watch for changes to the image scale, which indicate the user
has either changed the zoom or has panned, and update the zoom_level.
"""
def update_zoom_level(event):
"""
Watch for changes in the zoom level from the viewer.
"""

old_zoom = self.zoom_level
new_zoom = self._astro_im.get_zoom_level()
if new_zoom is None or self._updating_zoom:
# There is no image yet, or this object is in the process
# of changing the zoom, so return
return

# Do nothing if the zoom has not changed
if np.abs(new_zoom - old_zoom) > 1e-3:
# Let the zoom_level handler know the GUI itself
# generated this zoom change which means the GUI
# does not need to be updated.
self._zoom_source_is_gui = True
self.zoom_level = new_zoom

# Observe changes to the maximum of the x scale. Observing the y scale
# or the minimum instead of the maximum is also fine.
x_scale = self._astro_im._scales['x']

# THIS IS TERRIBLE AND MAKES THINGS SUPER LAGGY!!!! Needs to be
# throttled or something. Look at the ImageGL observe options.
x_scale.observe(update_zoom_level, names='max')

def _interval_and_stretch(self):
"""
Stretch and normalize the data before sending to the viewer.
@@ -466,6 +528,7 @@ def _interval_and_stretch(self):
def _send_data(self, reset_view=True):
self._astro_im.set_data(self._interval_and_stretch(),
reset_view=reset_view)
self.zoom_level = self._astro_im.get_zoom_level()

def _get_interval(self):
if self._interval is None:
@@ -539,8 +602,15 @@ def _observe_cuts(self, change):
@trait.observe('zoom_level')
def _update_zoom_level(self, change):
zl = change['new']

self._astro_im.set_zoom_level(zl)
if not self._zoom_source_is_gui:
# User has changed the zoom value so update the viewer
self._updating_zoom = True
self._astro_im.set_zoom_level(zl)
self._updating_zoom = False
else:
# GUI updated the value so do nothing except reset the source
# of the event
self._zoom_source_is_gui = False

def _currently_marking_error_msg(self, caller):
return (f'Cannot set {caller} while doing interactive '
20 changes: 19 additions & 1 deletion astrowidgets/tests/widget_api_test.py
Original file line number Diff line number Diff line change
@@ -85,8 +85,26 @@ def test_offset_by(self, data, wcs):

self.image.offset_by(10 * u.arcmin, 10 * u.arcmin)

def test_zoom_level(self):
def test_zoom_level_initial_value(self, data):
# With no data, value is zero? Or should it be undefined?
assert self.image.zoom_level == 0

self.image.load_array(data)

# After setting data the value should not be zero
assert self.image.zoom_level != 0

# In fact, for 100 x 100 data and a 250 x 100 image the zoom level
# should be 250 / 100
assert np.abs(self.image.zoom_level - 2.5) < 1e-4

def test_zoom_level(self, data):
# Set data first, since that is needed to determine zoom level
print(self.image.zoom_level)
self.image.load_array(data)
print(self.image.zoom_level)
self.image.zoom_level = 5
print(self.image.zoom_level)
assert self.image.zoom_level == 5

def test_zoom(self):