Return Empty Space

Hi all!
In my project, sometimes my callbacks must return empty figures. However, I’d like to return an empty space, i.e. white blank space, like there wasn’t a dcc.Graph there. Setting return {} the plot still appears (its axis and background lines) in spite of it’s not fulfilled with anything. I would actually like to return no image at all. How could I do that? Thank you

Hello @rlarango,

You could also use patch to return to the style:

Output(graph, figure),
Output(graph, style)

...

newStyle = Patch()
if no_data:
    newStyle['display'] = 'none'
    return {}, newStyle
newStyle['display'] = 'initial'
return figure, newStyle

Something like this might work.

That’s part of the code. Nothing is changing.

@app.callback(
    Output('graph', 'figure'),
    Output('graph_2', 'figure'),
    Output('graph_3', 'figure'),
    Input('button', 'n_clicks'),
    State('comp', 'value'),
    State('grat', 'value'),
    State('info', 'value') 
)
def FT_MM_func(n_clicks, comp_value, grat_value, info_value):  ##Segundo ninho de IF's não têm elif pois haverá mais opções ainda (mais de uma seleção)
    if comp_value == 'FT':
        if info_value == ['gen']:
            if grat_value == 'Ind':
                dff = df
                fig = px.histogram(
                    dff,
                    y= 'Categoria',
                    color= 'Gênero',
                    template= 'plotly_white',
                    barmode= 'group',
                    title= '<b>FORÇA DE TRABALHO POR GÊNERO</b>',
                    category_orders= {
                        'Categoria': [
                            'Membra/Membro',
                            'Analista',
                            'Técnica/Técnico',
                            'Pessoal sem vínculo',
                            'Requisitada/Requisitado',
                            'Estagiária/Estagiário'
                        ],
                        'Gênero': ['Mulheres', 'Homens']
                    },
                    hover_data= {
                        'Gênero': False,
                        'Categoria': False
                    } 
                )

                fig.update_layout(
                    font_family= 'Bahnschrift',
                    font_size= 13,
                    xaxis_title= '',
                    yaxis_title= '',
                    height= 450,
                    width= 900,
                    legend_title= '',
                    legend_x= 1,
                    legend_y= 1.25,
                    legend_xanchor= 'right',
                    xaxis_dtick= 100,
                    hoverlabel_font_color= 'white' 
                )

                fig.update_traces(
                    marker_line_width= 0.3,
                    marker_line_color= 'black',
                    hovertemplate= (
                        'Total: %{x} <extra></extra>'
                    )
                )
                patched_figure = Patch()
                patched_figure['display'] = 'none'
                return fig, patched_figure, patched_figure

How things stand:

Not figure, it needs to be returned to the style.

Well, I don’t know how to implement this solution in my code. I’m actually newbie. Sry :confused:

Give this a try:

@app.callback(
    Output('graph', 'figure'),
    Output('graph_2', 'figure'),
    Output('graph_3', 'style'),
    Input('button', 'n_clicks'),
    State('comp', 'value'),
    State('grat', 'value'),
    State('info', 'value') 
)
def FT_MM_func(n_clicks, comp_value, grat_value, info_value):  ##Segundo ninho de IF's não têm elif pois haverá mais opções ainda (mais de uma seleção)
    if comp_value == 'FT':
        if info_value == ['gen']:
            if grat_value == 'Ind':
                dff = df
                fig = px.histogram(
                    dff,
                    y= 'Categoria',
                    color= 'Gênero',
                    template= 'plotly_white',
                    barmode= 'group',
                    title= '<b>FORÇA DE TRABALHO POR GÊNERO</b>',
                    category_orders= {
                        'Categoria': [
                            'Membra/Membro',
                            'Analista',
                            'Técnica/Técnico',
                            'Pessoal sem vínculo',
                            'Requisitada/Requisitado',
                            'Estagiária/Estagiário'
                        ],
                        'Gênero': ['Mulheres', 'Homens']
                    },
                    hover_data= {
                        'Gênero': False,
                        'Categoria': False
                    } 
                )

                fig.update_layout(
                    font_family= 'Bahnschrift',
                    font_size= 13,
                    xaxis_title= '',
                    yaxis_title= '',
                    height= 450,
                    width= 900,
                    legend_title= '',
                    legend_x= 1,
                    legend_y= 1.25,
                    legend_xanchor= 'right',
                    xaxis_dtick= 100,
                    hoverlabel_font_color= 'white' 
                )

                fig.update_traces(
                    marker_line_width= 0.3,
                    marker_line_color= 'black',
                    hovertemplate= (
                        'Total: %{x} <extra></extra>'
                    )
                )
                patched_style = Patch()
                patched_style ['display'] = 'none'
                return fig, patched_figure, patched_style 
