new_gui bookmarks: editable labels + per-sample persistence
- each bookmark has an inline-editable label (rename signal) - bookmarks persist per-sample via QSettings (keyed by sample db_id): saved on add/remove/rename, reloaded when the same sample is remounted (and across GUI restarts) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
05421bf944
commit
c09c2e31e3
@@ -80,8 +80,10 @@ Backend contract is documented in the `new-gui-backend-paths` memory.
|
||||
`sample_missing` shows a modal (suppressed during automation).
|
||||
- **Bookmarks** (mounted): + Bookmark captures the current smargon position
|
||||
(shown as numbered markers on the camera, projected via `smargon_to_picture`);
|
||||
**Go** returns there (`move_smargon` + `set_omega`); **Collect** launches a
|
||||
rotation scan with `start` = the bookmark (data collection *from* that point).
|
||||
each has an **editable label**; **Go** returns there (`move_smargon` +
|
||||
`set_omega`); **Collect** launches a rotation scan with `start` = the bookmark
|
||||
(data collection *from* that point). Bookmarks **persist per-sample** (QSettings,
|
||||
keyed by sample db_id) so they survive unmount/remount and GUI restart.
|
||||
- **Mount / Unmount** → `mount(SampleShortInfo)` / `unmount()`.
|
||||
- **Motors** → `set_omega_rel`, `move_smargon`, `zoom`, `front_light`, `back_light`,
|
||||
and a staff-only **Energy** control → `change_energy` (keV).
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
|
||||
from PySide6.QtCore import Qt, Signal
|
||||
from PySide6.QtWidgets import (
|
||||
QButtonGroup,
|
||||
@@ -49,7 +51,8 @@ class ManualView(QWidget):
|
||||
self._grid_rect_img: tuple[float, float, float, float] | None = None
|
||||
self._raster_grids: list = []
|
||||
self._grids_dialog = None
|
||||
self._bookmarks: list = [] # [{coord: SmargonCoordinate, omega: float}]
|
||||
self._bookmarks: list = [] # [{coord, omega, label}]
|
||||
self._bookmark_loaded_key = None
|
||||
|
||||
root = QHBoxLayout(self)
|
||||
root.setContentsMargins(0, 0, 0, 0)
|
||||
@@ -167,6 +170,7 @@ class ManualView(QWidget):
|
||||
bm.bookmark_goto.connect(self._on_bookmark_goto)
|
||||
bm.bookmark_collect.connect(self._on_bookmark_collect)
|
||||
bm.bookmark_remove.connect(self._on_bookmark_remove)
|
||||
bm.bookmark_rename.connect(self._on_bookmark_rename)
|
||||
|
||||
# ------------------------------------------------------- run handlers
|
||||
def _on_run_center(self, method: str) -> None:
|
||||
@@ -307,6 +311,9 @@ class ManualView(QWidget):
|
||||
self.status_message.emit("Grid has no stored position to go to.")
|
||||
|
||||
# ----------------------------------------------------- bookmarks
|
||||
def _refresh_bookmark_bar(self) -> None:
|
||||
self.pipeline.bookmarks.set_bookmarks([b["label"] for b in self._bookmarks])
|
||||
|
||||
def _on_bookmark_add(self) -> None:
|
||||
from copy import deepcopy
|
||||
geom = getattr(self._last_status, "geom", None)
|
||||
@@ -315,9 +322,13 @@ class ManualView(QWidget):
|
||||
self.status_message.emit("No smargon position yet; cannot bookmark.")
|
||||
return
|
||||
omega = getattr(geom, "omega_deg", 0.0) or 0.0
|
||||
self._bookmarks.append({"coord": deepcopy(smg), "omega": float(omega)})
|
||||
self.pipeline.bookmarks.set_bookmarks(len(self._bookmarks))
|
||||
self._bookmarks.append({
|
||||
"coord": deepcopy(smg), "omega": float(omega),
|
||||
"label": f"Spot {len(self._bookmarks) + 1}",
|
||||
})
|
||||
self._refresh_bookmark_bar()
|
||||
self._project_bookmarks()
|
||||
self._save_bookmarks()
|
||||
|
||||
def _on_bookmark_goto(self, index: int) -> None:
|
||||
if 0 <= index < len(self._bookmarks):
|
||||
@@ -344,8 +355,53 @@ class ManualView(QWidget):
|
||||
def _on_bookmark_remove(self, index: int) -> None:
|
||||
if 0 <= index < len(self._bookmarks):
|
||||
del self._bookmarks[index]
|
||||
self.pipeline.bookmarks.set_bookmarks(len(self._bookmarks))
|
||||
self._refresh_bookmark_bar()
|
||||
self._project_bookmarks()
|
||||
self._save_bookmarks()
|
||||
|
||||
def _on_bookmark_rename(self, index: int, label: str) -> None:
|
||||
if 0 <= index < len(self._bookmarks):
|
||||
self._bookmarks[index]["label"] = label or f"Spot {index + 1}"
|
||||
self._save_bookmarks()
|
||||
|
||||
# ---- per-sample persistence (QSettings) ----
|
||||
def _bookmark_key(self) -> str | None:
|
||||
sample = self._state.mount_sample
|
||||
if sample is None:
|
||||
return None
|
||||
return f"bookmarks/{sample_id(sample)}"
|
||||
|
||||
def _save_bookmarks(self) -> None:
|
||||
from PySide6.QtCore import QSettings
|
||||
key = self._bookmark_key()
|
||||
if key is None:
|
||||
return
|
||||
data = []
|
||||
for b in self._bookmarks:
|
||||
coord = b["coord"]
|
||||
dump = coord.model_dump() if hasattr(coord, "model_dump") else None
|
||||
data.append({"coord": dump, "omega": b["omega"], "label": b["label"]})
|
||||
QSettings("PSI", "AareGUI-new").setValue(key, json.dumps(data))
|
||||
|
||||
def _load_bookmarks(self) -> None:
|
||||
from PySide6.QtCore import QSettings
|
||||
from aare.common.coordinate import SmargonCoordinate
|
||||
self._bookmarks = []
|
||||
key = self._bookmark_key()
|
||||
raw = QSettings("PSI", "AareGUI-new").value(key) if key else None
|
||||
if raw:
|
||||
try:
|
||||
for entry in json.loads(raw):
|
||||
coord = SmargonCoordinate.model_validate(entry["coord"])
|
||||
self._bookmarks.append({
|
||||
"coord": coord,
|
||||
"omega": float(entry.get("omega", 0.0)),
|
||||
"label": entry.get("label", "Spot"),
|
||||
})
|
||||
except Exception as exc:
|
||||
self.status_message.emit(f"Could not load bookmarks: {exc}")
|
||||
self._refresh_bookmark_bar()
|
||||
self._project_bookmarks()
|
||||
|
||||
def _project_bookmarks(self) -> None:
|
||||
from aare.gui.new_gui.widgets.bookmarks import BOOKMARK_COLORS
|
||||
@@ -381,11 +437,17 @@ class ManualView(QWidget):
|
||||
params = getattr(sample, "aaredb_params", None)
|
||||
if params is not None:
|
||||
self.pipeline.settings.set_db_params(params)
|
||||
# load this sample's saved bookmarks (once per sample)
|
||||
key = self._bookmark_key()
|
||||
if key != self._bookmark_loaded_key:
|
||||
self._bookmark_loaded_key = key
|
||||
self._load_bookmarks()
|
||||
if phase != "mounted":
|
||||
self.camera.clear_grid()
|
||||
self._grid_rect_img = None
|
||||
self._bookmarks = [] # bookmarks are per-sample
|
||||
self.pipeline.bookmarks.set_bookmarks(0)
|
||||
self._bookmarks = [] # cleared from view (persisted)
|
||||
self._bookmark_loaded_key = None
|
||||
self.pipeline.bookmarks.set_bookmarks([])
|
||||
self.camera.set_bookmarks([])
|
||||
self.pipeline.update_from_state(self._state)
|
||||
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
"""Data-collection bookmarks: mark smargon positions, return to them or collect.
|
||||
|
||||
A compact bar: "+ Bookmark" captures the current position; each saved bookmark
|
||||
is a chip with Go (move there), Collect (launch data collection from there) and
|
||||
remove. Markers are drawn on the camera by the Manual view.
|
||||
is a chip with an editable label, Go (move there), Collect (launch data
|
||||
collection from there) and remove. Markers are drawn on the camera by the
|
||||
Manual view. Bookmarks persist per-sample (see ManualView).
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from PySide6.QtCore import Qt, Signal
|
||||
from PySide6.QtWidgets import QHBoxLayout, QLabel, QPushButton, QWidget
|
||||
from PySide6.QtWidgets import QHBoxLayout, QLabel, QLineEdit, QPushButton, QWidget
|
||||
|
||||
# Bookmark colours (match the old SmargonBookmark palette).
|
||||
BOOKMARK_COLORS = ("#cf222e", "#1a7f37", "#4d8bff", "#6d4bd1", "#9bcc3f")
|
||||
@@ -19,6 +20,7 @@ class BookmarksBar(QWidget):
|
||||
bookmark_goto = Signal(int)
|
||||
bookmark_collect = Signal(int)
|
||||
bookmark_remove = Signal(int)
|
||||
bookmark_rename = Signal(int, str)
|
||||
|
||||
def __init__(self, palette, parent=None):
|
||||
super().__init__(parent)
|
||||
@@ -41,18 +43,18 @@ class BookmarksBar(QWidget):
|
||||
self._chips_from = self._lay.count() # where chips start
|
||||
self._lay.addStretch(1)
|
||||
|
||||
def set_bookmarks(self, count: int) -> None:
|
||||
# remove existing chips (keep label + add button + trailing stretch)
|
||||
def set_bookmarks(self, labels: list[str]) -> None:
|
||||
"""Rebuild chips from the list of labels (index = bookmark index)."""
|
||||
while self._lay.count() > self._chips_from + 1:
|
||||
item = self._lay.takeAt(self._chips_from)
|
||||
w = item.widget()
|
||||
if w:
|
||||
w.setParent(None)
|
||||
w.deleteLater()
|
||||
for i in range(count):
|
||||
self._lay.insertWidget(self._lay.count() - 1, self._make_chip(i))
|
||||
for i, label in enumerate(labels):
|
||||
self._lay.insertWidget(self._lay.count() - 1, self._make_chip(i, label))
|
||||
|
||||
def _make_chip(self, i: int) -> QWidget:
|
||||
def _make_chip(self, i: int, label: str) -> QWidget:
|
||||
p = self._p
|
||||
color = BOOKMARK_COLORS[i % len(BOOKMARK_COLORS)]
|
||||
chip = QWidget()
|
||||
@@ -69,6 +71,15 @@ class BookmarksBar(QWidget):
|
||||
f"background:{color}; color:#fff; border-radius:8px; font-size:10px;"
|
||||
f" font-weight:700;")
|
||||
cl.addWidget(dot)
|
||||
name = QLineEdit(label)
|
||||
name.setFixedWidth(96)
|
||||
name.setToolTip("Rename bookmark")
|
||||
name.setStyleSheet(
|
||||
f"QLineEdit {{ border:none; background:transparent; font-size:12px;"
|
||||
f" color:{p.text_primary}; }}")
|
||||
name.editingFinished.connect(
|
||||
lambda ix=i, le=name: self.bookmark_rename.emit(ix, le.text().strip()))
|
||||
cl.addWidget(name)
|
||||
go = self._mini("Go", p)
|
||||
go.clicked.connect(lambda _=False, ix=i: self.bookmark_goto.emit(ix))
|
||||
col = self._mini("Collect", p, accent=True)
|
||||
|
||||
Reference in New Issue
Block a user