From 84d4c9c712ed2538305bf46b6f2f78bf5852d555 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mose=20M=C3=BCller?= Date: Thu, 17 Aug 2023 11:12:42 +0200 Subject: [PATCH] Update component section in readme --- README.md | 73 ++++++++++++++++++------------- docs/images/Slider_component.png | Bin 0 -> 23175 bytes 2 files changed, 42 insertions(+), 31 deletions(-) create mode 100644 docs/images/Slider_component.png diff --git a/README.md b/README.md index 884d429..a609b91 100644 --- a/README.md +++ b/README.md @@ -15,8 +15,11 @@ - [Understanding Tasks in pydase](#understanding-tasks-in-pydase) - [Understanding Units in pydase](#understanding-units-in-pydase) - [Understanding the Component System](#understanding-the-component-system) - - [Available Components](#available-components) - - [Extending with New Components](#extending-with-new-components) + - [Built-in Type and Enum Components](#built-in-type-and-enum-components) + - [Method Components](#method-components) + - [DataService Instances (Nested Classes)](#dataservice-instances-nested-classes) + - [Custom Components (`pydase.components`)](#custom-components-pydasecomponents) + - [Extending with New Components](#extending-with-new-components) - [Documentation](#documentation) - [Contributing](#contributing) - [License](#license) @@ -289,43 +292,34 @@ For more information about what you can do with the units, please consult the do ## Understanding the Component System In `pydase`, components are fundamental building blocks that bridge the Python backend logic with frontend visual representation and interactions. This system can be understood based on the following categories: -1. **Built-in Type and Enum Components:** +### Built-in Type and Enum Components - `pydase` automatically maps standard Python data types to corresponding frontend components. - - `str`: rendered as a `StringComponent` in the frontend. - - `int` and `float`: Represented by the `NumberComponent`. - - `bool`: displayed as a `ButtonComponent`. - - `list`: each entry of the list is rendered separately. Its name corresponds to the list attribute name and the element index. - - `enum.Enum`: rendered as an `EnumComponent` which provides a dropdown selection. +`pydase` automatically maps standard Python data types to their corresponding frontend components: +- `str`: Translated into a `StringComponent` on the frontend. +- `int` and `float`: Manifested as the `NumberComponent`. +- `bool`: Rendered as a `ButtonComponent`. +- `list`: Each item displayed individually, named after the list attribute and its index. +- `enum.Enum`: Presented as an `EnumComponent`, facilitating dropdown selection. -2. **Method Components:** +### Method Components - Methods within the `DataService` class can also have visual representation and interactivity in the frontend. - - Regular Methods: Rendered as a `MethodComponent` with an execute button. - - Asynchronous Methods: Displayed with a start/stop button in lieu of the standard execute button. +Methods within the `DataService` class have frontend representations: +- Regular Methods: These are rendered as a `MethodComponent` in the frontend, allowing users to execute the method via an "execute" button. +- Asynchronous Methods: These are manifested as the `AsyncMethodComponent` with "start"/"stop" buttons to manage the execution of [tasks](#understanding-tasks-in-pydase). -3. **DataService Instances (Nested Classes):** +### DataService Instances (Nested Classes) - Nested `DataService` instances allow for a hierarchy of components and more complex applications. Each nested instance can have its attributes and methods, which also get corresponding frontend components. +Nested `DataService` instances offer an organized hierarchy for components, enabling richer applications. Each nested class might have its own attributes and methods, each mapped to a frontend component. -4. **`pydase.components` and Custom Components:** +### Custom Components (`pydase.components`) +The custom components in `pydase` have two main parts: - Beyond built-in types and methods, `pydase` provides specialized components, such as `Image` and `NumberSlider`. These can be found in the `pydase.components` module. +- A **Python Component Class** in the backend, implementing the logic needed to set, update, and manage the component's state and data. +- A **Frontend React Component** that renders and manages user interaction in the browser. +Below are the components available in the `pydase.components` module, accompanied by their Python usage: -### Available Components - -As of the current version, pydase includes the following components: - -- `ButtonComponent`: This component corresponds to attributes of type `bool` in your `DataService` class - -- `NumberComponent`: this component corresponds to attribute of type `int` or `float` in your `DataService` class - -- `StringComponent`: this component corresponds to attribute of type `str` in your `DataService` class - -- `MethodComponent`: this component corresponds to methods in your `DataService` class. If the method is asynchronous (see [tasks in `pydase`](#understanding-tasks-in-pydase)), the frontend will render a start / stop button instead of the execute button of synchronous functions. - -- `Image`: This component allows users to display and update images within the application. It comprises an Image backend class and a frontend React component for rendering. +- `Image`: This component allows users to display and update images within the application. ```python import matplotlib.pyplot as plt @@ -361,9 +355,26 @@ As of the current version, pydase includes the following components: - `NumberSlider`: An interactive slider component to adjust numerical values, including floats and integers, on the frontend while synchronizing the data with the backend in real-time. -### Extending with New Components + ```python + import pydase + from pydase.components import NumberSlider + + + class MyService(pydase.DataService): + slider = NumberSlider(value=3.5, min=0, max=10, step_size=0.1) + + + if __name__ == "__main__": + service = MyService() + pydase.Server(service).run() + ``` + + ![Slider Component](docs/images/Slider_component.png) + +#### Extending with New Components Users can also extend the library by creating custom components. This involves defining the behavior on the Python backend and the visual representation on the frontend. For those looking to introduce new components, the [guide on adding components](./docs/dev-guide/Adding_Components.md) provides detailed steps on achieving this. + ## Documentation The full documentation provides more detailed information about `pydase`, including advanced usage examples, API references, and tips for troubleshooting common issues. See the [full documentation](URL_TO_YOUR_DOCUMENTATION) for more information. diff --git a/docs/images/Slider_component.png b/docs/images/Slider_component.png new file mode 100644 index 0000000000000000000000000000000000000000..38967f48cf14c33bfbaa91540a6dd1244257fc6b GIT binary patch literal 23175 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 literal 0 HcmV?d00001