3 Likes

Thanks you for the answer, @jinnyzor

Hi @rlarango,
Patch is a pretty advanced feature in Dash since it requires a good understanding of the output and its property type. I recommend you read a few examples in the docs; that should help make things a little clearer.

1 Like

I’ve tried and this is what I got:

Failed component prop type: Invalid component prop `figure` key `display` supplied to Graph.
Bad object: {
  "display": "none"
}
Valid keys: [
  "data",
  "layout",
  "frames"
]

I’ve also tried ‘layout’ instead of ‘display’ and this is the error message:

Invalid argument `figure.layout` passed into Graph with ID "graph_3". Expected `object`. Was supplied type `string`.

Please provide a working example of your app, I cannot help any more than I have provided.

from dash import Dash, html, dcc, Input, Output, State, Patch
from dash.exceptions import PreventUpdate
import dash_bootstrap_components as dbc
from dash_bootstrap_templates import load_figure_template
import plotly.express as px
import plotly.graph_objects as go
import pandas as pd
import numpy as np

#================================================#
app = Dash(__name__, external_stylesheets= [dbc.themes.JOURNAL], prevent_initial_callbacks="initial_duplicate",
           meta_tags= [{'name':'viewport', 'content':'width=device-width, initial_scale= 1.0'}])

df = pd.read_csv('df.csv')
df_fun = pd.read_csv('df_fun.csv')

#==================*LAYOUT*====================#
load_figure_template('journal')

sidebar = dbc.Row(
       dbc.Col(
    [
        html.Img(src= 'assets/MPT_logo.png', alt= 'Logo MPT', style= {'max-width':'100%'}),
        html.H3('Censo', style= {'text-align':'center', 'color':'brown', 'font-weight':'bold'}),
        html.H6('Política Nacional de Equidade de Gênero, Raça e Diversidade', style= {'text-align':'center', 'color':'brown'}),
        html.Hr(style= {'border-top': '1px dashed red'}),
        html.P("Selecione abaixo as informações de interesse:"),
        html.P('Composição', style= {'font-weight':'bold'}),
        dbc.RadioItems(options=[
            {'label':'Força de Trabalho Total', 'value':'FT'},
            {'label':'Membras/Membros', 'value':'MM'},
            {'label':'Servidores', 'value':'SS'}], value= 'FT', id= 'comp'),
        html.Hr(style= {'border-top': '1px solid red'}),
        html.P('Gratificação', style= {'font-weight':'bold'}),
        dbc.RadioItems(options=[
            {'label':'Com e Sem Função', 'value':'Ind'},
            {'label':'Com Função', 'value':'CF'},
            {'label':'Sem Função', 'value':'SF'}], value= 'Ind', id= 'grat'),
        html.Hr(style= {'border-top': '1px solid red'}),
        html.P('Informação', style= {'font-weight':'bold'}),
        dbc.RadioItems(options=[
            {'label':'Gênero', 'value':'gen'},
            {'label':'Cor/Raça', 'value':'C/R'},
            {'label':'Faixa de Idade', 'value':'FxI'},
            {'label':'Pessoa com Deficiência', 'value':'PCD'}], value= 'gen', id= 'info'),
        html.Br(),
        dbc.Button('Gerar', id= 'button', n_clicks= 0),
        html.Hr(style= {'border-top': '1px solid red'}),
        html.P('Membras/Membros - Cargos Superiores', style= {'font-weight':'bold'}),
        dbc.RadioItems(options=[
               {'label':'Alta Administração', 'value':'AM'},
               {'label': 'Cargos de Gestão', 'value':'CG'}], value = '', id= 'mm_cargos'),
        html.Br(),
        dbc.Button('Gerar', id= 'button_2', n_clicks= 0),
        html.Hr(style= {'border-top': '1px solid red'}),
        html.P('Servidoras/Servidores - Detalhamento Gratificações', style= {'font-weight':'bold'}),
        dbc.RadioItems(options=[
               {'label':'Cargo em Comissão', 'value':'CC'},
               {'label': 'Função de Confiança', 'value':'FC'}], value = '', id= 'ss_grats'),
        html.Br(),
        dbc.Button('Gerar', id= 'button_3', n_clicks= 0)      
    ], width= {'size': 2}, className= 'sidebar')
)

