Plotly 4.8, template not adding image layout when applied

I am building a plotly template to reduce work in “prettifying” R&D visualizations for use in presentations.

When trying to add a logo to plots through templating I have encountered something annoying.

Adding a logo image to the template is simple enough. When the template is later applied, in this case through pandas plot with the plotly backend, the image is not added. The image layout exists within the figure template, and can be retrieved and assigned to the figure’s layout.images, revealing the image.

The code snippet requires plotly 4.8 or greater.

import plotly.graph_objs as go
import plotly.io as pio
import pandas as pd
import plotly.express as px

pd.options.plotting.backend = "plotly"

# Add a test image to the template layout
pio.templates['test_template'] = go.layout.Template({'layout': {
     'images': [{'sizex': 0.5, 'sizey': 0.5,
                 'source': ('https://upload.wikimedia.org/wikipedia/commons/8/85/Logo-Test.png'),
                 'x': 0.5, 'xanchor': 'center', 'xref': 'paper',
                 'y': 0, 'yanchor': 'bottom', 'yref': 'paper'}],
}})

# apply template when plotting mock data, does not display test logo image.
fig = pd.DataFrame(dict(a=[1, 2, 3], b = [3, 2, 1])).plot.bar(template = "test_template")
fig.show()

# Retrieving the image layout from within the figure template makes the image appear
fig.layout.images=fig.layout.template.layout.images
fig.show()

I found the same behaviour when using fig.update_layout with templating. When applying the layout part of the template through go.Figure(layout=template.layout) the image is shown.

Is there something I’m missing here?

Ah, sorry about the cryptic docs here, but there is another attribute name you need to set in your layout image to get it to actually appear in the figures that use the template: https://plotly.com/python/reference/layout/images/#layout-images-items-image-name

This is set up this way so that charts using templates can “conditionally consume” template attributes i.e. you set up the image part of the template so that the logo is always in the same place, but the logo only appears if the URL is set in a figure, allowing one template to be used for figures with various logos, say.

1 Like

I am afraid I still don’t understand. Could you elaborate a little more? Say I set

pio.templates["new"] = go.layout.Template( 
    layout_images=[
        dict(
            name="imageName",
            source="https://images.plot.ly/language-icons/api-home/python-logo.png",
            xref="x",
            yref="y",
            x=0,
            y=0,
            sizex=100,
            sizey=100,
            sizing="stretch",
            visible=True,
        )
    ]
) 

fig = px.scatter(df_2007, 
                 x="gdpPercap", y="lifeExp", size="pop", color="continent",
                 log_x=False, size_max=60,
                 template="new", title="Gapminder 2007")

would I need to specify something else when I use the template? I can’t get the image to show, everything else I set in the template does.

The image is actually rendered, at the data values x=0/y=0, and with a data-width of 100 (i.e. 100 $ of gdpPercap, starting at 0 and going right) and a data-height of 100 (i.e. 100 years of lifeExp, starting at 0 and going downwards). If you set xref and yref to “paper” you’ll probably have better luck :slight_smile:

Here’s a full example… one tricky aspect here is that the x and y size in paper coordinates is set in fractions, so if you just switch your xref/yref above to “paper” you’ll get an image whose top-left corner is at the bottom-left corner of the plotting area, and which is 100 times the size of the plotting area, and given that there’s a bit of white at the top of the image you won’t see it. In the example below I’ve set the x and y anchors to position the bottom-middle/right point at x=1/y=1.05, and the image is sized as 0.2 plot fractions. It’s a bit of a clunky system!

import plotly.express as px
import plotly.io as pio
import plotly.graph_objects as go

df_2007 = px.data.gapminder().query("year==2007")
pio.templates["new"] = go.layout.Template( 
    layout_images=[
        dict(
            name="imageName",
            source="https://images.plot.ly/language-icons/api-home/python-logo.png",
            xref="paper", yref="paper",
            x=1, y=1.05,
            sizex=0.2, sizey=0.2,
            xanchor="right", yanchor="bottom"
        )
    ]
) 

fig = px.scatter(df_2007, 
                 x="gdpPercap", y="lifeExp", size="pop", color="continent",
                 log_x=False, size_max=60,
                 template="new", title="Gapminder 2007")
fig.show()

1 Like