Thanks to visit codestin.com
Credit goes to github.com

Skip to content

add colorbar to hlut tool, add pause_events() context manager #550

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

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all 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 fastplotlib/graphics/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from .image import ImageGraphic
from .text import TextGraphic
from .line_collection import LineCollection, LineStack
from .utils import pause_events


__all__ = [
Expand All @@ -12,4 +13,5 @@
"TextGraphic",
"LineCollection",
"LineStack",
"pause_events"
]
21 changes: 21 additions & 0 deletions fastplotlib/graphics/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
from contextlib import contextmanager

from ._base import Graphic


@contextmanager
def pause_events(*graphics: Graphic):
if not all([isinstance(g, Graphic) for g in graphics]):
raise TypeError(
f"`pause_events` only takes Graphic instances as arguments, "
f"you have passed the following types:\n{[type(g) for g in graphics]}"
)

original_vals = [g.block_events for g in graphics]

for g in graphics:
g.block_events = True
yield

for g, value in zip(graphics, original_vals):
g.block_events = value
135 changes: 93 additions & 42 deletions fastplotlib/widgets/histogram_lut.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
from math import ceil
import weakref

import numpy as np

from pygfx import Group
import pygfx

from ..graphics import LineGraphic, ImageGraphic, TextGraphic
from ..graphics import LineGraphic, ImageGraphic, TextGraphic, pause_events
from ..graphics._base import Graphic
from ..graphics.selectors import LinearRegionSelector

