Skip to main content

Sigvue

Sigvue turns a file-backed scientific script into a local browser application. It does not impose separate processing and presentation stages.

If you can read and display your data, you already have the application:

def view(data, ui):
    products = analyze(data)
    with ui.tab("Results"):
        ui.plot(lambda: plot(products), key="results")

The author returns one Workspace. A reusable Reader handles discovery and exact buffering; one unrestricted view(data, ui) callback does everything after that.

Install and run

python -m pip install sigvue
sigvue --config browser.toml

Open http://127.0.0.1:8000.

The API

The API diagram

There is one application object:

Workspace(
    identifier="my-data",
    name="My Data",
    description="Inspect recordings.",
    reader=reader,
    view=view,
)

Inside view, the author decides what is shared, what is deferred, and whether processing and plotting are separate at all.

Minimal file-backed workspace

from pathlib import Path

import plotly.graph_objects as go

from sigvue import Files, Workspace


def open_samples(path: Path) -> tuple[float, ...]:
    return tuple(float(value) for value in path.read_text().split(","))


def view(samples, ui):
    gain = float(ui.number("gain", default=2.0, minimum=0.0))
    values = tuple(value * gain for value in samples)

    ui.stat("Samples", len(samples))
    with ui.tab("Values"):
        ui.plot(
            lambda: go.Figure(go.Scatter(y=values)),
            key="values",
        )


def create_workspace(config=None):
    return Workspace(
        identifier="values",
        name="Values",
        description="Inspect sample files.",
        reader=Files("data", "*.samples", open_samples),
        view=view,
    )

There is no framework boundary between multiplication and plotting. The callback can call one combined function just as easily:

def view(samples, ui):
    with ui.tab("Values"):
        ui.plot(lambda: process_and_plot(samples), key="values")

That lambda is lazy. Hidden tabs and switcher choices are not executed.

Headless data access

Reader.discover() returns native author-owned references. Paths stay Path objects; grouped collections and database keys stay domain objects.

reader = Files("data", "*.samples", open_samples)
path = reader.discover()[0]
samples = reader.load(path)
figure = process_and_plot(samples)

The same reader used by the browser is useful in scripts, notebooks, tests, and batch jobs.

Windowed data

reader = Files(root, "*.bin", open_recording).windowed(
    read_window,  # read_window(recording, start, stop)
    duration=lambda recording: recording.duration_seconds,
    default=0.100,
    minimum=0.010,
    step=0.010,
    overview=power_overview,
    overview_label="Median power",
)

Headlessly:

window = reader.load(path, start=2.0, stop=2.1)

Opened recordings, recent exact buffers, and overviews are revision-aware cached for repeated browser requests. Reader buffering never approximates scientific data.

Segmented data

from sigvue import Segment

reader = Files(root, "*.json", open_collection).segmented(
    read_segment,  # read_segment(collection, selected_segment)
    duration=lambda collection: collection.duration,
    segments=lambda collection: tuple(
        Segment(event.id, event.start, event.duration, event.label)
        for event in collection.events
    ),
)

The callback receives the selected Segment, so irregular events and scans retain their identity.

Seek and live playback

reader = Files(root, "*.bin", open_recording).playback(
    read_window,
    duration=lambda recording: recording.duration_seconds,
    default=0.020,
    minimum=0.001,
    maximum=0.100,
    buffer_step=0.001,
    mode="live",
    seek_step=0.010,
    refresh_interval=1.0,
)

This supplies an exact moving buffer. When maximum is given, the browser also exposes its width.

Application-specific buffering

If a domain needs coupled controls that do not fit the regular helpers, the reader boundary stays open:

reader = Files(root, "*.collection", open_collection).buffered(
    read_buffer,       # ordinary headless read(opened, ...)
    select_buffer,     # select_buffer(opened, ui)
)

reader.load(reference, ...) calls read_buffer directly. The browser calls select_buffer, which may declare buffer controls and then use that same exact read. This does not introduce another processing or presentation stage.

Processing, caching, and lazy work

Sigvue does not decide how the callback is divided.

Direct processing is ordinary Python:

