Source code for blissoda.tomo.flint_tomo_imshow
from __future__ import annotations
import logging
import time
from collections import OrderedDict
from pathlib import Path
from typing import Optional
import numpy
import silx
from blissoda.tomo.utils import apply_labels
from blissoda.tomo.utils import compute_axes
from ..bliss_globals import current_session
from ..flint import capture_errors
from ..flint.plotter import BasePlotter
from ..import_utils import unavailable_class
try:
from flint.viewers.custom_image.client import ImageView
except ImportError as ex:
ImageView = unavailable_class(ex)
_logger = logging.getLogger(__name__)
[docs]
class SingleSliceImshow(BasePlotter):
"""
Manage a Flint window showing the most recent reconstructed slice with enhanced controls,
using the flint DataPlot–based ImageView.
"""
TITLE = "Last Reconstructed Slice"
def __init__(self, history: int = 1) -> None:
super().__init__(max_plots=history)
self._cache: OrderedDict[str, numpy.ndarray] = OrderedDict()
def _load_and_show(self, img_path):
with silx.io.open(img_path) as h5In:
img = h5In["entry0000/reconstruction/results/data"][:]
self.set_image(numpy.squeeze(img))
[docs]
def monitor_and_display_slice(self, output_path: str) -> None:
"""
Start monitoring the slice output directory and display the first reconstructed
slice as soon as an HDF5 file becomes available.
"""
output_path = Path(output_path)
output_dir = output_path.parent.parent / "slices"
file_prefix = output_path.stem
if len(list(output_dir.glob(f"{file_prefix}*_xy_*.h*5"))) > 0:
_logger.warning("Slice file already exists in %s", output_dir)
img_path = self._wait_for_slice_file(output_dir, file_prefix=file_prefix)
if img_path is not None:
self._load_and_show(img_path)
def _wait_for_slice_file(
self,
output_dir: Path,
file_prefix: str,
poll_s: float = 0.5,
timeout_s: float = 600.0,
) -> Optional[str]:
"""Wait for an HDF5 slice file to appear in the output directory."""
deadline = time.monotonic() + timeout_s
while time.monotonic() < deadline:
files = list(output_dir.glob(f"{file_prefix}*_xy_*.hdf5"))
time.sleep(poll_s)
if files:
return str(files[0])
_logger.warning("No slice HDF5 found in %s after %.1fs", output_dir, timeout_s)
return None
[docs]
@capture_errors
def set_image(self, image: numpy.ndarray) -> None:
"""
Display a new image with physical axis limits based on pixel size and image center.
"""
widget = self._get_plot(self.TITLE, ImageView)
self._set_title(widget)
x_axis, y_axis = compute_axes(image)
origin = (float(x_axis[0]), float(y_axis[0]))
dx = float(x_axis[1] - x_axis[0]) if len(x_axis) > 1 else 1.0
dy = float(y_axis[1] - y_axis[0]) if len(y_axis) > 1 else 1.0
scale = (dx, dy)
widget.set_data(image, origin=origin, scale=scale)
apply_labels(widget)
self._cache[self.TITLE] = image
self.purge_tasks()
self._purge()
def _set_title(self, widget: ImageView) -> None:
"""
Set the plot window title using the current data filename from scan saving.
"""
widget.title = current_session.scan_saving.data_filename
def _purge(self) -> None:
"""
Remove oldest images beyond history.
"""
while len(self._cache) > self._max_plots:
self._cache.popitem(last=False)