diff --git a/src/firefly/plans/grid_scan.py b/src/firefly/plans/grid_scan.py
index fec696a8..0c540a84 100644
--- a/src/firefly/plans/grid_scan.py
+++ b/src/firefly/plans/grid_scan.py
@@ -23,7 +23,8 @@ def setup_ui(self):
"Motor",
"Start",
"Stop",
- "Scan points",
+ "N. points",
+ "Size",
"Snake",
"Fly",
]
@@ -38,7 +39,7 @@ def setup_ui(self):
# fix widths so the labels are aligned with GridScanRegions
Qlabels_all["Priority axis"].setFixedWidth(70)
Qlabels_all["Motor"].setFixedWidth(100)
- Qlabels_all["Scan points"].setFixedWidth(68)
+ Qlabels_all["N. points"].setFixedWidth(68)
Qlabels_all["Snake"].setFixedWidth(53)
Qlabels_all["Fly"].setFixedWidth(43)
@@ -52,7 +53,7 @@ class GridScanRegion(regions_display.RegionBase):
def setup_ui(self):
self.layout = QtWidgets.QHBoxLayout()
- # First item, motor No.
+ # motor No.
self.motor_label = QtWidgets.QLCDNumber()
self.motor_label.setStyleSheet(
"QLCDNumber { background-color: white; color: red; }"
@@ -60,40 +61,76 @@ def setup_ui(self):
self.motor_label.display(self.line_label)
self.layout.addWidget(self.motor_label)
- # Second item, ComponentSelector
+ # ComponentSelector
self.motor_box = ComponentSelector()
self.layout.addWidget(self.motor_box)
- # Third item, start point
+ # Start point
self.start_line_edit = QtWidgets.QLineEdit()
self.start_line_edit.setValidator(QDoubleValidator()) # only takes floats
self.start_line_edit.setPlaceholderText("Start…")
self.layout.addWidget(self.start_line_edit)
- # Forth item, stop point
+ # Stop point
self.stop_line_edit = QtWidgets.QLineEdit()
self.stop_line_edit.setValidator(QDoubleValidator()) # only takes floats
self.stop_line_edit.setPlaceholderText("Stop…")
self.layout.addWidget(self.stop_line_edit)
- # Fifth item, number of scan point
+ # Number of scan point
self.scan_pts_spin_box = QtWidgets.QSpinBox()
self.scan_pts_spin_box.setMinimum(1)
self.scan_pts_spin_box.setMaximum(99999)
self.layout.addWidget(self.scan_pts_spin_box)
- # Sixth item, snake checkbox
+ # Step size (non-editable)
+ self.step_size_line_edit = QtWidgets.QLineEdit()
+ self.step_size_line_edit.setReadOnly(True)
+ self.step_size_line_edit.setDisabled(True)
+ self.step_size_line_edit.setPlaceholderText("Step Size…")
+ self.layout.addWidget(self.step_size_line_edit)
+
+ # Snake checkbox
self.snake_checkbox = QtWidgets.QCheckBox()
self.snake_checkbox.setText("Snake")
self.snake_checkbox.setEnabled(True)
self.layout.addWidget(self.snake_checkbox)
- # Seventh item, fly checkbox # not available right now
+ # Fly checkbox # not available right now
self.fly_checkbox = QtWidgets.QCheckBox()
self.fly_checkbox.setText("Fly")
self.fly_checkbox.setEnabled(False)
self.layout.addWidget(self.fly_checkbox)
+ # Connect signals
+ self.start_line_edit.textChanged.connect(self.update_step_size)
+ self.stop_line_edit.textChanged.connect(self.update_step_size)
+ self.scan_pts_spin_box.valueChanged.connect(self.update_step_size)
+
+ def update_step_size(self):
+ try:
+ # Get Start and Stop values
+ start_text = self.start_line_edit.text().strip()
+ stop_text = self.stop_line_edit.text().strip()
+ if not start_text or not stop_text:
+ self.step_size_line_edit.setText("N/A")
+ return
+
+ start = float(start_text)
+ stop = float(stop_text)
+
+ # Ensure num_points is an integer
+ num_points = int(self.scan_pts_spin_box.value()) # Corrected method call
+
+ # Calculate step size
+ if num_points > 1:
+ step_size = (stop - start) / (num_points - 1)
+ self.step_size_line_edit.setText(f"{step_size}")
+ else:
+ self.step_size_line_edit.setText("N/A")
+ except ValueError:
+ self.step_size_line_edit.setText("N/A")
+
class GridScanDisplay(regions_display.RegionsDisplay):
Region = GridScanRegion
@@ -108,10 +145,6 @@ def customize_ui(self):
# add title layout
self.title_region = TitleRegion()
self.ui.title_layout.addLayout(self.title_region.layout)
- # When selections of detectors changed update_total_time
- # self.ui.detectors_list.selectionModel().selectionChanged.connect(
- # self.update_total_time
- # )
self.ui.spinBox_repeat_scan_num.valueChanged.connect(self.update_total_time)
# Connect scan points change to update total time
for region in self.regions:
diff --git a/src/firefly/plans/grid_scan.ui b/src/firefly/plans/grid_scan.ui
index fa6c5761..847b9fe5 100644
--- a/src/firefly/plans/grid_scan.ui
+++ b/src/firefly/plans/grid_scan.ui
@@ -6,8 +6,8 @@
0
0
- 983
- 432
+ 800
+ 385
@@ -65,9 +65,15 @@
-
+
+ background-color: rgb(255, 85, 127);
+
Relative
+
+ true
+
-
@@ -86,93 +92,97 @@
-
-
+
-
-
-
- true
+
+
+ Qt::Horizontal
-
-
-
- 0
- 0
- 471
- 215
-
+
+
+
+ 500
+ 0
+
-
-
-
-
-
- 3
-
-
- 3
-
-
-
+
+ true
+
+
+
+
+ 0
+ 0
+ 498
+ 207
+
+
+
+ -
+
+
+ 3
+
+
+ 3
+
+
+
+ -
+
+
+ 3
+
+
+ 3
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 40
+
+
+
+
+
+
+
+
+
-
-
-
- 3
+
+
+
+ 0
+ 0
+
-
- 3
+
+ Detectors
-
+
-
-
-
- Qt::Vertical
+
+
+ <html><head/><body><p>Use <span style=" font-weight:600;">ctrl</span> to select multiple detectors</p></body></html>
-
-
- 20
- 40
-
+
+ QAbstractItemView::MultiSelection
-
+
- -
-
-
- Qt::Vertical
-
-
-
- -
-
-
-
-
-
-
- 0
- 0
-
-
-
- Detectors
-
-
-
- -
-
-
- <html><head/><body><p>Use <span style=" font-weight:600;">ctrl</span> to select multiple detectors</p></body></html>
-
-
- QAbstractItemView::MultiSelection
-
-
-
-
-
-
@@ -182,6 +192,13 @@
+ -
+
+
+ Qt::Horizontal
+
+
+
-
-
@@ -190,7 +207,7 @@
Experiment purpose:
- Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+ Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter
@@ -235,7 +252,7 @@
Exposure time each scan:
- Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+ Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter
@@ -248,7 +265,7 @@
Total exposure time:
- Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+ Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter
@@ -502,13 +519,6 @@
- -
-
-
- Qt::Horizontal
-
-
-
diff --git a/src/firefly/plans/line_scan.py b/src/firefly/plans/line_scan.py
index 8e3d8a94..afb450e4 100644
--- a/src/firefly/plans/line_scan.py
+++ b/src/firefly/plans/line_scan.py
@@ -32,6 +32,41 @@ def setup_ui(self):
self.stop_line_edit.setPlaceholderText("Stop…")
self.layout.addWidget(self.stop_line_edit)
+ # Step size (non-editable)
+ self.step_size_line_edit = QtWidgets.QLineEdit()
+ self.step_size_line_edit.setReadOnly(True)
+ self.step_size_line_edit.setDisabled(True)
+ self.step_size_line_edit.setPlaceholderText("Step Size…")
+ self.layout.addWidget(self.step_size_line_edit)
+
+ # Connect signals
+ self.start_line_edit.textChanged.connect(self.update_step_size)
+ self.stop_line_edit.textChanged.connect(self.update_step_size)
+
+ def update_step_size(self, num_points=None):
+ try:
+ # Get Start and Stop values
+ start_text = self.start_line_edit.text().strip()
+ stop_text = self.stop_line_edit.text().strip()
+ if not start_text or not stop_text:
+ self.step_size_line_edit.setText("N/A")
+ return
+
+ start = float(start_text)
+ stop = float(stop_text)
+
+ # Ensure num_points is an integer
+ num_points = int(num_points) if num_points is not None else 2
+
+ # Calculate step size
+ if num_points > 1:
+ step_size = (stop - start) / (num_points - 1)
+ self.step_size_line_edit.setText(f"{step_size}")
+ else:
+ self.step_size_line_edit.setText("N/A")
+ except ValueError:
+ self.step_size_line_edit.setText("N/A")
+
class LineScanDisplay(regions_display.RegionsDisplay):
Region = LineScanRegion
@@ -55,6 +90,14 @@ def customize_ui(self):
)
self.ui.spinBox_repeat_scan_num.valueChanged.connect(self.update_total_time)
+ # Connect scan_pts_spin_box value change to regions
+ self.ui.scan_pts_spin_box.valueChanged.connect(self.update_regions_step_size)
+
+ def update_regions_step_size(self, num_points):
+ """Update the step size for all regions."""
+ for region in self.regions:
+ region.update_step_size(num_points)
+
def queue_plan(self, *args, **kwargs):
"""Execute this plan on the queueserver."""
detectors, motor_args, repeat_scan_num = self.get_scan_parameters()
diff --git a/src/firefly/plans/line_scan.ui b/src/firefly/plans/line_scan.ui
index f21e7b6b..cbe938a0 100644
--- a/src/firefly/plans/line_scan.ui
+++ b/src/firefly/plans/line_scan.ui
@@ -7,7 +7,7 @@
0
0
795
- 389
+ 384
@@ -16,83 +16,128 @@
-
-
-
-
+
-
+
+
-
+
+
+
+ 0
+ 0
+
+
+
+ Num. Motors
+
+
+
-
-
-
+
+
true
-
-
-
- 0
- 0
- 377
- 215
-
-
-
-
-
-
-
- 3
-
-
- 3
-
-
-
- -
-
-
- Qt::Vertical
-
-
-
- 20
- 40
-
-
-
-
-
-
+
+ false
+
+
+ false
+
+
+ QAbstractSpinBox::CorrectToNearestValue
+
+
+ 1
+
+
+ 1
+
+
+ 10
+
-
-
+
Qt::Vertical
-
-
-
-
-
-
-
- 0
- 0
-
-
-
- Detectors
-
-
-
- -
-
-
- <html><head/><body><p>Use <span style=" font-weight:600;">ctrl</span> to select multiple detectors</p></body></html>
-
-
- QAbstractItemView::MultiSelection
-
-
-
-
+
+
+ background-color: rgb(255, 85, 127);
+
+
+ Relative
+
+
+ true
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ -
+
+
+ Log
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Scan Points
+
+
+
+ -
+
+
+ 1
+
+
+ 10000
+
+
+ 2
+
+
+ 10
+
+
@@ -346,147 +391,32 @@
- -
-
-
- Do this scan by multiple times
-
-
- Num. of scans
-
-
-
-
-
-
- Do this scan by multiple times
-
-
- 1
-
-
- 999
-
-
-
-
-
- -
-
-
-
-
-
-
- 0
- 0
-
-
-
- Num. Motors
-
-
-
- -
-
-
- true
-
-
- false
-
-
- false
-
-
- QAbstractSpinBox::CorrectToNearestValue
-
-
- 1
-
-
- 1
-
-
- 10
-
-
-
- -
-
-
- Qt::Vertical
-
-
-
- -
-
-
- Relative
-
-
-
- -
-
-
- Qt::Vertical
-
-
-
- -
-
-
- Log
-
-
-
- -
-
-
- Qt::Vertical
-
-
-
- -
-
-
- Qt::Horizontal
-
-
-
- 40
- 20
-
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
- Scan Points
-
-
-
- -
-
-
- 1
-
-
- 10000
-
-
- 2
-
-
- 10
-
-
+
+
-
+
+
+ Do this scan by multiple times
+
+
+ Num. of scans:
+
+
+
+ -
+
+
+ Do this scan by multiple times
+
+
+ 1
+
+
+ 999
+
+
+
+
@@ -497,6 +427,89 @@
+ -
+
+
+ Qt::Horizontal
+
+
+
+
+ 500
+ 0
+
+
+
+ true
+
+
+
+
+ 0
+ 0
+ 498
+ 208
+
+
+
+
-
+
+
+ 3
+
+
+ 3
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+ QSizePolicy::Expanding
+
+
+
+ 0
+ 10
+
+
+
+
+
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Detectors
+
+
+
+ -
+
+
+ <html><head/><body><p>Use <span style=" font-weight:600;">ctrl</span> to select multiple detectors</p></body></html>
+
+
+ QAbstractItemView::MultiSelection
+
+
+
+
+
+
+
-
diff --git a/src/firefly/plans/regions_display.py b/src/firefly/plans/regions_display.py
index da2a3893..9e2b7755 100644
--- a/src/firefly/plans/regions_display.py
+++ b/src/firefly/plans/regions_display.py
@@ -56,7 +56,6 @@ def customize_ui(self):
# Disable the line edits in spin box (use up/down buttons instead)
self.ui.num_motor_spin_box.lineEdit().setReadOnly(True)
-
# Create the initial (blank) regions
self.regions = []
self.ui.num_motor_spin_box.setValue(self.default_num_regions)
@@ -64,6 +63,23 @@ def customize_ui(self):
# Set up the mechanism for changing region number
self.ui.num_motor_spin_box.valueChanged.connect(self.update_regions_slot)
self.ui.run_button.clicked.connect(self.queue_plan)
+ # Color highlights for relative checkbox
+ if hasattr(self, "relative_scan_checkbox"):
+ self.ui.relative_scan_checkbox.stateChanged.connect(self.change_background)
+
+ def change_background(self, state):
+ """
+ Change the background color of the relative scan checkbox based on its state.
+ """
+ if state: # Checked
+ self.ui.relative_scan_checkbox.setStyleSheet(
+ "background-color: rgb(255, 85, 127);"
+ )
+
+ else: # Unchecked
+ self.ui.relative_scan_checkbox.setStyleSheet(
+ "background-color: rgb(0, 170, 255);"
+ )
@asyncSlot(object)
async def update_devices(self, registry):
diff --git a/src/firefly/tests/test_grid_scan_window.py b/src/firefly/tests/test_grid_scan_window.py
index 4d1efb54..dc440715 100644
--- a/src/firefly/tests/test_grid_scan_window.py
+++ b/src/firefly/tests/test_grid_scan_window.py
@@ -59,7 +59,58 @@ async def test_time_calculator(display, sim_registry, ion_chamber):
@pytest.mark.asyncio
-async def test_grid_scan_plan_queued(display, qtbot, sim_registry, ion_chamber):
+async def test_step_size_calculation(display):
+ # Set up the display with 2 regions
+ await display.update_regions(2)
+
+ # Region 0: Set Start, Stop, and Points
+ region_0 = display.regions[0]
+ region_0.start_line_edit.setText("0")
+ region_0.stop_line_edit.setText("10")
+ region_0.scan_pts_spin_box.setValue(5)
+
+ # Trigger step size calculation
+ region_0.update_step_size()
+
+ # Check step size calculation for Region 0
+ assert region_0.step_size_line_edit.text() == "2.5"
+
+ # Region 1: Set Start, Stop, and Points
+ region_1 = display.regions[1]
+ region_1.start_line_edit.setText("5")
+ region_1.stop_line_edit.setText("15")
+ region_1.scan_pts_spin_box.setValue(3)
+
+ # Trigger step size calculation
+ region_1.update_step_size()
+
+ # Check step size calculation for Region 1
+ assert region_1.step_size_line_edit.text() == "5.0"
+
+ # Test invalid input for Region 0
+ region_0.start_line_edit.setText("invalid")
+ region_0.update_step_size()
+ assert region_0.step_size_line_edit.text() == "N/A"
+
+ # Test edge case: num_points = 1 for Region 1
+ region_1.scan_pts_spin_box.setValue(1)
+ region_1.update_step_size()
+ assert region_1.step_size_line_edit.text() == "N/A"
+
+ # Reset valid values for Region 0
+ region_0.start_line_edit.setText("10")
+ region_0.stop_line_edit.setText("30")
+ region_0.scan_pts_spin_box.setValue(4)
+ region_0.update_step_size()
+ assert (
+ region_0.step_size_line_edit.text() == "6.666666666666667"
+ ) # Expect float precision
+
+
+@pytest.mark.asyncio
+async def test_grid_scan_plan_queued(
+ display, sim_registry, ion_chamber, monkeypatch, qtbot
+):
await display.update_regions(2)
# set up a test motor 1
@@ -87,7 +138,7 @@ async def test_grid_scan_plan_queued(display, qtbot, sim_registry, ion_chamber):
display.ui.textEdit_notes.setText("notes")
expected_item = BPlan(
- "grid_scan",
+ "rel_grid_scan",
["vortex_me4", "I00"],
"async_motor_1",
2.0,
diff --git a/src/firefly/tests/test_line_scan_window.py b/src/firefly/tests/test_line_scan_window.py
index 693d4cae..dde258ca 100644
--- a/src/firefly/tests/test_line_scan_window.py
+++ b/src/firefly/tests/test_line_scan_window.py
@@ -71,7 +71,42 @@ async def test_time_calculator(display, sim_registry, ion_chamber, qtbot, qapp):
@pytest.mark.asyncio
-async def test_line_scan_plan_queued(qtbot, display):
+async def test_step_size_calculation(display, qtbot):
+ await display.update_regions(1)
+ region = display.regions[0]
+ region.start_line_edit.setText("0")
+ region.stop_line_edit.setText("10")
+
+ # Set num_points and emit the signal
+ display.ui.scan_pts_spin_box.setValue(5)
+ region.update_step_size(5) # Emit the signal with the new num_points value
+ assert region.step_size_line_edit.text() == "2.5"
+
+ # Change the number of points and verify step size updates
+ display.ui.scan_pts_spin_box.setValue(3)
+ region.update_step_size(3)
+ assert region.step_size_line_edit.text() == "5.0"
+
+ # Test invalid input
+ region.start_line_edit.setText("Start..")
+ region.update_step_size(3)
+ assert region.step_size_line_edit.text() == "N/A"
+
+ # Test edge case: num_points = 1
+ display.ui.scan_pts_spin_box.setValue(1)
+ region.update_step_size(1)
+ assert region.step_size_line_edit.text() == "N/A"
+
+ # Reset to a valid state and verify
+ region.start_line_edit.setText("0")
+ region.stop_line_edit.setText("10")
+ display.ui.scan_pts_spin_box.setValue(6)
+ region.update_step_size(6)
+ assert region.step_size_line_edit.text() == "2.0"
+
+
+@pytest.mark.asyncio
+async def test_line_scan_plan_queued(display, monkeypatch, qtbot):
# set up motor num
await display.update_regions(2)
@@ -99,7 +134,7 @@ async def test_line_scan_plan_queued(qtbot, display):
display.ui.textEdit_notes.setText("notes")
expected_item = BPlan(
- "scan",
+ "rel_scan",
["vortex_me4", "I0"],
"async_motor_1",
1.0,