Show only the bundle's name in the sensor configuration row
The row carried the origin, the calibration selection and the SDK's verdict, which a 30 px line cannot explain; it is now the descriptor's display_name, else the folder name, and the sensor dialog states the rest under the selector — name and folder, profile, shape, root, verdict, calibration and every finding — with a suffix only where two rows would otherwise read alike. Verified with the studio suite, the catalog tests in shared/tests, ui_check and the lint gate.
This commit is contained in:
@@ -22,6 +22,7 @@ from orisys_app_shared.sensors import (
|
||||
SHAPE_COMPATIBILITY,
|
||||
SHAPE_DESCRIPTOR,
|
||||
SHAPE_UNREADABLE,
|
||||
STATE_UNREADABLE,
|
||||
)
|
||||
from PySide6.QtWidgets import (
|
||||
QDialog,
|
||||
@@ -42,6 +43,16 @@ _SHAPE_KEYS: dict[str, str] = {
|
||||
SHAPE_UNREADABLE: "sensor.catalog_shape_unreadable",
|
||||
}
|
||||
|
||||
#: The SDK's verdicts, and the catalog's own "nothing could be read here", as the sentence that
|
||||
#: says what the verdict means for the operator. Same rule as the shapes: a literal key per state,
|
||||
#: and an unknown state falls back to words rather than to the identifier the SDK happened to use.
|
||||
_STATE_KEYS: dict[str, str] = {
|
||||
"ok": "sensor.catalog_state_ok",
|
||||
"degraded": "sensor.catalog_state_degraded",
|
||||
"failed": "sensor.catalog_state_failed",
|
||||
STATE_UNREADABLE: "sensor.catalog_state_invalid",
|
||||
}
|
||||
|
||||
|
||||
class SensorDialog(QDialog):
|
||||
"""Sensor type / config picker plus the calibration facts that follow it."""
|
||||
@@ -62,6 +73,17 @@ class SensorDialog(QDialog):
|
||||
self.picker_row.addStretch(1)
|
||||
root.addLayout(self.picker_row)
|
||||
|
||||
#: Everything behind the selected configuration, said in full: the bundle's own name and
|
||||
#: the folder it lives in, the profile the SDK assigned, how the bundle was read, the
|
||||
#: root it came from, the verdict with what it means, the calibration selection and every
|
||||
#: finding — plus, when it must not be started, why. It sits directly under the selector
|
||||
#: on purpose: the picker row carries the name alone, so this is the surface the rest has
|
||||
#: to be readable on, without a hover and without hunting for it.
|
||||
self.profile_label = ShrinkableLabel()
|
||||
self.profile_label.setObjectName("panelMeta")
|
||||
self.profile_label.setWordWrap(True)
|
||||
root.addWidget(self.profile_label)
|
||||
|
||||
root.addWidget(self._section_rule())
|
||||
self.calib_title = QLabel()
|
||||
self.calib_title.setObjectName("panelTitle")
|
||||
@@ -72,12 +94,6 @@ class SensorDialog(QDialog):
|
||||
self.axes_label.setObjectName("panelMeta")
|
||||
self.config_label = ShrinkableLabel()
|
||||
self.config_label.setObjectName("panelMeta")
|
||||
#: What the catalog says the selected configuration *is*: family, profile, origin and
|
||||
#: the SDK's verdict — plus, when it must not be started, why. The picker can only fit
|
||||
#: a label on a 30 px rail; the reason needs this room.
|
||||
self.profile_label = ShrinkableLabel()
|
||||
self.profile_label.setObjectName("panelMeta")
|
||||
self.profile_label.setWordWrap(True)
|
||||
for label in (self.version_label, self.axes_label):
|
||||
root.addWidget(label)
|
||||
config_row = QHBoxLayout()
|
||||
@@ -88,7 +104,6 @@ class SensorDialog(QDialog):
|
||||
config_row.addWidget(self.btn_reveal)
|
||||
config_row.addStretch(1)
|
||||
root.addLayout(config_row)
|
||||
root.addWidget(self.profile_label)
|
||||
|
||||
root.addWidget(self._section_rule())
|
||||
self.export_title = QLabel()
|
||||
@@ -203,10 +218,11 @@ class SensorDialog(QDialog):
|
||||
def _refresh_profile(self) -> None:
|
||||
"""State the selected catalog entry, and the reason when it cannot be started.
|
||||
|
||||
This is the surface that keeps the facts the picker row no longer carries: the identity
|
||||
the SDK assigned, how the bundle was read and its validation. It is read from the picker
|
||||
rather than re-derived here, so the window and the dialog can never say two different
|
||||
things about one bundle.
|
||||
This is the surface that keeps the facts the picker row no longer carries: the name and
|
||||
the folder, the identity the SDK assigned, how the bundle was read, the root it was found
|
||||
in, the verdict with what it means, the calibration selection, and every finding as a
|
||||
sentence. It is read from the picker rather than re-derived here, so the window and the
|
||||
dialog can never say two different things about one bundle.
|
||||
"""
|
||||
read_entry = getattr(self.picker, "selected_entry", None)
|
||||
entry = read_entry() if callable(read_entry) else None
|
||||
@@ -214,19 +230,40 @@ class SensorDialog(QDialog):
|
||||
self.profile_label.setText("")
|
||||
self.profile_label.setToolTip("")
|
||||
return
|
||||
line = t(
|
||||
"sensor.catalog_profile",
|
||||
profile_id=entry.profile_id or entry.folder,
|
||||
shape=t(_SHAPE_KEYS.get(entry.shape, _SHAPE_KEYS[SHAPE_UNREADABLE])),
|
||||
origin=entry.origin,
|
||||
state=entry.state,
|
||||
)
|
||||
lines = [
|
||||
t("sensor.catalog_name", name=entry.name, folder=entry.folder),
|
||||
t(
|
||||
"sensor.catalog_profile",
|
||||
profile_id=entry.profile_id or entry.folder,
|
||||
shape=t(_SHAPE_KEYS.get(entry.shape, _SHAPE_KEYS[SHAPE_UNREADABLE])),
|
||||
origin=entry.origin,
|
||||
),
|
||||
t("sensor.catalog_root", origin=entry.origin, path=entry.bundle_path),
|
||||
t(
|
||||
"sensor.catalog_state",
|
||||
state=entry.state,
|
||||
note=t(_STATE_KEYS.get(entry.state, "sensor.catalog_state_unknown")),
|
||||
),
|
||||
(
|
||||
t("sensor.catalog_calibration_selected", name=entry.selected_calibration)
|
||||
if entry.selected_calibration
|
||||
else t("sensor.catalog_calibration_none")
|
||||
),
|
||||
]
|
||||
if entry.duplicates:
|
||||
lines.append(t("sensor.catalog_duplicate", refs=", ".join(entry.duplicates)))
|
||||
blocked = entry.start_blocked_reason
|
||||
if blocked:
|
||||
# In the label, not only the tooltip: a refused configuration has to say why without
|
||||
# being hovered over, and this is the surface that has room for the sentence.
|
||||
line = f"{line}\n{t('sensor.catalog_blocked', reason=blocked)}"
|
||||
self.profile_label.setText(line)
|
||||
lines.append(t("sensor.catalog_blocked", reason=blocked))
|
||||
lines.extend(
|
||||
t("sensor.catalog_error", sentence=text) for text in entry.error_sentences()
|
||||
)
|
||||
lines.extend(
|
||||
t("sensor.catalog_warning", sentence=text) for text in entry.warning_sentences()
|
||||
)
|
||||
self.profile_label.setText("\n".join(lines))
|
||||
self.profile_label.setToolTip(entry.tooltip())
|
||||
|
||||
def retranslate(self) -> None:
|
||||
|
||||
@@ -1,18 +1,22 @@
|
||||
"""Sensor type + config picker.
|
||||
|
||||
One row, because it lives at the left of the status rail: a badge naming the
|
||||
selected product, then the product/sensor choice and the config file that
|
||||
belongs to it. The order matters — picking ``box`` / ``mini`` / ``finger`` is
|
||||
what decides which configs exist, so the two sit next to each other with their
|
||||
own labels and tooltips instead of a bare "配置" combo.
|
||||
selected product, then the product/sensor choice and the configuration bundle
|
||||
that belongs to it. The order matters — picking ``box`` / ``mini`` / ``finger``
|
||||
is what decides which configurations exist, so the two sit next to each other
|
||||
with their own labels and tooltips instead of a bare "配置" combo.
|
||||
|
||||
Both combos are filled from the configuration catalog
|
||||
(:mod:`orisys_app_shared.sensors`), not from a hard-coded product list: the type
|
||||
combo offers the families bundles actually exist for, and the config combo offers
|
||||
the catalog's entries for the chosen family — each labelled with what it is, where
|
||||
it came from and what the calibration selection gives, so the choice is between
|
||||
products and outcomes rather than between ``profile_id`` strings. An entry the SDK
|
||||
rejects stays listed, because that is how its reason reaches the operator, and
|
||||
the catalog's entries for the chosen family. A row is the **bundle's name** — the
|
||||
descriptor's ``display_name``, else the folder name — and nothing else; the origin,
|
||||
the calibration selection and the SDK's verdict are read in the dialog, whose
|
||||
details surface sits directly below this row. The one exception is a collision: two
|
||||
entries offered together that would otherwise read identically (``shipped/box``
|
||||
beside ``sensors-user/box``) each carry the suffix that tells them apart, so no
|
||||
entry is collapsed or shadowed. An entry the SDK rejects stays listed too, because
|
||||
that is how its reason reaches the operator, and
|
||||
:meth:`SensorPickerWidget.start_blocked_reason` states why the current selection
|
||||
must not be started (see also :meth:`selected_entry`).
|
||||
"""
|
||||
@@ -186,7 +190,7 @@ class SensorPickerWidget(QWidget):
|
||||
self.config_combo.blockSignals(True)
|
||||
self.config_combo.clear()
|
||||
for entry in entries:
|
||||
self.config_combo.addItem(_entry_label(entry), entry.key)
|
||||
self.config_combo.addItem(_entry_label(self._catalog, entry), entry.key)
|
||||
self.config_combo.setItemData(
|
||||
self.config_combo.count() - 1,
|
||||
entry.tooltip(),
|
||||
@@ -238,12 +242,14 @@ def _family_label(family: str) -> str:
|
||||
return t("sensor.family_unidentified") if family == FAMILY_UNIDENTIFIED else family
|
||||
|
||||
|
||||
def _entry_label(entry: CatalogEntry) -> str:
|
||||
"""One combo item: the catalog's operator label, with nothing internal in it.
|
||||
def _entry_label(catalog: SensorCatalog, entry: CatalogEntry) -> str:
|
||||
"""One combo item: the catalog's row for an entry, with nothing internal in it.
|
||||
|
||||
The catalog names an entry by its display name, so the internal marker for "no product
|
||||
family" cannot reach the row today; it is still substituted here because a sentinel is
|
||||
never something an operator should read, and the item text is the one place it could
|
||||
surface if an entry ever had to fall back to its family.
|
||||
The row is the bundle's name, and the catalog appends the origin only where two visible
|
||||
entries would otherwise read alike, so the internal marker for "no product family" cannot
|
||||
reach it today; it is still substituted here, where translating belongs, because a sentinel
|
||||
is never something an operator should read and the item text is the one place it could
|
||||
surface.
|
||||
"""
|
||||
return entry.label().replace(FAMILY_UNIDENTIFIED, t("sensor.family_unidentified"))
|
||||
row = catalog.row_label(entry)
|
||||
return row.replace(FAMILY_UNIDENTIFIED, t("sensor.family_unidentified"))
|
||||
|
||||
Reference in New Issue
Block a user