bec_widgets.utils#

Submodules#

Classes#

BECConnector

Connection mixin class to handle BEC client and device manager

BECDispatcher

Utility class to keep track of slots connected to a particular redis connector

BECTable

Table widget with custom keyPressEvent to delete rows with backspace or delete key

Colors

ConnectionConfig

Configuration for BECConnector mixin class

Crosshair

Crosshair for 1D and 2D plots.

DoubleValidationDelegate

EntryValidator

GridLayoutManager

GridLayoutManager class is used to manage widgets in a QGridLayout and extend its functionality.

UILoader

Universal UI loader for PyQt6 and PySide6.

WidgetContainerUtils

Functions#

register_rpc_methods(cls)

Class decorator to scan for rpc_public methods and add them to USER_ACCESS.

rpc_public(func)

Package Contents#

class BECConnector(client=None, config: ConnectionConfig | None = None, gui_id: str | None = None, object_name: str | None = None, root_widget: bool = False, rpc_exposed: bool = True, rpc_passthrough_children: bool = True, **kwargs)#

Connection mixin class to handle BEC client and device manager

BECConnector mixin class to handle BEC client and device manager.

Parameters:
  • client (BECClient, optional) – The BEC client.

  • config (ConnectionConfig, optional) – The connection configuration with specific gui id.

  • gui_id (str, optional) – The GUI ID.

  • object_name (str, optional) – The object name.

  • root_widget (bool, optional) – If set to True, the parent_id will be always set to None, thus enforcing that the widget is accessible as a root widget of the BECGuiClient object.

  • rpc_exposed (bool, optional) – If set to False, this instance is excluded from RPC registry broadcast and CLI namespace discovery.

  • rpc_passthrough_children (bool, optional) – Only relevant when rpc_exposed=False. If True, RPC-visible children rebind to the next visible ancestor. If False (default), children stay hidden behind this widget.

  • **kwargs

_enforce_unique_sibling_name()#

Enforce that this BECConnector has a unique objectName among its siblings.

Sibling logic:
  • If there’s a nearest BECConnector parent, only compare with children of that parent.

  • If parent is None (i.e., top-level object), compare with all other top-level BECConnectors.

_get_all_rpc() dict#

Get all registered RPC objects.

_get_bec_meta_objects() dict#

Get BEC meta objects for the widget.

Returns:

BEC meta objects.

Return type:

dict

_get_rpc_parent_ancestor() BECConnector | None#

Find the nearest ancestor that is RPC-addressable.

Rules:
  • If an ancestor has rpc_exposed=False, it is an explicit visibility boundary unless rpc_passthrough_children=True.

  • If an ancestor has RPC=False (but remains rpc_exposed), it is treated as structural and children continue to the next ancestor.

  • Lookup always happens through WidgetHierarchy.get_becwidget_ancestor so plain QWidget nodes between connectors are ignored.

_set_gui_id(gui_id: str) None#

Set the GUI ID for the widget.

Parameters:

gui_id (str) – GUI ID.

_update_object_name() None#

Enforce a unique object name among siblings and register the object for RPC. This method is called through a single shot timer kicked off in the constructor.

apply_config(config: dict, generate_new_id: bool = True) None#

Apply the configuration to the widget.

Parameters:
  • config (dict) – Configuration settings.

  • generate_new_id (bool) – If True, generate a new GUI ID for the widget.

change_object_name(name: str) None#

Change the object name of the widget. Unregister old name and register the new one.

Parameters:

name (str) – The new object name.

export_settings() dict#

Export the settings of the widget as dict.

Returns:

The exported settings of the widget.

Return type:

dict

get_bec_shortcuts()#

Get BEC shortcuts for the widget.

get_config(dict_output: bool = True) dict | pydantic.BaseModel#

Get the configuration of the widget.

Parameters:

dict_output (bool) – If True, return the configuration as a dictionary. If False, return the configuration as a pydantic model.

Returns:

The configuration of the widget.

Return type:

dict | BaseModel

get_obj_by_id(obj_id: str)#
load_config(path: str | None = None, gui: bool = False)#

Load the configuration of the widget from YAML.

Parameters:
  • path (str | None) – Path to the configuration file for non-GUI dialog mode.

  • gui (bool) – If True, use the GUI dialog to load the configuration file.

load_settings(settings: dict) None#

Load the settings of the widget from dict.

Parameters:

settings (dict) – The settings to load into the widget.

on_config_update(config: ConnectionConfig | dict) None#

Update the configuration for the widget.

Parameters:

config (ConnectionConfig | dict) – Configuration settings.

remove()#

Cleanup the BECConnector

save_config(path: str | None = None, gui: bool = False)#

Save the configuration of the widget to YAML.

Parameters:
  • path (str | None) – Path to save the configuration file for non-GUI dialog mode.

  • gui (bool) – If True, use the GUI dialog to save the configuration file.

setObjectName(name: str) None#

Set the object name of the widget.

Parameters:

name (str) – The new object name.

submit_task(fn, *args, on_complete: bec_widgets.utils.error_popups.SafeSlot = None, **kwargs) Worker#

Submit a task to run in a separate thread. The task will run the specified function with the provided arguments and emit the completed signal when done.

Use this method if you want to wait for a task to complete without blocking the main thread.

Parameters:
  • fn – Function to run in a separate thread.

  • *args – Arguments for the function.

  • on_complete – Slot to run when the task is complete.

  • **kwargs – Keyword arguments for the function.

Returns:

The worker object that will run the task.

Return type:

worker

Examples

>>> def my_function(a, b):
>>>     print(a + b)
>>> self.submit_task(my_function, 1, 2)
>>> def my_function(a, b):
>>>     print(a + b)
>>> def on_complete():
>>>     print("Task complete")
>>> self.submit_task(my_function, 1, 2, on_complete=on_complete)
update_client(client) None#

Update the client and device manager from BEC and create object for BEC shortcuts.

Parameters:

client – BEC client.

EXIT_HANDLERS#
USER_ACCESS = ['_config_dict', '_get_all_rpc', '_rpc_id']#
property _config_dict: dict#

Get the configuration of the widget.

Returns:

The configuration of the widget.

Return type:

dict

property _rpc_id: str#

Get the RPC ID of the widget.

bec_dispatcher#
client#
error_utility = None#
name_established#
object_name#
property parent_id: str | None#
root_widget = False#
rpc_exposed = True#
rpc_passthrough_children = True#
rpc_register#
widget_removed#
class BECDispatcher(client=None, config: str | bec_lib.service_config.ServiceConfig | None = None, gui_id: str = None)#

Utility class to keep track of slots connected to a particular redis connector

connect_slot(slot: collections.abc.Callable, topics: bec_lib.endpoints.EndpointInfo | str | list[bec_lib.endpoints.EndpointInfo] | list[str], cb_info: dict | None = None, **kwargs) None#

Connect widget’s qt slot, so that it is called on new pub/sub topic message.

Parameters:
  • slot (Callable) – A slot method/function that accepts two inputs: content and metadata of the corresponding pub/sub message

  • list[str] (topics EndpointInfo | str | list[EndpointInfo] |) – A topic or list of topics that can typically be acquired via bec_lib.MessageEndpoints

  • cb_info (dict | None) – A dictionary containing information about the callback. Defaults to None.

disconnect_all(*args, **kwargs)#

Disconnect all slots from all topics.

Parameters:
  • *args – Arbitrary positional arguments

  • **kwargs – Arbitrary keyword arguments

disconnect_slot(slot: collections.abc.Callable, topics: bec_lib.endpoints.EndpointInfo | str | list[bec_lib.endpoints.EndpointInfo] | list[str])#

Disconnect a slot from a topic.

Parameters:
  • slot (Callable) – The slot to disconnect

  • list[str] (topics EndpointInfo | str | list[EndpointInfo] |) – A topic or list of topics to unsub from.

disconnect_topics(topics: str | list)#

Disconnect all slots from a topic.

Parameters:

topics (Union[str, list]) – The topic(s) to disconnect from

static generate_unique_identifier(length: int = 4) str#

Generate a unique identifier for the application.

Parameters:

length – The length of the identifier. Defaults to 4.

Returns:

The unique identifier.

Return type:

str

classmethod reset_singleton()#

Reset the singleton instance of the BECDispatcher.

start_cli_server(gui_id: str | None = None)#

Start the CLI server.

Parameters:

gui_id (str, optional) – The GUI ID. Defaults to None. If None, a unique identifier will be generated.

stop_cli_server()#

Stop the CLI server.

cli_server: bec_widgets.utils.rpc_server.RPCServer | None = None#
client: bec_lib.client.BECClient#
class BECTable#

Bases: qtpy.QtWidgets.QTableWidget

Table widget with custom keyPressEvent to delete rows with backspace or delete key

keyPressEvent(event) None#

Delete selected rows with backspace or delete key

Parameters:

event – keyPressEvent

class Colors#
static _blend(background: qtpy.QtGui.QColor, accent: qtpy.QtGui.QColor, t: float) qtpy.QtGui.QColor#

Blend two colors based on a tint strength t.

static _get_colormap_cached(name: str) pyqtgraph.ColorMap#
static _tint_strength(accent: qtpy.QtGui.QColor, background: qtpy.QtGui.QColor, min_tint: float = 0.06, max_tint: float = 0.18) float#

Calculate the tint strength based on the contrast between the accent and background colors. min_tint and max_tint define the range of tint strength and are empirically chosen.

Parameters:
  • accent (QColor) – The accent color.

  • background (QColor) – The background color.

  • min_tint (float) – The minimum tint strength.

  • max_tint (float) – The maximum tint strength.

Returns:

The tint strength between 0 and 1.

Return type:

float

static canonical_colormap_name(color_map: str) str#

Return an available colormap/preset name if a case-insensitive match exists.

static evenly_spaced_colors(colormap: str, num: int, format: Literal['QColor', 'HEX', 'RGB'] = 'QColor', theme_offset=0.2, theme: Literal['light', 'dark'] | None = None) list#

Extract num colors from the specified colormap, evenly spaced along its range, and return them in the specified format.

Parameters:
  • colormap (str) – Name of the colormap.

  • num (int) – Number of requested colors.

  • format (Literal["QColor","HEX","RGB"]) – The format of the returned colors (‘RGB’, ‘HEX’, ‘QColor’).

  • theme_offset (float) – Has to be between 0-1. Offset to avoid colors too close to white or black with light or dark theme respectively for pyqtgraph plot background.

  • theme (Literal['light', 'dark'] | None) – The theme to be applied. Overrides the QApplication theme if specified.

Returns:

List of colors in the specified format.

Return type:

list

Raises:

ValueError – If theme_offset is not between 0 and 1.

static get_colormap(color_map: str) pyqtgraph.ColorMap#

Resolve a string into a pg.ColorMap using either: - the pg.colormap registry (optionally including matplotlib/colorcet backends), or - GradientEditorItem presets (HistogramLUT right-click menu).

static golden_angle_color(colormap: str, num: int, format: Literal['QColor', 'HEX', 'RGB'] = 'QColor', theme_offset=0.2, theme: Literal['dark', 'light'] | None = None) list#

Extract num colors from the specified colormap following golden angle distribution and return them in the specified format.

Parameters:
  • colormap (str) – Name of the colormap.

  • num (int) – Number of requested colors.

  • format (Literal["QColor","HEX","RGB"]) – The format of the returned colors (‘RGB’, ‘HEX’, ‘QColor’).

  • theme_offset (float) – Has to be between 0-1. Offset to avoid colors too close to white or black with light or dark theme respectively for pyqtgraph plot background.

Returns:

List of colors in the specified format.

Return type:

list

Raises:

ValueError – If theme_offset is not between 0 and 1.

static golden_ratio(num: int) list#

Calculate the golden ratio for a given number of angles.

Parameters:

num (int) – Number of angles

Returns:

List of angles calculated using the golden ratio.

Return type:

list

static hex_to_rgba(hex_color: str, alpha=255) tuple#

Convert HEX color to RGBA.

Parameters:
  • hex_color (str) – HEX color string.

  • alpha (int) – Alpha value (0-255). Default is 255 (opaque).

Returns:

RGBA color tuple (r, g, b, a).

Return type:

tuple

static list_available_colormaps() list[str]#

List colormap names available via the pyqtgraph colormap registry.

Note: This does not include GradientEditorItem presets (used by HistogramLUT menus).

static list_available_gradient_presets() list[str]#

List GradientEditorItem preset names (HistogramLUT right-click menu entries).

static relative_luminance(color: qtpy.QtGui.QColor) float#

Calculate the relative luminance of a QColor according to WCAG 2.0 standards. See https://www.w3.org/TR/WCAG21/#dfn-relative-luminance.

Parameters:

color (QColor) – The color to calculate the relative luminance for.

Returns:

The relative luminance of the color.

Return type:

float

static rgba_to_hex(r: int, g: int, b: int, a: int = 255) str#

Convert RGBA color to HEX.

Parameters:
  • r (int) – Red value (0-255).

  • g (int) – Green value (0-255).

  • b (int) – Blue value (0-255).

  • a (int) – Alpha value (0-255). Default is 255 (opaque).

Returns:

HEX color string.

Return type:

hec_color(str)

static set_theme_offset(theme: Literal['light', 'dark'] | None = None, offset=0.2) tuple#

Set the theme offset to avoid colors too close to white or black with light or dark theme respectively for pyqtgraph plot background.

Parameters:
  • theme (str) – The theme to be applied.

  • offset (float) – Offset to avoid colors too close to white or black with light or dark theme respectively for pyqtgraph plot background.

Returns:

Tuple of min_pos and max_pos.

Return type:

tuple

Raises:

ValueError – If theme_offset is not between 0 and 1.

static subtle_background_color(accent: qtpy.QtGui.QColor, background: qtpy.QtGui.QColor) qtpy.QtGui.QColor#

Generate a subtle, contrast-safe background color derived from an accent color.

Parameters:
  • accent (QColor) – The accent color.

  • background (QColor) – The background color.

Returns:

The generated subtle background color.

Return type:

QColor

static validate_color(color: tuple | str) tuple | str#

Validate the color input if it is HEX or RGBA compatible. Can be used in any pydantic model as a field validator.

Parameters:

color (tuple|str) – The color to be validated. Can be a tuple of RGBA values or a HEX string.

Returns:

The validated color.

Return type:

tuple|str

static validate_color_map(color_map: str, return_error: bool = True) str | bool#

Validate the colormap input if it is supported by pyqtgraph. Can be used in any pydantic model as a field validator. If validation fails it prints all available colormaps from pyqtgraph instance.

Parameters:

color_map (str) – The colormap to be validated.

Returns:

The validated colormap, if colormap is valid. bool: False, if colormap is invalid.

Return type:

str

Raises:

PydanticCustomError – If colormap is invalid.

class ConnectionConfig(/, **data: Any)#

Bases: pydantic.BaseModel

Configuration for BECConnector mixin class

Create a new model by parsing and validating input data from keyword arguments.

Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.

self is explicitly positional-only to allow self as a field name.

classmethod generate_gui_id(v, values)#

Generate a GUI ID if none is provided.

gui_id: str | None = None#
model_config: dict#

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

widget_class: str = None#
class Crosshair(plot_item: pyqtgraph.PlotItem, precision: int | None = None, *, min_precision: int = 2, parent=None)#

Bases: qtpy.QtCore.QObject

Crosshair for 1D and 2D plots.

Parameters:
  • plot_item (pyqtgraph.PlotItem) – The plot item to which the crosshair will be attached.

  • precision (int | None, optional) – Fixed number of decimal places to display. If None, precision is chosen dynamically from the current view range.

  • min_precision (int, optional) – The lower bound (in decimal places) used when dynamic precision is enabled. Defaults to 2.

  • parent (QObject, optional) – Parent object for the QObject. Defaults to None.

_connect_to_theme_change()#

Connect to the theme change signal.

_current_precision() int#

Get the current precision based on the view range or fixed precision.

_get_transformed_position(x: float, y: float, transform: qtpy.QtGui.QTransform) tuple[qtpy.QtCore.QPointF, qtpy.QtCore.QPointF]#

Maps the given x and y coordinates to the transformed position using the provided transform. :param x: The x-coordinate to transform. :type x: float :param y: The y-coordinate to transform. :type y: float :param transform: The transformation to apply. :type transform: QTransform

_update_theme(theme: str | None = None)#

Update the theme.

apply_theme(theme: str)#

Apply the theme to the plot.

check_derivatives()#

Checks if the derivatives are enabled and updates the internal state accordingly.

check_log()#

Checks if the x or y axis is in log scale and updates the internal state accordingly.

cleanup()#
clear_markers()#

Clears the markers from the plot.

closest_x_y_value(input_x: float, list_x: list, list_y: list) tuple#

Find the closest x and y value to the input value.

Parameters:
  • input_x (float) – Input value

  • list_x (list) – List of x values

  • list_y (list) – List of y values

Returns:

Closest x and y value

Return type:

tuple

mouse_clicked(event)#

Handles the mouse clicked event, updating the crosshair position and emitting signals.

Parameters:

event – The mouse clicked event

mouse_moved(event=None, manual_pos=None)#

Handles the mouse moved event, updating the crosshair position and emitting signals.

Parameters:
  • event (object) – The mouse moved event, which contains the scene position.

  • manual_pos (tuple, optional) – A tuple containing the (x, y) coordinates to manually set the crosshair position.

reset()#

Resets the crosshair to its initial state.

scale_emitted_coordinates(x, y)#

Scales the emitted coordinates if the axes are in log scale.

Parameters:
  • x (float) – The x-coordinate

  • y (float) – The y-coordinate

Returns:

The scaled x and y coordinates

Return type:

tuple

snap_to_data(x: float, y: float) tuple[None, None] | tuple[collections.defaultdict[Any, list], collections.defaultdict[Any, list]]#

Finds the nearest data points to the given x and y coordinates.

Parameters:
  • x (float) – The x-coordinate of the mouse cursor

  • y (float) – The y-coordinate of the mouse cursor

Returns:

x and y values snapped to the nearest data

Return type:

tuple

update_coord_label(pos: tuple)#

Updates the coordinate label based on the crosshair position and axis scales.

Parameters:

pos (tuple) – The (x, y) position of the crosshair.

update_highlighted_curve(curve_index: int)#

Update the highlighted curve in the case of multiple curves in a plot item.

Parameters:

curve_index (int) – The index of curve to highlight

update_markers()#

Update the markers for the crosshair, creating new ones if necessary.

update_markers_on_image_change()#

Update markers when the image changes, e.g. when the image shape or transformation changes.

coord_label#
coordinatesChanged1D#
coordinatesChanged2D#
coordinatesClicked1D#
coordinatesClicked2D#
crosshairChanged#
crosshairClicked#
h_line#
highlighted_curve_index = None#
is_derivative = None#
is_log_x = None#
is_log_y = None#
items = []#
marker_2d_col = None#
marker_2d_row = None#
marker_clicked_1d#
marker_moved_1d#
property min_precision: int#

Lower bound on decimals when dynamic precision is used.

plot_item#
positionChanged#
positionClicked#
property precision: int | None#

Fixed number of decimals; None enables dynamic mode.

proxy#
v_line#
class DoubleValidationDelegate#

Bases: qtpy.QtWidgets.QStyledItemDelegate

createEditor(parent, option, index)#
class EntryValidator(devices)#
validate_monitor(monitor: str) str#

Validate a monitor entry for a given device.

Parameters:

monitor (str) – Monitor entry

Returns:

Monitor entry

Return type:

str

validate_signal(name: str, entry: str = None) str#

Validate a signal entry for a given device. If the entry is not provided, the first signal entry will be used from the device hints.

Parameters:
  • name (str) – Device name

  • entry (str) – Signal entry

Returns:

Signal entry

Return type:

str

devices#
class GridLayoutManager(layout: qtpy.QtWidgets.QGridLayout)#

GridLayoutManager class is used to manage widgets in a QGridLayout and extend its functionality.

The GridLayoutManager class provides methods to add, move, and check the position of widgets in a QGridLayout. It also provides a method to get the positions of all widgets in the layout.

Parameters:

layout (QGridLayout) – The layout to manage.

add_widget(widget: qtpy.QtWidgets.QWidget, row=None, col=0, rowspan=1, colspan=1, shift: Literal['down', 'up', 'left', 'right'] = 'down')#

Add a widget to the layout at the specified position.

Parameters:
  • widget (QWidget) – The widget to add.

  • row (int) – The row to add the widget to. If None, the widget will be added to the next available row.

  • col (int) – The column to add the widget to. Default is 0.

  • rowspan (int) – The number of rows the widget will span. Default is 1.

  • colspan (int) – The number of columns the widget will span. Default is 1.

  • shift (str) – The direction to shift the widgets if the position is occupied. Can be “down”, “up”, “left”, or “right”.

get_widgets_positions() dict#

Get the positions of all widgets in the layout. :returns: A dictionary with the positions of the widgets in the layout. :rtype: dict

is_position_occupied(row: int, col: int) bool#

Check if the position in the layout is occupied by a widget.

Parameters:
  • row (int) – The row to check.

  • col (int) – The column to check.

Returns:

True if the position is occupied, False otherwise.

Return type:

bool

move_widget(widget: qtpy.QtWidgets.QWidget, new_row: int, new_col: int)#

Move a widget to a new position in the layout.

Parameters:
  • widget (QWidget) – The widget to move.

  • new_row (int) – The new row to move the widget to.

  • new_col (int) – The new column to move the widget to.

shift_widgets(direction: Literal['down', 'up', 'left', 'right'] = 'down', start_row: int = 0, start_col: int = 0)#

Shift widgets in the layout in the specified direction starting from the specified position.

Parameters:
  • direction (str) – The direction to shift the widgets. Can be “down”, “up”, “left”, or “right”.

  • start_row (int) – The row to start shifting from. Default is 0.

  • start_col (int) – The column to start shifting from. Default is 0.

layout#
class UILoader(parent=None)#

Universal UI loader for PyQt6 and PySide6.

load_ui(ui_file, parent=None)#

Universal UI loader method. :param ui_file: Path to the .ui file. :type ui_file: str :param parent: Parent widget. :type parent: QWidget

Returns:

The loaded widget.

Return type:

QWidget

load_ui_pyqt6(ui_file, parent=None)#

Specific loader for PyQt6 using loadUi. :param ui_file: Path to the .ui file. :type ui_file: str :param parent: Parent widget. :type parent: QWidget

Returns:

The loaded widget.

Return type:

QWidget

load_ui_pyside6(ui_file, parent=None)#

Specific loader for PySide6 using QUiLoader. :param ui_file: Path to the .ui file. :type ui_file: str :param parent: Parent widget. :type parent: QWidget

Returns:

The loaded widget.

Return type:

QWidget

custom_widgets#
parent = None#
class WidgetContainerUtils#
static find_first_widget_by_class(container: dict, widget_class: Type[qtpy.QtWidgets.QWidget], can_fail: bool = True) qtpy.QtWidgets.QWidget | None#

Find the first widget of a given class in the figure.

Parameters:
  • container (dict) – The container of widgets.

  • widget_class (Type) – The class of the widget to find.

  • can_fail (bool) – If True, the method will return None if no widget is found. If False, it will raise an error.

Returns:

The widget of the given class.

Return type:

widget

static generate_unique_name(name: str, list_of_names: list[str] | None = None) str#

Generate a unique ID.

Parameters:

name (str) – The name of the widget.

Returns:

The unique name

Return type:

tuple (str)

static has_name_valid_chars(name: str) bool#

Check if the name is valid.

Parameters:

name (str) – The name to be checked.

Returns:

True if the name is valid, False otherwise.

Return type:

bool

static name_is_protected(name: str, container: Any = None) bool#

Check if the name is not protected.

Parameters:

name (str) – The name to be checked.

Returns:

True if the name is not protected, False otherwise.

Return type:

bool

static raise_for_invalid_name(name: str, container: Any = None) None#

Check if the name is valid. If not, raise a ValueError.

Parameters:

name (str) – The name to be checked.

Raises:

ValueError – If the name is not valid.

register_rpc_methods(cls)#

Class decorator to scan for rpc_public methods and add them to USER_ACCESS.

rpc_public(func)#