Skip to content

Commit

Permalink
support PIL images as image source
Browse files Browse the repository at this point in the history
  • Loading branch information
falkoschindler committed Nov 22, 2023
1 parent e9c5824 commit 07c60aa
Show file tree
Hide file tree
Showing 3 changed files with 44 additions and 3 deletions.
27 changes: 25 additions & 2 deletions nicegui/elements/image.py
Original file line number Diff line number Diff line change
@@ -1,23 +1,46 @@
import base64
import io
import time
from pathlib import Path
from typing import Union

from PIL.Image import Image as PIL_Image

from .mixins.source_element import SourceElement


class Image(SourceElement, component='image.js'):

def __init__(self, source: Union[str, Path] = '') -> None:
def __init__(self, source: Union[str, Path, PIL_Image] = '') -> None:
"""Image
Displays an image.
This element is based on Quasar's `QImg <https://quasar.dev/vue-components/img>`_ component.
:param source: the source of the image; can be a URL, local file path or a base64 string
:param source: the source of the image; can be a URL, local file path, a base64 string or a PIL image
"""
super().__init__(source=source)

def _set_props(self, source: Union[str, Path]) -> None:
if isinstance(source, PIL_Image):
source = image_to_base64(source)
super()._set_props(source)

def force_reload(self) -> None:
"""Force the image to reload from the source."""
self._props['t'] = time.time()
self.update()


def image_to_base64(pil_image: PIL_Image, image_format: str = 'PNG') -> str:
"""Convert a PIL image to a base64 string which can be used as image source.
:param pil_image: the PIL image
:param image_format: the image format (default: 'PNG')
:return: the base64 string
"""
buffer = io.BytesIO()
pil_image.save(buffer, image_format)
base64_encoded = base64.b64encode(buffer.getvalue())
base64_string = base64_encoded.decode('utf-8')
return f'data:image/{image_format.lower()};base64,{base64_string}'
10 changes: 9 additions & 1 deletion nicegui/elements/interactive_image.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
from __future__ import annotations
import time

import time
from pathlib import Path
from typing import Any, Callable, List, Optional, Union, cast

from PIL.Image import Image as PIL_Image

from ..events import GenericEventArguments, MouseEventArguments, handle_event
from .image import image_to_base64
from .mixins.content_element import ContentElement
from .mixins.source_element import SourceElement

Expand Down Expand Up @@ -57,6 +60,11 @@ def handle_mouse(e: GenericEventArguments) -> None:
handle_event(on_mouse, arguments)
self.on('mouse', handle_mouse)

def _set_props(self, source: Union[str, Path]) -> None:
if isinstance(source, PIL_Image):
source = image_to_base64(source)
super()._set_props(source)

def force_reload(self) -> None:
"""Force the image to reload from the source."""
self._props['t'] = time.time()
Expand Down
10 changes: 10 additions & 0 deletions website/documentation/more/image_documentation.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,16 @@ def base64():
base64 = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=='
ui.image(base64).classes('w-2 h-2 m-auto')

@text_demo('PIL image', '''
You can also use a PIL image as image source.
''')
def pil():
import numpy as np
from PIL import Image

image = Image.fromarray(np.random.randint(0, 255, (100, 100), dtype=np.uint8))
ui.image(image).classes('w-32')

@text_demo('Lottie files', '''
You can also use [Lottie files](https://lottiefiles.com/) with animations.
''')
Expand Down

0 comments on commit 07c60aa

Please sign in to comment.