Flash Animation System
Game engine-style O(1) per-window flash animations for UI feedback.
Module: openhcs.pyqt_gui.widgets.shared.flash_mixin
Overview
The flash animation system provides visual feedback when configuration values change. It uses a game engine architecture to achieve O(1) rendering per window regardless of how many elements are flashing.
Architecture
The system consists of three core components:
_GlobalFlashCoordinator (singleton): ONE 60fps timer for ALL windows
WindowFlashOverlay (per-window): Renders ALL flash rectangles in ONE paintEvent
FlashMixin (per-widget): API for registering elements and triggering flashes
┌─────────────────────────────────────────────────────────────┐
│ _GlobalFlashCoordinator │
│ ┌─────────────────┐ ┌──────────────────────────────────┐ │
│ │ _flash_start_ │ │ _computed_colors: Dict[key, QColor] │
│ │ times: Dict │ │ (pre-computed each tick) │ │
│ └─────────────────┘ └──────────────────────────────────┘ │
│ │ │
│ ▼ │
│ [60fps timer tick] │
│ │ │
└──────────────────────────────┼──────────────────────────────┘
│
┌──────────────────────┼──────────────────────┐
▼ ▼ ▼
┌───────────────┐ ┌───────────────┐ ┌───────────────┐
│WindowFlashOverlay│ │WindowFlashOverlay│ │WindowFlashOverlay│
│ (Window A) │ │ (Window B) │ │ (Window C) │
│ │ │ │ │ │
│ ONE paintEvent│ │ ONE paintEvent│ │ ONE paintEvent│
│ renders ALL │ │ renders ALL │ │ renders ALL │
│ flash rects │ │ flash rects │ │ flash rects │
└───────────────┘ └───────────────┘ └───────────────┘
Performance Model
Before (O(n) per tick):
Timer tick → compute N colors → store in dict → N widget repaints
After (O(1) per window):
Timer tick → compute colors once → prune expired → ONE overlay.update() per window
Each WindowFlashOverlay.paintEvent() renders all flash rectangles for its window
in a single paint call. Geometry is cached and only recomputed on scroll/resize.
Animation Phases
Flash animations have three phases with configurable durations:
fade_in (100ms): Quick fade-in with OutQuad easing
hold (50ms): Hold at maximum intensity
fade_out (350ms): Slow fade-out with InOutCubic easing
FlashElement Types
The system supports multiple element types via FlashElement dataclass:
Element Type |
Factory Function |
Use Case |
|---|---|---|
Groupbox |
|
Form section headers |
Tree Item |
|
Config hierarchy trees |
List Item |
|
Step/function lists |
Usage with FlashMixin
Widgets inherit FlashMixin (alias: VisualUpdateMixin) to participate:
from openhcs.pyqt_gui.widgets.shared.flash_mixin import FlashMixin
class MyWidget(QWidget, FlashMixin):
def __init__(self):
super().__init__()
self._init_flash_mixin()
def setup_flash(self, groupbox: QGroupBox):
# Register element for flashing
self.register_flash_groupbox("my_key", groupbox)
def trigger_flash(self):
# Trigger flash (global - all windows with this key)
self.queue_flash("my_key")
# Or local flash (this window only)
self.queue_flash_local("my_key")
Scope-Based Flash Keys
Flash keys are automatically scoped to prevent cross-window contamination:
# Key "well_filter" becomes "orchestrator::plate_1::well_filter"
scoped_key = self._get_scoped_flash_key("well_filter")
This ensures flashing step_0 in plate1 window doesn’t flash step_0 in plate2.
OpenGL Acceleration
On systems with OpenGL 3.3+, the system uses WindowFlashOverlayGL for GPU-accelerated
rendering via instanced draw calls. Falls back to QPainter automatically.
See Also
GUI Performance Patterns - Cross-window preview system
AbstractManagerWidget Architecture - AbstractManagerWidget uses FlashMixin