Expand Down Expand Up @@ -46,7 +47,7 @@ def __init__(

self._histogram_line = LineGraphic(line_data)

bounds = (edges[0], edges[-1])
bounds = (edges[0] * self._scale_factor, edges[-1] * self._scale_factor)
limits = (edges_flanked[0], edges_flanked[-1])
size = 120 # since it's scaled to 100
origin = (hist_scaled.max() / 2, 0)
Expand All @@ -63,8 +64,8 @@ def __init__(

# there will be a small difference with the histogram edges so this makes them both line up exactly
self._linear_region_selector.selection = (
self._image_graphic.vmin,
self._image_graphic.vmax,
self._image_graphic.vmin * self._scale_factor,
self._image_graphic.vmax * self._scale_factor,
)

self._vmin = self.image_graphic.vmin
Expand Down Expand Up @@ -94,7 +95,7 @@ def __init__(

self._text_vmax.world_object.material.pick_write = False

widget_wo = Group()
widget_wo = pygfx.Group()
widget_wo.add(
self._histogram_line.world_object,
self._linear_region_selector.world_object,
Expand All @@ -114,7 +115,39 @@ def __init__(
self._linear_region_handler, "selection"
)

self.image_graphic.add_event_handler(self._image_cmap_handler, "vmin", "vmax")
self.image_graphic.add_event_handler(self._image_cmap_handler, "vmin", "vmax", "cmap")

# colorbar for grayscale images
if self.image_graphic.data.value.ndim != 3:
self._colorbar: ImageGraphic = self._make_colorbar(edges_flanked)

self.world_object.add(self._colorbar.world_object)
else:
self._colorbar = None
self._cmap = None

def _make_colorbar(self, edges_flanked) -> ImageGraphic:
# use the histogram edge values as data for an
# image with 2 columns, this will be our colorbar!
colorbar_data = np.column_stack(
[np.linspace(edges_flanked[0], edges_flanked[-1], ceil(np.ptp(edges_flanked)))] * 2
).astype(np.float32)

colorbar_data /= self._scale_factor

cbar = ImageGraphic(
data=colorbar_data,
vmin=self.vmin,
vmax=self.vmax,
cmap=self.image_graphic.cmap,
interpolation="linear",
offset=(-55, edges_flanked[0], -1)
)

cbar.world_object.world.scale_x = 20
self._cmap = self.image_graphic.cmap

return cbar

def _get_vmin_vmax_str(self) -> tuple[str, str]:
if self.vmin < 0.001 or self.vmin > 99_999:
Expand All @@ -135,6 +168,7 @@ def _fpl_add_plot_area_hook(self, plot_area):
self._histogram_line._fpl_add_plot_area_hook(plot_area)

self._plot_area.auto_scale()
self._plot_area.controller.enabled = True

def _calculate_histogram(self, data):
if data.ndim > 2:
Expand Down Expand Up @@ -205,27 +239,39 @@ def _linear_region_handler(self, ev):
def _image_cmap_handler(self, ev):
setattr(self, ev.type, ev.info["value"])

@property
def cmap(self) -> str:
return self._cmap

@cmap.setter
def cmap(self, name: str):
if self._colorbar is None:
return

with pause_events(self.image_graphic):
self.image_graphic.cmap = name

self._cmap = name
self._colorbar.cmap = name

@property
def vmin(self) -> float:
return self._vmin

@vmin.setter
def vmin(self, value: float):
self.image_graphic.block_events = True
self._linear_region_selector.block_events = True

# must use world coordinate values directly from selection()
# otherwise the linear region bounds jump to the closest bin edges
self._linear_region_selector.selection = (
value * self._scale_factor,
self._linear_region_selector.selection[1],
)
self.image_graphic.vmin = value

self.image_graphic.block_events = False
self._linear_region_selector.block_events = False
with pause_events(self.image_graphic, self._linear_region_selector):
# must use world coordinate values directly from selection()
# otherwise the linear region bounds jump to the closest bin edges
self._linear_region_selector.selection = (
value * self._scale_factor,
self._linear_region_selector.selection[1],
)
self.image_graphic.vmin = value

self._vmin = value
if self._colorbar is not None:
self._colorbar.vmin = value

vmin_str, vmax_str = self._get_vmin_vmax_str()
self._text_vmin.offset = (-120, self._linear_region_selector.selection[0], 0)
Expand All @@ -237,22 +283,19 @@ def vmax(self) -> float:

@vmax.setter
def vmax(self, value: float):
self.image_graphic.block_events = True
self._linear_region_selector.block_events = True

# must use world coordinate values directly from selection()
# otherwise the linear region bounds jump to the closest bin edges
self._linear_region_selector.selection = (
self._linear_region_selector.selection[0],
value * self._scale_factor,
)

self.image_graphic.vmax = value
with pause_events(self.image_graphic, self._linear_region_selector):
# must use world coordinate values directly from selection()
# otherwise the linear region bounds jump to the closest bin edges
self._linear_region_selector.selection = (
self._linear_region_selector.selection[0],
value * self._scale_factor,
)

self.image_graphic.block_events = False
self._linear_region_selector.block_events = False
self.image_graphic.vmax = value

self._vmax = value
if self._colorbar is not None:
self._colorbar.vmax = value

vmin_str, vmax_str = self._get_vmin_vmax_str()
self._text_vmax.offset = (-120, self._linear_region_selector.selection[1], 0)
Expand All @@ -275,15 +318,23 @@ def set_data(self, data, reset_vmin_vmax: bool = True):
self._linear_region_selector.limits = limits
self._linear_region_selector.selection = bounds
else:
# don't change the current selection
self.image_graphic.block_events = True
self._linear_region_selector.block_events = True
self._linear_region_selector.limits = limits
self.image_graphic.block_events = False
self._linear_region_selector.block_events = False
with pause_events(self.image_graphic, self._linear_region_selector):
# don't change the current selection
self._linear_region_selector.limits = limits

self._data = weakref.proxy(data)

if self._colorbar is not None:
self.world_object.remove(self._colorbar.world_object)

if self.image_graphic.data.value.ndim != 3:
self._colorbar: ImageGraphic = self._make_colorbar(edges_flanked)

self.world_object.add(self._colorbar.world_object)
else:
self._colorbar = None
self._cmap = None

# reset plotarea dims
self._plot_area.auto_scale()

Expand All @@ -300,14 +351,14 @@ def image_graphic(self, graphic):

if self._image_graphic is not None:
# cleanup events from current image graphic
self._image_graphic.remove_event_handler(self._image_cmap_handler)
self._image_graphic.remove_event_handler(self._image_cmap_handler, "vmin", "vmax", "cmap")

self._image_graphic = graphic

self.image_graphic.add_event_handler(self._image_cmap_handler)
self.image_graphic.add_event_handler(self._image_cmap_handler, "vmin", "vmax", "cmap")

def disconnect_image_graphic(self):
self._image_graphic.remove_event_handler(self._image_cmap_handler)
self._image_graphic.remove_event_handler(self._image_cmap_handler, "vmin", "vmax", "cmap")
del self._image_graphic
# self._image_graphic = None

Expand Down
2 changes: 1 addition & 1 deletion fastplotlib/widgets/image.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ def managed_graphics(self) -> list[ImageGraphic]:
def cmap(self) -> list[str]:
cmaps = list()
for g in self.managed_graphics:
cmaps.append(g.cmap.name)
cmaps.append(g.cmap)

return cmaps

Expand Down