Exemple #1
0
    def test_image_to_url(self, img, expected_prefix):

        url = image.image_to_url(
            img,
            width=-1,
            clamp=False,
            channels="RGB",
            output_format="JPEG",
            image_id="blah",
            allow_emoji=True,
        )
        self.assertTrue(url.startswith(expected_prefix))
Exemple #2
0
    def test_image_to_url(self, image, expected_prefix):
        from streamlit.elements.image import image_to_url

        url = image_to_url(
            image,
            width=-1,
            clamp=False,
            channels="RGB",
            output_format="JPEG",
            image_id="blah",
            allow_emoji=True,
        )
        self.assertTrue(url.startswith(expected_prefix))
Exemple #3
0
def st_canvas(
    fill_color: str = "#eee",
    stroke_width: int = 20,
    stroke_color: str = "black",
    background_color: str = "",
    background_image: Image = None,
    update_streamlit: bool = True,
    height: int = 400,
    width: int = 600,
    drawing_mode: str = "freedraw",
    initial_drawing: dict = None,
    display_toolbar: bool = True,
    point_display_radius: int = 3,
    key=None,
) -> CanvasResult:
    """Create a drawing canvas in Streamlit app. Retrieve the RGBA image data into a 4D numpy array (r, g, b, alpha)
    on mouse up event.

    Parameters
    ----------
    fill_color: str
        Color of fill for Rect in CSS color property. Defaults to "#eee".
    stroke_width: str
        Width of drawing brush in CSS color property. Defaults to 20.
    stroke_color: str
        Color of drawing brush in hex. Defaults to "black".
    background_color: str
        Color of canvas background in CSS color property. Defaults to "" which is transparent.
        Overriden by background_image.
        Note: Changing background_color will reset the drawing.
    background_image: Image
        Pillow Image to display behind canvas.
        Automatically resized to canvas dimensions.
        Being behind the canvas, it is not sent back to Streamlit on mouse event.
    update_streamlit: bool
        Whenever True, send canvas data to Streamlit when object/selection is updated or mouse up.
    height: int
        Height of canvas in pixels. Defaults to 400.
    width: int
        Width of canvas in pixels. Defaults to 600.
    drawing_mode: {'freedraw', 'transform', 'line', 'rect', 'circle', 'point', 'polygon'}
        Enable free drawing when "freedraw", object manipulation when "transform", "line", "rect", "circle", "point", "polygon".
        Defaults to "freedraw".
    initial_drawing: dict
        Redraw canvas with given initial_drawing. If changed to None then empties canvas.
        Should generally be the `json_data` output from other canvas, which you can manipulate.
        Beware: if importing from a bigger/smaller canvas, no rescaling is done in the canvas,
        it should be ran on user's side.
    display_toolbar: bool
        Display the undo/redo/reset toolbar.
    point_display_radius: int
        The radius to use when displaying point objects. Defaults to 3.
    key: str
        An optional string to use as the unique key for the widget.
        Assign a key so the component is not remount every time the script is rerun.

    Returns
    -------
    result: CanvasResult
        `image_data` contains reshaped RGBA image 4D numpy array (r, g, b, alpha),
        `json_data` stores the canvas/objects JSON representation which you can manipulate, store
        load and then reinject into another canvas through the `initial_drawing` argument.
    """
    # Resize background_image to canvas dimensions by default
    # Then override background_color
    background_image_url = None
    if background_image:
        background_image = _resize_img(background_image, height, width)
        # Reduce network traffic and cache when switch another configure, use streamlit in-mem filemanager to convert image to URL
        background_image_url = st_image.image_to_url(
            background_image, width, True, "RGB", "PNG",
            f"drawable-canvas-bg-{md5(background_image.tobytes()).hexdigest()}-{key}"
        )
        # on a dev environment, tell React to fetch image from Streamlit server
        # on a build environment, the URL hosts are the same
        if not _RELEASE:
            background_image_url = f"http://localhost:8501{background_image_url}"
        background_color = ""

    # Clean initial drawing, override its background color
    initial_drawing = ({
        "version": "4.4.0"
    } if initial_drawing is None else initial_drawing)
    initial_drawing["background"] = background_color

    component_value = _component_func(
        fillColor=fill_color,
        strokeWidth=stroke_width,
        strokeColor=stroke_color,
        backgroundColor=background_color,
        backgroundImageURL=background_image_url,
        realtimeUpdateStreamlit=update_streamlit
        and (drawing_mode != "polygon"),
        canvasHeight=height,
        canvasWidth=width,
        drawingMode=drawing_mode,
        initialDrawing=initial_drawing,
        displayToolbar=display_toolbar,
        displayRadius=point_display_radius,
        key=key,
        default=None,
    )
    if component_value is None:
        return CanvasResult

    return CanvasResult(
        np.asarray(_data_url_to_image(component_value["data"])),
        component_value["raw"],
    )
