Skip to content

visualdynamics.plot.bands

bands

The bands of a specification, dragged on the plot.

A specification's warning and abort bands are read off the plot as shaded zones (plot._shade_limit_zones); while its sheet is open they are also set there. Each edge of each band — warning below and above, abort below and above — carries one handle per run of segments sharing a band, for the channel drawn; dragging a handle moves that edge by the decibels dragged, and lands when the drag ends (Brandon, 2026-09-06: the only place the bands are shown is the plot, the edit applies to every selected channel in that frequency range, and it lands when the drag stops). The handle knows nothing of channels or constraints: it reports (kind, side, segments, decibels) — the level the edge was let go at — and whoever opened the sheet lands it through the draft's with_band_edge, which puts every channel there under its own symmetric and uniform rules.

Functions:

Name Description
band_edges

The four band edges of one channel in display units, one piece

add_band_handles

One draggable handle per run of segments sharing a band, per

Functions:

band_edges

band_edges(
    draft: Any, channel: int, unit_system: Any
) -> dict[
    tuple[str, str],
    list[tuple[list[int], ndarray, ndarray]],
]

The four band edges of one channel in display units, one piece per run of segments sharing a band: (kind, side) -> [(segments, frequencies, levels)]. Each piece is its own band over its own span, corner to corner — so where the band steps at a breakpoint two pieces meet at one frequency at two levels, exactly as the shading does. Taken at the breakpoints instead, a corner belongs to the section on its right and the left section's line slanted to its neighbour's level (Brandon, 2026-09-06).

Source code in src/visualdynamics/plot/bands.py
def band_edges(draft: Any, channel: int, unit_system: Any
               ) -> dict[tuple[str, str], list[tuple[list[int], np.ndarray, np.ndarray]]]:
    """The four band edges of one channel in display units, one piece
    per run of segments sharing a band: `(kind, side) -> [(segments,
    frequencies, levels)]`. Each piece is its own band over its own
    span, corner to corner — so where the band steps at a breakpoint
    two pieces meet at one frequency at two levels, exactly as the
    shading does. Taken at the breakpoints instead, a corner belongs
    to the section on its right and the left section's line slanted
    to its neighbour's level (Brandon, 2026-09-06)."""
    f = np.asarray(draft.frequencies, dtype=float)
    density = f'{draft.dims[channel]}**2/frequency'
    target = np.asarray(unit_system.from_si(
        np.asarray(draft.levels[channel], dtype=float), density), dtype=float)
    edges: dict[tuple[str, str], list] = {}
    for kind in ('warning', 'abort'):
        for run in draft.band_runs(channel, kind):
            first, last = run[0], run[-1] + 1
            below, above = draft.bands[channel][kind][run[0]]
            span = target[first:last + 1]
            edges.setdefault((kind, 'lower'), []).append(
                (list(run), f[first:last + 1], span * 10 ** (below / 10.0)))
            edges.setdefault((kind, 'upper'), []).append(
                (list(run), f[first:last + 1], span * 10 ** (above / 10.0)))
    return edges

add_band_handles

add_band_handles(
    plot: Any,
    draft: Any,
    channel: int,
    unit_system: Any,
    colors: Any,
    on_release: Callable[
        [str, str, list[int], float], None
    ],
    on_menu: Callable[[Any], None] | None = None,
) -> list[Any]

One draggable handle per run of segments sharing a band, per edge, for the drawn channel. Returns the handles added.

Source code in src/visualdynamics/plot/bands.py
def add_band_handles(plot: Any, draft: Any, channel: int, unit_system: Any,
                     colors: Any,
                     on_release: Callable[[str, str, list[int], float], None],
                     on_menu: Callable[[Any], None] | None = None
                     ) -> list[Any]:
    """One draggable handle per run of segments sharing a band, per
    edge, for the drawn channel. Returns the handles added."""
    import pyqtgraph as pg
    from PySide6.QtCore import Qt
    from PySide6.QtGui import QColor

    box = plot.getViewBox()
    log_x, log_y = box.state['logMode']
    handles = []
    edges = band_edges(draft, channel, unit_system)
    for (kind, side), pieces in edges.items():
        for run, x, y in pieces:
            below, above = draft.bands[channel][kind][run[0]]
            colour = QColor(colors['limit_warning' if kind == 'warning'
                                   else 'exceed_over'])
            colour.setAlpha(160)
            handle = BandHandle(
                np.log10(x) if log_x else x, np.log10(y) if log_y else y,
                kind, side, list(run), log_y,
                below if side == 'lower' else above,
                on_release, on_menu,
                pen=pg.mkPen(colour, width=3,
                             style=Qt.PenStyle.DashLine))
            handle.setZValue(30)
            plot.addItem(handle, ignoreBounds=True)
            # the value, said on the plot: at the run's start, riding
            # with the handle (Brandon, 2026-09-06: the dB levels were
            # shown nowhere)
            label = pg.TextItem(handle.said(), color=colour,
                                anchor=(0.0, 1.0 if side == 'upper' else 0.0))
            label.setZValue(31)
            label.setPos(float(handle.xData[0]), float(handle.yData[0]))
            plot.addItem(label, ignoreBounds=True)
            handle.label = label
            handles.append(handle)
    return handles