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))
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))
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"], )
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