Skip to content

visualdynamics.gui.truncate_panel

truncate_panel

The truncation settings, as a table beside the time history.

Two times are set here, where the kept stretch starts and where it stops, and the plot shows the cut as it is made: the discarded ends are grayed over, and the kept span lies between two edges that can be dragged (plot.truncation), edited the same way as the averaging region.

The panel says what the settings are and reports when they change, and its Apply Truncation button makes the truncated record with whatever is set at that moment, through the project verb a script calls. This is the same split as in the averaging and filter panels, since these are also settings carried by the record. The button is disabled while the span covers the whole record.

Classes:

Name Description
TruncatePanel

The settings table. Edits are emitted as a whole Truncation.

Classes

TruncatePanel

TruncatePanel(parent: QWidget | None = None)

Bases: QWidget

The settings table. Edits are emitted as a whole Truncation.

Methods:

Name Description
show_history

Point the panel at a time history and the span set on it.

show_span

Point the panel at a span of time with no record behind it.

set_truncation

Show a truncation in the panel without emitting changed.

truncation

The truncation the editors currently describe.

Source code in src/visualdynamics/gui/truncate_panel.py
def __init__(self, parent: QWidget | None = None) -> None:
    super().__init__(parent)
    #: the record the parameters describe, for counting samples
    self._history = None
    #: the record's own first and last instants — the walls the
    #: span cannot leave
    self._first = 0.0
    self._last = 1.0
    #: the least daylight between start and stop: one sample
    #: period, or the boxes' own resolution where that is coarser
    self._gap = 1e-4
    #: the sample period, when the span has no record behind it
    #: to count samples off (an import window read off a preview)
    self._period = 0.0
    #: set while the panel is writing to its own editors, so that
    #: restating a clamped value does not read as a fresh edit
    self._loading = False

    self.title: QLabel = QLabel('Truncate')
    grid = panel_grid(self, self.title)

    self.start_box: DoubleSpinBox = DoubleSpinBox()
    self.start_box.setToolTip(
        'Where the kept stretch starts, on the record’s own clock. '
        'Drag the left edge of the span on the plot to set it by '
        'hand')
    self.stop_box: DoubleSpinBox = DoubleSpinBox()
    self.stop_box.setToolTip(
        'Where the kept stretch stops. Drag the right edge of the '
        'span on the plot to set it by hand')
    for box in (self.start_box, self.stop_box):
        box.setDecimals(4)
        box.setSuffix(' s')

    rows = (('Start', self.start_box), ('Stop', self.stop_box))
    add_settings(grid, rows)

    add_rule(grid, len(rows) + 1)

    # what follows from the two, shown so the cost of the cut is
    # visible while it is being made rather than after the record
    # comes out
    self.derived: dict[str, QLabel] = {}
    derived = (('record', 'Record'), ('kept', 'Kept'),
               ('samples', 'Samples'))
    for key, (_name, value) in add_derived(grid, derived,
                                           len(rows) + 2).items():
        self.derived[key] = value

    # the act itself, where its settings are set (Brandon,
    # 2026-08-28). Disabled while the span covers the whole
    # record — the cut of everything is a copy, and a button that
    # makes one is a button that does nothing. There is no
    # whole-record reset beside it (Brandon, 2026-08-28): the
    # boxes and the drag reach every span, this one included.
    self.apply_button: QPushButton = QPushButton('Apply Truncation')
    self.apply_button.setToolTip('Make a new object of the record cut to '
                                 'the span shown')
    self.apply_button.clicked.connect(self.apply_asked.emit)
    grid.addWidget(self.apply_button, len(rows) + 2 + len(derived),
                   0, 1, 2)
    grid.setRowStretch(len(rows) + 3 + len(derived), 1)

    commit_on_enter(self.start_box, self.stop_box)
    self.start_box.valueChanged.connect(self._edited)
    self.stop_box.valueChanged.connect(self._edited)
Methods:
show_history
show_history(history: TimeHistory, truncation: Truncation) -> None

Point the panel at a time history and the span set on it.

The limits a span cannot go past are the history's first and last times, not the numbers already in the boxes.

Source code in src/visualdynamics/gui/truncate_panel.py
def show_history(self, history: TimeHistory,
                 truncation: Truncation) -> None:
    """Point the panel at a time history and the span set on it.

    The limits a span cannot go past are the history's first and
    last times, not the numbers already in the boxes.
    """
    import numpy as np

    self._history = history
    abscissa = np.asarray(history.abscissa, dtype=float)
    self._first = float(abscissa[0])
    self._last = float(abscissa[-1])
    if len(abscissa) > 1:
        self._gap = max(float(abscissa[1] - abscissa[0]), 1e-4)
    self.set_truncation(truncation)
show_span
show_span(first: float, last: float, period: float, truncation: Truncation) -> None

Point the panel at a span of time with no record behind it.

This is for the import window, where the run is still in its file: the limits and the sample count come from its clock (the first and last times, and the period between samples) instead of from an abscissa that has not been read yet.

Source code in src/visualdynamics/gui/truncate_panel.py
def show_span(self, first: float, last: float, period: float,
              truncation: Truncation) -> None:
    """Point the panel at a span of time with no record behind it.

    This is for the import window, where the run is still in its
    file: the limits and the sample count come from its clock (the
    first and last times, and the `period` between samples) instead
    of from an abscissa that has not been read yet.
    """
    self._history = None
    self._period = float(period)
    self._first, self._last = float(first), float(last)
    self._gap = max(self._period, 1e-4)
    self.set_truncation(truncation)
set_truncation
set_truncation(truncation: Truncation) -> None

Show a truncation in the panel without emitting changed.

Source code in src/visualdynamics/gui/truncate_panel.py
def set_truncation(self, truncation: Truncation) -> None:
    """Show a truncation in the panel without emitting `changed`."""
    self._loading = True
    try:
        # the walls first, for the same reason every panel here
        # sets its limits first: a spin box clamps to the range it
        # has at the moment it is written
        self.start_box.setRange(self._first, self._last - self._gap)
        self.stop_box.setRange(self._first + self._gap, self._last)
        for box in (self.start_box, self.stop_box):
            box.setSingleStep(
                max((self._last - self._first) / 100.0, self._gap))
        self.start_box.setValue(truncation.start)
        self.stop_box.setValue(truncation.stop)
        self._constrain()
    finally:
        self._loading = False
    self._restate(self.truncation())
truncation
truncation() -> Truncation

The truncation the editors currently describe.

Source code in src/visualdynamics/gui/truncate_panel.py
def truncation(self) -> Truncation:
    """The truncation the editors currently describe."""
    return Truncation(self.start_box.value(), self.stop_box.value())

Functions: