The findings register in the report editor's pane (report feature
findings).
Shown under section 9's words when the deviations section is selected
and the report has the findings register on. A list of the findings
entered, by kind and number, each with the parts it still owes; a kind
and Add to enter one; and, for the finding chosen, its run, channel,
band and status, its four parts in the author's words, and a row that
reads a draft of its fact off a comparison the project holds, with the
draft shown beside Accept and Discard until the author decides.
The pane decides nothing. Every change is emitted as requested, and
the editor makes it through report.findings, which refuses what
cannot be entered; the pane is built again to show the outcome, with
the refusal's words when there was one.
Classes:
| Name |
Description |
FindingsPane |
The register's list, the chosen finding's fields, and its draft.
|
Classes
FindingsPane
FindingsPane(entries: Sequence[Mapping[str, Any]], selected: str | None, measured: Sequence[str] = (), specifications: Sequence[str] = (), message: str = '', parent: QWidget | None = None)
Bases: QWidget
The register's list, the chosen finding's fields, and its draft.
requested carries one change as a dict with 'action': 'select',
'add' (with 'kind'), 'remove', 'edit' (with 'fields'), 'draft' (with
'measured', 'specification' and 'limits'), 'accept' or 'discard',
each but 'add' with the finding's 'number'.
Methods:
| Name |
Description |
flush |
Enter whatever has been typed in the parts and not entered
|
Source code in src/visualdynamics/gui/findings_pane.py
| def __init__(self, entries: Sequence[Mapping[str, Any]],
selected: str | None, measured: Sequence[str] = (),
specifications: Sequence[str] = (), message: str = '',
parent: QWidget | None = None) -> None:
super().__init__(parent)
self._loading = True
#: the register's findings, in its order, as `report.findings` reads them
self.entries: list[Mapping[str, Any]] = list(entries)
chosen = [entry for entry in self.entries
if entry['number'] == selected]
#: the finding whose fields are shown, or None
self.entry: Mapping[str, Any] | None = chosen[0] if chosen else None
layout = QVBoxLayout(self)
layout.setContentsMargins(0, 0, 0, 0)
layout.setSpacing(4)
layout.addWidget(QLabel('<b>Findings register</b>'))
intro = QLabel('Each finding in four parts. A blank part is owed '
'on the page; None above says a kind has none.')
intro.setWordWrap(True)
layout.addWidget(intro)
self.list: QListWidget = QListWidget()
for entry in self.entries:
owed = core.owed(entry)
label = f'{entry["number"]} {entry["kind"].capitalize()}'
if owed:
named = [core.FOURTH[entry['kind']].lower()
if part == 'approval' else part for part in owed]
label += f' (owes {", ".join(named)})'
if entry['draft'] is not None:
label += ' [draft waiting]'
item = QListWidgetItem(label)
item.setData(Qt.ItemDataRole.UserRole, entry['number'])
self.list.addItem(item)
if self.entry is not None:
self.list.setCurrentRow(self.entries.index(self.entry))
self.list.currentRowChanged.connect(self._chosen)
self.list.setMaximumHeight(120)
layout.addWidget(self.list)
adding = QHBoxLayout()
self.kind_box: QComboBox = QComboBox()
for kind in core.KINDS:
self.kind_box.addItem(kind.capitalize(), kind)
self.add_button: QPushButton = QPushButton('Add')
self.add_button.setToolTip('Enter a finding of this kind under the '
'next number of its kind')
self.add_button.clicked.connect(lambda: self.requested.emit(
{'action': 'add', 'kind': self.kind_box.currentData()}))
self.remove_button: QPushButton = QPushButton('Remove')
self.remove_button.setEnabled(self.entry is not None)
self.remove_button.clicked.connect(lambda: self._ask('remove'))
adding.addWidget(self.kind_box, 1)
adding.addWidget(self.add_button)
adding.addWidget(self.remove_button)
layout.addLayout(adding)
#: the chosen finding's line fields and part editors, by field
self.lines: dict[str, QLineEdit] = {}
self.parts: dict[str, QPlainTextEdit] = {}
self.status_box: QComboBox | None = None
self.measured_box: QComboBox | None = None
self.specification_box: QComboBox | None = None
self.limits_box: QComboBox | None = None
self.draft_button: QPushButton | None = None
self.accept_button: QPushButton | None = None
self.discard_button: QPushButton | None = None
self._timer: QTimer = QTimer(self)
self._timer.setSingleShot(True)
self._timer.setInterval(PART_DEBOUNCE_MS)
self._timer.timeout.connect(self.flush)
if self.entry is not None:
layout.addWidget(self._fields(self.entry, measured,
specifications))
self.note: QLabel = QLabel(message)
self.note.setWordWrap(True)
self.note.setStyleSheet('color: #c0392b')
self.note.setVisible(bool(message))
layout.addWidget(self.note)
self._loading = False
|
Methods:
flush
Enter whatever has been typed in the parts and not entered
yet. The pane does this itself once typing pauses for
PART_DEBOUNCE_MS, and the editor before anything else.
Source code in src/visualdynamics/gui/findings_pane.py
| def flush(self) -> None:
"""Enter whatever has been typed in the parts and not entered
yet. The pane does this itself once typing pauses for
`PART_DEBOUNCE_MS`, and the editor before anything else."""
self._timer.stop()
if self.entry is None:
return
changed = {part: edit.toPlainText()
for part, edit in self.parts.items()
if edit.toPlainText().strip() != self.entry[part].strip()}
if changed:
self._edit(changed)
|