diff --git a/frontend/src/components/GenericComponent.tsx b/frontend/src/components/GenericComponent.tsx index 38af0c6..be09e98 100644 --- a/frontend/src/components/GenericComponent.tsx +++ b/frontend/src/components/GenericComponent.tsx @@ -160,6 +160,7 @@ export const GenericComponent = React.memo( readOnly={attribute.readonly} docString={attribute.doc} // Add any other specific props for the ImageComponent here + format={attribute.value['format']['value'] as string} /> ); } else { diff --git a/frontend/src/components/ImageComponent.tsx b/frontend/src/components/ImageComponent.tsx index 61a1519..2c3dece 100644 --- a/frontend/src/components/ImageComponent.tsx +++ b/frontend/src/components/ImageComponent.tsx @@ -9,33 +9,36 @@ interface ImageComponentProps { value: string; readOnly: boolean; docString: string; - // Define your component specific props here + format: string; } export const ImageComponent = React.memo((props: ImageComponentProps) => { const renderCount = useRef(0); - const { name, parentPath, value, docString } = props; - // Your component logic here + const { name, parentPath, value, docString, format } = props; useEffect(() => { renderCount.current++; - console.log(value); }); return (
- {process.env.NODE_ENV === 'development' && ( -

Render count: {renderCount.current}

- )} - - {/* Your component JSX here */} {name} - + + {process.env.NODE_ENV === 'development' && ( +

Render count: {renderCount.current}

+ )} + + {/* Your component JSX here */} + {format === '' && value === '' ? ( +

No image set in the backend.

+ ) : ( + + )}
); diff --git a/src/pydase/components/image.py b/src/pydase/components/image.py index e170296..8d94ddf 100644 --- a/src/pydase/components/image.py +++ b/src/pydase/components/image.py @@ -1,4 +1,9 @@ -from typing import Any +import base64 +import io +from pathlib import Path + +import PIL.Image +from loguru import logger from pydase.data_service.data_service import DataService @@ -6,13 +11,43 @@ from pydase.data_service.data_service import DataService class Image(DataService): def __init__( self, - value: bytes | str = "", ) -> None: - self.value = value + self._value: str = "" + self._format: str = "" super().__init__() - def __setattr__(self, __name: str, __value: Any) -> None: - if __name == "value": - if isinstance(__value, bytes): - __value = __value.decode() - return super().__setattr__(__name, __value) + @property + def value(self) -> str: + return self._value + + @property + def format(self) -> str: + return self._format + + def load_from_path(self, path: Path | str) -> None: + with PIL.Image.open(path) as image: + self._load_from_PIL_Image(image) + + def load_from_base64(self, value: bytes) -> None: + if isinstance(value, bytes): + # Decode the base64 string + image_data = base64.b64decode(value) + + # Create a writable memory buffer for the image + image_buffer = io.BytesIO(image_data) + + # Read the image from the buffer + image = PIL.Image.open(image_buffer) + self._load_from_PIL_Image(image) + + def _load_from_PIL_Image(self, image: PIL.Image.Image) -> None: + if isinstance(image, PIL.Image.Image): + if image.format is not None: + self._format = image.format + buffered = io.BytesIO() + image.save(buffered, format=self._format) + img_base64 = base64.b64encode(buffered.getvalue()) + + self._value = img_base64.decode() + else: + logger.error("Image format is 'None'. Skipping...")