mirror of
https://github.com/bec-project/bec_widgets.git
synced 2026-04-10 18:50:55 +02:00
Compare commits
15 Commits
v2.19.4
...
docs/wavef
| Author | SHA1 | Date | |
|---|---|---|---|
| 2f1526182b | |||
|
|
f10140e0f3 | ||
| 09c5a443aa | |||
| 3f5ab142a3 | |||
|
|
422d06d141 | ||
| 371bc485d0 | |||
|
|
70970ecf00 | ||
| 3d59c25aa9 | |||
|
|
70a06c5fd1 | ||
| 7ba8863d6a | |||
|
|
00ea8bb6c6 | ||
| e841468892 | |||
| 48a0e5831f | |||
| 1e9dd4cd25 | |||
| d10328cb5c |
60
CHANGELOG.md
60
CHANGELOG.md
@@ -1,6 +1,66 @@
|
||||
# CHANGELOG
|
||||
|
||||
|
||||
## v2.21.2 (2025-06-30)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- **waveform**: Fix waveform categorisation for aborted scans
|
||||
([`09c5a44`](https://github.com/bec-project/bec_widgets/commit/09c5a443aac675f02fa1e38179deb9863af152e2))
|
||||
|
||||
### Testing
|
||||
|
||||
- Assert config for equality, not identity
|
||||
([`3f5ab14`](https://github.com/bec-project/bec_widgets/commit/3f5ab142a3cb5446261c4faebdc7b13f10ef4a80))
|
||||
|
||||
|
||||
## v2.21.1 (2025-06-29)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- **sbb monitor**: Add missing pyproject file
|
||||
([`371bc48`](https://github.com/bec-project/bec_widgets/commit/371bc485d060404433082c9e3e00780961ce6ae3))
|
||||
|
||||
|
||||
## v2.21.0 (2025-06-28)
|
||||
|
||||
### Features
|
||||
|
||||
- **sbb monitor**: Add sbb monitor widget
|
||||
([`3d59c25`](https://github.com/bec-project/bec_widgets/commit/3d59c25aa93590a62ab4d31a4ab08589402bf407))
|
||||
|
||||
|
||||
## v2.20.1 (2025-06-28)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- **signal input base**: Unregister callback to avoid accessing deleted qt objects
|
||||
([`7ba8863`](https://github.com/bec-project/bec_widgets/commit/7ba8863d6a0c21f772e4ef8a5d4180c2a7ab49cb))
|
||||
|
||||
|
||||
## v2.20.0 (2025-06-26)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- **curve_settings**: Larger minimalWidth for the x device combobox selection
|
||||
([`48a0e58`](https://github.com/bec-project/bec_widgets/commit/48a0e5831feccd30f24218821bbc9d73f8c47933))
|
||||
|
||||
### Features
|
||||
|
||||
- **waveform**: Move x axis selection to a combobox
|
||||
([`d10328c`](https://github.com/bec-project/bec_widgets/commit/d10328cb5c775a9b7b40ed4e9f2889e63eb039ff))
|
||||
|
||||
### Refactoring
|
||||
|
||||
- **curve settings**: Move signal logic to SignalCombobox
|
||||
([`e841468`](https://github.com/bec-project/bec_widgets/commit/e84146889210165de1c4e63eb20b39f30cc5c623))
|
||||
|
||||
### Testing
|
||||
|
||||
- **curve settings**: Add curve tree elements to the dialog test
|
||||
([`1e9dd4c`](https://github.com/bec-project/bec_widgets/commit/1e9dd4cd2561d37bdda1cd86b511295c259b2831))
|
||||
|
||||
|
||||
## v2.19.4 (2025-06-26)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
@@ -49,6 +49,7 @@ _Widgets = {
|
||||
"ResetButton": "ResetButton",
|
||||
"ResumeButton": "ResumeButton",
|
||||
"RingProgressBar": "RingProgressBar",
|
||||
"SBBMonitor": "SBBMonitor",
|
||||
"ScanControl": "ScanControl",
|
||||
"ScatterWaveform": "ScatterWaveform",
|
||||
"SignalComboBox": "SignalComboBox",
|
||||
@@ -3249,6 +3250,12 @@ class RingProgressBar(RPCBase):
|
||||
"""
|
||||
|
||||
|
||||
class SBBMonitor(RPCBase):
|
||||
"""A widget to display the SBB monitor website."""
|
||||
|
||||
...
|
||||
|
||||
|
||||
class ScanControl(RPCBase):
|
||||
"""Widget to submit new scans to the queue."""
|
||||
|
||||
|
||||
@@ -169,6 +169,9 @@ class BECDockArea(BECWidget, QWidget):
|
||||
tooltip="Add LogPanel - Disabled",
|
||||
filled=True,
|
||||
),
|
||||
"sbb_monitor": MaterialIconAction(
|
||||
icon_name="train", tooltip="Add SBB Monitor", filled=True
|
||||
),
|
||||
},
|
||||
),
|
||||
"separator_2": SeparatorAction(),
|
||||
@@ -238,6 +241,9 @@ class BECDockArea(BECWidget, QWidget):
|
||||
# self.toolbar.widgets["menu_utils"].widgets["log_panel"].triggered.connect(
|
||||
# lambda: self._create_widget_from_toolbar(widget_name="LogPanel")
|
||||
# )
|
||||
self.toolbar.widgets["menu_utils"].widgets["sbb_monitor"].triggered.connect(
|
||||
lambda: self._create_widget_from_toolbar(widget_name="SBBMonitor")
|
||||
)
|
||||
|
||||
# Icons
|
||||
self.toolbar.widgets["attach_all"].action.triggered.connect(self.attach_all)
|
||||
|
||||
@@ -55,7 +55,7 @@ class DeviceSignalInputBase(BECWidget):
|
||||
self._hinted_signals = []
|
||||
self._normal_signals = []
|
||||
self._config_signals = []
|
||||
self.bec_dispatcher.client.callbacks.register(
|
||||
self._device_update_register = self.bec_dispatcher.client.callbacks.register(
|
||||
EventType.DEVICE_UPDATE, self.update_signals_from_filters
|
||||
)
|
||||
|
||||
@@ -289,3 +289,10 @@ class DeviceSignalInputBase(BECWidget):
|
||||
if config is None:
|
||||
return DeviceSignalInputBaseConfig(widget_class=self.__class__.__name__)
|
||||
return DeviceSignalInputBaseConfig.model_validate(config)
|
||||
|
||||
def cleanup(self):
|
||||
"""
|
||||
Cleanup the widget.
|
||||
"""
|
||||
self.bec_dispatcher.client.callbacks.remove(self._device_update_register)
|
||||
super().cleanup()
|
||||
|
||||
@@ -90,6 +90,36 @@ class SignalComboBox(DeviceSignalInputBase, QComboBox):
|
||||
self.insertItem(0, "Hinted Signals")
|
||||
self.model().item(0).setEnabled(False)
|
||||
|
||||
def set_to_obj_name(self, obj_name: str) -> bool:
|
||||
"""
|
||||
Set the combobox to the object name of the signal.
|
||||
|
||||
Args:
|
||||
obj_name (str): Object name of the signal.
|
||||
|
||||
Returns:
|
||||
bool: True if the object name was found and set, False otherwise.
|
||||
"""
|
||||
for i in range(self.count()):
|
||||
signal_data = self.itemData(i)
|
||||
if signal_data and signal_data.get("obj_name") == obj_name:
|
||||
self.setCurrentIndex(i)
|
||||
return True
|
||||
return False
|
||||
|
||||
def set_to_first_enabled(self) -> bool:
|
||||
"""
|
||||
Set the combobox to the first enabled item.
|
||||
|
||||
Returns:
|
||||
bool: True if an enabled item was found and set, False otherwise.
|
||||
"""
|
||||
for i in range(self.count()):
|
||||
if self.model().item(i).isEnabled():
|
||||
self.setCurrentIndex(i)
|
||||
return True
|
||||
return False
|
||||
|
||||
@SafeSlot()
|
||||
def reset_selection(self):
|
||||
"""Reset the selection of the combobox."""
|
||||
|
||||
0
bec_widgets/widgets/editors/sbb_monitor/__init__.py
Normal file
0
bec_widgets/widgets/editors/sbb_monitor/__init__.py
Normal file
@@ -0,0 +1,15 @@
|
||||
def main(): # pragma: no cover
|
||||
from qtpy import PYSIDE6
|
||||
|
||||
if not PYSIDE6:
|
||||
print("PYSIDE6 is not available in the environment. Cannot patch designer.")
|
||||
return
|
||||
from PySide6.QtDesigner import QPyDesignerCustomWidgetCollection
|
||||
|
||||
from bec_widgets.widgets.editors.sbb_monitor.sbb_monitor_plugin import SBBMonitorPlugin
|
||||
|
||||
QPyDesignerCustomWidgetCollection.addCustomWidget(SBBMonitorPlugin())
|
||||
|
||||
|
||||
if __name__ == "__main__": # pragma: no cover
|
||||
main()
|
||||
15
bec_widgets/widgets/editors/sbb_monitor/sbb_monitor.py
Normal file
15
bec_widgets/widgets/editors/sbb_monitor/sbb_monitor.py
Normal file
@@ -0,0 +1,15 @@
|
||||
from bec_widgets.widgets.editors.website.website import WebsiteWidget
|
||||
|
||||
|
||||
class SBBMonitor(WebsiteWidget):
|
||||
"""
|
||||
A widget to display the SBB monitor website.
|
||||
"""
|
||||
|
||||
PLUGIN = True
|
||||
ICON_NAME = "train"
|
||||
USER_ACCESS = []
|
||||
|
||||
def __init__(self, parent=None, **kwargs):
|
||||
url = "https://free.oevplus.ch/monitor/?viewType=splitView&layout=1&showClock=true&showPerron=true&stationGroup1Title=Villigen%2C%20PSI%20West&stationGroup2Title=Siggenthal-Würenlingen&station_1_id=85%3A3592&station_1_name=Villigen%2C%20PSI%20West&station_1_quantity=5&station_1_group=1&station_2_id=85%3A3502&station_2_name=Siggenthal-Würenlingen&station_2_quantity=5&station_2_group=2"
|
||||
super().__init__(parent=parent, url=url, **kwargs)
|
||||
@@ -0,0 +1 @@
|
||||
{'files': ['sbb_monitor.py']}
|
||||
@@ -0,0 +1,54 @@
|
||||
# Copyright (C) 2022 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
from qtpy.QtDesigner import QDesignerCustomWidgetInterface
|
||||
|
||||
from bec_widgets.utils.bec_designer import designer_material_icon
|
||||
from bec_widgets.widgets.editors.sbb_monitor.sbb_monitor import SBBMonitor
|
||||
|
||||
DOM_XML = """
|
||||
<ui language='c++'>
|
||||
<widget class='SBBMonitor' name='sbb_monitor'>
|
||||
</widget>
|
||||
</ui>
|
||||
"""
|
||||
|
||||
|
||||
class SBBMonitorPlugin(QDesignerCustomWidgetInterface): # pragma: no cover
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self._form_editor = None
|
||||
|
||||
def createWidget(self, parent):
|
||||
t = SBBMonitor(parent)
|
||||
return t
|
||||
|
||||
def domXml(self):
|
||||
return DOM_XML
|
||||
|
||||
def group(self):
|
||||
return ""
|
||||
|
||||
def icon(self):
|
||||
return designer_material_icon(SBBMonitor.ICON_NAME)
|
||||
|
||||
def includeFile(self):
|
||||
return "sbb_monitor"
|
||||
|
||||
def initialize(self, form_editor):
|
||||
self._form_editor = form_editor
|
||||
|
||||
def isContainer(self):
|
||||
return False
|
||||
|
||||
def isInitialized(self):
|
||||
return self._form_editor is not None
|
||||
|
||||
def name(self):
|
||||
return "SBBMonitor"
|
||||
|
||||
def toolTip(self):
|
||||
return ""
|
||||
|
||||
def whatsThis(self):
|
||||
return self.toolTip()
|
||||
@@ -2,13 +2,12 @@ from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from qtpy.QtCore import QSize
|
||||
from qtpy.QtCore import QSize, Qt
|
||||
from qtpy.QtWidgets import (
|
||||
QComboBox,
|
||||
QGroupBox,
|
||||
QHBoxLayout,
|
||||
QLabel,
|
||||
QLineEdit,
|
||||
QSizePolicy,
|
||||
QVBoxLayout,
|
||||
QWidget,
|
||||
@@ -16,9 +15,8 @@ from qtpy.QtWidgets import (
|
||||
|
||||
from bec_widgets.utils.error_popups import SafeSlot
|
||||
from bec_widgets.utils.settings_dialog import SettingWidget
|
||||
from bec_widgets.widgets.control.device_input.device_line_edit.device_line_edit import (
|
||||
DeviceLineEdit,
|
||||
)
|
||||
from bec_widgets.widgets.control.device_input.device_combobox.device_combobox import DeviceComboBox
|
||||
from bec_widgets.widgets.control.device_input.signal_combobox.signal_combobox import SignalComboBox
|
||||
from bec_widgets.widgets.plots.waveform.settings.curve_settings.curve_tree import CurveTree
|
||||
|
||||
if TYPE_CHECKING: # pragma: no cover
|
||||
@@ -30,6 +28,7 @@ class CurveSetting(SettingWidget):
|
||||
super().__init__(parent=parent, *args, **kwargs)
|
||||
self.setProperty("skip_settings", True)
|
||||
self.target_widget = target_widget
|
||||
self._x_settings_connected = False
|
||||
|
||||
self.layout = QVBoxLayout(self)
|
||||
|
||||
@@ -51,15 +50,23 @@ class CurveSetting(SettingWidget):
|
||||
self.mode_combo_label = QLabel("Mode")
|
||||
self.mode_combo = QComboBox()
|
||||
self.mode_combo.addItems(["auto", "index", "timestamp", "device"])
|
||||
self.mode_combo.setMinimumWidth(120)
|
||||
|
||||
self.spacer = QWidget()
|
||||
self.spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
|
||||
|
||||
self.device_x_label = QLabel("Device")
|
||||
self.device_x = DeviceLineEdit(parent=self)
|
||||
self.device_x = DeviceComboBox(parent=self)
|
||||
self.device_x.insertItem(0, "")
|
||||
self.device_x.setEditable(True)
|
||||
self.device_x.setMinimumWidth(180)
|
||||
|
||||
self.signal_x_label = QLabel("Signal")
|
||||
self.signal_x = QLineEdit()
|
||||
self.signal_x = SignalComboBox(parent=self)
|
||||
self.signal_x.include_config_signals = False
|
||||
self.signal_x.insertItem(0, "")
|
||||
self.signal_x.setEditable(True)
|
||||
self.signal_x.setMinimumWidth(180)
|
||||
|
||||
self._get_x_mode_from_waveform()
|
||||
self.switch_x_device_selection()
|
||||
@@ -85,11 +92,32 @@ class CurveSetting(SettingWidget):
|
||||
|
||||
def switch_x_device_selection(self):
|
||||
if self.mode_combo.currentText() == "device":
|
||||
self._x_settings_connected = True
|
||||
self.device_x.currentTextChanged.connect(self.signal_x.set_device)
|
||||
self.device_x.device_reset.connect(self.signal_x.reset_selection)
|
||||
|
||||
self.device_x.setEnabled(True)
|
||||
self.device_x.setText(self.target_widget.x_axis_mode["name"])
|
||||
self.signal_x.setText(self.target_widget.x_axis_mode["entry"])
|
||||
self.signal_x.setEnabled(True)
|
||||
item = self.device_x.findText(self.target_widget.x_axis_mode["name"])
|
||||
self.device_x.setCurrentIndex(item if item != -1 else 0)
|
||||
signal_x = self.target_widget.x_axis_mode.get("entry", "")
|
||||
if signal_x:
|
||||
self.signal_x.set_to_obj_name(signal_x)
|
||||
else:
|
||||
# If no match is found, set to the first enabled item
|
||||
if not self.signal_x.set_to_first_enabled():
|
||||
# If no enabled item is found, set to the first item
|
||||
self.signal_x.setCurrentIndex(0)
|
||||
else:
|
||||
self.device_x.setEnabled(False)
|
||||
self.signal_x.setEnabled(False)
|
||||
self.device_x.setCurrentIndex(0)
|
||||
self.signal_x.setCurrentIndex(0)
|
||||
|
||||
if self._x_settings_connected:
|
||||
self._x_settings_connected = False
|
||||
self.device_x.currentTextChanged.disconnect(self.signal_x.set_device)
|
||||
self.device_x.device_reset.disconnect(self.signal_x.reset_selection)
|
||||
|
||||
def _init_y_box(self):
|
||||
self.y_axis_box = QGroupBox("Y Axis")
|
||||
@@ -108,10 +136,11 @@ class CurveSetting(SettingWidget):
|
||||
Accepts the changes made in the settings widget and applies them to the target widget.
|
||||
"""
|
||||
if self.mode_combo.currentText() == "device":
|
||||
self.target_widget.x_mode = self.device_x.text()
|
||||
signal_x = self.signal_x.text()
|
||||
self.target_widget.x_mode = self.device_x.currentText()
|
||||
signal_x = self.signal_x.currentText()
|
||||
signal_data = self.signal_x.itemData(self.signal_x.currentIndex())
|
||||
if signal_x != "":
|
||||
self.target_widget.x_entry = signal_x
|
||||
self.target_widget.x_entry = signal_data.get("obj_name", signal_x)
|
||||
else:
|
||||
self.target_widget.x_mode = self.mode_combo.currentText()
|
||||
self.curve_manager.send_curve_json()
|
||||
@@ -126,5 +155,7 @@ class CurveSetting(SettingWidget):
|
||||
"""Cleanup the widget."""
|
||||
self.device_x.close()
|
||||
self.device_x.deleteLater()
|
||||
self.signal_x.close()
|
||||
self.signal_x.deleteLater()
|
||||
self.curve_manager.close()
|
||||
self.curve_manager.deleteLater()
|
||||
|
||||
@@ -142,20 +142,10 @@ class CurveRow(QTreeWidgetItem):
|
||||
# If the device name is not found, set the first enabled item
|
||||
self.device_edit.setCurrentIndex(0)
|
||||
|
||||
for i in range(self.entry_edit.count()):
|
||||
entry_data = self.entry_edit.itemData(i)
|
||||
if entry_data and entry_data.get("obj_name") == self.config.signal.entry:
|
||||
# If the device name matches an object name, set it
|
||||
self.entry_edit.setCurrentIndex(i)
|
||||
break
|
||||
else:
|
||||
# If no match found, set the first enabled item
|
||||
for i in range(self.entry_edit.count()):
|
||||
model = self.entry_edit.model()
|
||||
if model.flags(model.index(i, 0)) & Qt.ItemIsEnabled:
|
||||
self.entry_edit.setCurrentIndex(i)
|
||||
break
|
||||
else:
|
||||
if not self.entry_edit.set_to_obj_name(self.config.signal.entry):
|
||||
# If the entry is not found, try to set it to the first enabled item
|
||||
if not self.entry_edit.set_to_first_enabled():
|
||||
# If no enabled item is found, set to the first item
|
||||
self.entry_edit.setCurrentIndex(0)
|
||||
|
||||
self.tree.setItemWidget(self, 1, self.device_edit)
|
||||
|
||||
@@ -1683,9 +1683,14 @@ class Waveform(PlotBase):
|
||||
return None
|
||||
|
||||
if hasattr(self.scan_item, "live_data"):
|
||||
readout_priority = self.scan_item.status_message.info["readout_priority"] # live data
|
||||
readout_priority = self.scan_item.status_message.info.get(
|
||||
"readout_priority"
|
||||
) # live data
|
||||
else:
|
||||
readout_priority = self.scan_item.metadata["bec"]["readout_priority"] # history
|
||||
readout_priority = self.scan_item.metadata["bec"].get("readout_priority") # history
|
||||
|
||||
if readout_priority is None:
|
||||
return None
|
||||
|
||||
# Reset sync/async curve lists
|
||||
self._async_curves.clear()
|
||||
|
||||
94
docs/user/widgets/waveform/waveform_ruleset.md
Normal file
94
docs/user/widgets/waveform/waveform_ruleset.md
Normal file
@@ -0,0 +1,94 @@
|
||||
# Waveform Widget Plotting Ruleset
|
||||
|
||||
The Waveform widget allows plotting data from scans generated within the BEC framework. Each scan produces a **scan item
|
||||
**, which includes measurement data from various devices, as well as additional metadata (e.g., devices driving the
|
||||
scan, such as `motor_x`, `motor_y`, etc.).
|
||||
|
||||
The rules described below define how data from different devices and scans can be plotted together, depending on the
|
||||
selected `x_mode`.
|
||||
|
||||
---
|
||||
|
||||
## Types of Curves
|
||||
|
||||
- **Live Curves**
|
||||
- Continuously updated from the currently running scan.
|
||||
- Dynamically adapt based on live data.
|
||||
|
||||
- **History Curves**
|
||||
- Static curves representing data from a specific completed scan (e.g., scan 110).
|
||||
- Displayed only if compatible with the currently selected x-axis device.
|
||||
|
||||
---
|
||||
|
||||
## General Compatibility Rules
|
||||
|
||||
- Data from devices within **the same scan item** can always be plotted against each other, provided they have the same
|
||||
number of data points.
|
||||
- Example: plotting `detector_1` against `detector_2` from scan 110 to check signal correlation is valid.
|
||||
|
||||
- Data from **different scan items** cannot be plotted against each other.
|
||||
- Example: plotting x-data from `motor_x` in scan 110 against y-data from `detector_1` in scan 111 is not allowed.
|
||||
|
||||
---
|
||||
|
||||
## Specific Rules for Each `x_mode`
|
||||
|
||||
### 1. `x_mode='auto'` (default)
|
||||
|
||||
- Automatically determines the device for x-axis scaling from the currently running (live) scan.
|
||||
- The primary device used for scaling is the first device listed in the current scan's `scan_report_devices` (usually a
|
||||
positioner in the case of step scans).
|
||||
- **Live Curves**:
|
||||
- Always plotted against the selected x-axis device from the current scan.
|
||||
- **History Curves**:
|
||||
- Each history curve is linked to a specific scan item with scan ID.
|
||||
- Waveform widget checks compatibility with the currently selected x-axis device from the live scan.
|
||||
- If the selected x-axis device data exists in the history curve’s original scan item, the curve is displayed.
|
||||
- If the selected x-axis device data does not exist in the original scan item, the history curve remains **hidden**
|
||||
until a compatible device is selected again.
|
||||
|
||||
_Example Scenario_:
|
||||
|
||||
- The live scan currently uses `motor_x` as the x-axis device. Any history curve will only be displayed if its original
|
||||
scan item contains data for `motor_x`. If not, the history curve is hidden.
|
||||
|
||||
---
|
||||
|
||||
### 2. `x_mode='timestamp'`
|
||||
|
||||
- X-axis scaling is based on timestamps from each data point.
|
||||
- All curves, both live and history, are always compatible, as timestamps provide a universal and absolute reference for
|
||||
the x-axis.
|
||||
- Curves from different scan items can appear simultaneously, regardless of the devices measured.
|
||||
|
||||
---
|
||||
|
||||
### 3. `x_mode='index'`
|
||||
|
||||
- X-axis scaling uses data point indices (0, 1, 2, ..., N-1).
|
||||
- Allows plotting multiple curves of varying lengths in the same view.
|
||||
- All curves are always compatible, as indices represent relative positions, independent of device or timestamp, it is
|
||||
up to the user to interpret the x-axis.
|
||||
|
||||
---
|
||||
|
||||
### 4. `x_mode='device'`
|
||||
|
||||
- User explicitly selects a device to scale the x-axis.
|
||||
- The chosen device must exist in each curve’s respective scan item.
|
||||
- **Live Curves**:
|
||||
- Continuously displayed if the selected device data is being measured in the current scan.
|
||||
- **History Curves**:
|
||||
- Displayed only if the selected device exists in the scan item from which the history curve originates.
|
||||
- Remain **hidden** if the selected device is not present in the original scan item, reappearing only when a
|
||||
compatible device is chosen again.
|
||||
|
||||
---
|
||||
|
||||
## Key Technical Points
|
||||
|
||||
- Each curve stores its own independent x and y data sets (as it is defined by `pg.PlotDataItem`, allowing simultaneous
|
||||
plotting of multiple curves with different data lengths.
|
||||
- Compatibility checks ensure that plotting meaningful comparisons is always possible, avoiding combinations of
|
||||
unrelated or non-compatible datasets.
|
||||
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
||||
|
||||
[project]
|
||||
name = "bec_widgets"
|
||||
version = "2.19.4"
|
||||
version = "2.21.2"
|
||||
description = "BEC Widgets"
|
||||
requires-python = ">=3.10"
|
||||
classifiers = [
|
||||
|
||||
@@ -93,7 +93,7 @@ def test_curve_setting_switch_device_mode(curve_setting_fixture, qtbot):
|
||||
assert curve_setting.device_x.isEnabled()
|
||||
|
||||
# This line edit should reflect the waveform.x_axis_mode["name"], or be blank if none
|
||||
assert curve_setting.device_x.text() == wf.x_axis_mode["name"]
|
||||
assert curve_setting.device_x.currentText() == ""
|
||||
|
||||
|
||||
def test_curve_setting_refresh(curve_setting_fixture, qtbot):
|
||||
@@ -127,8 +127,8 @@ def test_change_device_from_target_widget(curve_setting_fixture, qtbot):
|
||||
|
||||
assert curve_setting.mode_combo.currentText() == "device"
|
||||
assert curve_setting.device_x.isEnabled()
|
||||
assert curve_setting.device_x.text() == wf.x_axis_mode["name"]
|
||||
assert curve_setting.signal_x.text() == wf.x_axis_mode["entry"]
|
||||
assert curve_setting.device_x.currentText() == wf.x_axis_mode["name"]
|
||||
assert curve_setting.signal_x.currentText() == f"{wf.x_axis_mode['entry']} (readback)"
|
||||
|
||||
|
||||
##################################################
|
||||
|
||||
@@ -144,3 +144,12 @@ def test_signal_lineedit(device_signal_line_edit):
|
||||
assert device_signal_line_edit._is_valid_input is True
|
||||
device_signal_line_edit.setText("invalid")
|
||||
assert device_signal_line_edit._is_valid_input is False
|
||||
|
||||
|
||||
def test_device_signal_input_base_cleanup(qtbot, mocked_client):
|
||||
|
||||
widget = DeviceInputWidget(client=mocked_client)
|
||||
widget.close()
|
||||
widget.deleteLater()
|
||||
|
||||
mocked_client.callbacks.remove.assert_called_once_with(widget._device_update_register)
|
||||
|
||||
@@ -29,4 +29,4 @@ def test_gui_server_get_service_config(gui_server):
|
||||
"""
|
||||
Test that the server is started with the correct arguments.
|
||||
"""
|
||||
assert gui_server._get_service_config().config is ServiceConfig().config
|
||||
assert gui_server._get_service_config().config == ServiceConfig().config
|
||||
|
||||
@@ -806,6 +806,13 @@ def test_show_curve_settings_popup(qtbot, mocked_client):
|
||||
assert wf.curve_settings_dialog.isVisible()
|
||||
assert curve_action.isChecked()
|
||||
|
||||
# add a new row to the curve tree
|
||||
wf.curve_settings_dialog.widget.curve_manager.toolbar.widgets["add"].action.trigger()
|
||||
wf.curve_settings_dialog.widget.curve_manager.toolbar.widgets["add"].action.trigger()
|
||||
qtbot.wait(100)
|
||||
# Check that the new row is added
|
||||
assert wf.curve_settings_dialog.widget.curve_manager.tree.model().rowCount() == 2
|
||||
|
||||
wf.curve_settings_dialog.close()
|
||||
assert wf.curve_settings_dialog is None
|
||||
assert not curve_action.isChecked(), "Should be unchecked after closing dialog"
|
||||
|
||||
Reference in New Issue
Block a user