I'm updating some code to use Dash and plotly. The main code for graphing is defined within a class. I replaced some Bokeh widgets with Dash controls, and ended up with a callback that looks like this:
class MakeStuff:
def __init__(self, ..., **optional):
...
self.app = dash.Dash(...)
...
@self.app.callback(
dash.dependencies.Output('indicator-graphic', 'figure'),
[dash.dependencies.Input('start-time-slider', 'value'),
dash.dependencies.Input('graph-width-slider', 'value')]
)
def update_graphs(self,range_start,graph_width):
print(...)
I am following some examples from the Dash website. I was able to run the examples, including callbacks. In my code, without the decorator, the code runs without error, producing the graphics and controls as I expected it to. (Of course, the code is incomplete, but there is no error.) When I include the decorator, I get this error:
NameError: name 'self' is not defined
I tired it this way, first, just mimicking the code examples:
class MakeStuff:
def __init__(self, ..., **optional):
...
app = dash.Dash(...)
...
@app.callback(
dash.dependencies.Output('indicator-graphic', 'figure'),
[dash.dependencies.Input('start-time-slider', 'value'),
dash.dependencies.Input('graph-width-slider', 'value')]
)
def update_graphs(self,range_start,graph_width):
print(...)
Of course, the variable "app" is only know within the scope of the init function, so it's no surprise that that doesn't work, giving the similar error:
NameError: name 'app' is not defined
Is there a straightforward way to set up this decorator to work while still keeping my code within a class definition? I am guessing some pre-processing is going on with the decorator, but I don't understand it well enough to come up with a solution.
You could call the callback function not as a decorator, as shown in this answer. This should work from within your __init__
function:
class MakeStuff:
def __init__(self, ..., **optional):
...
self.app = dash.Dash(...)
self.app.callback(
dash.dependencies.Output('indicator-graphic', 'figure'),
[dash.dependencies.Input('start-time-slider', 'value'),
dash.dependencies.Input('graph-width-slider', 'value')]
)(self.update_graphs)
...
def update_graphs(self,range_start,graph_width):
print(...)
I've never tried it with a class instance before, but see no reason for it not to work.
ned2 provides a solution here, he uses the following structure to set up the decorators within a class definition.
class BaseBlock:
def __init__(self, app=None):
self.app = app
if self.app is not None and hasattr(self, 'callbacks'):
self.callbacks(self.app)
class MyBlock(BaseBlock):
layout = html.Div('layout for this "block".')
def callbacks(self, app):
@app.callback(Output('foo', 'figure'), [Input('bar')])
def do_things(bar):
return SOME_DATA
@app.callback(Output('baz', 'figure'), [Input('boop')])
def do_things(boop):
return OTHER_DATA
# creating a new MyBlock will register all callbacks
block = MyBlock(app=app)
# now insert this component into the app's layout
app.layout['slot'] = block.layout
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With