refactor: add crosshair for center and live mode button
This commit is contained in:
@@ -81,8 +81,12 @@ ROI and mapping provenance:
|
||||
| `raw_roi_x`, `raw_roi_y` | Raw ROI origin in image coordinates. |
|
||||
| `raw_roi_width`, `raw_roi_height` | Raw ROI size in image coordinates. |
|
||||
| `correction_scale_x`, `correction_scale_y` | Scale factors used to convert ROI coordinates to positions. |
|
||||
| `correction_offset_x`, `correction_offset_y` | Offsets used after scaling. |
|
||||
| `correction_offset_x`, `correction_offset_y` | Physical positions at the mapping reference point. |
|
||||
| `correction_direction_x`, `correction_direction_y` | Direction signs, usually `1` or `-1`. |
|
||||
| `correction_reference_x`, `correction_reference_y` | Image coordinates used as the zero/reference point for the correction. New rows use the image center when image data exists, otherwise the ROI center. |
|
||||
|
||||
The mapping reference is reset to the image center only when a first image becomes available
|
||||
or the image shape changes. While no image data is available, it follows the ROI center.
|
||||
|
||||
Bookkeeping and compatibility fields:
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ from qtpy.QtWidgets import (
|
||||
QGridLayout,
|
||||
QGroupBox,
|
||||
QLabel,
|
||||
QLineEdit,
|
||||
QPushButton,
|
||||
)
|
||||
|
||||
@@ -238,6 +239,13 @@ class ROIMappingBox(QGroupBox):
|
||||
self.offset_y = spinbox(value=0.0, single_step=0.1, parent=self)
|
||||
self.direction_y = direction_combo(self)
|
||||
self.mapping_lock_toggle = ToggleSwitch(self, checked=False)
|
||||
self.reference_x = QLineEdit("0", self)
|
||||
self.reference_y = QLineEdit("0", self)
|
||||
for reference in [self.reference_x, self.reference_y]:
|
||||
reference.setObjectName("saxs_reference_field")
|
||||
reference.setReadOnly(True)
|
||||
reference.setAlignment(Qt.AlignmentFlag.AlignRight)
|
||||
reference.setToolTip("Image coordinate used as the correction offset reference.")
|
||||
|
||||
layout.addWidget(
|
||||
field_label("Edit lock", self), 0, 2, alignment=Qt.AlignmentFlag.AlignRight
|
||||
@@ -256,6 +264,11 @@ class ROIMappingBox(QGroupBox):
|
||||
layout.addWidget(self.offset_y, 3, 2)
|
||||
layout.addWidget(self.direction_y, 3, 3)
|
||||
|
||||
layout.addWidget(field_label("Reference", self), 4, 0)
|
||||
layout.addWidget(self.reference_x, 4, 1)
|
||||
layout.addWidget(self.reference_y, 4, 2)
|
||||
layout.addWidget(field_label("x / y", self), 4, 3)
|
||||
|
||||
self.apply_corrections_button = QPushButton("Apply corrections", self)
|
||||
self.apply_corrections_button.setIcon(
|
||||
material_icon("published_with_changes", size=(16, 16))
|
||||
@@ -265,7 +278,7 @@ class ROIMappingBox(QGroupBox):
|
||||
self.apply_corrections_button.setCursor(Qt.CursorShape.PointingHandCursor)
|
||||
self.apply_corrections_button.setFlat(True)
|
||||
self.apply_corrections_button.setProperty("saxsButtonRole", "toolbar")
|
||||
layout.addWidget(self.apply_corrections_button, 4, 0, 1, 4)
|
||||
layout.addWidget(self.apply_corrections_button, 5, 0, 1, 4)
|
||||
|
||||
self.apply_corrections_button.clicked.connect(self.apply_requested.emit)
|
||||
self.mapping_lock_toggle.stateChanged.connect(self.set_locked)
|
||||
@@ -279,8 +292,14 @@ class ROIMappingBox(QGroupBox):
|
||||
scale_y=self.scale_y.value(),
|
||||
offset_y=self.offset_y.value(),
|
||||
direction_y=int(self.direction_y.currentData() or 1),
|
||||
reference_x=float(self.reference_x.text() or 0.0),
|
||||
reference_y=float(self.reference_y.text() or 0.0),
|
||||
)
|
||||
|
||||
def set_reference_position(self, x: float, y: float) -> None:
|
||||
self.reference_x.setText(f"{x:.6g}")
|
||||
self.reference_y.setText(f"{y:.6g}")
|
||||
|
||||
def is_locked(self) -> bool:
|
||||
return self.mapping_lock_toggle.isChecked()
|
||||
|
||||
|
||||
@@ -232,6 +232,8 @@ class Correction:
|
||||
scale_y: float
|
||||
offset_y: float
|
||||
direction_y: int
|
||||
reference_x: float = 0.0
|
||||
reference_y: float = 0.0
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
@@ -397,6 +399,8 @@ class SAXSRow:
|
||||
"correction_scale_y": self.correction.scale_y,
|
||||
"correction_offset_y": self.correction.offset_y,
|
||||
"correction_direction_y": self.correction.direction_y,
|
||||
"correction_reference_x": self.correction.reference_x,
|
||||
"correction_reference_y": self.correction.reference_y,
|
||||
"scan_results": scan_results_json,
|
||||
# Compatibility aliases for older consumers.
|
||||
"step_size": geometry.fast_step,
|
||||
@@ -453,11 +457,13 @@ class SAXSRow:
|
||||
"raw_roi_width": "Raw ROI width in image coordinates.",
|
||||
"raw_roi_height": "Raw ROI height in image coordinates.",
|
||||
"correction_scale_x": "Scale factor applied from ROI X to physical X.",
|
||||
"correction_offset_x": "Offset applied after ROI X scaling.",
|
||||
"correction_offset_x": "Physical X position at correction_reference_x.",
|
||||
"correction_direction_x": "Direction sign for X mapping, typically +1 or -1.",
|
||||
"correction_scale_y": "Scale factor applied from ROI Y to physical Y.",
|
||||
"correction_offset_y": "Offset applied after ROI Y scaling.",
|
||||
"correction_offset_y": "Physical Y position at correction_reference_y.",
|
||||
"correction_direction_y": "Direction sign for Y mapping, typically +1 or -1.",
|
||||
"correction_reference_x": "Image coordinate that maps to correction_offset_x.",
|
||||
"correction_reference_y": "Image coordinate that maps to correction_offset_y.",
|
||||
"scan_results": "JSON string list of scan result objects with scan_number and success.",
|
||||
"step_size": "Compatibility alias of fast_step.",
|
||||
"step_size_x": "Compatibility alias of step size in X coordinates.",
|
||||
@@ -484,14 +490,18 @@ def corrected_geometry(
|
||||
|
||||
def axis_limits(axis: Literal["x", "y"]) -> tuple[float, float]:
|
||||
if axis == "x":
|
||||
start = correction.offset_x + correction.direction_x * correction.scale_x * raw_roi.x
|
||||
start = correction.offset_x + correction.direction_x * correction.scale_x * (
|
||||
raw_roi.x - correction.reference_x
|
||||
)
|
||||
stop = correction.offset_x + correction.direction_x * correction.scale_x * (
|
||||
raw_roi.x + raw_roi.width
|
||||
raw_roi.x + raw_roi.width - correction.reference_x
|
||||
)
|
||||
return start, stop
|
||||
start = correction.offset_y + correction.direction_y * correction.scale_y * raw_roi.y
|
||||
start = correction.offset_y + correction.direction_y * correction.scale_y * (
|
||||
raw_roi.y - correction.reference_y
|
||||
)
|
||||
stop = correction.offset_y + correction.direction_y * correction.scale_y * (
|
||||
raw_roi.y + raw_roi.height
|
||||
raw_roi.y + raw_roi.height - correction.reference_y
|
||||
)
|
||||
return start, stop
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ from bec_qthemes import material_icon
|
||||
from bec_widgets.utils.bec_widget import BECWidget
|
||||
from bec_widgets.utils.colors import Colors, get_accent_colors, get_theme_name, rgba, theme_color
|
||||
from bec_widgets.utils.error_popups import SafeSlot
|
||||
from bec_widgets.utils.toolbars.actions import MaterialIconAction
|
||||
from bec_widgets.widgets.control.device_control.positioner_box import PositionerBox2D
|
||||
from bec_widgets.widgets.plots.image.image import Image
|
||||
from bec_widgets.widgets.plots.roi.image_roi import RectangularROI
|
||||
@@ -84,8 +85,6 @@ class SAXSWidget(BECWidget, QWidget):
|
||||
"mark_scan_result",
|
||||
]
|
||||
|
||||
submission_requested = Signal(object)
|
||||
|
||||
ROI_NAME = "SAXS ROI"
|
||||
|
||||
def __init__(self, parent=None, **kwargs):
|
||||
@@ -101,6 +100,10 @@ class SAXSWidget(BECWidget, QWidget):
|
||||
self._last_stepper_step = DEFAULT_STEP_SIZE
|
||||
self._active_session: str | None = None
|
||||
self._session_dirty = False
|
||||
self._crosshair_pin_enabled = False
|
||||
self._mapping_reference: tuple[float, float] | None = None
|
||||
self._mapping_reference_image_shape: tuple[int, int] | None = None
|
||||
self._crosshair_pin_position: tuple[float, float] | None = None
|
||||
|
||||
self._accent_colors = get_accent_colors()
|
||||
self._roi_color = self._accent_colors.highlight or "#00bcd4"
|
||||
@@ -108,6 +111,8 @@ class SAXSWidget(BECWidget, QWidget):
|
||||
self.apply_theme()
|
||||
self._connect_signals()
|
||||
self._ensure_saxs_roi()
|
||||
self._sync_mapping_reference_to_display()
|
||||
self._refresh_crosshair_pin_if_enabled()
|
||||
|
||||
def _build_ui(self) -> None:
|
||||
root = QVBoxLayout(self)
|
||||
@@ -126,9 +131,7 @@ class SAXSWidget(BECWidget, QWidget):
|
||||
top_layout.setSpacing(8)
|
||||
|
||||
self.image = Image(parent=top)
|
||||
self.image.enable_full_colorbar = True
|
||||
self.image.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)
|
||||
self.image.toolbar.show_bundles(["device_selection"])
|
||||
self._customize_image_widget(self.image)
|
||||
top_layout.addWidget(self.image, 1)
|
||||
|
||||
side_panel = QFrame(top)
|
||||
@@ -214,6 +217,209 @@ class SAXSWidget(BECWidget, QWidget):
|
||||
self.main_splitter.setStretchFactor(1, 2)
|
||||
self._update_session_ui_state()
|
||||
|
||||
def _customize_image_widget(self, image: Image) -> None:
|
||||
image.enable_full_colorbar = True
|
||||
image.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)
|
||||
# Enable crosshair toggle in the image toolbar.
|
||||
toggle_cross_action = MaterialIconAction(
|
||||
icon_name="add",
|
||||
checkable=True,
|
||||
label_text="Show/Hide Crosshair",
|
||||
text_position="under",
|
||||
parent=image,
|
||||
tooltip="Toggle crosshair visibility in the image",
|
||||
)
|
||||
toggle_cross_action.action.toggled.connect(self._toggle_crosshair_visibility)
|
||||
toggle_cross_action.action.setChecked(True)
|
||||
image.toolbar.components.add_safe("toggle_cross_action", toggle_cross_action)
|
||||
image.toolbar.get_bundle("device_selection").add_action("toggle_cross_action")
|
||||
# Enable Live mode for camera
|
||||
toggle_cam = MaterialIconAction(
|
||||
icon_name="videocam",
|
||||
checkable=True,
|
||||
label_text="Live Camera Mode",
|
||||
text_position="under",
|
||||
parent=image,
|
||||
tooltip="Toggle camera live mode",
|
||||
)
|
||||
toggle_cam.action.toggled.connect(self._toggle_camera_live_mode)
|
||||
toggle_cam.action.setChecked(True)
|
||||
image.toolbar.components.add_safe("toggle_cam", toggle_cam)
|
||||
image.toolbar.get_bundle("device_selection").add_action("toggle_cam")
|
||||
image.toolbar.show_bundles(["device_selection"])
|
||||
|
||||
def _toggle_crosshair_visibility(self, enabled: bool) -> None:
|
||||
self._crosshair_pin_enabled = bool(enabled)
|
||||
if enabled:
|
||||
self._refresh_crosshair_pin_if_enabled(force=True)
|
||||
return
|
||||
self._clear_reference_crosshair_pin()
|
||||
# self.image.toggle_crosshair(False)
|
||||
|
||||
def _toggle_camera_live_mode(self, enabled: bool) -> None:
|
||||
# If enabled, set the camera to live mode, if disabled, stop the live mode.
|
||||
print(f"Toggling camera live mode to {'enabled' if enabled else 'disabled'}")
|
||||
|
||||
def _refresh_crosshair_pin_if_enabled(self, *_args, force: bool = False) -> None:
|
||||
if self._crosshair_pin_enabled:
|
||||
pin_position = self._mapping_reference_position()
|
||||
if (
|
||||
not force
|
||||
and self._positions_match(self._crosshair_pin_position, pin_position)
|
||||
and self._has_reference_crosshair_pin()
|
||||
):
|
||||
self._update_reference_crosshair_label(*pin_position)
|
||||
return
|
||||
self._place_reference_crosshair_pin(pin_position)
|
||||
|
||||
def _sync_mapping_reference_to_display(self) -> None:
|
||||
shape = self._display_image_shape()
|
||||
if shape is None:
|
||||
if self._mapping_reference is None or self._mapping_reference_image_shape is not None:
|
||||
self._set_mapping_reference_position(*self._roi_center_position(), image_shape=None)
|
||||
return
|
||||
self._update_mapping_reference_fields()
|
||||
return
|
||||
|
||||
if self._mapping_reference is None or self._mapping_reference_image_shape != shape:
|
||||
self._set_mapping_reference_position(
|
||||
*self._image_center_position(shape), image_shape=shape
|
||||
)
|
||||
return
|
||||
self._update_mapping_reference_fields()
|
||||
|
||||
def _on_image_updated(self, *_args) -> None:
|
||||
self._sync_mapping_reference_to_display()
|
||||
self._refresh_crosshair_pin_if_enabled()
|
||||
|
||||
def _on_roi_reference_changed(self, *_args) -> None:
|
||||
shape = self._display_image_shape()
|
||||
if shape is None:
|
||||
self._set_mapping_reference_position(*self._roi_center_position(), image_shape=None)
|
||||
elif self._mapping_reference is None:
|
||||
self._set_mapping_reference_position(
|
||||
*self._image_center_position(shape), image_shape=shape
|
||||
)
|
||||
self._refresh_crosshair_pin_if_enabled()
|
||||
|
||||
def _place_reference_crosshair_pin(
|
||||
self, pin_position: tuple[float, float] | None = None
|
||||
) -> None:
|
||||
if getattr(self, "image", None) is None:
|
||||
return
|
||||
if self.image.crosshair is None:
|
||||
self.image.toggle_crosshair(True)
|
||||
crosshair = self.image.crosshair
|
||||
if crosshair is None:
|
||||
return
|
||||
|
||||
pin_x, pin_y = pin_position or self._mapping_reference_position()
|
||||
image_item = self.image.main_image
|
||||
item_name = image_item.objectName() or str(id(image_item))
|
||||
snap_x = {item_name: pin_x - 0.5}
|
||||
snap_y = {item_name: pin_y - 0.5}
|
||||
crosshair.set_pin(pin_x, pin_y, snap_x, snap_y)
|
||||
if crosshair.pinned_pos is None:
|
||||
# ``set_pin`` needs at least one visible data item. During early widget
|
||||
# construction there may be no image data yet, so draw the same pin
|
||||
# position directly as a fallback.
|
||||
crosshair._draw_pin(pin_x, pin_y)
|
||||
crosshair.pinned_pos = (pin_x, pin_y)
|
||||
crosshair.mouse_moved(manual_pos=(pin_x, pin_y))
|
||||
self._crosshair_pin_position = (pin_x, pin_y)
|
||||
self._update_reference_crosshair_label(pin_x, pin_y)
|
||||
self.image.toggle_crosshair(False)
|
||||
|
||||
@staticmethod
|
||||
def _positions_match(
|
||||
first: tuple[float, float] | None, second: tuple[float, float] | None
|
||||
) -> bool:
|
||||
if first is None or second is None:
|
||||
return False
|
||||
return abs(first[0] - second[0]) <= 1e-9 and abs(first[1] - second[1]) <= 1e-9
|
||||
|
||||
def _has_reference_crosshair_pin(self) -> bool:
|
||||
crosshair = getattr(self.image, "crosshair", None)
|
||||
if crosshair is not None and crosshair.pinned_pos is not None:
|
||||
return True
|
||||
detached_pin = getattr(self.image, "_detached_pin", None)
|
||||
return detached_pin is not None and detached_pin.get("pos") is not None
|
||||
|
||||
def _clear_reference_crosshair_pin(self) -> None:
|
||||
crosshair = getattr(self.image, "crosshair", None)
|
||||
if crosshair is not None:
|
||||
crosshair.clear_pin()
|
||||
if hasattr(self.image, "_discard_detached_pin"):
|
||||
self.image._discard_detached_pin()
|
||||
self._crosshair_pin_position = None
|
||||
|
||||
def _update_reference_crosshair_label(self, image_x: float, image_y: float) -> None:
|
||||
crosshair = getattr(self.image, "crosshair", None)
|
||||
pinned_label = crosshair.pinned_label if crosshair is not None else None
|
||||
if pinned_label is None:
|
||||
detached_pin = getattr(self.image, "_detached_pin", None)
|
||||
if detached_pin is not None:
|
||||
pinned_label = detached_pin.get("label")
|
||||
if pinned_label is None:
|
||||
return
|
||||
samx, samy = self._map_image_point_to_position(image_x, image_y)
|
||||
x_name = (
|
||||
getattr(self, "device_box", None).x_positioner if hasattr(self, "device_box") else ""
|
||||
)
|
||||
y_name = (
|
||||
getattr(self, "device_box", None).y_positioner if hasattr(self, "device_box") else ""
|
||||
)
|
||||
x_name = x_name or "samx"
|
||||
y_name = y_name or "samy"
|
||||
pinned_label.setText(f"pin\n{x_name}: {samx:.6g}\n{y_name}: {samy:.6g}")
|
||||
|
||||
def _display_image_shape(self) -> tuple[int, int] | None:
|
||||
try:
|
||||
data = self.image.main_image.image
|
||||
except (AttributeError, KeyError):
|
||||
return None
|
||||
shape = getattr(data, "shape", None)
|
||||
if data is None or shape is None or len(shape) < 2:
|
||||
return None
|
||||
if shape[0] <= 0 or shape[1] <= 0:
|
||||
return None
|
||||
return int(shape[0]), int(shape[1])
|
||||
|
||||
@staticmethod
|
||||
def _image_center_position(shape: tuple[int, int]) -> tuple[float, float]:
|
||||
return float(shape[0]) / 2.0, float(shape[1]) / 2.0
|
||||
|
||||
def _roi_center_position(self) -> tuple[float, float]:
|
||||
roi = self._ensure_saxs_roi()
|
||||
coords = roi.get_coordinates(typed=True)
|
||||
return float(coords["center_x"]), float(coords["center_y"])
|
||||
|
||||
def _set_mapping_reference_position(
|
||||
self, x: float, y: float, *, image_shape: tuple[int, int] | None
|
||||
) -> None:
|
||||
self._mapping_reference = (float(x), float(y))
|
||||
self._mapping_reference_image_shape = image_shape
|
||||
self._update_mapping_reference_fields()
|
||||
|
||||
def _update_mapping_reference_fields(self) -> None:
|
||||
if self._mapping_reference is None or not hasattr(self, "mapping_box"):
|
||||
return
|
||||
self.mapping_box.set_reference_position(*self._mapping_reference)
|
||||
|
||||
def _mapping_reference_position(self) -> tuple[float, float]:
|
||||
self._sync_mapping_reference_to_display()
|
||||
return self._mapping_reference or (0.0, 0.0)
|
||||
|
||||
def _map_image_point_to_position(self, image_x: float, image_y: float) -> tuple[float, float]:
|
||||
correction = self._current_correction()
|
||||
samx = correction.offset_x + correction.direction_x * correction.scale_x * (
|
||||
image_x - correction.reference_x
|
||||
)
|
||||
samy = correction.offset_y + correction.direction_y * correction.scale_y * (
|
||||
image_y - correction.reference_y
|
||||
)
|
||||
return samx, samy
|
||||
|
||||
def _build_table_session_bar(self) -> QWidget:
|
||||
bar = QFrame(self.table_group)
|
||||
bar.setObjectName("saxs_session_bar")
|
||||
@@ -268,12 +474,13 @@ class SAXSWidget(BECWidget, QWidget):
|
||||
self.device_box.y_positioner_changed.connect(self.positioner_box.set_positioner_ver)
|
||||
self.device_box.sastt_enabled_changed.connect(self._update_sastt_enabled)
|
||||
self.mapping_box.apply_requested.connect(self._prompt_apply_corrections)
|
||||
self.image.image_updated.connect(self._on_image_updated)
|
||||
self._connect_mapping_reference_signals()
|
||||
|
||||
self.table_widget.edit_requested.connect(self._edit_row_by_index)
|
||||
self.table_widget.clear_selected_requested.connect(self.delete_selected_rows)
|
||||
self.table_widget.clear_all_requested.connect(self.clear_rows)
|
||||
self.table_widget.save_requested.connect(self.save_session)
|
||||
self.table_widget.upload_requested.connect(self.submit_selected)
|
||||
self.table_widget.store_csv_requested.connect(self.store_rows_as_csv)
|
||||
|
||||
self.new_session_button.clicked.connect(self._new_session)
|
||||
@@ -286,6 +493,21 @@ class SAXSWidget(BECWidget, QWidget):
|
||||
|
||||
self._update_sastt_enabled(self.device_box.is_sastt_enabled())
|
||||
|
||||
def _connect_mapping_reference_signals(self) -> None:
|
||||
for spin_box in [
|
||||
self.mapping_box.scale_x,
|
||||
self.mapping_box.offset_x,
|
||||
self.mapping_box.scale_y,
|
||||
self.mapping_box.offset_y,
|
||||
]:
|
||||
spin_box.valueChanged.connect(self._refresh_crosshair_pin_if_enabled)
|
||||
self.mapping_box.direction_x.currentIndexChanged.connect(
|
||||
self._refresh_crosshair_pin_if_enabled
|
||||
)
|
||||
self.mapping_box.direction_y.currentIndexChanged.connect(
|
||||
self._refresh_crosshair_pin_if_enabled
|
||||
)
|
||||
|
||||
@SafeSlot(str)
|
||||
@SafeSlot()
|
||||
def apply_theme(self, _theme: str | None = None) -> None:
|
||||
@@ -357,6 +579,13 @@ class SAXSWidget(BECWidget, QWidget):
|
||||
"font-weight: 700;"
|
||||
"font-size: 11px;"
|
||||
"}"
|
||||
"QLineEdit#saxs_reference_field {"
|
||||
f"background-color: {recessed_bg.name()};"
|
||||
f"border: 1px solid {subtle_border.name()};"
|
||||
"border-radius: 4px;"
|
||||
f"color: {muted.name()};"
|
||||
"padding: 3px 6px;"
|
||||
"}"
|
||||
"QFrame#saxs_session_bar, QFrame#saxs_table_controls {"
|
||||
f"background-color: {panel_bg.name()};"
|
||||
f"border: 1px solid {accent_border.name()};"
|
||||
@@ -442,6 +671,7 @@ class SAXSWidget(BECWidget, QWidget):
|
||||
)
|
||||
roi.line_color = self._roi_color
|
||||
roi.removable = False
|
||||
roi.edgesChanged.connect(self._on_roi_reference_changed)
|
||||
self._saxs_roi = roi
|
||||
return roi
|
||||
|
||||
@@ -459,7 +689,29 @@ class SAXSWidget(BECWidget, QWidget):
|
||||
)
|
||||
|
||||
def _current_correction(self) -> Correction:
|
||||
return self.mapping_box.correction()
|
||||
reference_x, reference_y = self._mapping_reference_position()
|
||||
if not hasattr(self, "mapping_box"):
|
||||
return Correction(
|
||||
scale_x=1.0,
|
||||
offset_x=0.0,
|
||||
direction_x=1,
|
||||
scale_y=1.0,
|
||||
offset_y=0.0,
|
||||
direction_y=1,
|
||||
reference_x=reference_x,
|
||||
reference_y=reference_y,
|
||||
)
|
||||
correction = self.mapping_box.correction()
|
||||
return Correction(
|
||||
scale_x=correction.scale_x,
|
||||
offset_x=correction.offset_x,
|
||||
direction_x=correction.direction_x,
|
||||
scale_y=correction.scale_y,
|
||||
offset_y=correction.offset_y,
|
||||
direction_y=correction.direction_y,
|
||||
reference_x=reference_x,
|
||||
reference_y=reference_y,
|
||||
)
|
||||
|
||||
def _make_sample_id(self) -> str:
|
||||
return uuid4().hex
|
||||
@@ -851,22 +1103,6 @@ class SAXSWidget(BECWidget, QWidget):
|
||||
self._rows[index].scan_results.append(ScanResult(str(scan_number), bool(success)))
|
||||
self._refresh_table()
|
||||
|
||||
@SafeSlot()
|
||||
def submit_selected(self) -> None:
|
||||
rows = self.table_widget.selected_model_rows()
|
||||
if not rows:
|
||||
rows = self._rows
|
||||
if not rows:
|
||||
self._warn("There are no rows to upload.")
|
||||
return
|
||||
if self._session_dirty:
|
||||
self._warn("Save the session before uploading.")
|
||||
return
|
||||
session_name = self._active_session or ""
|
||||
payload = [row.to_dict() for row in rows]
|
||||
self.submission_requested.emit((session_name, payload))
|
||||
self.status_label.setText(f"Submitted {len(payload)} rows")
|
||||
|
||||
@SafeSlot()
|
||||
def save_session(self, show_feedback: bool = True) -> None:
|
||||
if not self._active_session:
|
||||
@@ -1074,6 +1310,8 @@ class SAXSWidget(BECWidget, QWidget):
|
||||
scale_y=float(entry["correction_scale_y"]),
|
||||
offset_y=float(entry["correction_offset_y"]),
|
||||
direction_y=int(entry["correction_direction_y"]),
|
||||
reference_x=float(entry.get("correction_reference_x", 0.0)),
|
||||
reference_y=float(entry.get("correction_reference_y", 0.0)),
|
||||
)
|
||||
geometry = corrected_geometry(
|
||||
raw_roi,
|
||||
|
||||
Reference in New Issue
Block a user