Skip to content
Open
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
37 changes: 37 additions & 0 deletions ndviewer_light/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -1484,6 +1484,7 @@ def __init__(self, dataset_path: str = ""):
self._plane_cache = MemoryBoundedLRUCache(PLANE_CACHE_MAX_MEMORY_BYTES)
self._updating_sliders: bool = False # Prevent recursive updates
self._acquisition_active: bool = False # True during live acquisition
self._auto_contrast_done: bool = False # Lock contrast after first FOV
self._time_play_timer: Optional[QTimer] = None # Timer for T slider animation
self._fov_play_timer: Optional[QTimer] = None # Timer for FOV slider animation
self._load_debounce_timer: Optional[QTimer] = (
Expand Down Expand Up @@ -1781,6 +1782,7 @@ def start_acquisition(
self._current_time_idx = 0
self._max_time_idx = 0
self._acquisition_active = True
self._auto_contrast_done = False

# Update sliders
self._updating_sliders = True
Expand Down Expand Up @@ -2187,6 +2189,41 @@ def _update_ndv_data(self, data):
# Fallback: full rebuild (shouldn't happen often)
self._set_ndv_data(xarr)

# After the first FOV is displayed, schedule locking contrast limits
# so subsequent FOVs don't re-autoscale.
if not self._auto_contrast_done:
self._auto_contrast_done = True # Prevent redundant scheduling
QTimer.singleShot(500, self._lock_contrast_limits)

def _lock_contrast_limits(self, _retries: int = 3):
"""Lock current contrast limits so subsequent FOVs don't re-autoscale.

Reads the auto-computed clims from each channel and switches them
to manual mode with the same values. Retries if clims aren't
computed yet (data may still be loading).
"""
if not self.ndv_viewer:
return
try:
from ndv.models._lut_model import ClimsManual

display = self.ndv_viewer._data_model.display
all_locked = True
for key, lut_model in display.luts.items():
cached = lut_model.clims.cached_clims
if cached is not None:
lut_model.clims = ClimsManual(min=cached[0], max=cached[1])
else:
all_locked = False
if all_locked:
logger.info("Contrast limits locked after first FOV")
elif _retries > 0:
QTimer.singleShot(500, lambda: self._lock_contrast_limits(_retries - 1))
else:
logger.warning("Could not lock contrast limits: clims not yet computed")
except (ImportError, AttributeError) as e:
logger.warning("Could not lock contrast limits: %s", e)

def end_acquisition(self):
"""Mark acquisition as ended.

Expand Down
Loading