app.layout = dbc.Container([sidebar,
                            dbc.Col([ 
                            html.Div([dcc.Graph(id= 'graph', figure= {}, className= 'graph-div-style'),
                                     html.Br(),
                                     dcc.Graph(id= 'graph_2', figure= {}, className= 'graph-div-style'),
                                     html.Br(),
                                     dcc.Graph(id= 'graph_3', figure= {}, className= 'graph-div-style')])], width= {'size': 8, 'offset':2})
                            ], className= 'container')

#===============*CALLBACKS*====================#

@app.callback(
    Output('graph', 'figure'),
    Output('graph_2', 'figure'),
    Output('graph_3', 'figure'),
    Input('button', 'n_clicks'),
    State('comp', 'value'),
    State('grat', 'value'),
    State('info', 'value') 
)
def FT_MM_func(n_clicks, comp_value, grat_value, info_value):  ##Segundo ninho de IF's não têm elif pois haverá mais opções ainda (mais de uma seleção)
    if comp_value == 'FT':
        if info_value == 'gen':
            if grat_value == 'Ind':
                dff = df
                fig = px.histogram(
                    dff,
                    y= 'Categoria',
                    color= 'Gênero',
                    template= 'journal',
                    barmode= 'group',
                    title= '<b>FORÇA DE TRABALHO POR GÊNERO</b>',
                    category_orders= {
                        'Categoria': [
                            'Membra/Membro',
                            'Analista',
                            'Técnica/Técnico',
                            'Pessoal sem vínculo',
                            'Requisitada/Requisitado',
                            'Estagiária/Estagiário'
                        ],
                        'Gênero': ['Mulheres', 'Homens']
                    },
                    hover_data= {
                        'Gênero': False,
                        'Categoria': False
                    } 
                )

                fig.update_layout(
                    font_family= 'Bahnschrift',
                    font_size= 13,
                    xaxis_title= '',
                    yaxis_title= '',
                    height= 450,
                    width= 900,
                    legend_title= '',
                    legend_x= 1,
                    legend_y= 1.25,
                    legend_xanchor= 'right',
                    xaxis_dtick= 100,
                    hoverlabel_font_color= 'white',
                    paper_bgcolor= 'rgba(0,0,0,0)',

                )

                fig.update_traces(
                    marker_line_width= 0.3,
                    marker_line_color= 'black',
                    hovertemplate= (
                        'Total: %{x} <extra></extra>'
                    )
                )
                patched_style = Patch()
                patched_style ['layout'] = 'none'
                return fig, patched_style, patched_style

Try this:

from dash import Dash, html, dcc, Input, Output, State, Patch
from dash.exceptions import PreventUpdate
import dash_bootstrap_components as dbc
from dash_bootstrap_templates import load_figure_template
import plotly.express as px
import plotly.graph_objects as go
import pandas as pd
import numpy as np

