Skip to content

visualdynamics.gui.report_runs_pane

report_runs_pane

The report editor's pieces for runs and chronology (report feature run_sections).

Two panels, shown only while the feature is on and the editor renders a project:

  • RunsPanel, in the pane when no block is selected, under the Report Features panel: a row per run with its record, axis, run, level, start and comment. A cell's tooltip says where its value came from (the file name, the comparison, an entry) or why it was not read. A cell typed in enters that field; an empty cell clears it. Under the table, Results for Every Run repeats the report's Results for each run it does not draw on yet.
  • ChronologyPanel, in the pane of the Chronology section: Draft from the Runs reads the records and offers a draft, which the page shows under the owed section marked as drafted. While a draft is on offer and the section is unwritten, the panel shows it and Accept Draft makes it the section's words, for the author to edit from there.

The panels decide nothing. Each change is emitted as requested, and operate makes it through the project's verbs (set_run_identity, report_runs, report_chronology, accept_chronology), so each is journaled as a script would make it, and refusals come from the core.

Classes:

Name Description
RunsPanel

The runs of the project, one row each, with their identities.

ChronologyPanel

Draft from the Runs, and the draft on offer with Accept Draft.

Functions:

Name Description
project_of

objects when it is a project (what the window hands the editor),

shown

Whether the runs panels belong in the pane: the report has the

operate

Make one runs-panel change through the project's verbs, and return

Classes

RunsPanel

RunsPanel(project: Any, message: str = '', parent: QWidget | None = None)

Bases: QWidget

The runs of the project, one row each, with their identities.

requested carries one change: {'op': 'run', 'name': record, 'field': field, 'value': text} for a cell typed in, or {'op': 'repeat_runs'} for Results for Every Run.

Source code in src/visualdynamics/gui/report_runs_pane.py
def __init__(self, project: Any, message: str = '',
             parent: QWidget | None = None) -> None:
    from ..core.runs import level_text
    from ..report.runs import histories, run_order

    super().__init__(parent)
    layout = QVBoxLayout(self)
    layout.setContentsMargins(0, 0, 0, 0)
    layout.setSpacing(3)
    layout.addWidget(QLabel('<b>Runs</b>'))
    intro = QLabel('Each time history is a run. What the file name or '
                   'the comparison could not give one way, enter here; '
                   'a cell left empty goes back to what the record '
                   'shows.')
    intro.setWordWrap(True)
    layout.addWidget(intro)
    names, found = run_order(project, histories(project),
                              project.object_groups)
    self.names: list[str] = names
    self.table: QTableWidget = QTableWidget(len(names), len(COLUMNS))
    self.table.setHorizontalHeaderLabels([title for title, _f
                                          in COLUMNS])
    self.table.verticalHeader().setVisible(False)
    self.table.setEditTriggers(
        QAbstractItemView.EditTrigger.DoubleClicked
        | QAbstractItemView.EditTrigger.EditKeyPressed
        | QAbstractItemView.EditTrigger.AnyKeyPressed)
    for row, name in enumerate(names):
        identity = found[name]
        for column, (_title, field) in enumerate(COLUMNS):
            if not field:
                item = QTableWidgetItem(name)
                item.setFlags(item.flags() & ~Qt.ItemFlag.ItemIsEditable)
                item.setToolTip('the time history')
            else:
                value = identity.value(field)
                why = identity.reason(field)
                if why:
                    text, tip = NOT_READ, f'not read: {why}; enter it'
                elif value in ('', None):
                    text, tip = '', 'not known; enter it'
                else:
                    text = (level_text(value).removesuffix(' dB')
                            if field == 'level_db' else str(value))
                    tip = f'from the {identity.source(field)}' \
                        if identity.source(field) != 'entered' \
                        else 'entered'
                item = QTableWidgetItem(text)
                item.setToolTip(tip)
                if why:
                    item.setForeground(Qt.GlobalColor.gray)
            self.table.setItem(row, column, item)
    self.table.resizeColumnsToContents()
    self.table.itemChanged.connect(self._edited)
    layout.addWidget(self.table)
    self.repeat_button: QPushButton = QPushButton('Results for Every Run')
    self.repeat_button.setToolTip(
        "Adds the report's Results for its first run again for each "
        "run it does not draw on yet, bound to that run's own objects")
    self.repeat_button.clicked.connect(
        lambda: self.requested.emit({'op': 'repeat_runs'}))
    layout.addWidget(self.repeat_button)
    self.message: QLabel = QLabel(message)
    self.message.setWordWrap(True)
    self.message.setVisible(bool(message))
    layout.addWidget(self.message)