Exemple #4
0
def set_page_config(
    page_title=None,
    page_icon=None,
    layout="centered",
    initial_sidebar_state="auto",
    menu_items=None,
):
    """
    Configures the default settings of the page.

    .. note::
        This must be the first Streamlit command used in your app, and must only
        be set once.

    Parameters
    ----------
    page_title: str or None
        The page title, shown in the browser tab. If None, defaults to the
        filename of the script ("app.py" would show "app • Streamlit").
    page_icon : Anything supported by st.image or str or None
        The page favicon.
        Besides the types supported by `st.image` (like URLs or numpy arrays),
        you can pass in an emoji as a string ("🦈") or a shortcode (":shark:").
        If you're feeling lucky, try "random" for a random emoji!
        Emoji icons are courtesy of Twemoji and loaded from MaxCDN.
    layout: "centered" or "wide"
        How the page content should be laid out. Defaults to "centered",
        which constrains the elements into a centered column of fixed width;
        "wide" uses the entire screen.
    initial_sidebar_state: "auto" or "expanded" or "collapsed"
        How the sidebar should start out. Defaults to "auto",
        which hides the sidebar on mobile-sized devices, and shows it otherwise.
        "expanded" shows the sidebar initially; "collapsed" hides it.
    menu_items: dict
        Configure the menu that appears on the top-right side of this app.
        The keys in this dict denote the menu item you'd like to configure:

        - "Get help": str or None
            The URL this menu item should point to.
            If None, hides this menu item.
        - "Report a Bug": str or None
            The URL this menu item should point to.
            If None, hides this menu item.
        - "About": str or None
            A markdown string to show in the About dialog.
            If None, only shows Streamlit's default About text.


    Example
    -------
    >>> st.set_page_config(
    ...     page_title="Ex-stream-ly Cool App",
    ...     page_icon="🧊",
    ...     layout="wide",
    ...     initial_sidebar_state="expanded",
    ...     menu_items={
    ...         'Get Help': 'https://www.extremelycoolapp.com/help',
    ...         'Report a bug': "https://www.extremelycoolapp.com/bug",
    ...         'About': "# This is a header. This is an *extremely* cool app!"
    ...     }
    ... )
    """

    msg = ForwardMsg_pb2.ForwardMsg()

    if page_title:
        msg.page_config_changed.title = page_title

    if page_icon:
        if page_icon == "random":
            page_icon = get_random_emoji()

        msg.page_config_changed.favicon = image.image_to_url(
            page_icon,
            width=-1,  # Always use full width for favicons
            clamp=False,
            channels="RGB",
            output_format="JPEG",
            image_id="favicon",
            allow_emoji=True,
        )

    if layout == "centered":
        layout = PageConfig_pb2.PageConfig.CENTERED
    elif layout == "wide":
        layout = PageConfig_pb2.PageConfig.WIDE
    else:
        raise StreamlitAPIException(
            f'`layout` must be "centered" or "wide" (got "{layout}")'
        )
    msg.page_config_changed.layout = layout

    if initial_sidebar_state == "auto":
        initial_sidebar_state = PageConfig_pb2.PageConfig.AUTO
    elif initial_sidebar_state == "expanded":
        initial_sidebar_state = PageConfig_pb2.PageConfig.EXPANDED
    elif initial_sidebar_state == "collapsed":
        initial_sidebar_state = PageConfig_pb2.PageConfig.COLLAPSED
    else:
        raise StreamlitAPIException(
            '`initial_sidebar_state` must be "auto" or "expanded" or "collapsed" '
            + f'(got "{initial_sidebar_state}")'
        )

    msg.page_config_changed.initial_sidebar_state = initial_sidebar_state

    if menu_items is not None:
        lowercase_menu_items = lower_clean_dict_keys(menu_items)
        validate_menu_items(lowercase_menu_items)
        menu_items_proto = msg.page_config_changed.menu_items
        set_menu_items_proto(lowercase_menu_items, menu_items_proto)

    ctx = get_report_ctx()
    if ctx is None:
        return
    ctx.enqueue(msg)