def view(data, ui):
    threshold = float(ui.number("threshold", default=3.0))
    events = detect_events(data, threshold)
    with ui.tab("Events"):
        ui.plot(lambda: plot_events(data, events), key="events")

For shared expensive work, ui.compute is an optional generic cache:

def view(data, ui):
    threshold = float(ui.number("threshold", default=3.0))
    events = ui.compute(
        "event-detection",
        lambda: detect_events(data, threshold),
    )
    with ui.tab("Events"):
        ui.plot(lambda: plot_events(data, events), key="events")
    with ui.tab("Table"):
        ui.table(lambda: event_rows(events), key="event-table")

By default, the cache key includes:

  • the reader/source revision;
  • the selected window, segment, or playback position;
  • every control declared before ui.compute.

The author may specify depends_on=(...) to narrow control dependencies, or ignore ui.compute entirely.

Slow work needed by only one view belongs inside that view:

def view(data, ui):
    with ui.tab("Overview"):
        ui.plot(lambda: plot_overview(data), key="overview")

    with ui.tab("Slow diagnostics"):
        ui.plot(
            lambda: analyze_and_plot_diagnostics(data),
            key="diagnostics",
        )

Opening Overview does not run the diagnostics.

Exact complex layouts

The same callback supports all current layouts without another abstraction:

def view(data, ui):
    reference = str(ui.select(
        "phase_reference",
        default="Channel 1",
        options=channel_names(data),
    ))
    calibrated = ui.compute(
        "calibrated-radar",
        lambda: calibrate(data, phase_reference=reference),
    )

    ui.stat("Channels", calibrated.channel_count)

    with ui.tab("Waterfall"):
        ui.view_switcher(
            ("Domain", "Channel"),
            waterfall_views(calibrated, ui),
            key="waterfall-domain",
            selector=("buttons", "dropdown"),
            axis_navigation="bounded",
        )

    with ui.tab("Calibration", columns=(1, 1)):
        ui.plot(lambda: phase_plot(calibrated), key="phase")
        ui.table(calibrated.calibration_rows, key="calibration")

Tabs, weighted grids, nested groups, multidimensional switchers, display controls, inline processing controls, tables, text, and deferred plots all stay in the one nested ui API.

Exact complex layouts diagram

Custom discovery metadata

Paths receive useful defaults. Custom references may provide describe:

from sigvue import DataResource, Reader

reader = Reader(
    discover=discover_sequences,
    open=open_sequence,
    describe=lambda sequence: DataResource(
        identifier=sequence.id,
        title=sequence.title,
        source=sequence,
        timestamp=sequence.timestamp,
        tags=("radar", sequence.station),
        summary={"scan_count": sequence.scan_count},
    ),
)

Optional DiscoveryColumn values on Workspace define typed catalog columns. Recursive readers preserve their relative directories as browser folders by default. Set flatten_discovery=True on Workspace to show every discovered item at the workspace root without changing its identifier, source, or reader.

Optional capabilities

Annotation, export, and batch support remain independent:

Workspace(
    ...,
    reader=reader,
    view=view,
    annotator=MyAnnotator(),
    exporter=MyExporter(),
    batch=MyBatchActions(),
    discovery_columns=MY_COLUMNS,
)

These contracts are imported directly from sigvue. Format-specific readers and capabilities belong with the application or examples, not in Sigvue core. Batch actions run independently of the current browser route. The notification bell shows queued and running actions, follows them while the user browses other workspaces or views, and retains their completed outputs for opening or path copying. Reloading the page reconnects to jobs still owned by the server.

Optional capabilities diagram

Configuration

A package exposes create_workspace(config):

[project.entry-points."sigvue.workspaces"]
my-analysis = "my_package.sigvue:create_workspace"

browser.toml chooses instances:

[browser]
title = "My data browser"

[[workspaces]]
use = "my-analysis"
id = "recordings"
name = "Recordings"

[workspaces.config]
data_root = "./data"

The factory reads that location with WorkspaceConfig(config).path("data_root"). The bundled examples deliberately provide no code-level fallback, so browser.toml is their single source of truth for data locations. The same factory may still be reused in several TOML entries with different roots and metadata.