ChronologyPanel

ChronologyPanel(offered: str = '', message: str = '', parent: QWidget | None = None)

Bases: QWidget

Draft from the Runs, and the draft on offer with Accept Draft.

requested carries {'op': 'draft_chronology'} or {'op': 'accept_chronology'}.

Source code in src/visualdynamics/gui/report_runs_pane.py
def __init__(self, offered: str = '', message: str = '',
             parent: QWidget | None = None) -> None:
    super().__init__(parent)
    layout = QVBoxLayout(self)
    layout.setContentsMargins(0, 0, 0, 0)
    layout.setSpacing(3)
    self.draft_button: QPushButton = QPushButton('Draft from the Runs')
    self.draft_button.setToolTip(
        'Reads every run and offers a chronology drafted from the '
        'records. The section stays owed until the draft is accepted '
        'or the section is written.')
    self.draft_button.clicked.connect(
        lambda: self.requested.emit({'op': 'draft_chronology'}))
    layout.addWidget(self.draft_button)
    self.draft_view: QPlainTextEdit | None = None
    self.accept_button: QPushButton | None = None
    if offered:
        told = QLabel('Drafted from the records, not yet accepted:')
        told.setWordWrap(True)
        layout.addWidget(told)
        self.draft_view = QPlainTextEdit(offered)
        self.draft_view.setReadOnly(True)
        layout.addWidget(self.draft_view)
        self.accept_button = QPushButton('Accept Draft')
        self.accept_button.setToolTip(
            "Makes the draft the section's words, to edit from there")
        self.accept_button.clicked.connect(
            lambda: self.requested.emit({'op': 'accept_chronology'}))
        layout.addWidget(self.accept_button)
    self.message: QLabel = QLabel(message)
    self.message.setWordWrap(True)
    self.message.setVisible(bool(message))
    layout.addWidget(self.message)

Functions:

project_of

project_of(objects: Any) -> Any

objects when it is a project (what the window hands the editor), otherwise None: the panels act through the project's verbs.

Source code in src/visualdynamics/gui/report_runs_pane.py
def project_of(objects: Any) -> Any:
    """`objects` when it is a project (what the window hands the editor),
    otherwise None: the panels act through the project's verbs."""
    from ..project import Project

    return objects if isinstance(objects, Project) else None

shown

shown(objects: Any, report: Any) -> bool

Whether the runs panels belong in the pane: the report has the feature on and the editor renders a project.

Source code in src/visualdynamics/gui/report_runs_pane.py
def shown(objects: Any, report: Any) -> bool:
    """Whether the runs panels belong in the pane: the report has the
    feature on and the editor renders a project."""
    from ..report.features import enabled
    from ..report.runs import FEATURE

    return (report is not None and FEATURE in enabled(report)
            and project_of(objects) is not None)

operate

operate(project: Any, report: Any, operation: dict, run_long: Callable[..., Any] | None = None) -> str

Make one runs-panel change through the project's verbs, and return what the panel should say afterwards ('' for nothing).

A refusal is returned as the message, and nothing changed. Drafting the chronology reads every record, so it runs through run_long (the window's worker) when one is given.

Source code in src/visualdynamics/gui/report_runs_pane.py
def operate(project: Any, report: Any, operation: dict,
            run_long: Callable[..., Any] | None = None) -> str:
    """Make one runs-panel change through the project's verbs, and return
    what the panel should say afterwards ('' for nothing).

    A refusal is returned as the message, and nothing changed. Drafting
    the chronology reads every record, so it runs through `run_long`
    (the window's worker) when one is given.
    """
    from ..core.progress import Cancelled

    name = project.name_of(report)
    op = operation.get('op')
    try:
        if op == 'run':
            field = str(operation['field'])
            value = operation.get('value', '')
            if field == 'level_db':
                value = _level(str(value))
            project.set_run_identity(operation['name'], **{field: value})
            return ''
        if op == 'repeat_runs':
            return '\n'.join(project.report_runs(name))
        if op == 'draft_chronology':
            if run_long is not None:
                run_long('Drafting the chronology from the runs',
                         project.report_chronology, name)
            else:
                project.report_chronology(name)
            return ('Drafted from the records. Read it, then accept it or '
                    'write the section yourself.')
        if op == 'accept_chronology':
            project.accept_chronology(name)
            return ''
    except (TypeError, ValueError) as refused:
        return str(refused)
    except Cancelled:
        return 'Drafting the chronology was cancelled; nothing was offered.'
    return ''