def set_page_config(page_title=None,
                    page_icon=None,
                    layout="centered",
                    initial_sidebar_state="auto"):
    """
    Configures the default settings of the page.

    .. note::
        This must be the first Streamlit command used in your app, and must only
        be set once.

    Parameters
    ----------
    page_title: str or None
        The page title, shown in the browser tab. If None, defaults to the
        filename of the script ("app.py" would show "app • Streamlit").
    page_icon : Anything supported by st.image or str or None
        The page favicon.
        Besides the types supported by `st.image` (like URLs or numpy arrays),
        you can pass in an emoji as a string ("🦈") or a shortcode (":shark:").
        If you're feeling lucky, try "random" for a random emoji!
        Emoji icons are courtesy of Twemoji and loaded from MaxCDN.
    layout: "centered" or "wide"
        How the page content should be laid out. Defaults to "centered",
        which constrains the elements into a centered column of fixed width;
        "wide" uses the entire screen.
    initial_sidebar_state: "auto" or "expanded" or "collapsed"
        How the sidebar should start out. Defaults to "auto",
        which hides the sidebar on mobile-sized devices, and shows it otherwise.
        "expanded" shows the sidebar initially; "collapsed" hides it.

    Example
    -------
    >>> st.set_page_config(
    ...     page_title="Ex-stream-ly Cool App",
    ...     page_icon="🧊",
    ...     layout="wide",
    ...     initial_sidebar_state="expanded",
    ... )
    """

    msg = ForwardMsg_pb2.ForwardMsg()

    if page_title:
        msg.page_config_changed.title = page_title

    if page_icon:
        if page_icon == "random":
            page_icon = get_random_emoji()

        msg.page_config_changed.favicon = image.image_to_url(
            page_icon,
            width=-1,  # Always use full width for favicons
            clamp=False,
            channels="RGB",
            output_format="JPEG",
            image_id="favicon",
            allow_emoji=True,
        )

    if layout == "centered":
        layout = PageConfig_pb2.PageConfig.CENTERED
    elif layout == "wide":
        layout = PageConfig_pb2.PageConfig.WIDE
    else:
        raise StreamlitAPIException(
            f'`layout` must be "centered" or "wide" (got "{layout}")')
    msg.page_config_changed.layout = layout

    if initial_sidebar_state == "auto":
        initial_sidebar_state = PageConfig_pb2.PageConfig.AUTO
    elif initial_sidebar_state == "expanded":
        initial_sidebar_state = PageConfig_pb2.PageConfig.EXPANDED
    elif initial_sidebar_state == "collapsed":
        initial_sidebar_state = PageConfig_pb2.PageConfig.COLLAPSED
    else:
        raise StreamlitAPIException(
            '`initial_sidebar_state` must be "auto" or "expanded" or "collapsed" '
            + f'(got "{initial_sidebar_state}")')

    msg.page_config_changed.initial_sidebar_state = initial_sidebar_state

    ctx = get_report_ctx()
    if ctx is None:
        return
    ctx.enqueue(msg)
def image_crop(
    image: Union[bytes, Image],
    crop: Optional[Crop] = None,
    width_preview: Optional[int] = None,
    image_alt: Optional[str] = None,
    min_width: Optional[int] = None,
    min_height: Optional[int] = None,
    max_width: Optional[int] = None,
    max_height: Optional[int] = None,
    # FIXME: Changing these properties, the component is rerendered unfortunately.
    # ----
    # keep_selection: Optional[bool] = None,
    # disabled: Optional[bool] = None,
    # locked: Optional[bool] = None,
    rule_of_thirds: Optional[bool] = None,
    circular_crop: Optional[bool] = None,
    # ----
    key: Optional[str] = None,
) -> Optional[Image]:
    import dataclasses
    from io import BytesIO
    from os import path

    import streamlit as st
    from PIL.Image import composite as composite_image
    from PIL.Image import new as new_image
    from PIL.Image import open as open_image
    from PIL.ImageDraw import Draw
    from streamlit.components import v1 as components
    from streamlit.elements.image import image_to_url

    global _impl

    if _impl is None:
        if _DEBUG:
            option_address = st.get_option("browser.serverAddress")
            option_port = st.get_option("browser.serverPort")
            _impl = (
                components.declare_component(
                    "image_crop",
                    url="http://localhost:3001",
                ),
                lambda s: f"http://{option_address}:{option_port}" + s,
            )
        else:
            _impl = (
                components.declare_component(
                    "image_crop",
                    path=path.join(path.dirname(path.abspath(__file__)),
                                   "frontend/build"),
                ),
                lambda s: s,
            )

    if isinstance(image, Image):
        image_ = image
    else:
        image_ = open_image(BytesIO(image))

    width, _ = image_.size

    src = image_to_url(
        image_,
        width=min(width, width_preview) if width_preview else width,
        clamp=False,
        channels="RGB",
        output_format="auto",
        image_id="foo",
    )

    crop_ = None if crop is None else dataclasses.asdict(crop)

    default = {
        "width": 0.0,
        "height": 0.0,
        "x": 0.0,
        "y": 0.0,
    }

    component, build_url = _impl

    result = component(
        src=build_url(src),
        image_alt=image_alt,
        minWidth=min_width,
        minHeight=min_height,
        maxWidth=max_width,
        maxHeight=max_height,
        # FIXME: Changing these properties, the component is rerendered unfortunately.
        # ----
        keepSelection=None,
        disabled=None,
        locked=None,
        ruleOfThirds=rule_of_thirds,
        circularCrop=circular_crop,
        # ----
        crop=crop_,
        key=key,
        default=default,
    )

    w, h = image_.size

    w_crop = int(w * float(result["width"]) / 100)
    h_crop = int(h * float(result["height"]) / 100)
    x0 = int(w * float(result["x"]) / 100)
    y0 = int(h * float(result["y"]) / 100)
    x1 = x0 + w_crop
    y1 = y0 + h_crop

    if w_crop <= 0 or h_crop <= 0:
        return None
    else:
        image_crop = image_.crop((x0, y0, x1, y1))
        if circular_crop:
            background = new_image("RGBA", (w_crop, h_crop), (0, 0, 0, 0))
            mask = new_image("L", (w_crop, h_crop), 0)
            draw = Draw(mask)
            draw.ellipse((0, 0, w_crop, h_crop), fill="white")
            image_crop = composite_image(image_crop, background, mask)

        return image_crop