Scope Visual Feedback System

CIELAB perceptual colors and scope-based visual styling.

Modules: pyqt_reactive.widgets.shared.scope_color_, pyqt_reactive.widgets.shared.scope_visual_config*

Overview

The scope visual feedback system provides color-coded visual feedback based on hierarchical scope IDs. Each orchestrator (plate) gets a distinct base color, and steps within that orchestrator inherit the base color with tint/pattern variations.

Color Strategy Architecture

Colors are generated via pluggable strategies:

Strategy

Description

IndexBasedStrategy

Primary palette (12 colors) assigned in discovery order

MD5HashStrategy

Fallback using distinctipy palette (50 colors)

ManualColorStrategy

User-selected colors with persistence

ScopeColorService (singleton) manages strategy selection and caching.

CIELAB Perceptual Colors

Border tints use CIELAB color space for perceptual uniformity. L* values (lightness) are equidistant in perceptual space:

# Perceptually equidistant L* levels (0-100 scale)
BORDER_LAB_LIGHTNESS = (30.0, 55.0, 80.0)  # dark, mid, light

The tint_color_perceptual() function converts RGB → LAB, adjusts L* to target level while preserving a*/b* (chromatic channels), then converts back.

Scope Hierarchy

Orchestrator (plate_path)
├── Gets BASE color from palette (by discovery order)
└── Steps (plate_path::functionstep_N)
    └── Inherit BASE color, vary by tint index + border pattern

Border patterns for steps: 3 tints × 4 patterns = 12 visual combinations.

ScopeColorScheme

Computed color scheme for a scope:

@dataclass
class ScopeColorScheme:
    scope_id: Optional[str]
    hue: int
    orchestrator_item_bg_rgb: tuple[int, int, int]
    orchestrator_item_border_rgb: tuple[int, int, int]
    step_window_border_rgb: tuple[int, int, int]
    step_item_bg_rgb: Optional[tuple[int, int, int]]
    step_border_width: int
    step_border_layers: list  # [(width, tint_idx, pattern), ...]
    base_color_rgb: tuple[int, int, int]

Usage

from pyqt_reactive.widgets.shared.scope_color_utils import get_scope_color_scheme

# Get color scheme for a scope
scheme = get_scope_color_scheme("plate_path::functionstep_0")

# Access colors
bg_color = scheme.to_qcolor_orchestrator_bg()
border_color = scheme.to_qcolor_step_window_border()

# Or with explicit step index (for list item position)
scheme = get_scope_color_scheme("plate_path::functionstep_0", step_index=5)

ScopeVisualConfig

Static configuration for visual parameters:

@dataclass
class ScopeVisualConfig:
    ORCHESTRATOR_ITEM_BG_OPACITY: float = 0.10
    STEP_ITEM_BG_OPACITY: float = 0.10
    GROUPBOX_BG_OPACITY: float = 0.05
    STEP_WINDOW_BORDER_WIDTH_PX: int = 4
    FLASH_DURATION_MS: int = 300
    # ...

Access via get_scope_visual_config() singleton.

Integration with SccopedBorderMixin

Widgets use ScopedBorderMixin to apply scope-based border styling:

from pyqt_reactive.widgets.shared.scoped_border_mixin import ScopedBorderMixin

class StepEditorDialog(QDialog, ScopedBorderMixin):
    def __init__(self, scope_id: str):
        super().__init__()
        self._apply_scope_border(scope_id)

See Also