Skip to content

visualdynamics.gui.mixed_panel

mixed_panel

The mixed-mode readout: a sine-on-random or random-on-random record's data sheet, read in the sine view.

A random and sine project holds two requirements for one record, the random specification of the floor and the sine specification of the tones, and judges them in two reports. When the project beside the record holds both, this readout sits under the extraction's settings and reads them as one test (core.mixed_mode.judge_mixed): the tones tracked and taken out, the floor judged on what is left with the lines around each tone left out, each narrowband at its own degrees of freedom, and the total reported unjudged. It shows the data sheet, one row for the floor, the tones together and each tone in amplitude, each narrowband and the total, each with its tolerance and verdict, the settings that decide what the numbers mean, and a small spectrum of the residual over the reference with the left-out lines hatched, the same drawing plot_mixed_mode makes (plot.mixed_mode.draw_mixed_mode).

The channel, the width left out either side of a tone, the tracking band and the tones' tolerance are set here; the narrowbands are the floor's own (Specification.narrowbands). Reading a long record takes seconds, so the first reading of a record waits for the button; once the sheet is up, an edit reads again at once. It is a reading, not an act: nothing is made, and every number shown is the reading's.

Classes:

Name Description
MixedPlot

The residual over the reference, small, the left-out lines

MixedPanel

The settings, the button, then the data sheet and its spectrum.

Functions:

Name Description
status_line

A reading for the status line: the sheet in one line

Classes

MixedPlot

MixedPlot(parent: QWidget | None = None)

Bases: PreviewPlot

The residual over the reference, small, the left-out lines hatched: log on both axes, as a density is read.

Methods:

Name Description
show_reading

Draw reading, or clear the plot when it is None.

Source code in src/visualdynamics/gui/mixed_panel.py
def __init__(self, parent: QWidget | None = None) -> None:
    super().__init__(parent)
    self.items: list[Any] = []
Methods:
show_reading
show_reading(reading: Any, unit_system: Any) -> None

Draw reading, or clear the plot when it is None.

Source code in src/visualdynamics/gui/mixed_panel.py
def show_reading(self, reading: Any, unit_system: Any) -> None:
    """Draw `reading`, or clear the plot when it is None."""
    from ..plot.mixed_mode import draw_mixed_mode

    for item in self.items:
        self.plot.removeItem(item)
    self.items = []
    if reading is not None:
        self.items = draw_mixed_mode(self.plot, reading,
                                     unit_system=unit_system,
                                     colors=self.colors, legend=False,
                                     markers=False)

MixedPanel

MixedPanel(parent: QWidget | None = None)

Bases: QWidget

The settings, the button, then the data sheet and its spectrum.

Methods:

Name Description
show_mixed

Point the readout at history, read against floor (a random

settings

The reading's settings: 'channel', 'exclusion' (Hz either

apply_theme

Redraw the small plot and the sheet in a new theme's colors.

Source code in src/visualdynamics/gui/mixed_panel.py
def __init__(self, parent: QWidget | None = None) -> None:
    super().__init__(parent)
    grid = QGridLayout(self)
    grid.setContentsMargins(0, 4, 0, 0)
    grid.setHorizontalSpacing(8)
    grid.setVerticalSpacing(4)
    rule = QFrame()
    rule.setFrameShape(QFrame.Shape.HLine)
    rule.setFrameShadow(QFrame.Shadow.Sunken)
    grid.addWidget(rule, 0, 0, 1, 2)
    self.title: QLabel = QLabel('Mixed Mode')
    font = self.title.font()
    font.setBold(True)
    self.title.setFont(font)
    grid.addWidget(self.title, 1, 0, 1, 2)
    #: what is being read, and the reading last made of it
    self._history: Any = None
    self._floor: Any = None
    self._tones: Any = None
    self._unit_system: Any = None
    self._key: tuple | None = None
    self._colors: Mapping[str, str] | None = None
    self.reading: Any = None
    self._loading = False

    self.channel_box: QComboBox = QComboBox()
    self.channel_box.setToolTip(
        'The control channel read: one the floor is written at and '
        'the record holds')
    self.exclusion_box: DoubleSpinBox = DoubleSpinBox()
    self.exclusion_box.setRange(0.0, 100.0)
    self.exclusion_box.setDecimals(2)
    self.exclusion_box.setSingleStep(0.5)
    self.exclusion_box.setPrefix('±')
    self.exclusion_box.setSuffix(' Hz')
    self.exclusion_box.setValue(EXCLUSION)
    self.exclusion_box.setToolTip(
        'The width left out of the floor either side of each tone: '
        'wide enough to hold the window\'s spread of a tone at the '
        'line spacing. A floor feature inside it is not judged')
    self.band_box: DoubleSpinBox = DoubleSpinBox()
    self.band_box.setRange(0.01, 100.0)
    self.band_box.setDecimals(2)
    self.band_box.setSingleStep(0.1)
    self.band_box.setSuffix(' Hz')
    self.band_box.setValue(TRACKING_BAND)
    self.band_box.setToolTip(
        'The tracking band each tone is read through. A narrower band '
        'admits less of the floor into a reading and takes longer to '
        'settle, about one over its width')
    self.tolerance_box: DoubleSpinBox = DoubleSpinBox()
    self.tolerance_box.setRange(0.1, 100.0)
    self.tolerance_box.setDecimals(1)
    self.tolerance_box.setSingleStep(1.0)
    self.tolerance_box.setPrefix('±')
    self.tolerance_box.setSuffix(' %')
    self.tolerance_box.setValue(TONE_TOLERANCE)
    self.tolerance_box.setToolTip(
        'The tolerance on each tone\'s amplitude, an example of what '
        'test documents ask for')
    self.names, self.editors = add_keyed_settings(grid, [
        ('channel', 'Channel', self.channel_box),
        ('exclusion', 'Left out', self.exclusion_box),
        ('band', 'Tracking band', self.band_box),
        ('tone_tolerance', 'Tone tolerance', self.tolerance_box)],
        first_row=2)
    self.read_button: QPushButton = QPushButton('Read Data Sheet')
    self.read_button.setToolTip(
        'Track the tones and take them out, then judge the floor, '
        'each tone, each narrowband and the total, each by its own '
        'rule')
    self.read_button.clicked.connect(self._read)
    #: how a reading is run, `runner(status, work)`: the window sets
    #: its `_run_long`, so the tracking and the averaging (seconds on
    #: a long record) run on the worker with the bar up and the
    #: window alive; called directly when None, as a script's panel
    #: is
    self.runner: Any = None
    grid.addWidget(self.read_button, 6, 0, 1, 2)
    #: the data sheet, as rich text
    self.sheet: QLabel = QLabel('')
    self.sheet.setTextFormat(Qt.TextFormat.RichText)
    self.sheet.setWordWrap(True)
    self.sheet.setAlignment(Qt.AlignmentFlag.AlignLeft)
    self.sheet.setTextInteractionFlags(
        Qt.TextInteractionFlag.TextSelectableByMouse)
    grid.addWidget(self.sheet, 7, 0, 1, 2)
    self.preview: MixedPlot = MixedPlot()
    self.preview.hide()
    grid.addWidget(self.preview, 8, 0, 1, 2)
    commit_on_enter(self.exclusion_box, self.band_box, self.tolerance_box)
    self.channel_box.currentIndexChanged.connect(self._edited)
    for box in (self.exclusion_box, self.band_box, self.tolerance_box):
        box.valueChanged.connect(self._edited)
Methods:
show_mixed
show_mixed(history: Any, floor: Any, tones: Any, unit_system: Any = None) -> Any

Point the readout at history, read against floor (a random specification) and tones (a sine specification, or None); hide, and return None, when there is no floor or it shares no channel with the record (principle 3). Shows the reading already held for the same record and settings; otherwise waits for the button. Returns the reading shown, or None.

Source code in src/visualdynamics/gui/mixed_panel.py
def show_mixed(self, history: Any, floor: Any, tones: Any,
               unit_system: Any = None) -> Any:
    """Point the readout at `history`, read against `floor` (a random
    specification) and `tones` (a sine specification, or None); hide,
    and return None, when there is no floor or it shares no channel
    with the record (principle 3). Shows the reading already held
    for the same record and settings; otherwise waits for the
    button. Returns the reading shown, or None."""
    dofs = [str(dof) for dof in history.response_dof]
    if floor is None:
        self.hide()
        self.reading = None
        return None
    channels = [str(dof) for dof in floor.response_dof if str(dof) in dofs]
    if not channels:
        self.hide()
        self.reading = None
        return None
    held = self.channel_box.currentText()
    same = (history is self._history and floor is self._floor
            and tones is self._tones)
    self._loading = True
    try:
        if [self.channel_box.itemText(i)
                for i in range(self.channel_box.count())] != channels:
            self.channel_box.clear()
            self.channel_box.addItems(channels)
        self.channel_box.setCurrentIndex(channels.index(held)
                                         if held in channels else 0)
        if not same:
            width = getattr(floor, 'tone_exclusion', None)
            self.exclusion_box.setValue(EXCLUSION if width is None
                                        else float(width))
    finally:
        self._loading = False
    self._history, self._floor, self._tones = history, floor, tones
    self._unit_system = unit_system
    self.show()
    if self._key is not None and self._key == self._key_now():
        self._show(self.reading)
    else:
        self.reading = None
        self._key = None
        self._show(None, 'Press Read Data Sheet to judge the floor, the '
                   'tones and the narrowbands, each by its own rule.')
    return self.reading
settings
settings() -> dict[str, Any]

The reading's settings: 'channel', 'exclusion' (Hz either side), 'band' (the tracking band's width, Hz) and 'tone_tolerance' (percent).

Source code in src/visualdynamics/gui/mixed_panel.py
def settings(self) -> dict[str, Any]:
    """The reading's settings: 'channel', 'exclusion' (Hz either
    side), 'band' (the tracking band's width, Hz) and
    'tone_tolerance' (percent)."""
    return {'channel': self.channel_box.currentText() or None,
            'exclusion': round(self.exclusion_box.value(), 6),
            'band': round(self.band_box.value(), 6),
            'tone_tolerance': round(self.tolerance_box.value(), 6)}
apply_theme
apply_theme(colors: Mapping[str, str]) -> None

Redraw the small plot and the sheet in a new theme's colors.

Source code in src/visualdynamics/gui/mixed_panel.py
def apply_theme(self, colors: Mapping[str, str]) -> None:
    """Redraw the small plot and the sheet in a new theme's colors."""
    self._colors = colors
    self.preview.apply_theme(colors)
    if self.reading is not None:
        self._show(self.reading)

Functions:

status_line

status_line(reading: Any, unit_system: Any = None) -> str

A reading for the status line: the sheet in one line (MixedReading.describe, in the display unit), then each of its notes, so a part not judged, or read where it may read low, says so where the result is read and not only on the sheet.

Source code in src/visualdynamics/gui/mixed_panel.py
def status_line(reading: Any, unit_system: Any = None) -> str:
    """A reading for the status line: the sheet in one line
    (`MixedReading.describe`, in the display unit), then each of its
    notes, so a part not judged, or read where it may read low, says so
    where the result is read and not only on the sheet."""
    from ..plot.mixed_mode import scales

    scale, _dense, unit = scales(reading.dimension, unit_system)
    words = reading.describe(unit, scale)
    return '; '.join([words, *reading.notes])