When a profile contains exactly one enabled workspace, the browser opens that workspace's item discovery directly at /. Profiles with multiple workspaces retain the searchable workspace catalog. Workspace code and configuration are identical in both cases.

Runnable examples

Small examples live under examples/. The standalone examples distribution covers communications, LTE waterfalls, calibrated multi-channel radar, annotated ECG, weather radar, passive acoustics, seismology, stored events, and native planetary data.

python -m pip install -e ".[examples]"
python -m examples.scripts.generate_all
sigvue --config examples/browser.toml

PyPI documentation

PyPI does not render Mermaid. The release workflow renders the diagrams:

python scripts/build_pypi_readme.py --ref "v$VERSION"

scripts/puppeteer-ci.json supplies Chromium's CI sandbox arguments.

Development

python -m pip install -e ".[test,release]"
python -m pytest -q
python -m build
python -m twine check dist/*

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

sigvue-2026.44.tar.gz (123.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

sigvue-2026.44-py3-none-any.whl (98.6 kB view details)

Uploaded Python 3

File details

Details for the file sigvue-2026.44.tar.gz.

File metadata

  • Download URL: sigvue-2026.44.tar.gz
  • Upload date:
  • Size: 123.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.14

File hashes

Hashes for sigvue-2026.44.tar.gz
Algorithm Hash digest
SHA256 ba9e47cdb46dd9b37f3582a597d6dd2785d9be1ecf9d502d2b962da8eb279524
MD5 5e32479cf4f6d0e58288212b4e5b2948
BLAKE2b-256 ef2e1a2abe6859e9432649b26f01d5b771006f4515b6a14113fa03a79992d1c7

See more details on using hashes here.

File details

Details for the file sigvue-2026.44-py3-none-any.whl.

File metadata

  • Download URL: sigvue-2026.44-py3-none-any.whl
  • Upload date:
  • Size: 98.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.14

File hashes

Hashes for sigvue-2026.44-py3-none-any.whl
Algorithm Hash digest
SHA256 b17a9e73a6a6833cbd078bce3277e47c6e2563d28b83f4ce852688406f9a54fd
MD5 2ad0a9cb339b531a6556e40d7bff6b47
BLAKE2b-256 37752e494c95724be8b42ed15e067855b462fd8cd81f5064ebad94a4717f44ec

See more details on using hashes here.

Release history Release notifications | RSS feed

2026.78

2 files

2026.77

2 files

2026.76

2 files

2026.75

2 files

2026.74

2 files

2026.73

2 files

2026.72

2 files

2026.71

2 files

2026.70

2 files

2026.69

2 files

2026.68

2 files

2026.67

2 files

2026.66

2 files

2026.65

2 files

2026.64

2 files

2026.63

2 files

2026.62

2 files

2026.61

2 files

2026.60

2 files

2026.59

2 files

2026.58

2 files

2026.57

2 files

2026.56

2 files

2026.55

2 files

2026.54

2 files

2026.53

2 files

2026.52

2 files

2026.51

2 files

2026.50

2 files

2026.49

2 files

2026.48

2 files

2026.47

2 files

2026.46

2 files

2026.45

2 files

This release

2026.44 This release

2 files

2026.43

2 files

2026.42

2 files

2026.40

2 files

2026.39

2 files

2026.38

2 files

2026.37

2 files

2026.36

2 files

2026.35

2 files

2026.34

2 files

2026.33

2 files

2026.31

2 files

2026.30

2 files

2026.29

2 files

2026.28

2 files

2026.27

2 files

2026.26

2 files

2026.25

2 files

2026.24

2 files

2026.23

2 files

2026.22

2 files

2026.21

2 files

2026.20

2 files

2026.19

2 files

2026.18

2 files

2026.17

2 files

2026.16

2 files

2026.15

2 files

2026.14

2 files

2026.13

2 files

2026.12

2 files

2026.11

2 files

2026.10

2 files

2026.9

2 files

2026.8

2 files

2026.7

2 files

2026.6

2 files

2026.5

2 files

2026.4

2 files

2026.3

2 files

2026.2

2 files

2026.1

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page