Skip to content

facet_row_spacing modified in a callback for px.imshow changes the figure dict but not the plot #4794

Closed
@celia-lm

Description

@celia-lm

Description of the issue

  • I have an app with a callback that uses a dcc.Input (type='number') as Input and recreates the figure property of two dcc.Graphs using the input value as the facet_row_spacing argument.
  • Both graphs use facet_col, facet_col_wrap and facet_row_spacing.
  • The recreation works fine for the facetted scatterplot but not for the facetted heatmap/imshow; that is, the facet_row_spacing gets adjusted for the scatterplot but always remains the same (the initial value) for the imshow.
  • The figure dict gets updated correctly in both cases, as shown in the div under each figure - I know this because the difference between the domain end of the first row and the domain start of the second row is equal to the value specified in the dcc.Input.
  • The subplot titles also get moved to their correct (new) position.
  • However, if we use the plotly crowbar instead to retrieve the figure dict, the values for yaxis domains show a value that matches the spacing we see on the screen, that is: always the initial facet_row_spacing value for the facetted imshow [this is not shown in the video].

JS code to copy in the console to get the plotly crowbar:

javascript:document.querySelectorAll(".js-plotly-plot").forEach(function(gd){ const txt = document.createElement("textarea"); txt.appendChild(document.createTextNode(JSON.stringify({data: gd.data, layout: gd.layout})));txt.style.position='absolute';txt.style.zIndex=9999; gd.appendChild(txt);})
Screen.Recording.2024-10-11.at.13.56.32.mov

Things I have not tested

  • If it happens with facet_row+facet_col_spacing too.

MRE

Python 3.10
plotly==5.24.1
dash==2.18.1
plotlyjs==2.35.2 (according to the modebar)

from dash import Dash, html, dcc, callback, Input, Output
import plotly
import json

# https://community.plotly.com/t/facet-row-and-facet-row-labels-when-using-imshow/85905
print(plotly.__version__)
app = Dash(__name__)

import numpy as np
import plotly.express as px

def generate_fig1(row_spacing):
    fig1 = px.imshow(
        np.random.uniform(0, 1, size = (18, 28, 28)),
        facet_col = 0, facet_col_wrap = 6, facet_row_spacing=row_spacing,
    )
    return fig1

df=px.data.gapminder().query("continent == 'Americas'")
def generate_fig2(row_spacing): 
    fig2 = px.line(
        df, x="year", y="lifeExp", 
        facet_col="country", facet_col_wrap=8, facet_row_spacing=row_spacing,
    )  
    return fig2

app.layout=html.Div([
    dcc.Input(value=0.3, min=0, max=0.3, step=0.05, id='spacing', type='number'),
    dcc.Graph(id='fig1'),
    html.H3('fig1'),
    html.Div(id='div1'),
    dcc.Graph(id='fig2'),
    html.H3('fig2'),
    html.Div(id='div2'),
])

@callback(
    Output('fig1', 'figure'),
    Output('div1', 'children'),
    Output('fig2', 'figure'),
    Output('div2', 'children'),
    Input('spacing', 'value'),
)
def update_outputs(spacing):
    fig1 = generate_fig1(spacing)
    fig2 = generate_fig2(spacing)

    text1 = {}
    for i in range (1,19):
        if i == 1:
            i = ""  
        text1[f'yaxis{i}']= fig1.layout[f'yaxis{i}']['domain']

    text2 = {}
    for i in range (1,26):
        if i == 1:
            i = ""  
        text2[f'yaxis{i}']= fig2.layout[f'yaxis{i}']['domain']
    
    return fig1, str(text1), fig2, str(text2)

if __name__ == "__main__":
    app.run_server(debug=True)

Metadata

Metadata

Assignees

Labels

P2considered for next cyclebugsomething brokensev-3annoyance with workaround

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions