From 50c08853d80d268520963352b0fe0684e9c0b31c Mon Sep 17 00:00:00 2001 From: Dawn Date: Fri, 4 Sep 2026 14:23:37 +0200 Subject: [PATCH 1/7] feat(gui): hide ML detections by default Fresh profiles start with the sample-camera ML box overlay off; the QSettings key samcam/show_detections still persists a user opt-in. Co-Authored-By: Claude Fable 5.1 --- src/aare/gui/main_window.py | 2 +- src/aare/gui/panels/samcam_panel.py | 2 +- src/aare/gui/widgets/camera_image.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/aare/gui/main_window.py b/src/aare/gui/main_window.py index aafb3bb9..6917a8c9 100644 --- a/src/aare/gui/main_window.py +++ b/src/aare/gui/main_window.py @@ -1545,7 +1545,7 @@ class MainWindow(QMainWindow): settings = QSettings("PSI", "AareGUI") # bool()/str() wraps: QSettings.value is typed "object" even with # type=..., so the wraps are runtime no-ops for the pyright gate. - show_detections = bool(settings.value("samcam/show_detections", True, type=bool)) + show_detections = bool(settings.value("samcam/show_detections", False, type=bool)) show_detection_polygons = bool( settings.value("samcam/show_detection_polygons", True, type=bool) ) diff --git a/src/aare/gui/panels/samcam_panel.py b/src/aare/gui/panels/samcam_panel.py index f4498d85..0b577a6e 100644 --- a/src/aare/gui/panels/samcam_panel.py +++ b/src/aare/gui/panels/samcam_panel.py @@ -86,7 +86,7 @@ class SamcamPanel(QWidget): # Overlay checkboxes, two columns to save vertical space; related # toggles share a row. self.show_detections_checkbox = QCheckBox("Show ML detections") - self.show_detections_checkbox.setChecked(True) # Default to checked + self.show_detections_checkbox.setChecked(False) # Default to checked self.show_detections_checkbox.toggled.connect(self.show_detections_changed.emit) self.show_detection_polygons_checkbox = QCheckBox("Show ML polygons") diff --git a/src/aare/gui/widgets/camera_image.py b/src/aare/gui/widgets/camera_image.py index b1a90dab..c57482ca 100644 --- a/src/aare/gui/widgets/camera_image.py +++ b/src/aare/gui/widgets/camera_image.py @@ -142,7 +142,7 @@ class SampleCameraImageLabel(QGraphicsView): self._helical_end = SmargonCoordinate() self._raster_alpha = 127 self._bounding_box = None - self._show_detections = True + self._show_detections = False self._show_detection_polygons = True self._show_target_point = True -- 2.54.0 From 85815278bb64986bd780becd5447f84f8126352d Mon Sep 17 00:00:00 2001 From: Dawn Date: Fri, 4 Sep 2026 14:40:28 +0200 Subject: [PATCH 2/7] feat: just mount the next sample in the queue list --- src/aare/gui/main_window.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/aare/gui/main_window.py b/src/aare/gui/main_window.py index 6917a8c9..5a3187e8 100644 --- a/src/aare/gui/main_window.py +++ b/src/aare/gui/main_window.py @@ -597,6 +597,11 @@ class MainWindow(QMainWindow): self.quick_unmount_button = QPushButton("⏏ Unmount", dewar_tab) self.quick_unmount_button.clicked.connect(lambda: self._on_manual_unmount_requested()) + # main_window.py, next to quick_unmount_button (~L597) + self.mount_next_button = QPushButton("⏭ Mount next", dewar_tab) + self.mount_next_button.clicked.connect(self._mount_next_from_queue) + # add to automation_row loop + self._queue_action_buttons as kind "next" + automation_row = QHBoxLayout() for w in ( self.job_list_panel.play_button, @@ -2395,6 +2400,19 @@ class MainWindow(QMainWindow): return "The hutch safety alarm is active. Mounting is blocked until it clears." return None + def _mount_next_from_queue(self) -> None: + """Manual step-through: mount queue head; if head already on gonio, pop it and mount the following one. + Pops only after /status confirms mount, so a failed robot move never loses a sample.""" + queue = self.job_list_panel.table_model.samples + mounted = getattr(self._latest_daq_status, "sample", None) + if queue and mounted is not None and mounted.db_id == queue[0].db_id: + self.job_list_panel.table_model.remove_sample(mounted.db_id) + queue = self.job_list_panel.table_model.samples + if not queue: + self._on_manual_unmount_requested() # or no-op; your call + return + self._on_manual_mount_requested(queue[0]) + def _on_manual_mount_requested(self, sample, reference: bool = False) -> None: """Pre-check the hutch before sending a manual mount to the server. -- 2.54.0 From 046720ae43e093cbd22f2d13a1543814fab15aaf Mon Sep 17 00:00:00 2001 From: Dawn Date: Fri, 4 Sep 2026 16:26:01 +0200 Subject: [PATCH 3/7] fix: max Chi should be 30 instead of 40 --- src/aare/gui/panels/smargon_panel.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/aare/gui/panels/smargon_panel.py b/src/aare/gui/panels/smargon_panel.py index 5e65966f..da20a50b 100644 --- a/src/aare/gui/panels/smargon_panel.py +++ b/src/aare/gui/panels/smargon_panel.py @@ -64,7 +64,8 @@ class SmargonPanel(QWidget): ) grid_layout.addWidget(QLabel("Chi", parent=self), 1, 0) - self.chi_enter = NumberLineEdit(-0.2, 40, decimals=1, parent=self) + # Vincent says Chi is limited to 0 to 30 + self.chi_enter = NumberLineEdit(-0.2, 30, decimals=1, parent=self) grid_layout.addWidget(self.chi_enter, 1, 1) grid_layout.addWidget(QLabel("°", parent=self), 1, 2) -- 2.54.0 From 416302224ce5c67746a84180462c7c16ae50054e Mon Sep 17 00:00:00 2001 From: Dawn Date: Sat, 5 Sep 2026 11:04:18 +0200 Subject: [PATCH 4/7] feat: 900 Hz at 6d and 120 Hz at 10s for data collection. Paint input correspondingly. --- .../gui/panels/rotation_data_collection.py | 52 +++++++++++- src/aare/gui/widgets/number_line_edit.py | 1 + tests/unit/gui/test_rotation_speed_limits.py | 81 +++++++++++++++++++ 3 files changed, 130 insertions(+), 4 deletions(-) create mode 100644 tests/unit/gui/test_rotation_speed_limits.py diff --git a/src/aare/gui/panels/rotation_data_collection.py b/src/aare/gui/panels/rotation_data_collection.py index 31e23f2b..f4e21b63 100644 --- a/src/aare/gui/panels/rotation_data_collection.py +++ b/src/aare/gui/panels/rotation_data_collection.py @@ -1,8 +1,9 @@ from pathlib import Path -from aarecommon.config.beamline import cfg_get +from aarecommon.config.beamline import cfg_get, mx_beamline from aarecommon.config.logger import setup_logger from aarecommon.math.diffraction_geometry import DiffractionGeometry +from aarecommon.models.beamline import MXBeamline from aarecommon.models.models import BeamlineStateEnum, DAQStatusModel from aarecommon.models.rotation_scan import RotationScanRequest from PySide6.QtCore import Qt, Signal, Slot @@ -15,6 +16,10 @@ from aare.gui.widgets.number_line_edit import DbOverrideLineEdit, NumberLineEdit logger = setup_logger(LOGGER_NAME) +MAX_OMEGA_SPEED_DEG_S = 500.0 +# 900 Hz at X06DA, 120 Hz at X10SA +MIN_EXP_TIME_S = {MXBeamline.X06DA: 1 / 900, MXBeamline.X10SA: 1 / 120} + def add_screening_to_path(path): p = Path(path) @@ -56,6 +61,7 @@ class RotationDataCollectionPanel(ScanSettingsPanel): self._omega = 0 self._dose_mgy = 0 self._total_time = 0.0 + self._min_exp_time_s = MIN_EXP_TIME_S.get(mx_beamline(), 0.0005) self._layout.addWidget(QLabel("Start angle", parent=self), 3, 0) self.start_angle = NumberLineEdit( @@ -97,7 +103,12 @@ class RotationDataCollectionPanel(ScanSettingsPanel): "daq.data_collection_settings.default_screening_settings.exp_time_s", 0.1 ) self.screening_image_time_enter = NumberLineEdit( - 0.0005, 10.0, default_screening_exp_time, decimals=4, parent=self, track_pending=True + self._min_exp_time_s, + 10.0, + default_screening_exp_time, + decimals=4, + parent=self, + track_pending=True, ) self._layout.addWidget(self.screening_image_time_enter, 7, 1, 1, 3) self._layout.addWidget(QLabel("s", parent=self), 7, 4) @@ -140,13 +151,12 @@ class RotationDataCollectionPanel(ScanSettingsPanel): self._layout.addWidget(self.image_angle, 12, 1, 1, 3) self._layout.addWidget(QLabel("°", parent=self), 12, 4) self._register_override_field(self.image_angle) - # TODO add protection on X10SA to prevent too short exposure time/ too high detector rep rate self._layout.addWidget(QLabel("Image time", parent=self), 13, 0) default_image_exp_time = cfg_get( "daq.data_collection_settings.default_rotation_settings.exp_time_s", 0.01 ) self.image_time_enter = DbOverrideLineEdit( - 0.0005, 10.0, default=default_image_exp_time, decimals=4, parent=self + self._min_exp_time_s, 10.0, default=default_image_exp_time, decimals=4, parent=self ) self._layout.addWidget(self.image_time_enter, 13, 1, 1, 3) self._layout.addWidget(QLabel("s", parent=self), 13, 4) @@ -180,12 +190,44 @@ class RotationDataCollectionPanel(ScanSettingsPanel): self._layout.addWidget(self.abort_button, 18, 0, 1, 6) self._reset_to_defaults() + # Speed cap couples each angle/time pair: recompute limits whenever either commits. + self.screening_image_angle.newValue.connect(self._update_speed_limits) + self.screening_image_time_enter.newValue.connect(self._update_speed_limits) + self.image_angle.valueChanged.connect(self._update_speed_limits) + self.image_time_enter.valueChanged.connect(self._update_speed_limits) + self._update_speed_limits() + + @Slot() + def _update_speed_limits(self): + for angle, time, angle_max in ( + (self.screening_image_angle, self.screening_image_time_enter, 90.0), + (self.image_angle, self.image_time_enter, 10.0), + ): + angle.update_limits(0, min(angle_max, MAX_OMEGA_SPEED_DEG_S * time.value)) + time.update_limits(max(self._min_exp_time_s, angle.value / MAX_OMEGA_SPEED_DEG_S), 10.0) + + def _fields_out_of_range(self, *fields) -> bool: + """True (plus error box) when a field sits outside its current limits. + Needed because QDoubleValidator only paints the field red; ``.value`` + still reads the raw text, so without this gate the request goes out.""" + if all(getattr(f, "editor", f).hasAcceptableInput() for f in fields): + return False + msg = ( + "Image angle / Image time outside limits " + f"(max {MAX_OMEGA_SPEED_DEG_S:.0f} °/s, min image time {self._min_exp_time_s:.4f} s)" + ) + logger.error(msg) + QMessageBox.critical(None, "Error", msg) + return True + @Slot() def run_screening(self): if self._beamline_state != BeamlineStateEnum.SampleAlignment: logger.error(f"Beamline state {self._beamline_state} is not Sample Alignment") QMessageBox.critical(None, "Error", "Beamline state is not Sample Alignment") return + if self._fields_out_of_range(self.screening_image_angle, self.screening_image_time_enter): + return if not self.check_before_run(scan_kind="screening"): logger.error("Cannot run measurement because of check") return @@ -229,6 +271,8 @@ class RotationDataCollectionPanel(ScanSettingsPanel): logger.error(f"Beamline state {self._beamline_state} is not Sample Alignment") QMessageBox.critical(None, "Error", "Beamline state is not Sample Alignment") return + if self._fields_out_of_range(self.image_angle, self.image_time_enter): + return if not self.check_before_run(scan_kind="rotation"): logger.error("Cannot run measurement because of check") return diff --git a/src/aare/gui/widgets/number_line_edit.py b/src/aare/gui/widgets/number_line_edit.py index 835070f0..13584a64 100644 --- a/src/aare/gui/widgets/number_line_edit.py +++ b/src/aare/gui/widgets/number_line_edit.py @@ -126,6 +126,7 @@ class NumberLineEdit(QLineEdit): self.setToolTip( f"Minimum: {self.to_string(min_val):s}\nMaximum: {self.to_string(max_val):s}" ) + self.on_text_changed(self.text()) def validate(self, text) -> bool: return self.range_validator.validate(str(text), 0)[0] == QDoubleValidator.State.Acceptable diff --git a/tests/unit/gui/test_rotation_speed_limits.py b/tests/unit/gui/test_rotation_speed_limits.py new file mode 100644 index 00000000..a0f652de --- /dev/null +++ b/tests/unit/gui/test_rotation_speed_limits.py @@ -0,0 +1,81 @@ +"""Omega speed cap (500 deg/s) and per-beamline min image time on the rotation panel. + +Why: QDoubleValidator only paints a field red, so the interlock lives in the +linked limits plus the Run-button gate. These fail if either half breaks. +""" + +import pytest +from aarecommon.math.diffraction_geometry import DiffractionGeometry +from aarecommon.models.models import BeamlineStateEnum +from PySide6.QtWidgets import QMessageBox + +from aare.gui.panels.rotation_data_collection import RotationDataCollectionPanel + + +def _panel(monkeypatch, beamline): + # mx_beamline() reads the env on every call, so set it before construction + monkeypatch.setenv("BEAMLINE", beamline) + diffraction = DiffractionGeometry( + energy_keV=12.0, + dtz_mm=150.0, + pixel_size_mm=0.075, + beam_center_pxl=(1000.0, 1000.0), + detector_size_pxl=(2000, 2000), + detector_description="Eiger 16M", + detector_serial_number="123", + poni_rot1_rad=0.0, + poni_rot2_rad=0.0, + ) + return RotationDataCollectionPanel(diffraction=diffraction) + + +def _editor(field): + # NumberLineEdit is the editor itself; DbOverrideLineEdit wraps one in .editor + return getattr(field, "editor", field) + + +def _commit(field, text): + _editor(field).setText(text) + _editor(field).on_editing_finished() + + +def _accepts(field, text) -> bool: + _editor(field).setText(text) + return _editor(field).hasAcceptableInput() + + +@pytest.mark.parametrize( + "beamline,too_short,ok", [("X06DA", "0.0010", "0.0012"), ("X10SA", "0.0050", "0.0090")] +) +def test_min_image_time_per_beamline(qapp, monkeypatch, beamline, too_short, ok): + panel = _panel(monkeypatch, beamline) + for field in (panel.image_time_enter, panel.screening_image_time_enter): + assert not _accepts(field, too_short) + assert _accepts(field, ok) + + +def test_speed_cap_couples_angle_and_time(qapp, monkeypatch): + panel = _panel(monkeypatch, "X06DA") + + _commit(panel.image_angle, "1.000") # -> time floor 1/500 = 0.002 s + assert not _accepts(panel.image_time_enter, "0.0010") + assert _accepts(panel.image_time_enter, "0.0020") + + _commit(panel.image_time_enter, "0.0100") # -> angle max 5 deg + assert not _accepts(panel.image_angle, "6.000") + assert _accepts(panel.image_angle, "4.000") + + +def test_run_blocked_while_field_red(qapp, monkeypatch): + panel = _panel(monkeypatch, "X06DA") + boxes = [] + monkeypatch.setattr(QMessageBox, "critical", lambda *a, **k: boxes.append(a)) + emitted = [] + panel.rotation_scan.connect(emitted.append) + panel._beamline_state = BeamlineStateEnum.SampleAlignment + + _editor(panel.image_time_enter).setText("0.0010") # below 1/900 s, stays red + panel.run_measurement() + + assert emitted == [] + assert len(boxes) == 1 -- 2.54.0 From cb368f87cd06172a05f39d2a1ce527b192c89091 Mon Sep 17 00:00:00 2001 From: Dawn Date: Sun, 6 Sep 2026 18:02:19 +0200 Subject: [PATCH 5/7] fix: increase baton hand over button size and make divider more visible --- src/aare/gui/styles.py | 19 +++++++++++++++---- src/aare/gui/widgets/baton_request_dialog.py | 17 ++++++++++++++--- 2 files changed, 29 insertions(+), 7 deletions(-) diff --git a/src/aare/gui/styles.py b/src/aare/gui/styles.py index 618f9a81..a3cf3852 100644 --- a/src/aare/gui/styles.py +++ b/src/aare/gui/styles.py @@ -118,6 +118,9 @@ BANNER_TAB_GAP = 6 # separator under the cursor fills with SEPARATOR_HINT. The rest/drag gate # lives in MainWindow.event(); the QSS :hover part picks the one separator. SEPARATOR_HINT = "rgba(168, 178, 192, 20%)" # scrollbar-track grey @50% +# Idle fill so the resize line is findable before the hover rest — 10% of the +# hover alpha. Any base QSS fill replaces the native dotted grip; accepted. +SEPARATOR_IDLE = "rgba(168, 178, 192, 2%)" SEPARATOR_HINT_DELAY_MS = 66 # int, used in code, not QSS # Theme-switch screenshot cross-fade duration (int ms, used in code). @@ -223,6 +226,7 @@ DARK_DISABLED = "#3c4a66" # disabled — disabled fills # Accents — gold is IDENTITY (titles, highlights), blue is ACTION (primary # buttons); the site keeps the two apart on purpose: DARK_ACCENT = "#e0913f" # gold +DARK_SEPARATOR_IDLE = "rgba(224, 145, 63, 10%)" # DARK_ACCENT @10%, idle resize line DARK_ACCENT_HOVER = "#eaa253" # accent2 — brighter gold DARK_ACCENT_FILL = "#89b4fa" # action blue DARK_ACCENT_FILL_HOVER = "#9ec2fb" # +10% white, derived (site has no step) @@ -1092,9 +1096,12 @@ def _sunrise_stylesheet(overrides: dict[str, str] | None = None) -> str: font-weight: 700; } - /* No base ::separator rule ON PURPOSE: any QSS fill would replace the - native dotted-grip drawing, and the dots (visible in the dark theme, - which never styled separators) are wanted in both themes. */ + /* Idle resize lines: faint fill (10% of the hover alpha) so the drag + target is findable without hunting. This base fill replaces the + native dotted grip — traded away on purpose for discoverability. */ + QMainWindow::separator, QSplitter::handle { + background: $separator_idle; + } /* Resize-line hint — the separatorHint property is flipped by MainWindow.event() after a 1s hover rest or on press; :hover limits @@ -1749,7 +1756,11 @@ def _sunset_stylesheet() -> str: background: transparent; } - /* Resize-line hint, dark flavor — see the light-theme note. */ + /* Idle + hover resize lines, dark flavor — see the light-theme note. */ + QMainWindow::separator, QSplitter::handle { + background: $dark_separator_idle; + } + QMainWindow[separatorHint="true"]::separator:hover { background: $dark_accent; } diff --git a/src/aare/gui/widgets/baton_request_dialog.py b/src/aare/gui/widgets/baton_request_dialog.py index 1d0136b8..0bc9c919 100644 --- a/src/aare/gui/widgets/baton_request_dialog.py +++ b/src/aare/gui/widgets/baton_request_dialog.py @@ -28,6 +28,10 @@ from aare.gui.styles import ( WHITE, ) +# Tall touch targets for the three baton hand-over buttons (Accept / Refuse / +# Cancel Request) — operators hit these under time pressure. +_BUTTON_HEIGHT = "88px" + class BatonRequestDialog(QDialog): """ @@ -122,14 +126,18 @@ class BatonRequestDialog(QDialog): button_layout = QHBoxLayout() button_layout.setSpacing(20) + # Height lives in QSS, not setMinimumHeight(): the app stylesheet caps + # every QPushButton at 16px (styles.py) and Qt re-applies that cap via + # setMin/MaxHeight at polish, overwriting any value set here in code. self.accept_btn = QPushButton("✓ Accept") - self.accept_btn.setMinimumHeight(88) self.accept_btn.setStyleSheet(f""" QPushButton {{ background-color: {BATON_OK_BG}; color: {WHITE}; border: none; border-radius: 5px; + min-height: {_BUTTON_HEIGHT}; + max-height: {_BUTTON_HEIGHT}; font-weight: bold; font-size: {FONT_LABEL}; }} @@ -144,13 +152,14 @@ class BatonRequestDialog(QDialog): button_layout.addWidget(self.accept_btn) self.refuse_btn = QPushButton("✗ Refuse") - self.refuse_btn.setMinimumHeight(88) self.refuse_btn.setStyleSheet(f""" QPushButton {{ background-color: {BATON_DANGER_BG}; color: {WHITE}; border: none; border-radius: 5px; + min-height: {_BUTTON_HEIGHT}; + max-height: {_BUTTON_HEIGHT}; font-weight: bold; font-size: {FONT_LABEL}; }} @@ -304,13 +313,15 @@ class BatonPendingDialog(QDialog): button_layout = QHBoxLayout() self.cancel_btn = QPushButton("✗ Cancel Request") - self.cancel_btn.setMinimumHeight(88) + # See accept_btn in BatonRequestDialog for why height is QSS-only. self.cancel_btn.setStyleSheet(f""" QPushButton {{ background-color: {BATON_DANGER_BG}; color: {WHITE}; border: none; border-radius: 5px; + min-height: {_BUTTON_HEIGHT}; + max-height: {_BUTTON_HEIGHT}; font-weight: bold; font-size: {FONT_LABEL}; }} -- 2.54.0 From c32f708716c1fe090e9c3d446b4240b5a7a90ff8 Mon Sep 17 00:00:00 2001 From: Dawn Date: Sun, 6 Sep 2026 18:06:11 +0200 Subject: [PATCH 6/7] style: increase visibility of separator --- src/aare/gui/styles.py | 4 ++-- uv.lock | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/aare/gui/styles.py b/src/aare/gui/styles.py index a3cf3852..ed30b0cf 100644 --- a/src/aare/gui/styles.py +++ b/src/aare/gui/styles.py @@ -117,10 +117,10 @@ BANNER_TAB_GAP = 6 # one for SEPARATOR_HINT_DELAY_MS (or a drag starts) — then only the exact # separator under the cursor fills with SEPARATOR_HINT. The rest/drag gate # lives in MainWindow.event(); the QSS :hover part picks the one separator. -SEPARATOR_HINT = "rgba(168, 178, 192, 20%)" # scrollbar-track grey @50% +SEPARATOR_HINT = "rgba(168, 178, 192, 30%)" # scrollbar-track grey @50% # Idle fill so the resize line is findable before the hover rest — 10% of the # hover alpha. Any base QSS fill replaces the native dotted grip; accepted. -SEPARATOR_IDLE = "rgba(168, 178, 192, 2%)" +SEPARATOR_IDLE = "rgba(168, 178, 192, 10%)" SEPARATOR_HINT_DELAY_MS = 66 # int, used in code, not QSS # Theme-switch screenshot cross-fade duration (int ms, used in code). diff --git a/uv.lock b/uv.lock index 0a9c2ba6..a3bbd4b1 100644 --- a/uv.lock +++ b/uv.lock @@ -31,7 +31,7 @@ wheels = [ [[package]] name = "aaredaq" -version = "0.17.5" +version = "0.21.3" source = { editable = "." } dependencies = [ { name = "aarecommon" }, @@ -93,7 +93,7 @@ requires-dist = [ { name = "fastapi" }, { name = "gunicorn" }, { name = "httpx", specifier = ">=0.28.1" }, - { name = "jfjoch-client", specifier = "==1.0.0rc146" }, + { name = "jfjoch-client", specifier = ">=1.0.0rc165" }, { name = "matplotlib", specifier = ">=3.10.3" }, { name = "numpy" }, { name = "opencv-python-headless" }, @@ -1191,7 +1191,7 @@ wheels = [ [[package]] name = "jfjoch-client" -version = "1.0.0rc146" +version = "1.0.0rc166" source = { registry = "https://gitea.psi.ch/api/packages/mx/pypi/simple" } dependencies = [ { name = "pydantic" }, @@ -1199,9 +1199,9 @@ dependencies = [ { name = "typing-extensions" }, { name = "urllib3" }, ] -sdist = { url = "https://gitea.psi.ch/api/packages/mx/pypi/files/jfjoch-client/1.0.0rc146/jfjoch_client-1.0.0rc146.tar.gz", hash = "sha256:8e06d9671981d48b5ae0e60b0cbc0c32bdae7dfec72861d653e0338147da6fe8" } +sdist = { url = "https://gitea.psi.ch/api/packages/mx/pypi/files/jfjoch-client/1.0.0rc166/jfjoch_client-1.0.0rc166.tar.gz", hash = "sha256:697025dcbcf8bbce62c02948f0a3149a1198237260841dfd6951da7ec848fee1" } wheels = [ - { url = "https://gitea.psi.ch/api/packages/mx/pypi/files/jfjoch-client/1.0.0rc146/jfjoch_client-1.0.0rc146-py3-none-any.whl", hash = "sha256:d9014d756308ebf484e2eb09b5a77d9f51ecfa1a185fb623315026c23617c389" }, + { url = "https://gitea.psi.ch/api/packages/mx/pypi/files/jfjoch-client/1.0.0rc166/jfjoch_client-1.0.0rc166-py3-none-any.whl", hash = "sha256:b868a43f1713fb94db45b0f0d1192ce2182836c9ff1620cdf1bbbed91c14e863" }, ] [[package]] -- 2.54.0 From 9c489ffbb5a5577cb90794bf0547bb4b0bc8acbd Mon Sep 17 00:00:00 2001 From: Dawn Date: Mon, 7 Sep 2026 10:05:32 +0200 Subject: [PATCH 7/7] fix: lint error --- src/aare/gui/panels/rotation_data_collection.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/aare/gui/panels/rotation_data_collection.py b/src/aare/gui/panels/rotation_data_collection.py index f4e21b63..3899087a 100644 --- a/src/aare/gui/panels/rotation_data_collection.py +++ b/src/aare/gui/panels/rotation_data_collection.py @@ -217,7 +217,7 @@ class RotationDataCollectionPanel(ScanSettingsPanel): f"(max {MAX_OMEGA_SPEED_DEG_S:.0f} °/s, min image time {self._min_exp_time_s:.4f} s)" ) logger.error(msg) - QMessageBox.critical(None, "Error", msg) + QMessageBox.critical(self, "Error", msg) return True @Slot() -- 2.54.0