Skip to content

Sweep usability: top-down channels, softer palette, fixed amplitude zoom, controls widget#3

Merged
cboulay merged 5 commits intomainfrom
cboulay/sweep_usability
Apr 20, 2026
Merged

Sweep usability: top-down channels, softer palette, fixed amplitude zoom, controls widget#3
cboulay merged 5 commits intomainfrom
cboulay/sweep_usability

Conversation

@cboulay
Copy link
Copy Markdown
Member

@cboulay cboulay commented Apr 20, 2026

Summary

Five targeted improvements to SweepWidget / ChannelPlotWidget driven by real usage in the intent-tools viewer apps. Each is a small, opt-in change with sensible defaults; no breaking changes to existing callers beyond the default-look adjustments noted below.

What changed

1. Top-down channel order (SweepBuffer, SweepConfig)

SweepConfig.channel_order: "top_down" | "bottom_up". Default flipped to
"top_down" — channel 0 at the top of the canvas, growing downward, which matches what most scientific multichannel viewers do. Existing callers can opt back in with channel_order="bottom_up".

SweepBuffer.set_channel_order() lets the host swap orientation at runtime without rebuilding the widget.

2. Softer default palette and thinner default line

SweepWidget now uses SOFT_CHANNEL_COLORS (lower-saturation 10-color palette) and DEFAULT_LINE_THICKNESS = 0.8 (down from 1.5). Way easier on the eyes when many channels are on screen.

SweepConfig grows two configurable fields:

  • colors: list | None — pass phosphor.constants.CHANNEL_COLORS for the legacy bright palette; None (default) uses the new soft palette.
  • line_thickness: float — defaults to DEFAULT_LINE_THICKNESS.

3. Per-row amplitude zoom (real fix for _zoom_amplitude)

The previous _zoom_amplitude scaled the camera's Y axis, which also scaled the channel-row offsets — channels visibly drifted apart or collapsed together as the user adjusted amplitude. Confusing and wrong.

Replaced with a per-row amplitude multiplier baked into SweepBuffer's data construction:

out_y = (data * y_scale - ch_mid) * amplitude_scale + ch_mid

ch_mid is the per-channel midpoint of the visible window's range, recomputed on every full rebuild. Subtracting it before the multiply means each channel scales around its own visual center, so DC bias doesn't translate the line as amplitude grows.

SweepConfig.amplitude_scale sets the initial multiplier; SweepBuffer.set_amplitude_scale(scale) is the runtime knob. ChannelPlotWidget._zoom_amplitude now routes to the buffer when it exposes set_amplitude_scale (sweep) and falls back to the legacy camera scaling for buffers that don't (scatter, spectrum).

4. ChannelPlotWidget.set_mouse_enabled(bool)

Detaches/reattaches the wheel-scroll and pointer-move (hover-tooltip) handlers without affecting keyboard shortcuts. For hosts that provide their own controls UI and don't want the plot's own mouse handlers competing.

5. New ChannelPlotControlsWidget

Optional Qt toolbar that mirrors the keyboard shortcuts already defined on ChannelPlotWidget: channel scroll up/down, page up/down, n_visible halve/double + spinbox, amplitude zoom in/out, time zoom in/out (only when the underlying widget exposes _time_zoom), and an autoscale toggle.

Tight default styling so it doesn't dominate the canvas. A 200 ms timer re-syncs widget state from the buffer so external mutations (keyboard, programmatic) keep the toolbar accurate.

Exported as phosphor.ChannelPlotControlsWidget.

Why this matters

Driven by intent-tools' intent-timeseries GUI feedback:

  • Operators expect channels top-to-bottom (matches CMP layouts and other viewers like Central).
  • Bright palette + 1.5 thickness was visually overwhelming with 64+ channels visible at once.
  • The old amplitude zoom was so confusing users avoided it; the new per-row scaling does what they actually want.
  • Click-driven controls are useful for users who don't memorize the keyboard shortcuts; the mouse toggle lets the host disable in-canvas mouse handling cleanly.

Backward compatibility

  • Default look changes: channel order is now top-down; default palette is softer; default line is thinner. Visible to existing users. Restore the old look with:
    from phosphor.constants import CHANNEL_COLORS
    SweepConfig(..., channel_order="bottom_up", colors=CHANNEL_COLORS, line_thickness=1.5)
  • New SweepConfig fields are all keyword-with-default; positional callers unaffected.
  • _zoom_amplitude semantics changed for SweepWidget only (now scales waveforms around channel midpoints instead of the camera). ScatterWidget and SpectrumWidget keep the legacy camera-scale behavior because their buffers don't expose set_amplitude_scale.
  • ChannelPlotControlsWidget is additive — opt-in.

cboulay added 5 commits April 20, 2026 01:24
Most scientific multichannel viewers put channel index 0 at the top of the canvas; phosphor was bottom-up. Add a SweepConfig.channel_order field ("top_down" | "bottom_up") that flips the per-channel Z indices in SweepBuffer._build_multiline_array. Default is "top_down" because that's what users expect; existing callers can pass "bottom_up" to opt back in.

set_channel_order() lets the host swap orientation at runtime; marks the column range dirty so the next frame rebuilds with the new layout.
…lors

The bright CHANNEL_COLORS palette is hard on the eyes when many channels are visible at once; the 1.5-thickness lines compound it. Switch the SweepWidget default to a lower-saturation palette (SOFT_CHANNEL_COLORS) and DEFAULT_LINE_THICKNESS = 0.8.

SweepConfig grows a `colors` field (None → soft default; pass constants.CHANNEL_COLORS for the legacy bright look) and a `line_thickness` field. Both are configurable so callers who liked the old look can opt back in.
The previous _zoom_amplitude scaled the camera's Y axis, which also rescaled the channel-row offsets — channels visibly drifted apart or collapsed together as the user adjusted amplitude. Replace with a per-row amplitude multiplier baked into SweepBuffer's data construction:

  out_y = (data * y_scale - ch_mid) * amplitude_scale + ch_mid

Where ch_mid is the per-channel midpoint of the visible window's range. Subtracting it before the multiply means each channel scales around its own visual center rather than around y=0, so DC bias doesn't translate the line as amplitude grows.

ChannelPlotWidget._zoom_amplitude routes to buffer.set_amplitude_scale when the buffer exposes it; other buffers (scatter, spectrum) keep the legacy camera-scale path.
When a host app provides its own controls widget (incoming), the plot's own mouse handlers (wheel-scroll for channel/zoom, hover tooltip) become redundant and sometimes interfere. Expose set_mouse_enabled(False) so hosts can detach them while keeping keyboard shortcuts active.
… shortcuts

Embeddable Qt toolbar exposing the keyboard shortcuts already defined on ChannelPlotWidget (channel scroll/page, n_visible halve/double + spinbox, amplitude zoom, time zoom for sweep/spectrum, autoscale toggle). Tight default styling so it doesn't dominate the canvas. Useful for hosts who prefer click-driven UI over memorized keys.

Periodically re-syncs widget state from the buffer so external mutations (keyboard, programmatic) keep the toolbar accurate.
@cboulay cboulay merged commit 66ad92d into main Apr 20, 2026
11 checks passed
@cboulay cboulay deleted the cboulay/sweep_usability branch April 20, 2026 05:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant