From dd7c71bb1e0b7ef5398b1e1a05fc1147c772420a Mon Sep 17 00:00:00 2001 From: wyzula-jan Date: Wed, 21 Aug 2024 23:10:57 +0200 Subject: [PATCH] docs(dispatcher): docs added --- bec_widgets/utils/bec_dispatcher.py | 22 ++- .../widget_development/bec_dispatcher.md | 143 ++++++++++++++++++ .../widget_development/widget_development.md | 2 + 3 files changed, 166 insertions(+), 1 deletion(-) create mode 100644 docs/developer/widget_development/bec_dispatcher.md diff --git a/bec_widgets/utils/bec_dispatcher.py b/bec_widgets/utils/bec_dispatcher.py index 86c9b3ea..7133b7e6 100644 --- a/bec_widgets/utils/bec_dispatcher.py +++ b/bec_widgets/utils/bec_dispatcher.py @@ -125,7 +125,7 @@ class BECDispatcher: topics: Union[EndpointInfo, str, list[Union[EndpointInfo, str]]], **kwargs, ) -> None: - """Connect widget's pyqt slot, so that it is called on new pub/sub topic message. + """Connect widget's qt slot, so that it is called on new pub/sub topic message. Args: slot (Callable): A slot method/function that accepts two inputs: content and metadata of @@ -138,6 +138,13 @@ class BECDispatcher: self._slots[slot].update(set(topics_str)) def disconnect_slot(self, slot: Callable, topics: Union[str, list]): + """ + Disconnect a slot from a topic. + + Args: + slot(Callable): The slot to disconnect + topics(Union[str, list]): The topic(s) to disconnect from + """ # find the right slot to disconnect from ; # slot callbacks are wrapped in QtThreadSafeCallback objects, # but the slot we receive here is the original callable @@ -153,6 +160,12 @@ class BECDispatcher: del self._slots[connected_slot] def disconnect_topics(self, topics: Union[str, list]): + """ + Disconnect all slots from a topic. + + Args: + topics(Union[str, list]): The topic(s) to disconnect from + """ self.client.connector.unregister(topics) topics_str, _ = self.client.connector._convert_endpointinfo(topics) for slot in list(self._slots.keys()): @@ -162,4 +175,11 @@ class BECDispatcher: del self._slots[slot] def disconnect_all(self, *args, **kwargs): + """ + Disconnect all slots from all topics. + + Args: + *args: Arbitrary positional arguments + **kwargs: Arbitrary keyword arguments + """ self.disconnect_topics(self.client.connector._topics_cb) diff --git a/docs/developer/widget_development/bec_dispatcher.md b/docs/developer/widget_development/bec_dispatcher.md new file mode 100644 index 00000000..1b18eee8 --- /dev/null +++ b/docs/developer/widget_development/bec_dispatcher.md @@ -0,0 +1,143 @@ +(developer.bec_dispatcher)= + +# BECDispatcher + +## Overview + +The [`BECDispatcher`](https://bec.readthedocs.io/projects/bec-widgets/en/latest/api_reference/_autosummary/bec_widgets.utils.bec_dispatcher.BECDispatcher.html#bec_widgets.utils.bec_dispatcher.BECDispatcher) +is a powerful tool that +simplifies the process of connecting Qt slots to message updates from the BEC server. It enables real-time communication +between your widget and the BEC server by listening to specific message channels and triggering callbacks when new data +is received. + +This tool is especially useful for creating widgets that need to respond to dynamic data, such as device readbacks or +scan updates. By +using [`BECDispatcher`](https://bec.readthedocs.io/projects/bec-widgets/en/latest/api_reference/_autosummary/bec_widgets.utils.bec_dispatcher.BECDispatcher.html#bec_widgets.utils.bec_dispatcher.BECDispatcher), +you +can create callback functions that react to incoming messages and update your widget's state or perform other tasks +based on the data received. + +## How It Works + +When you create a widget that needs to respond to updates from the BEC server, you can use +the [`BECDispatcher`](https://bec.readthedocs.io/projects/bec-widgets/en/latest/api_reference/_autosummary/bec_widgets.utils.bec_dispatcher.BECDispatcher.html#bec_widgets.utils.bec_dispatcher.BECDispatcher) +to +connect specific Qt slots (callback functions) to message endpoints. These endpoints are defined within the BEC system +and represent specific channels of information ( +e.g., [`device readback`](https://beamline-experiment-control.readthedocs.io/en/latest/api_reference/_autosummary/bec_lib.endpoints.MessageEndpoints.html#bec_lib.endpoints.MessageEndpoints.device_readback), +[`scan_segment`](https://beamline-experiment-control.readthedocs.io/en/latest/api_reference/_autosummary/bec_lib.endpoints.MessageEndpoints.html#bec_lib.endpoints.MessageEndpoints.scan_segment), +etc.). + +### Step-by-Step Guide + +1. **Create a Callback Function**: Define a function within your widget that will handle the data received from the BEC + server. This function should usually accept two parameters: `msg_content` (the message content) and `metadata` ( + additional + information about the message). + + ```python + # Example for a callback function that updates a widget display based on motor readback data + from qtpy.QtCore import Slot + + @Slot(dict, dict) + def on_device_readback(self, msg_content, metadata): + # Process the incoming data + new_value = msg_content["signals"]['motor_x']["value"] + # Update the widget's display or perform another action + self.update_display(new_value) + ``` + +2. **Connect the Slot to an Endpoint**: Use + the [`BECDispatcher`](https://bec.readthedocs.io/projects/bec-widgets/en/latest/api_reference/_autosummary/bec_widgets.utils.bec_dispatcher.BECDispatcher.html#bec_widgets.utils.bec_dispatcher.BECDispatcher) + to connect your callback function to a specific + endpoint. The endpoint represents the type of data or message you're interested in. + + ```python + from bec_lib.endpoints import MessageEndpoints + + self.bec_dispatcher.connect_slot(self.on_device_readback, MessageEndpoints.device_readback("motor_x")) + ``` + +3. **Handle Incoming Data**: Your callback function will be triggered automatically whenever a new message is received + on the connected endpoint. Use the data in `msg_content` to update your widget or perform other actions. + +4. **Clean Up Connections**: If your widget is being destroyed or you no longer need to listen for updates, make sure to + disconnect your slots from + the [`BECDispatcher`](https://bec.readthedocs.io/projects/bec-widgets/en/latest/api_reference/_autosummary/bec_widgets.utils.bec_dispatcher.BECDispatcher.html#bec_widgets.utils.bec_dispatcher.BECDispatcher) + to avoid memory or thread leaks. + + ```python + self.bec_dispatcher.disconnect_slot(self.on_device_readback, MessageEndpoints.device_readback("motor_x")) + ``` + +### Example: Motor Map Widget + +The [`BECMotorMap`](https://bec.readthedocs.io/projects/bec-widgets/en/latest/api_reference/_autosummary/bec_widgets.widgets.figure.plots.motor_map.motor_map.BECMotorMap.html#bec-widgets-widgets-figure-plots-motor-map-motor-map-becmotormap) +widget is a great example of +how [`BECDispatcher`](https://bec.readthedocs.io/projects/bec-widgets/en/latest/api_reference/_autosummary/bec_widgets.utils.bec_dispatcher.BECDispatcher.html#bec_widgets.utils.bec_dispatcher.BECDispatcher) +can be used to handle real-time data updates. This +widget listens for updates on specific motor positions and dynamically updates the motor map display. + +Here's a breakdown of +how [`BECDispatcher`](https://bec.readthedocs.io/projects/bec-widgets/en/latest/api_reference/_autosummary/bec_widgets.utils.bec_dispatcher.BECDispatcher.html#bec_widgets.utils.bec_dispatcher.BECDispatcher) +is used in +the [`BECMotorMap`](https://bec.readthedocs.io/projects/bec-widgets/en/latest/api_reference/_autosummary/bec_widgets.widgets.figure.plots.motor_map.motor_map.BECMotorMap.html#bec-widgets-widgets-figure-plots-motor-map-motor-map-becmotormap) +widget: + +1. **Connecting to Motor Readbacks**: + The widget connects to + the [`device readback`](https://beamline-experiment-control.readthedocs.io/en/latest/api_reference/_autosummary/bec_lib.endpoints.MessageEndpoints.html#bec_lib.endpoints.MessageEndpoints.device_readback) + endpoints using + the [`connect_slot`](https://bec.readthedocs.io/projects/bec-widgets/en/latest/api_reference/_autosummary/bec_widgets.utils.bec_dispatcher.BECDispatcher.html#bec_widgets.utils.bec_dispatcher.BECDispatcher.connect_slot) + method + of [`BECDispatcher`](https://bec.readthedocs.io/projects/bec-widgets/en/latest/api_reference/_autosummary/bec_widgets.utils.bec_dispatcher.BECDispatcher.html#bec_widgets.utils.bec_dispatcher.BECDispatcher). + This allows + the widget to receive real-time updates about the motor positions. + +```{literalinclude} ../../../bec_widgets/widgets/figure/plots/motor_map/motor_map.py +:language: python +:pyobject: BECMotorMap._connect_motor_to_slots +:dedent: 4 +``` + +2. **Handling Readback Data**: + The `on_device_readback` slot is called whenever new data is received from the motor readback. This slot processes + the data and updates the motor map plot accordingly. + +```{literalinclude} ../../../bec_widgets/widgets/figure/plots/motor_map/motor_map.py +:language: python +:pyobject: BECMotorMap.on_device_readback +:dedent: 4 +``` + +3. **Updating the Plot**: + The motor map plot is updated in response to the new data, providing a real-time visualization of the motor's + position. + +```{literalinclude} ../../../bec_widgets/widgets/figure/plots/motor_map/motor_map.py +:language: python +:pyobject: BECMotorMap._update_plot +:dedent: 4 +``` + +4. **Disconnecting When No Longer Needed**: + The widget ensures that connections are properly cleaned up when no longer needed. + +```{literalinclude} ../../../bec_widgets/widgets/figure/plots/motor_map/motor_map.py +:language: python +:pyobject: BECMotorMap._update_plot +:dedent: 4 +``` + +## Conclusion + +The [`BECDispatcher`](https://bec.readthedocs.io/projects/bec-widgets/en/latest/api_reference/_autosummary/bec_widgets.utils.bec_dispatcher.BECDispatcher.html#bec_widgets.utils.bec_dispatcher.BECDispatcher) +is a key tool for developing interactive and responsive widgets within the BEC framework. By +leveraging this tool, you can create widgets that automatically respond to real-time data updates from the BEC server, +enhancing the interactivity and functionality of your user interface. + +In next tutorials we will cover how to create a custom widget using the BECDispatcher and BECWidget base class. + +```{note} +For more details on specific messages and endpoints, please refer to the [Message Endpoints Documentation](https://beamline-experiment-control.readthedocs.io/en/latest/api_reference/_autosummary/bec_lib.endpoints.MessageEndpoints.html#bec-lib-endpoints-messageendpoints). +``` \ No newline at end of file diff --git a/docs/developer/widget_development/widget_development.md b/docs/developer/widget_development/widget_development.md index 413b40a5..9bc499ef 100644 --- a/docs/developer/widget_development/widget_development.md +++ b/docs/developer/widget_development/widget_development.md @@ -8,4 +8,6 @@ maxdepth: 2 hidden: false --- +bec_dispatcher + ``` \ No newline at end of file