Python "`functools.partial`" to monkey-patch Shiny's core API "`ui.output_image`"

Modify output_image style tags to remove the hard-coded 400px height from the “express” API in Shiny for Python

Currently, using the @render.image decorator in Shiny for Python’s express API results in the rendered image HTML wrapped in a <div> container with a fixed 400px height. You can see this with the Developer Tools inside the browser on the element with id=your_wrapped_image_func, if the basic implemenation from the Shiny docs is used.

from shiny.express import render

@render.image
def your_wrapped_image_func():
    output_image = dict(
        {
            "src": image_path,
            "width": "200px", # Controls the image width, but not the div wrapped around it
            "height": "20px", # Controls the image height, but not the div wrapped around it
        },
    )
    return output_image

Injecting CSS with “ui.tags.style” doesn’t help #

In the case of the div that render.image generates, the style is hard-coded to be width=100%;height=400px, overridding any inherited style.

The following is a nice feature of the express API, but it doesn’t help in this case.

from shiny.express import ui, render

# Not sure if this is the accepted way to navigate the logos from a brand yaml...
sidebar_logo_path = str(ui.Theme.from_brand("_brand.yml").brand.logo.small.light.path)

ui.tags.style("#sidebar_logo { height: 20px; }")

with ui.sidebar(
    bg="#ececec",
    width=250,
    gap=12,
    padding=None,
):


    @render.image
    def sidebar_logo(): # CSS element reference #sidebar_logo
        output_image = dict(
            {
                "src": sidebar_logo_path,
                "width": "200px",
            },
        )
        return output_image

Solved by patching Shiny’s core API ‘output_image’ #

Using partial from Python’s standard library functools, Shiny’s core API ui.output_image can be replaced with a “partial”-ly called version of the identical Shiny function with the height kwarg specified differently than its’ default. (Which is that problematic “400px” value.)

from shiny.express import ui, render
from functools import partial
from shiny import (
    ui as core,
)  # monkey patch to remove the 400px height div container for render.image

sidebar_logo_path = str(ui.Theme.from_brand("_brand.yml").brand.logo.small.light.path)

# Start app UI and layout
ui.page_opts(title=APP_TITLE, theme=ui.Theme.from_brand(__file__))

core.output_image = partial(
    core.output_image, height=None
)  # monkey patch to remove the 400px height div container for render.image

# Shiny ui Sidebar context manager in express
with ui.sidebar(
    bg="#ececec",
    width=250,
    gap=12,
    padding=None,
):


    @render.image # Uses core's `ui.output_image` internally
    def sidebar_logo():
        output_image = dict(
            {
                "src": sidebar_logo_path,
                "width": "200px",
                "height": "20px" 
            },
        )
        return output_image