#================================================#
app = Dash(__name__, external_stylesheets= [dbc.themes.JOURNAL], prevent_initial_callbacks="initial_duplicate",
           meta_tags= [{'name':'viewport', 'content':'width=device-width, initial_scale= 1.0'}])

df = pd.read_csv('df.csv')
df_fun = pd.read_csv('df_fun.csv')

#==================*LAYOUT*====================#
load_figure_template('journal')

sidebar = dbc.Row(
       dbc.Col(
    [
        html.Img(src= 'assets/MPT_logo.png', alt= 'Logo MPT', style= {'max-width':'100%'}),
        html.H3('Censo', style= {'text-align':'center', 'color':'brown', 'font-weight':'bold'}),
        html.H6('Política Nacional de Equidade de Gênero, Raça e Diversidade', style= {'text-align':'center', 'color':'brown'}),
        html.Hr(style= {'border-top': '1px dashed red'}),
        html.P("Selecione abaixo as informações de interesse:"),
        html.P('Composição', style= {'font-weight':'bold'}),
        dbc.RadioItems(options=[
            {'label':'Força de Trabalho Total', 'value':'FT'},
            {'label':'Membras/Membros', 'value':'MM'},
            {'label':'Servidores', 'value':'SS'}], value= 'FT', id= 'comp'),
        html.Hr(style= {'border-top': '1px solid red'}),
        html.P('Gratificação', style= {'font-weight':'bold'}),
        dbc.RadioItems(options=[
            {'label':'Com e Sem Função', 'value':'Ind'},
            {'label':'Com Função', 'value':'CF'},
            {'label':'Sem Função', 'value':'SF'}], value= 'Ind', id= 'grat'),
        html.Hr(style= {'border-top': '1px solid red'}),
        html.P('Informação', style= {'font-weight':'bold'}),
        dbc.RadioItems(options=[
            {'label':'Gênero', 'value':'gen'},
            {'label':'Cor/Raça', 'value':'C/R'},
            {'label':'Faixa de Idade', 'value':'FxI'},
            {'label':'Pessoa com Deficiência', 'value':'PCD'}], value= 'gen', id= 'info'),
        html.Br(),
        dbc.Button('Gerar', id= 'button', n_clicks= 0),
        html.Hr(style= {'border-top': '1px solid red'}),
        html.P('Membras/Membros - Cargos Superiores', style= {'font-weight':'bold'}),
        dbc.RadioItems(options=[
               {'label':'Alta Administração', 'value':'AM'},
               {'label': 'Cargos de Gestão', 'value':'CG'}], value = '', id= 'mm_cargos'),
        html.Br(),
        dbc.Button('Gerar', id= 'button_2', n_clicks= 0),
        html.Hr(style= {'border-top': '1px solid red'}),
        html.P('Servidoras/Servidores - Detalhamento Gratificações', style= {'font-weight':'bold'}),
        dbc.RadioItems(options=[
               {'label':'Cargo em Comissão', 'value':'CC'},
               {'label': 'Função de Confiança', 'value':'FC'}], value = '', id= 'ss_grats'),
        html.Br(),
        dbc.Button('Gerar', id= 'button_3', n_clicks= 0)      
    ], width= {'size': 2}, className= 'sidebar')
)

app.layout = dbc.Container([sidebar,
                            dbc.Col([ 
                            html.Div([dcc.Graph(id= 'graph', figure= {}, className= 'graph-div-style'),
                                     html.Br(),
                                     dcc.Graph(id= 'graph_2', figure= {}, className= 'graph-div-style'),
                                     html.Br(),
                                     dcc.Graph(id= 'graph_3', figure= {}, className= 'graph-div-style')])], width= {'size': 8, 'offset':2})
                            ], className= 'container')

#===============*CALLBACKS*====================#

