mirror of
https://github.com/bec-project/bec_widgets.git
synced 2026-05-12 17:45:42 +02:00
Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 603edede9c | |||
| 30ef25533a | |||
| 73b44cffb2 | |||
| a614d662d6 | |||
| 3f1aa80756 | |||
| 409c9e5bfa | |||
| 19b5c8f724 | |||
| 5056ef8946 |
@@ -1,4 +1,7 @@
|
|||||||
#!/usr/bin/env python3
|
##########################
|
||||||
|
### AI-generated file. ###
|
||||||
|
##########################
|
||||||
|
|
||||||
"""Aggregate and merge benchmark JSON files.
|
"""Aggregate and merge benchmark JSON files.
|
||||||
|
|
||||||
The workflow runs the same benchmark suite on multiple independent runners.
|
The workflow runs the same benchmark suite on multiple independent runners.
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
#!/usr/bin/env python3
|
##########################
|
||||||
|
### AI-generated file. ###
|
||||||
|
##########################
|
||||||
|
|
||||||
"""Compare benchmark JSON files and write a GitHub Actions summary.
|
"""Compare benchmark JSON files and write a GitHub Actions summary.
|
||||||
|
|
||||||
The script supports JSON emitted by hyperfine, JSON emitted by pytest-benchmark,
|
The script supports JSON emitted by hyperfine, JSON emitted by pytest-benchmark,
|
||||||
|
|||||||
@@ -1,4 +1,9 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
##########################
|
||||||
|
### AI-generated file. ###
|
||||||
|
##########################
|
||||||
|
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
mkdir -p benchmark-results
|
mkdir -p benchmark-results
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
#!/usr/bin/env python3
|
##########################
|
||||||
|
### AI-generated file. ###
|
||||||
|
##########################
|
||||||
|
|
||||||
"""Run a command with BEC e2e services available."""
|
"""Run a command with BEC e2e services available."""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
name: BW Benchmarks
|
name: BW Benchmarks
|
||||||
|
|
||||||
on: [workflow_call]
|
on: [ workflow_call ]
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
@@ -10,7 +10,7 @@ env:
|
|||||||
BENCHMARK_BASELINE_JSON: gh-pages-benchmark-data/benchmarks/latest.json
|
BENCHMARK_BASELINE_JSON: gh-pages-benchmark-data/benchmarks/latest.json
|
||||||
BENCHMARK_SUMMARY: benchmark-results/summary.md
|
BENCHMARK_SUMMARY: benchmark-results/summary.md
|
||||||
BENCHMARK_COMMAND: "bash .github/scripts/run_benchmarks.sh"
|
BENCHMARK_COMMAND: "bash .github/scripts/run_benchmarks.sh"
|
||||||
BENCHMARK_THRESHOLD_PERCENT: 10
|
BENCHMARK_THRESHOLD_PERCENT: 20
|
||||||
BENCHMARK_HIGHER_IS_BETTER: false
|
BENCHMARK_HIGHER_IS_BETTER: false
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
@@ -25,7 +25,7 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
attempt: [1, 2, 3]
|
attempt: [ 1, 2, 3 ]
|
||||||
|
|
||||||
env:
|
env:
|
||||||
BENCHMARK_JSON: benchmark-results/current-${{ matrix.attempt }}.json
|
BENCHMARK_JSON: benchmark-results/current-${{ matrix.attempt }}.json
|
||||||
@@ -84,7 +84,7 @@ jobs:
|
|||||||
path: ${{ env.BENCHMARK_JSON }}
|
path: ${{ env.BENCHMARK_JSON }}
|
||||||
|
|
||||||
benchmark:
|
benchmark:
|
||||||
needs: [benchmark_attempt]
|
needs: [ benchmark_attempt ]
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
@@ -191,7 +191,7 @@ jobs:
|
|||||||
run: exit 1
|
run: exit 1
|
||||||
|
|
||||||
publish:
|
publish:
|
||||||
needs: [benchmark]
|
needs: [ benchmark ]
|
||||||
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
|
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
permissions:
|
permissions:
|
||||||
@@ -208,7 +208,10 @@ jobs:
|
|||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: bw-benchmark-json
|
name: bw-benchmark-json
|
||||||
path: .
|
path: benchmark-results
|
||||||
|
|
||||||
|
- name: Verify aggregate benchmark artifact
|
||||||
|
run: test -s "$BENCHMARK_JSON"
|
||||||
|
|
||||||
- name: Prepare gh-pages for publishing
|
- name: Prepare gh-pages for publishing
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
@@ -1,6 +1,53 @@
|
|||||||
# CHANGELOG
|
# CHANGELOG
|
||||||
|
|
||||||
|
|
||||||
|
## v3.7.2 (2026-04-29)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
- **dock-area**: Avoid switching profile when saving new profile
|
||||||
|
([`73b44cf`](https://github.com/bec-project/bec_widgets/commit/73b44cffb219347cacb609f3b93068eda6701b42))
|
||||||
|
|
||||||
|
- **workspace-actions**: Use try/finally and restore previous blocked state in refresh_profiles
|
||||||
|
([`30ef255`](https://github.com/bec-project/bec_widgets/commit/30ef25533af9df5a9bd9e69808dc49fdf22f4318))
|
||||||
|
|
||||||
|
Agent-Logs-Url:
|
||||||
|
https://github.com/bec-project/bec_widgets/sessions/004cb4bc-5015-485e-a803-1e63876b7024
|
||||||
|
|
||||||
|
Co-authored-by: wyzula-jan <133381102+wyzula-jan@users.noreply.github.com>
|
||||||
|
|
||||||
|
### Build System
|
||||||
|
|
||||||
|
- Add pytest-benchmark dependency
|
||||||
|
([`551d38d`](https://github.com/bec-project/bec_widgets/commit/551d38d90111361e64bfcf10a1409be71dd298bc))
|
||||||
|
|
||||||
|
### Chores
|
||||||
|
|
||||||
|
- Update header comments in script files to indicate AI generation
|
||||||
|
([`3f1aa80`](https://github.com/bec-project/bec_widgets/commit/3f1aa80756368454c3631cb6cf9d29db28af177a))
|
||||||
|
|
||||||
|
### Continuous Integration
|
||||||
|
|
||||||
|
- Add benchmark workflow
|
||||||
|
([`999b7a2`](https://github.com/bec-project/bec_widgets/commit/999b7a2321f2f222c04b056a2db4280f66de9c48))
|
||||||
|
|
||||||
|
- Fix benchmark upload
|
||||||
|
([`19b5c8f`](https://github.com/bec-project/bec_widgets/commit/19b5c8f724dbdb7421957c290f9213bf072392df))
|
||||||
|
|
||||||
|
- Increase threshold to 20 percent
|
||||||
|
([`409c9e5`](https://github.com/bec-project/bec_widgets/commit/409c9e5bfacdfc003f1bc8c9944f01798bd3818e))
|
||||||
|
|
||||||
|
### Testing
|
||||||
|
|
||||||
|
- Fix assertions after updating ophyd devices templates
|
||||||
|
([`a614d66`](https://github.com/bec-project/bec_widgets/commit/a614d662d6da716a49afb4ed3a3903f108210386))
|
||||||
|
|
||||||
|
Co-authored-by: Copilot <copilot@github.com>
|
||||||
|
|
||||||
|
- Remove references to "scan_motors" in tests
|
||||||
|
([`5056ef8`](https://github.com/bec-project/bec_widgets/commit/5056ef8946d03a20e802709d3fe81c84c195fe41))
|
||||||
|
|
||||||
|
|
||||||
## v3.7.1 (2026-04-21)
|
## v3.7.1 (2026-04-21)
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|||||||
@@ -235,11 +235,8 @@ class BECDockArea(DockAreaWidget):
|
|||||||
def _load_initial_profile(self, name: str) -> None:
|
def _load_initial_profile(self, name: str) -> None:
|
||||||
"""Load the initial profile."""
|
"""Load the initial profile."""
|
||||||
self.load_profile(name)
|
self.load_profile(name)
|
||||||
combo = self.toolbar.components.get_action("workspace_combo").widget
|
|
||||||
combo.blockSignals(True)
|
|
||||||
if not self._empty_profile_active:
|
if not self._empty_profile_active:
|
||||||
combo.setCurrentText(name)
|
self._set_workspace_combo_text_silent(name)
|
||||||
combo.blockSignals(False)
|
|
||||||
|
|
||||||
def _start_empty_workspace(self) -> None:
|
def _start_empty_workspace(self) -> None:
|
||||||
"""
|
"""
|
||||||
@@ -669,6 +666,14 @@ class BECDockArea(DockAreaWidget):
|
|||||||
combo = self.toolbar.components.get_action("workspace_combo").widget
|
combo = self.toolbar.components.get_action("workspace_combo").widget
|
||||||
combo.refresh_profiles(active_profile=name)
|
combo.refresh_profiles(active_profile=name)
|
||||||
|
|
||||||
|
def _set_workspace_combo_text_silent(self, text: str) -> None:
|
||||||
|
combo = self.toolbar.components.get_action("workspace_combo").widget
|
||||||
|
was_blocked = combo.blockSignals(True)
|
||||||
|
try:
|
||||||
|
combo.setCurrentText(text)
|
||||||
|
finally:
|
||||||
|
combo.blockSignals(was_blocked)
|
||||||
|
|
||||||
def _enter_empty_profile_state(self) -> None:
|
def _enter_empty_profile_state(self) -> None:
|
||||||
"""
|
"""
|
||||||
Switch to the transient empty workspace state.
|
Switch to the transient empty workspace state.
|
||||||
@@ -796,7 +801,6 @@ class BECDockArea(DockAreaWidget):
|
|||||||
self._pending_autosave_skip = (current_profile, name)
|
self._pending_autosave_skip = (current_profile, name)
|
||||||
else:
|
else:
|
||||||
self._pending_autosave_skip = None
|
self._pending_autosave_skip = None
|
||||||
workspace_combo.setCurrentText(name)
|
|
||||||
self._finalize_profile_change(name, namespace)
|
self._finalize_profile_change(name, namespace)
|
||||||
|
|
||||||
@SafeSlot()
|
@SafeSlot()
|
||||||
|
|||||||
@@ -24,19 +24,9 @@ class ProfileComboBox(QComboBox):
|
|||||||
def set_quick_profile_provider(self, provider: Callable[[], list[str]]) -> None:
|
def set_quick_profile_provider(self, provider: Callable[[], list[str]]) -> None:
|
||||||
self._quick_provider = provider
|
self._quick_provider = provider
|
||||||
|
|
||||||
def refresh_profiles(
|
def _refresh_profiles(
|
||||||
self, active_profile: str | None = None, show_empty_profile: bool = False
|
self, current_text: str, active_profile: str | None = None, show_empty_profile: bool = False
|
||||||
) -> None:
|
) -> None:
|
||||||
"""
|
|
||||||
Refresh the profile list and ensure the active profile is visible.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
active_profile(str | None): The currently active profile name.
|
|
||||||
show_empty_profile(bool): If True, show an explicit empty unsaved workspace entry.
|
|
||||||
"""
|
|
||||||
|
|
||||||
current_text = active_profile or self.currentText()
|
|
||||||
self.blockSignals(True)
|
|
||||||
self.clear()
|
self.clear()
|
||||||
|
|
||||||
quick_profiles = self._quick_provider()
|
quick_profiles = self._quick_provider()
|
||||||
@@ -103,7 +93,6 @@ class ProfileComboBox(QComboBox):
|
|||||||
if index >= 0:
|
if index >= 0:
|
||||||
self.setCurrentIndex(index)
|
self.setCurrentIndex(index)
|
||||||
|
|
||||||
self.blockSignals(False)
|
|
||||||
if active_profile and self.currentText() != active_profile:
|
if active_profile and self.currentText() != active_profile:
|
||||||
idx = self.findText(active_profile)
|
idx = self.findText(active_profile)
|
||||||
if idx >= 0:
|
if idx >= 0:
|
||||||
@@ -115,6 +104,24 @@ class ProfileComboBox(QComboBox):
|
|||||||
else:
|
else:
|
||||||
self.setToolTip("")
|
self.setToolTip("")
|
||||||
|
|
||||||
|
def refresh_profiles(
|
||||||
|
self, active_profile: str | None = None, show_empty_profile: bool = False
|
||||||
|
) -> None:
|
||||||
|
"""
|
||||||
|
Refresh the profile list and ensure the active profile is visible.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
active_profile(str | None): The currently active profile name.
|
||||||
|
show_empty_profile(bool): If True, show an explicit empty unsaved workspace entry.
|
||||||
|
"""
|
||||||
|
|
||||||
|
current_text = active_profile or self.currentText()
|
||||||
|
was_blocked = self.blockSignals(True)
|
||||||
|
try:
|
||||||
|
self._refresh_profiles(current_text, active_profile, show_empty_profile)
|
||||||
|
finally:
|
||||||
|
self.blockSignals(was_blocked)
|
||||||
|
|
||||||
|
|
||||||
def workspace_bundle(components: ToolbarComponents, enable_tools: bool = True) -> ToolbarBundle:
|
def workspace_bundle(components: ToolbarComponents, enable_tools: bool = True) -> ToolbarBundle:
|
||||||
"""
|
"""
|
||||||
@@ -122,6 +129,7 @@ def workspace_bundle(components: ToolbarComponents, enable_tools: bool = True) -
|
|||||||
|
|
||||||
Args:
|
Args:
|
||||||
components (ToolbarComponents): The components to be added to the bundle.
|
components (ToolbarComponents): The components to be added to the bundle.
|
||||||
|
enable_tools(bool): If True, show the workspace management tools; otherwise, only show the profile combo.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
ToolbarBundle: The workspace toolbar bundle.
|
ToolbarBundle: The workspace toolbar bundle.
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
[project]
|
[project]
|
||||||
name = "bec_widgets"
|
name = "bec_widgets"
|
||||||
version = "3.7.1"
|
version = "3.7.2"
|
||||||
description = "BEC Widgets"
|
description = "BEC Widgets"
|
||||||
requires-python = ">=3.11"
|
requires-python = ">=3.11"
|
||||||
classifiers = [
|
classifiers = [
|
||||||
|
|||||||
@@ -59,5 +59,4 @@ def test_run_line_scan_with_parameters_e2e(scan_control, bec_client_lib, qtbot):
|
|||||||
last_scan = queue.scan_storage.storage[-1]
|
last_scan = queue.scan_storage.storage[-1]
|
||||||
assert last_scan.status_message.info["scan_name"] == scan_name
|
assert last_scan.status_message.info["scan_name"] == scan_name
|
||||||
assert last_scan.status_message.info["exp_time"] == kwargs["exp_time"]
|
assert last_scan.status_message.info["exp_time"] == kwargs["exp_time"]
|
||||||
assert last_scan.status_message.info["scan_motors"] == [args["device"]]
|
|
||||||
assert last_scan.status_message.info["num_points"] == kwargs["steps"]
|
assert last_scan.status_message.info["num_points"] == kwargs["steps"]
|
||||||
|
|||||||
@@ -84,7 +84,6 @@ def test_scan_metadata_for_custom_scan(
|
|||||||
last_scan = queue.scan_storage.storage[-1]
|
last_scan = queue.scan_storage.storage[-1]
|
||||||
assert last_scan.status_message.info["scan_name"] == scan_name
|
assert last_scan.status_message.info["scan_name"] == scan_name
|
||||||
assert last_scan.status_message.info["exp_time"] == kwargs["exp_time"]
|
assert last_scan.status_message.info["exp_time"] == kwargs["exp_time"]
|
||||||
assert last_scan.status_message.info["scan_motors"] == [args["device"]]
|
|
||||||
assert last_scan.status_message.info["num_points"] == kwargs["steps"]
|
assert last_scan.status_message.info["num_points"] == kwargs["steps"]
|
||||||
|
|
||||||
if valid:
|
if valid:
|
||||||
|
|||||||
@@ -71,7 +71,6 @@ def bec_queue_msg_full():
|
|||||||
},
|
},
|
||||||
"report_instructions": [{"scan_progress": 20}],
|
"report_instructions": [{"scan_progress": 20}],
|
||||||
"scan_id": "2d704cc3-c172-404c-866d-608ce09fce40",
|
"scan_id": "2d704cc3-c172-404c-866d-608ce09fce40",
|
||||||
"scan_motors": ["samx"],
|
|
||||||
"scan_number": 1289,
|
"scan_number": 1289,
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -146,10 +146,7 @@ class TestDeviceManagerViewDialogs:
|
|||||||
group_combo: QtWidgets.QComboBox = dialog._control_widgets["group_combo"]
|
group_combo: QtWidgets.QComboBox = dialog._control_widgets["group_combo"]
|
||||||
assert group_combo.count() == len(OPHYD_DEVICE_TEMPLATES)
|
assert group_combo.count() == len(OPHYD_DEVICE_TEMPLATES)
|
||||||
|
|
||||||
# Test select a group from available templates
|
|
||||||
variant_combo = dialog._control_widgets["variant_combo"]
|
variant_combo = dialog._control_widgets["variant_combo"]
|
||||||
assert variant_combo.isEnabled() is False
|
|
||||||
|
|
||||||
with qtbot.waitSignal(group_combo.currentTextChanged):
|
with qtbot.waitSignal(group_combo.currentTextChanged):
|
||||||
epics_signal_index = group_combo.findText("EpicsSignal")
|
epics_signal_index = group_combo.findText("EpicsSignal")
|
||||||
group_combo.setCurrentIndex(epics_signal_index) # Select "EpicsSignal" group
|
group_combo.setCurrentIndex(epics_signal_index) # Select "EpicsSignal" group
|
||||||
@@ -235,7 +232,7 @@ class TestDeviceManagerViewDialogs:
|
|||||||
sample_config = {
|
sample_config = {
|
||||||
"name": "TestDevice",
|
"name": "TestDevice",
|
||||||
"enabled": True,
|
"enabled": True,
|
||||||
"deviceClass": "ophyd.EpicsSignal",
|
"deviceClass": "ophyd_devices.EpicsSignal",
|
||||||
"readoutPriority": "baseline",
|
"readoutPriority": "baseline",
|
||||||
"deviceConfig": {"read_pv": "X25DA-ES1-MOT:GET"},
|
"deviceConfig": {"read_pv": "X25DA-ES1-MOT:GET"},
|
||||||
}
|
}
|
||||||
@@ -248,7 +245,7 @@ class TestDeviceManagerViewDialogs:
|
|||||||
assert variant_combo.currentText() == "EpicsSignal"
|
assert variant_combo.currentText() == "EpicsSignal"
|
||||||
config = dialog._device_config_template.get_config_fields()
|
config = dialog._device_config_template.get_config_fields()
|
||||||
assert config["name"] == "TestDevice"
|
assert config["name"] == "TestDevice"
|
||||||
assert config["deviceClass"] == "ophyd.EpicsSignal"
|
assert config["deviceClass"] == "ophyd_devices.EpicsSignal"
|
||||||
assert config["deviceConfig"]["read_pv"] == "X25DA-ES1-MOT:GET"
|
assert config["deviceConfig"]["read_pv"] == "X25DA-ES1-MOT:GET"
|
||||||
|
|
||||||
# Test now to add the device config with different validation results
|
# Test now to add the device config with different validation results
|
||||||
|
|||||||
@@ -1863,9 +1863,14 @@ class TestWorkspaceProfileOperations:
|
|||||||
with patch(
|
with patch(
|
||||||
"bec_widgets.widgets.containers.dock_area.dock_area.SaveProfileDialog", StubDialog
|
"bec_widgets.widgets.containers.dock_area.dock_area.SaveProfileDialog", StubDialog
|
||||||
):
|
):
|
||||||
advanced_dock_area.save_profile(show_dialog=True)
|
widgets_before_save = list(advanced_dock_area.widget_list())
|
||||||
|
with patch.object(advanced_dock_area, "load_profile") as mock_load_profile:
|
||||||
|
advanced_dock_area.save_profile(show_dialog=True)
|
||||||
|
qtbot.wait(100)
|
||||||
|
mock_load_profile.assert_not_called()
|
||||||
|
|
||||||
qtbot.wait(500)
|
qtbot.wait(500)
|
||||||
|
assert list(advanced_dock_area.widget_list()) == widgets_before_save
|
||||||
source_manifest = read_manifest(helper.open_user(source_profile))
|
source_manifest = read_manifest(helper.open_user(source_profile))
|
||||||
new_manifest = read_manifest(helper.open_user(new_profile))
|
new_manifest = read_manifest(helper.open_user(new_profile))
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user