astropy / astropy/astrowidgets

bqplot backend: `set_cuts`/`set_stretch` with a non-displayed `image_label` re-render the displayed image with the wrong settings

Open
#212 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
Python
Stars
34
Forks
19
Avg merge
1d 12h
Merged PRs (30d)
4

Description

The bqplot backend displays one image at a time, but its display-refresh path does not check whether the `image_label` being modified is the one on screen. Line references below are to `astrowidgets/bqplot.py` on current `main`.

## (a) Settings for a non-displayed image restyle the displayed image

`set_stretch` and `set_cuts` (bqplot.py:581-591) store the new value via the API layer and then call `_refresh_display(image_label=image_label)`. `_refresh_display` (bqplot.py:549-559) recomputes the *displayed* array (`self._data`, the last-loaded image's data) using the stored cuts/stretch of whatever label it is given, and sends the result to the viewer:

```python
def _refresh_display(self, image_label=None, reset_view=False):
if self._data is None or self._refresh_deferred:
return
self._send_data(cuts=self.get_cuts(image_label=image_label),
stretch=self.get_stretch(image_label=image_label),
reset_view=reset_view)
```

So a call targeting a non-displayed image applies that image's settings to the displayed one:

```python
viewer.load_image(img_a, image_label="a")
viewer.load_image(img_b, image_label="b") # "b" is displayed
viewer.set_cuts((0, 10), image_label="a")
# stored cuts for "a" update correctly, but the on-screen image "b"
# is immediately re-rendered with "a"'s cuts
```

The same applies to `set_stretch`.

## (b) `_current_image_label` is an implicit, fragile stand-in for "displayed image"

`_current_image_label` (bqplot.py:574-579) is derived as the last key of the API layer's image dict:

```python
@property
def _current_image_label(self):
return list(self._images.keys())[-1]
```

It is used as "the displayed image" when attributing GUI pan/zoom changes to a label (bqplot.py:500) and when carrying settings forward in `load_image` (bqplot.py:611). This only works because of undocumented `ImageViewerLogic` implementation details: `_images` is a `defaultdict` whose first key is a permanent `None` sentinel, and `load_image` happens to delete and re-insert an existing key so that insertion order tracks load order. An upstream change that updates an entry in place, or any accidental `defaultdict` lookup that materializes a key, silently breaks it.

## Relation to the ginga backend

This is the same class of bug that was found and fixed in the ginga backend in #205, where `set_cuts`/`set_stretch`/`set_colormap`/`set_viewport` for a non-displayed label clobbered the displayed image's state and `get_viewport` for a non-displayed label corrupted its stored viewport. That fix tracks a private `self._displayed_image_label`, set in `load_image`, and guards the apply/sync helpers on it.

## Proposed fix

Adopt the `displayed_image_label` concept proposed for `ImageViewerLogic` in astropy/astro-image-display-api#114: `load_image` records the resolved label, and `_refresh_display` only pushes to the viewer when the resolved label matches. If that proposal stalls, the bqplot backend can track it privately the way `ginga.py` does in #205; once upstream provides it, both backends can drop their private tracking.

## Existing issues

Not covered by the open bqplot issues: #208 (marker shapes/size rendering), #209 (bqplot 0.13.x update), #172 (marker properties info) are all about markers or library versions, not cross-label settings.

Contributor guide

No contributing guide indexed for this repository

Research direction

Start in astrowidgets/bqplot.py at _refresh_display, set_cuts, set_stretch, _current_image_label, and load_image. Compare the displayed-label tracking and guarded helpers in ginga.py from #205, while checking the displayed_image_label proposal in astropy/astro-image-display-api#114. Done means changing cuts or stretch for a non-displayed image no longer re-renders the displayed image with that image's settings.

Written by the indexing model from the issue text.

Assessment

Tech stack
jupyter-notebook, python
Domain
data-visualization, frontend
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
64/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.