@app.callback(
    Output('graph', 'figure'),
    Output('graph_2', 'figure'),
    Output('graph_3', 'style'),
    Input('button', 'n_clicks'),
    State('comp', 'value'),
    State('grat', 'value'),
    State('info', 'value') 
)
def FT_MM_func(n_clicks, comp_value, grat_value, info_value):  ##Segundo ninho de IF's não têm elif pois haverá mais opções ainda (mais de uma seleção)
    if comp_value == 'FT':
        if info_value == 'gen':
            if grat_value == 'Ind':
                dff = df
                fig = px.histogram(
                    dff,
                    y= 'Categoria',
                    color= 'Gênero',
                    template= 'journal',
                    barmode= 'group',
                    title= '<b>FORÇA DE TRABALHO POR GÊNERO</b>',
                    category_orders= {
                        'Categoria': [
                            'Membra/Membro',
                            'Analista',
                            'Técnica/Técnico',
                            'Pessoal sem vínculo',
                            'Requisitada/Requisitado',
                            'Estagiária/Estagiário'
                        ],
                        'Gênero': ['Mulheres', 'Homens']
                    },
                    hover_data= {
                        'Gênero': False,
                        'Categoria': False
                    } 
                )

                fig.update_layout(
                    font_family= 'Bahnschrift',
                    font_size= 13,
                    xaxis_title= '',
                    yaxis_title= '',
                    height= 450,
                    width= 900,
                    legend_title= '',
                    legend_x= 1,
                    legend_y= 1.25,
                    legend_xanchor= 'right',
                    xaxis_dtick= 100,
                    hoverlabel_font_color= 'white',
                    paper_bgcolor= 'rgba(0,0,0,0)',

                )

                fig.update_traces(
                    marker_line_width= 0.3,
                    marker_line_color= 'black',
                    hovertemplate= (
                        'Total: %{x} <extra></extra>'
                    )
                )
                patched_style = Patch()
                patched_style['display'] = 'none'
                return fig, patched_style, patched_style

Nop. The same error message

Failed component prop type: Invalid component prop figure key display supplied to Graph.
Bad object: {
“display”: “none”
}
Valid keys: [
“data”,
“layout”,
“frames”
]

Please notice the change that I am making in the callback structure:

@app.callback(
    Output('graph', 'figure'),
    Output('graph_2', 'figure'),
    Output('graph_3', 'style'), ### altered here
    Input('button', 'n_clicks'),
    State('comp', 'value'),
    State('grat', 'value'),
    State('info', 'value') 
)
1 Like

Oh, sry. Yeah, that’s make sense. I’m gonna make some changes here and I’ll let you know if things went well. Thanks!

1 Like

It turns out that some conditions return one figure, others 2 or 3 figures. I should pass a list of properties (figure, style) as a callback output component property, but I’m sure it’s not allowed. How should I proceed then?

If the conditions are easy to determine, you could have a seperate callback that targets only the styles, that way it can be simpler to determine instead of trying to do all of it in the same callback.

But… if you want it in the same callback, then you’d just add all the styles for the figures:

Output('graph', 'figure'),
Output('graph', 'style'),
Output('graph_2', 'figure'),
Output('graph_2', 'style'),
Output('graph_3', 'figure'),
Output('graph_3', 'style'),

Then just return all 6 together, and when you reset the display, just do this:

patched_style['display'] = 'initial'

Well… I’ve rearranged the conditions and created new callbacks accordingly with the number of outputs that actually generate figures. I’m receiving the frightening duplicate callback error and I’m not figuring out how to make it work. Here it is.

Since the code is too long I took a picture

HELP :smiling_face_with_tear:

You cant have a single input for all of the duplicates, there needs to be a different input, thats how the serialization works for determining the id for the callback.

Is there not a way that you can compile them into 1 or 2 callbacks? If two, then just use style for 1 and figure for the other.

I’m trying this solution but I didn’t understand this. Could you enlighten me?

I mean… why does this work?

And this keeps receiving duplicate callback errors?