Dash: How to call a function that is within a class

This should be common functionality but I cannot see this asked elsewhere or in the docs?

How can my call back call a function that is within a class?

from dash import Dash, html, dcc, callback, Output, Input
import plotly.express as px
import pandas as pd

df = pd.read_csv('https://raw.githubusercontent.com/plotly/datasets/master/gapminder_unfiltered.csv')

class S:
    def __init__(self) -> None:
        pass

    @callback(
        Output('graph-content', 'figure'),
        [Input('dropdown-selection', 'value')]
    )
    def update_graph(self, value):
        dff = df[df.country==value]
        return px.line(dff, x='year', y='pop')


app = Dash(__name__)

app.layout = html.Div([
    html.H1(children='Title of Dash App', style={'textAlign':'center'}),
    dcc.Dropdown(df.country.unique(), 'Canada', id='dropdown-selection'),
    dcc.Graph(id='graph-content')
])

if __name__ == '__main__':
    app.run(debug=True)

This gives the error TypeError: S.update_graph() missing 1 required positional argument: 'value' which is typical if a class function is missing the self arg.

Hi @this_josh, not sure, but in this example your function could be a @staticmethod, try deleting the self parameter.

Hi, in my actual use case, the plots require object properties, e.g.

def update_graph(self, value):
     plot(value, self.name)

This is not possible with staticmethod

It seems strange for dash not to support this

Hello @this_josh,

Hmm, have you looked into the documentation for AIO components?

It may shed some light on this.

Thank you for highlighting this, the docs do shed some light, although it seems needlessly complex.

Here is a working example, the aio I replaced with just pattern-matching callbacks:

from dash import Dash, html, dcc, callback, Output, Input, MATCH
import plotly.express as px
import pandas as pd

df = pd.read_csv('https://raw.githubusercontent.com/plotly/datasets/master/gapminder_unfiltered.csv')

class S(html.Div):
    class ids:
        graph = lambda id: {"index": id, 'type': 'graph'}
        dropdown = lambda id: {"index": id, 'type': 'dropdown'}

    ids = ids

    def __init__(self, id, dropdownData, dropdownValue = None):
        super().__init__([
            dcc.Dropdown(dropdownData, dropdownValue, id=self.ids.dropdown(id)),
            dcc.Graph(id=self.ids.graph(id))
                          ])

    @callback(
        Output(ids.graph(MATCH), 'figure'),
        Input(ids.dropdown(MATCH), 'value')
    )
    def updateGraph(value):
        dff = df[df.country == value]
        return px.line(dff, x='year', y='pop')


app = Dash(__name__)

mygraph = S('graph-content', df.country.unique())

app.layout = html.Div([
    html.H1(children='Title of Dash App', style={'textAlign':'center'}),
    mygraph
])

if __name__ == '__main__':
    app.run(debug=True)
1 Like

You got me going, haha, check out the more watered down version without using pattern-matching:

from dash import Dash, html, dcc, callback, Output, Input, MATCH
import plotly.express as px
import pandas as pd

df = pd.read_csv('https://raw.githubusercontent.com/plotly/datasets/master/gapminder_unfiltered.csv')

class S(html.Div):
    def update_graph(self, value, charttype):
        dff = df[df.country == value]
        if charttype == 'scatter':
            fig = px.scatter(dff, x='year', y='pop')
        elif charttype == 'bar':
            fig = px.bar(dff, x='year', y='pop')
        else:
            fig = px.line(dff, x='year', y='pop')
        return fig

    def __init__(self, id, dropdown, charttype=None):
        @callback(
            Output(id, 'figure'),
            Input(dropdown, 'value')
        )
        def updateGraph(value):
            return self.update_graph(value, charttype)

        super().__init__([
            dcc.Graph(id=id)
                          ])



app = Dash(__name__)

dropdown = dcc.Dropdown(df.country.unique(), id='dropdowns')

app.layout = html.Div([
    html.H1(children='Title of Dash App', style={'textAlign':'center'}),
    dropdown,
    S('graph-content', 'dropdowns', 'scatter'),
    S('graph-content2', 'dropdowns'),
    S('graph-content3', 'dropdowns', 'bar')
])

if __name__ == '__main__':
    app.run(debug=True)

3 Likes

That’s nice, thanks, I particularly like the second method.

Cheers

1 Like