feat: disable Beamline tab when active pgroup is outside token pgroups
CI / lint (push) Skipped
CI / test (3.11) (push) Skipped
CI / test (3.12) (push) Skipped
CI / test (3.13) (push) Skipped
CI / test-with-beamline-plugins (pxi_bec) (push) Skipped
CI / test-with-beamline-plugins (pxii_bec) (push) Skipped
CI / test-with-beamline-plugins (pxiii_bec) (push) Skipped

Switching to a visitor pgroup means running a user experiment: grey out
the Beamline tab (tooltip: beamline scientists only), bring the
Experiment tab forward with Dataset path and Exp. Config. opened.
Gate applies on transitions only so manual tab choices survive the
1 Hz status ticks.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-13 14:35:20 +02:00
co-authored by Claude Fable 5
parent 869a66ff4b
commit c5a6a175bd
+26
View File
@@ -596,6 +596,9 @@ class MainWindow(QMainWindow):
# Tracks beamline-state TRANSITIONS for the default sample-tab switch
# (see _apply_default_sample_tab).
self._last_beamline_state: BeamlineStateEnum | None = None
# None = not yet evaluated, so the first status tick always applies
# the gate (the GUI may start while a visitor pgroup is active).
self._beamline_tab_gated: bool | None = None
# Wrapper for the left inset: QTabWidget ignores its own contents
# margins for the tab bar, so the padding lives one level up. Aligns
@@ -2578,10 +2581,33 @@ class MainWindow(QMainWindow):
else:
self.sample_lists_tabs.setCurrentIndex(0)
def _apply_pgroup_gate(self, pgroup: str | None) -> None:
# Switching to a pgroup outside the token's own (admin) pgroups means
# running a visitor's experiment: the Beamline tab is then off-limits,
# and the Experiment tab comes up with Dataset path + Exp. Config.
# open. Applied only on gate TRANSITIONS so a manual tab choice
# survives the 1 Hz status ticks.
gated = pgroup is not None and pgroup not in (self._decoded_token.pgroups or [])
if gated == self._beamline_tab_gated:
return
self._beamline_tab_gated = gated
self.left_column_tabs.setTabEnabled(0, not gated)
self.left_column_tabs.setTabToolTip(
0, "Only accessible for beamline scientists" if gated else ""
)
if gated:
self.left_column_tabs.setCurrentIndex(1)
# persist=False: programmatic open must not overwrite the user's
# saved per-panel collapse choice (same rule as the session gate).
for banner in self.data_collection.findChildren(TitleLabel):
if banner.text() in ("Dataset path", "Experiment configuration"):
banner.set_collapsed(False, persist=False)
@Slot(DAQStatusModel)
def update_daq_status(self, s: DAQStatusModel):
self._latest_daq_status = s
self._apply_session_gate(getattr(getattr(s, "session", None), "session", None))
self._apply_pgroup_gate(getattr(getattr(s, "session", None), "current_pgroup", None))
# Default tab only on state TRANSITIONS — a manual tab choice
# survives while the state stays put.