1
0
mirror of https://github.com/bec-project/bec_widgets.git synced 2026-04-08 09:47:52 +02:00

Compare commits

..

16 Commits

Author SHA1 Message Date
semantic-release
dc38f2308b 0.53.3
Automatically generated by python-semantic-release
2024-05-16 08:27:45 +00:00
7d64cac661 fix: removed apparently unnecessary sleep while waiting for an rpc response 2024-05-15 14:14:25 +02:00
semantic-release
ab4f1acd75 0.53.2
Automatically generated by python-semantic-release
2024-05-15 10:39:21 +00:00
9f8fbdd5fc fix: check device class without importing to speed up initial import time 2024-05-15 10:10:14 +02:00
d1e6cd388c fix: speed up initial import times using lazy import (from bec_lib) 2024-05-15 10:10:14 +02:00
5d09a13d88 fix: adapt to bec_lib changes (no more submodules in __init__.py) 2024-05-15 10:10:14 +02:00
0490e80c48 ci: added echo to highlight the current branch 2024-05-13 11:46:36 +02:00
semantic-release
48553ba9b1 0.53.1
Automatically generated by python-semantic-release
2024-05-09 18:58:27 +00:00
0f6a5e5fa9 fix: docs config 2024-05-09 20:51:26 +02:00
8ff36105d1 ci: fixed rtd pages url 2024-05-09 19:44:44 +02:00
semantic-release
ce78271af4 0.53.0
Automatically generated by python-semantic-release
2024-05-09 16:03:51 +00:00
57ee735e5c docs: update install instructions 2024-05-09 14:53:57 +02:00
32e1a9d847 fix: fixed semver job and upgraded to v9 2024-05-09 14:11:42 +02:00
5cc816d0af ci: use formatter config of toml file 2024-05-09 11:29:42 +02:00
4117fd7b5b refactor: applied formatter 2024-05-09 11:29:42 +02:00
c86ce302a9 feat: moved to pyproject.toml; closes #162 2024-05-09 11:21:18 +02:00
41 changed files with 334 additions and 1356 deletions

View File

@@ -7,7 +7,7 @@ variables:
DOCKER_TLS_CERTDIR: ""
BEC_CORE_BRANCH: "main"
OPHYD_DEVICES_BRANCH: "main"
CHILD_PIPELINE_BRANCH: "main"
CHILD_PIPELINE_BRANCH: $CI_DEFAULT_BRANCH
workflow:
rules:
@@ -34,6 +34,7 @@ stages:
before_script:
- if [[ "$CI_PROJECT_PATH" != "bec/bec_widgets" ]]; then
echo -e "\033[35;1m Using branch $CHILD_PIPELINE_BRANCH of BEC Widgets \033[0;m";
test -d bec_widgets || git clone --branch $CHILD_PIPELINE_BRANCH https://gitlab.psi.ch/bec/bec_widgets.git; cd bec_widgets;
fi
@@ -42,8 +43,8 @@ formatter:
needs: []
script:
- pip install black isort
- isort --check --diff --line-length=100 --profile=black --multi-line=3 --trailing-comma ./
- black --check --diff --color --line-length=100 ./
- isort --check --diff ./
- black --check --diff --color ./
rules:
- if: $CI_PROJECT_PATH == "bec/bec_widgets"
@@ -52,7 +53,7 @@ pylint:
needs: []
before_script:
- pip install pylint pylint-exit anybadge
- pip install -e .[dev]
- pip install -e .[dev,pyqt6]
script:
- mkdir ./pylint
- pylint ./bec_widgets --output-format=text --output=./pylint/pylint.log | tee ./pylint/pylint.log || pylint-exit $?
@@ -111,7 +112,7 @@ tests:
- apt-get update
- apt-get install -y libgl1-mesa-glx libegl1-mesa x11-utils libxkbcommon-x11-0 libdbus-1-3
- pip install -e ./bec/bec_lib[dev]
- pip install -e .[dev]
- pip install -e .[dev,pyqt6]
- coverage run --source=./bec_widgets -m pytest -v --junitxml=report.xml --random-order --full-trace ./tests/unit_tests
- coverage report
- coverage xml
@@ -164,7 +165,7 @@ end-2-end-conda:
- pip install -e ./bec_lib[dev]
- pip install -e ./bec_ipython_client[dev]
- cd ../
- pip install -e .[dev]
- pip install -e .[dev,pyqt6]
- cd ./tests/end-2-end
- pytest --start-servers --flush-redis --random-order
@@ -197,16 +198,15 @@ semver:
- git fetch --tags
- git tag
# build
- pip install python-semantic-release==7.* wheel
# build and publish package
- pip install python-semantic-release==9.* wheel build twine
- export GL_TOKEN=$CI_UPDATES
- export REPOSITORY_USERNAME=__token__
- export REPOSITORY_PASSWORD=$CI_PYPI_TOKEN
- >
semantic-release publish -v DEBUG
-D version_variable=./setup.py:__version__
-D hvcs=gitlab
-D branch=main
- semantic-release -vv version
# check if any artifacts were created
- if [ ! -d dist ]; then echo No release will be made; exit 0; fi
- twine upload dist/* -u __token__ -p $CI_PYPI_TOKEN --skip-existing
- semantic-release publish
allow_failure: false
rules:
@@ -223,4 +223,4 @@ pages:
TARGET_BRANCH: $CI_COMMIT_TAG
- if: '$CI_COMMIT_REF_NAME == "main" && $CI_PROJECT_PATH == "bec/bec_widgets"'
script:
- curl -X POST -d "branches=$CI_COMMIT_REF_NAME" -d "token=$RTD_TOKEN" https://readthedocs.org/api/v2/webhook/bec_widgets/253243/
- curl -X POST -d "branches=$CI_COMMIT_REF_NAME" -d "token=$RTD_TOKEN" https://readthedocs.org/api/v2/webhook/bec-widgets/253243/

File diff suppressed because it is too large Load Diff

View File

@@ -6,7 +6,7 @@ BEC Widgets is a GUI framework designed for interaction with [BEC (Beamline Expe
Use the package manager [pip](https://pip.pypa.io/en/stable/) to install BEC Widgets:
```bash
pip install bec_widgets
pip install bec_widgets PyQt6
```
For development purposes, you can clone the repository and install the package locally in editable mode:
@@ -14,10 +14,11 @@ For development purposes, you can clone the repository and install the package l
```bash
git clone https://gitlab.psi.ch/bec/bec-widgets
cd bec_widgets
pip install -e .[dev]
pip install -e .[dev,pyqt6]
```
BEC Widgets currently supports both PyQt5 and PyQt6. By default, PyQt6 is installed.
BEC Widgets currently supports both PyQt5 and PyQt6, however, no default distribution is specified. As a result, users must install one of the supported
Python Qt distributions manually.
To select a specific Python Qt distribution, install the package with an additional tag:
@@ -31,7 +32,7 @@ pip install bec_widgets[pyqt5]
```
## Documentation
Documentation of BEC Widgets can be found [here](https://bec-widgets.readthedocs.io/en/latest/). The documentation of the BEC can be found [here](https://beamline-experiment-control.readthedocs.io/en/latest/).
Documentation of BEC Widgets can be found [here](https://bec-widgets.readthedocs.io/en/latest/). The documentation of the BEC can be found [here](https://bec.readthedocs.io/en/latest/).
## Contributing

View File

@@ -1,2 +1,8 @@
from .auto_updates import AutoUpdates, ScanInfo
from bec_lib.utils.import_utils import lazy_import_from
# from .auto_updates import AutoUpdates, ScanInfo
# TODO: put back when Pydantic gets faster
AutoUpdates, ScanInfo = lazy_import_from(
"bec_widgets.cli.auto_updates", ("AutoUpdates", "ScanInfo")
)
from .client import BECDockArea, BECFigure

View File

@@ -177,7 +177,7 @@ class BECWaveform(RPCBase):
color_map_z: "Optional[str]" = "plasma",
label: "Optional[str]" = None,
validate_bec: "bool" = True,
**kwargs
**kwargs,
) -> "BECCurve":
"""
Add a curve to the plot widget from the scan segment.
@@ -205,7 +205,7 @@ class BECWaveform(RPCBase):
y: "list | np.ndarray",
label: "str" = None,
color: "str" = None,
**kwargs
**kwargs,
) -> "BECCurve":
"""
Add a custom data curve to the plot widget.
@@ -484,7 +484,7 @@ class BECFigure(RPCBase):
row: "int" = None,
col: "int" = None,
config=None,
**axis_kwargs
**axis_kwargs,
) -> "BECWaveform":
"""
Add a Waveform1D plot to the figure at the specified position.
@@ -508,7 +508,7 @@ class BECFigure(RPCBase):
row: "int" = None,
col: "int" = None,
config=None,
**axis_kwargs
**axis_kwargs,
) -> "BECImageShow":
"""
Add an image to the figure at the specified position.
@@ -536,7 +536,7 @@ class BECFigure(RPCBase):
row: "int" = None,
col: "int" = None,
config=None,
**axis_kwargs
**axis_kwargs,
) -> "BECMotorMap":
"""
Args:
@@ -566,7 +566,7 @@ class BECFigure(RPCBase):
color_map_z: "Optional[str]" = "plasma",
label: "Optional[str]" = None,
validate: "bool" = True,
**axis_kwargs
**axis_kwargs,
) -> "BECWaveform":
"""
Add a 1D waveform plot to the figure. Always access the first waveform widget in the figure.
@@ -598,7 +598,7 @@ class BECFigure(RPCBase):
color_map: "str" = "magma",
data: "np.ndarray" = None,
vrange: "tuple[float, float]" = None,
**axis_kwargs
**axis_kwargs,
) -> "BECImageShow":
"""
Add an image to the figure. Always access the first image widget in the figure.
@@ -872,7 +872,7 @@ class BECImageShow(RPCBase):
downsample: "Optional[bool]" = True,
opacity: "Optional[float]" = 1.0,
vrange: "Optional[tuple[int, int]]" = None,
**kwargs
**kwargs,
) -> "BECImageItem":
"""
None
@@ -888,7 +888,7 @@ class BECImageShow(RPCBase):
downsample: "Optional[bool]" = True,
opacity: "Optional[float]" = 1.0,
vrange: "Optional[tuple[int, int]]" = None,
**kwargs
**kwargs,
):
"""
None

View File

@@ -12,18 +12,23 @@ import uuid
from functools import wraps
from typing import TYPE_CHECKING
from bec_lib import MessageEndpoints, ServiceConfig, messages
from bec_lib.connector import MessageObject
from bec_lib.device import DeviceBase
from bec_lib.endpoints import MessageEndpoints
from bec_lib.service_config import ServiceConfig
from bec_lib.utils.import_utils import isinstance_based_on_class_name, lazy_import, lazy_import_from
from qtpy.QtCore import QCoreApplication
import bec_widgets.cli.client as client
from bec_widgets.cli.auto_updates import AutoUpdates
from bec_widgets.utils.bec_dispatcher import BECDispatcher
if TYPE_CHECKING:
from bec_lib.device import DeviceBase
from bec_widgets.cli.client import BECDockArea, BECFigure
messages = lazy_import("bec_lib.messages")
# from bec_lib.connector import MessageObject
MessageObject = lazy_import_from("bec_lib.connector", ("MessageObject",))
BECDispatcher = lazy_import_from("bec_widgets.utils.bec_dispatcher", ("BECDispatcher",))
def rpc_call(func):
"""
@@ -81,7 +86,7 @@ class BECGuiClientMixin:
@selected_device.setter
def selected_device(self, device: str | DeviceBase):
if isinstance(device, DeviceBase):
if isinstance_based_on_class_name(device, "bec_lib.device.DeviceBase"):
self._selected_device = device.name
elif isinstance(device, str):
self._selected_device = device
@@ -297,7 +302,6 @@ class RPCBase:
MessageEndpoints.gui_instruction_response(request_id)
)
QCoreApplication.processEvents() # keep UI responsive (and execute signals/slots)
time.sleep(0.1)
if response is None and (time.time() - start_time) >= timeout:
raise RPCResponseTimeoutError(request_id, timeout)

View File

@@ -5,9 +5,7 @@ from bec_widgets.widgets.figure import BECFigure
class RPCWidgetHandler:
"""Handler class for creating widgets from RPC messages."""
widget_classes = {
"BECFigure": BECFigure,
}
widget_classes = {"BECFigure": BECFigure}
@staticmethod
def create_widget(widget_type, **kwargs) -> BECConnector:

View File

@@ -3,7 +3,8 @@ import threading
import time
from typing import Literal, Union
from bec_lib import MessageEndpoints, messages
from bec_lib.endpoints import MessageEndpoints
from bec_lib.utils.import_utils import lazy_import
from qtpy.QtCore import QTimer
from bec_widgets.cli.rpc_register import RPCRegister
@@ -13,6 +14,8 @@ from bec_widgets.widgets.dock.dock_area import BECDockArea
from bec_widgets.widgets.figure import BECFigure
from bec_widgets.widgets.plots import BECCurve, BECImageShow, BECWaveform
messages = lazy_import("bec_lib.messages")
class BECWidgetsCLIServer:
WIDGETS = [BECWaveform, BECFigure, BECCurve, BECImageShow]

View File

@@ -1,7 +1,8 @@
# import simulation_progress as SP
import numpy as np
import pyqtgraph as pg
from bec_lib import MessageEndpoints, messages
from bec_lib import messages
from bec_lib.endpoints import MessageEndpoints
from qtpy.QtCore import Signal as pyqtSignal
from qtpy.QtCore import Slot as pyqtSlot
from qtpy.QtWidgets import QApplication, QVBoxLayout, QWidget
@@ -140,7 +141,7 @@ class StreamApp(QWidget):
if __name__ == "__main__":
import argparse
from bec_lib import RedisConnector
from bec_lib.redis_connector import RedisConnector
parser = argparse.ArgumentParser(description="Stream App.")
parser.add_argument("--port", type=str, default="pc15543:6379", help="Port for RedisConnector")

View File

@@ -1,6 +1,8 @@
import time
from bec_lib import MessageEndpoints, RedisConnector, messages
from bec_lib import messages
from bec_lib.endpoints import MessageEndpoints
from bec_lib.redis_connector import RedisConnector
connector = RedisConnector("localhost:6379")
metadata = {}

View File

@@ -106,10 +106,7 @@ CONFIG_SCAN_MODE = {
"sources": [
{
"type": "scan_segment",
"signals": {
"x": [{"name": "samy"}],
"y": [{"name": "gauss_adc2"}],
},
"signals": {"x": [{"name": "samy"}], "y": [{"name": "gauss_adc2"}]},
}
],
},

View File

@@ -42,7 +42,7 @@ CONFIG_DEFAULT = {
"x": [{"name": "samx", "entry": "samx"}],
"y": [{"name": "samy", "entry": "samy"}],
},
},
}
],
}

View File

@@ -5,7 +5,8 @@ from functools import partial
import numpy as np
import pyqtgraph as pg
from bec_lib import MessageEndpoints, messages
from bec_lib import messages
from bec_lib.endpoints import MessageEndpoints
from pyqtgraph.Qt import QtCore, QtWidgets, uic
from qtpy import QtGui
from qtpy.QtCore import Qt, QThread
@@ -1068,10 +1069,7 @@ class MotorApp(QWidget):
# PyQtGraph Controls
layout.addWidget(QLabel("Graph Window Controls:"))
graph_controls = [
("Left Drag", "Pan the view"),
("Right Drag or Scroll", "Zoom in/out"),
]
graph_controls = [("Left Drag", "Pan the view"), ("Right Drag or Scroll", "Zoom in/out")]
for action, description in graph_controls:
layout.addWidget(QLabel(f"{action} - {description}"))
@@ -1111,10 +1109,7 @@ class MotorControl(QThread):
motors_selected = pyqtSignal(object, object) # Signal to emit when the motors are selected
# progress_updated = pyqtSignal(int) #TODO Signal to emit progress percentage
def __init__(
self,
parent=None,
):
def __init__(self, parent=None):
super().__init__(parent)
self.action = None
@@ -1131,10 +1126,7 @@ class MotorControl(QThread):
self.motor_x_name = motor_x_name
self.motor_y_name = motor_y_name
self.motor_x, self.motor_y = (
dev[self.motor_x_name],
dev[self.motor_y_name],
)
self.motor_x, self.motor_y = (dev[self.motor_x_name], dev[self.motor_y_name])
(self.current_x, self.current_y) = self.get_coordinates()

View File

@@ -5,7 +5,8 @@ import time
import numpy as np
import pyqtgraph
import pyqtgraph as pg
from bec_lib import MessageEndpoints, messages
from bec_lib import messages
from bec_lib.endpoints import MessageEndpoints
from bec_lib.redis_connector import RedisConnector
from pyqtgraph import mkBrush, mkPen
from pyqtgraph.Qt import QtCore, QtWidgets, uic

View File

@@ -4,11 +4,13 @@ from __future__ import annotations
import time
from typing import Optional, Type
from bec_lib.utils.import_utils import lazy_import, lazy_import_from
from pydantic import BaseModel, Field, field_validator
from qtpy.QtCore import Slot as pyqtSlot
from bec_widgets.cli.rpc_register import RPCRegister
from bec_widgets.utils.bec_dispatcher import BECDispatcher
BECDispatcher = lazy_import_from("bec_widgets.utils.bec_dispatcher", ("BECDispatcher",))
class ConnectionConfig(BaseModel):

View File

@@ -6,8 +6,9 @@ from collections.abc import Callable
from typing import TYPE_CHECKING, Union
import redis
from bec_lib import BECClient, ServiceConfig
from bec_lib.client import BECClient
from bec_lib.redis_connector import MessageObject, RedisConnector
from bec_lib.service_config import ServiceConfig
from qtpy.QtCore import QObject
from qtpy.QtCore import Signal as pyqtSignal
@@ -112,9 +113,7 @@ class BECDispatcher:
cls._initialized = False
def connect_slot(
self,
slot: Callable,
topics: Union[EndpointInfo, str, list[Union[EndpointInfo, str]]],
self, slot: Callable, topics: Union[EndpointInfo, str, list[Union[EndpointInfo, str]]]
) -> None:
"""Connect widget's pyqt slot, so that it is called on new pub/sub topic message.

View File

@@ -70,10 +70,7 @@ class BECDockArea(BECConnector, DockArea):
self.docks = WeakValueDictionary(value)
def restore_state(
self,
state: dict = None,
missing: Literal["ignore", "error"] = "ignore",
extra="bottom",
self, state: dict = None, missing: Literal["ignore", "error"] = "ignore", extra="bottom"
):
"""
Restore the state of the dock area. If no state is provided, the last state is restored.

View File

@@ -263,12 +263,7 @@ class BECFigure(BECConnector, pg.GraphicsLayoutWidget):
)
# User wants to add custom curve
elif x is not None and y is not None and x_name is None and y_name is None:
waveform.add_curve_custom(
x=x,
y=y,
color=color,
label=label,
)
waveform.add_curve_custom(x=x, y=y, color=color, label=label)
return waveform
@@ -354,12 +349,7 @@ class BECFigure(BECConnector, pg.GraphicsLayoutWidget):
elif (
x is not None and y is not None and x_name is None and y_name is None and z_name is None
):
waveform.add_curve_custom(
x=x,
y=y,
color=color,
label=label,
)
waveform.add_curve_custom(x=x, y=y, color=color, label=label)
else:
raise ValueError(
"Invalid input. Provide either device names (x_name, y_name) or custom data."
@@ -534,9 +524,7 @@ class BECFigure(BECConnector, pg.GraphicsLayoutWidget):
widget_id = str(uuid.uuid4())
if config is None:
config = MotorMapConfig(
widget_class="BECMotorMap",
gui_id=widget_id,
parent_id=self.gui_id,
widget_class="BECMotorMap", gui_id=widget_id, parent_id=self.gui_id
)
motor_map = self.add_widget(
widget_type="MotorMap",

View File

@@ -113,10 +113,7 @@ CONFIG_SCAN_MODE = {
"sources": [
{
"type": "scan_segment",
"signals": {
"x": [{"name": "samy"}],
"y": [{"name": "gauss_adc2"}],
},
"signals": {"x": [{"name": "samy"}], "y": [{"name": "gauss_adc2"}]},
}
],
},
@@ -172,12 +169,7 @@ CONFIG_SCAN_MODE = {
class ConfigDialog(QWidget, Ui_Form):
config_updated = pyqtSignal(dict)
def __init__(
self,
client=None,
default_config=None,
skip_validation: bool = False,
):
def __init__(self, client=None, default_config=None, skip_validation: bool = False):
super(ConfigDialog, self).__init__()
self.setupUi(self)
@@ -386,15 +378,7 @@ class ConfigDialog(QWidget, Ui_Form):
"plot_name": self.safe_text(ui.lineEdit_plot_title),
"x_label": self.safe_text(ui.lineEdit_x_label),
"y_label": self.safe_text(ui.lineEdit_y_label),
"sources": [
{
"type": "scan_segment",
"signals": {
"x": x_signals,
"y": y_signals,
},
}
],
"sources": [{"type": "scan_segment", "signals": {"x": x_signals, "y": y_signals}}],
}
return plot_data

View File

@@ -2,7 +2,7 @@
import time
import pyqtgraph as pg
from bec_lib import MessageEndpoints
from bec_lib.endpoints import MessageEndpoints
from pydantic import ValidationError
from pyqtgraph import mkBrush, mkPen
from qtpy import QtCore

View File

@@ -6,7 +6,7 @@ from typing import Any, Union
import numpy as np
import pyqtgraph as pg
from bec_lib import MessageEndpoints
from bec_lib.endpoints import MessageEndpoints
from qtpy import QtCore, QtGui
from qtpy.QtCore import Signal as pyqtSignal
from qtpy.QtCore import Slot as pyqtSlot
@@ -214,10 +214,7 @@ class MotorMap(pg.GraphicsLayoutWidget):
endpoints.append(MessageEndpoints.device_readback(motor))
# Connect all topics to a single slot
bec_dispatcher.connect_slot(
self.on_device_readback,
endpoints,
)
bec_dispatcher.connect_slot(self.on_device_readback, endpoints)
def _add_limits_to_plot_data(self):
"""
@@ -491,11 +488,7 @@ class MotorMap(pg.GraphicsLayoutWidget):
# Update the scatter plot
self.curves_data[plot_name]["pos"].setData(
x=motor_x_data,
y=motor_y_data,
brush=brushes,
pen=None,
size=self.scatter_size,
x=motor_x_data, y=motor_y_data, brush=brushes, pen=None, size=self.scatter_size
)
# Get last know position for crosshair
@@ -595,11 +588,7 @@ if __name__ == "__main__": # pragma: no cover
client = BECDispatcher().client
client.start()
app = QApplication(sys.argv)
motor_map = MotorMap(
config=config,
gui_id=args.id,
skip_validation=True,
)
motor_map = MotorMap(config=config, gui_id=args.id, skip_validation=True)
motor_map.show()
sys.exit(app.exec())

View File

@@ -5,7 +5,7 @@ from typing import Any, Literal, Optional
import numpy as np
import pyqtgraph as pg
from bec_lib import MessageEndpoints
from bec_lib.endpoints import MessageEndpoints
from pydantic import BaseModel, Field, ValidationError
from qtpy.QtCore import QObject, QThread
from qtpy.QtCore import Signal as pyqtSignal

View File

@@ -5,7 +5,7 @@ from typing import Optional, Union
import numpy as np
import pyqtgraph as pg
from bec_lib import MessageEndpoints
from bec_lib.endpoints import MessageEndpoints
from pydantic import Field
from qtpy import QtCore, QtGui
from qtpy.QtCore import Signal as pyqtSignal
@@ -136,10 +136,7 @@ class BECMotorMap(BECPlotBase):
Returns:
dict: Data of the motor map.
"""
data = {
"x": self.database_buffer["x"],
"y": self.database_buffer["y"],
}
data = {"x": self.database_buffer["x"], "y": self.database_buffer["y"]}
return data
# TODO setup all visual properties
@@ -391,11 +388,7 @@ class BECMotorMap(BECPlotBase):
# Update the scatter plot
self.plot_components["scatter"].setData(
x=x,
y=y,
brush=brushes,
pen=None,
size=scatter_size,
x=x, y=y, brush=brushes, pen=None, size=scatter_size
)
# Get last know position for crosshair

View File

@@ -5,7 +5,7 @@ from typing import Any, Literal, Optional
import numpy as np
import pyqtgraph as pg
from bec_lib import MessageEndpoints
from bec_lib.endpoints import MessageEndpoints
from bec_lib.scan_data import ScanData
from pydantic import BaseModel, Field, ValidationError
from pyqtgraph import mkBrush

View File

@@ -1,5 +1,4 @@
import msgpack
from bec_lib import MessageEndpoints
from bec_lib.endpoints import MessageEndpoints
from qtpy.QtWidgets import (
QApplication,
QCheckBox,
@@ -438,9 +437,7 @@ if __name__ == "__main__": # pragma: no cover
client.start()
app = QApplication([])
scan_control = ScanControl(
client=client,
) # allowed_scans=["line_scan", "grid_scan"])
scan_control = ScanControl(client=client) # allowed_scans=["line_scan", "grid_scan"])
window = scan_control
window.show()

View File

@@ -9,6 +9,8 @@
import os
import pathlib
import tomli
project = "BEC Widgets"
copyright = "2023, Paul Scherrer Institute"
author = "Paul Scherrer Institute"
@@ -17,17 +19,14 @@ author = "Paul Scherrer Institute"
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
current_path = pathlib.Path(__file__).parent.parent.resolve()
version_path = f"{current_path}/pyproject.toml"
def get_version():
"""load the version from the version file"""
version_file = os.path.join(current_path, "setup.py")
with open(version_file, "r", encoding="utf-8") as file:
res = file.readline()
while not res.startswith("__version__"):
res = file.readline()
version = res.split("=")[1]
return version.strip().strip('"')
with open(version_path, "r", encoding="utf-8") as file:
res = tomli.loads(file.read())
return res["project"]["version"]
release = get_version()
@@ -79,7 +78,4 @@ html_theme = "pydata_sphinx_theme"
html_static_path = ["_static"]
html_css_files = ["css/custom.css"]
html_logo = "_static/bec.png"
html_theme_options = {
"show_nav_level": 1,
"navbar_align": "content",
}
html_theme_options = {"show_nav_level": 1, "navbar_align": "content"}

View File

@@ -20,7 +20,7 @@ cd bec_widgets
Installing the package in editable mode allows you to make changes to the code and test them in real-time.
```bash
pip install -e .[dev]
pip install -e .[dev,pyqt6]
```

View File

@@ -7,3 +7,4 @@ sphinx-copybutton
myst-parser
sphinx-design
bec-widgets
tomli

94
pyproject.toml Normal file
View File

@@ -0,0 +1,94 @@
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[project]
name = "bec_widgets"
version = "0.53.3"
description = "BEC Widgets"
requires-python = ">=3.10"
classifiers = [
"Development Status :: 3 - Alpha",
"Programming Language :: Python :: 3",
"Topic :: Scientific/Engineering",
]
dependencies = [
"pydantic",
"qtconsole",
"jedi",
"qtpy",
"pyqtgraph",
"bec_lib",
"zmq",
"h5py",
"pyqtdarktheme",
"black",
]
[project.optional-dependencies]
dev = [
"pytest",
"pytest-random-order",
"pytest-timeout",
"coverage",
"pytest-qt",
"isort",
"fakeredis",
]
pyqt5 = ["PyQt5>=5.9"]
pyqt6 = ["PyQt6>=6.7"]
[project.urls]
"Bug Tracker" = "https://gitlab.psi.ch/bec/bec_widgets/issues"
Homepage = "https://gitlab.psi.ch/bec/bec_widgets"
[tool.hatch.build.targets.wheel]
include = ["*"]
[tool.black]
line-length = 100
skip-magic-trailing-comma = true
[tool.isort]
profile = "black"
line_length = 100
multi_line_output = 3
include_trailing_comma = true
[tool.semantic_release]
build_command = "python -m build"
version_toml = ["pyproject.toml:project.version"]
[tool.semantic_release.commit_author]
env = "GIT_COMMIT_AUTHOR"
default = "semantic-release <semantic-release>"
[tool.semantic_release.commit_parser_options]
allowed_tags = [
"build",
"chore",
"ci",
"docs",
"feat",
"fix",
"perf",
"style",
"refactor",
"test",
]
minor_tags = ["feat"]
patch_tags = ["fix", "perf"]
default_bump_level = 0
[tool.semantic_release.remote]
name = "origin"
type = "gitlab"
ignore_token_for_push = false
[tool.semantic_release.remote.token]
env = "GL_TOKEN"
[tool.semantic_release.publish]
dist_glob_patterns = ["dist/*"]
upload_to_vcs_release = true

View File

@@ -1,21 +0,0 @@
[metadata]
name = bec_widgets
description = BEC Widgets
long_description = file: README.md
long_description_content_type = text/markdown
url = https://gitlab.psi.ch/bec/bec-widgets
project_urls =
Bug Tracker = https://gitlab.psi.ch/bec/bec-widgets/issues
classifiers =
Programming Language :: Python :: 3
Development Status :: 3 - Alpha
Topic :: Scientific/Engineering
[options]
package_dir =
= .
packages = find:
python_requires = >=3.10
[options.packages.find]
where = .

View File

@@ -1,56 +0,0 @@
# pylint: disable= missing-module-docstring
from setuptools import find_packages, setup
__version__ = "0.52.1"
# Default to PyQt6 if no other Qt binding is installed
QT_DEPENDENCY = "PyQt6>=6.7"
# pylint: disable=unused-import
try:
import PyQt5
except ImportError:
pass
else:
QT_DEPENDENCY = "PyQt5>=5.9"
if __name__ == "__main__":
setup(
install_requires=[
"pydantic",
"qtconsole",
QT_DEPENDENCY,
"jedi",
"qtpy",
"pyqtgraph",
"bec_lib",
"zmq",
"h5py",
"pyqtdarktheme",
"black",
],
extras_require={
"dev": [
"pytest",
"pytest-random-order",
"pytest-timeout",
"coverage",
"pytest-qt",
"black",
"isort",
"fakeredis",
],
"pyqt5": ["PyQt5>=5.9"],
"pyqt6": ["PyQt6>=6.7"],
},
version=__version__,
packages=find_packages(),
include_package_data=True,
package_data={
"": [
"*.ui",
"*.yaml",
"*.png",
]
},
)

View File

@@ -1,6 +1,6 @@
import numpy as np
import pytest
from bec_lib import MessageEndpoints
from bec_lib.endpoints import MessageEndpoints
from bec_widgets.cli.client import BECDockArea, BECFigure, BECImageShow, BECMotorMap, BECWaveform

View File

@@ -1,6 +1,6 @@
import numpy as np
import pytest
from bec_lib import MessageEndpoints
from bec_lib.endpoints import MessageEndpoints
from bec_widgets.cli.client import BECFigure, BECImageShow, BECMotorMap, BECWaveform

View File

@@ -3,9 +3,10 @@ from unittest.mock import MagicMock, patch
import fakeredis
import pytest
from bec_lib import BECClient, RedisConnector
from bec_lib.client import BECClient
from bec_lib.device import Positioner
from bec_lib.devicemanager import DeviceContainer
from bec_lib.redis_connector import RedisConnector
class FakeDevice:

View File

@@ -41,14 +41,7 @@ def send_msg_event():
@pytest.mark.parametrize(
"topics_msg_list",
[
(
("topic1", dummy_msg),
("topic2", dummy_msg),
("topic3", dummy_msg),
)
],
"topics_msg_list", [(("topic1", dummy_msg), ("topic2", dummy_msg), ("topic3", dummy_msg))]
)
def test_dispatcher_disconnect_all(bec_dispatcher_w_connector, qtbot, send_msg_event):
bec_dispatcher = bec_dispatcher_w_connector
@@ -70,15 +63,7 @@ def test_dispatcher_disconnect_all(bec_dispatcher_w_connector, qtbot, send_msg_e
assert len(bec_dispatcher.client.connector._topics_cb) == 0
@pytest.mark.parametrize(
"topics_msg_list",
[
(
("topic1", dummy_msg),
("topic2", dummy_msg),
)
],
)
@pytest.mark.parametrize("topics_msg_list", [(("topic1", dummy_msg), ("topic2", dummy_msg))])
def test_dispatcher_disconnect_one(bec_dispatcher_w_connector, qtbot, send_msg_event):
# test for BEC issue #276
bec_dispatcher = bec_dispatcher_w_connector
@@ -115,15 +100,7 @@ def test_dispatcher_2_cb_same_topic(bec_dispatcher_w_connector, qtbot, send_msg_
cb2.assert_called_once()
@pytest.mark.parametrize(
"topics_msg_list",
[
(
("topic1", dummy_msg),
("topic2", dummy_msg),
)
],
)
@pytest.mark.parametrize("topics_msg_list", [(("topic1", dummy_msg), ("topic2", dummy_msg))])
def test_dispatcher_2_topic_same_cb(bec_dispatcher_w_connector, qtbot, send_msg_event):
# test for BEC issue #276
bec_dispatcher = bec_dispatcher_w_connector

View File

@@ -26,11 +26,7 @@ def test_bec_figure_init(bec_figure):
def test_bec_figure_init_with_config(mocked_client):
config = {
"widget_class": "BECFigure",
"gui_id": "test_gui_id",
"theme": "dark",
}
config = {"widget_class": "BECFigure", "gui_id": "test_gui_id", "theme": "dark"}
widget = BECFigure(client=mocked_client, config=config)
assert widget.config.gui_id == "test_gui_id"
assert widget.config.theme == "dark"

View File

@@ -29,10 +29,7 @@ def test_motor_map_change_motors(bec_motor_map):
def test_motor_map_get_limits(bec_motor_map):
expected_limits = {
"samx": [-10, 10],
"samy": [-5, 5],
}
expected_limits = {"samx": [-10, 10], "samy": [-5, 5]}
for motor_name, expected_limit in expected_limits.items():
actual_limit = bec_motor_map._get_motor_limit(motor_name)

View File

@@ -43,11 +43,7 @@ def test_load_config(config_dialog, config_name):
@pytest.mark.parametrize(
"config_name, scan_mode",
[
("config_device", False),
("config_scan", True),
("config_device_no_entry", False),
],
[("config_device", False), ("config_scan", True), ("config_device_no_entry", False)],
)
def test_initialization(config_dialog, config_name, scan_mode):
config = load_test_config(config_name)

View File

@@ -50,7 +50,7 @@ CONFIG_DEFAULT = {
"x": [{"name": "samx", "entry": "samx"}],
"y": [{"name": "samy", "entry": "samy"}],
},
},
}
],
}

View File

@@ -58,7 +58,7 @@ CONFIG_ONE_DEVICE = {
"x": [{"name": "samx", "entry": "samx"}],
"y": [{"name": "samy", "entry": "samy"}],
},
},
}
],
}
@@ -73,10 +73,7 @@ def motor_map(qtbot, mocked_client):
def test_motor_limits_initialization(motor_map):
# Example test to check if motor limits are correctly initialized
expected_limits = {
"samx": [-10, 10],
"samy": [-5, 5],
}
expected_limits = {"samx": [-10, 10], "samy": [-5, 5]}
for motor_name, expected_limit in expected_limits.items():
actual_limit = motor_map._get_motor_limit(motor_name)
assert actual_limit == expected_limit
@@ -99,13 +96,7 @@ def test_motor_initial_position(motor_map):
assert actual_position == expected_position
@pytest.mark.parametrize(
"config, number_of_plots",
[
(CONFIG_DEFAULT, 2),
(CONFIG_ONE_DEVICE, 1),
],
)
@pytest.mark.parametrize("config, number_of_plots", [(CONFIG_DEFAULT, 2), (CONFIG_ONE_DEVICE, 1)])
def test_initialization(motor_map, config, number_of_plots):
config_load = config
motor_map.on_config_update(config_load)
@@ -131,16 +122,10 @@ def test_motor_movement_updates_position_and_database(motor_map):
motor_map.on_device_readback({"signals": {"samx": {"value": new_position_samx}}})
# Verify database update for 'samx'
assert motor_map.database["samx"]["samx"] == [
initial_position_samx,
new_position_samx,
]
assert motor_map.database["samx"]["samx"] == [initial_position_samx, new_position_samx]
# Verify 'samy' retains its last known position
assert motor_map.database["samy"]["samy"] == [
initial_position_samy,
initial_position_samy,
]
assert motor_map.database["samy"]["samy"] == [initial_position_samy, initial_position_samy]
def test_scatter_plot_rendering(motor_map):

View File

@@ -4,7 +4,8 @@ from unittest import mock
import numpy as np
import pytest
from bec_lib import RedisConnector, messages
from bec_lib import messages
from bec_lib.redis_connector import RedisConnector
from pytestqt import qtbot
from bec_widgets.examples.stream_plot.stream_plot import StreamPlot
@@ -85,12 +86,7 @@ def test_1d_plotting_data(qtbot, stream_app):
def test_flip_even_rows(qtbot, stream_app):
# Create a numpy array with some known data
original_array = np.array(
[
[1, 2, 3, 4, 5],
[6, 7, 8, 9, 10],
[11, 12, 13, 14, 15],
[16, 17, 18, 19, 20],
]
[[1, 2, 3, 4, 5], [6, 7, 8, 9, 10], [11, 12, 13, 14, 15], [16, 17, 18, 19, 20]]
)
# Call flip_even_rows on the original array
@@ -98,12 +94,7 @@ def test_flip_even_rows(qtbot, stream_app):
# Expected array flipped along the rows with even indices
expected_array = np.array(
[
[1, 2, 3, 4, 5],
[10, 9, 8, 7, 6],
[11, 12, 13, 14, 15],
[20, 19, 18, 17, 16],
]
[[1, 2, 3, 4, 5], [10, 9, 8, 7, 6], [11, 12, 13, 14, 15], [20, 19, 18, 17, 16]]
)
# Check that flip_even_rows returned the expected result