Black Lives Matter. Please consider donating to Black Girls Code today.

Class callbacks are not called when there is a dcc.location and its callback

Dears,

[edit]
I have changed my post because I found now where is the issue. So let’s go right to the problem.

I have created a class where the layout, components and the callbacks to update graph are created.

This is working well if in the MAIN, there is no dcc location and the callback associated.

But if the dcc.Location is added, then the class callbacks are not triggered. The graph is rended with default values, but can not be changed it if user clicks the dropdown component

Here is the class code and the 2 options of the MAIN program. First one is running well, second not.

Thks for your help
class code

import dash 
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output
import random

#class graph
class clsSimpleGraph():
	def __init__(self, pApp,*args, **kwargs):
		self.layout_graphID = 'graph_1'
		self.App = pApp
		self.x = [1, 2, 3, 4]
		self.y=[]
		self.layout= html.Div()


	#(re)build graph
	def clsBuildGraph(self, pY1=None):
		self.y = pY1 if pY1 else [1, 2, 3, 4]
		g=[]
		g.append(dcc.Graph(id='graph', figure={'data' : [{'x': self.x, 'y': self.y,'type':'bar' }],'layout':{'title' : 'test'}}))
		return g

	#build the layout + components and graph
	def clsBuildLayout(self):
		self.layout =html.Div([
					html.Div(id = 'filters', children=html.Span([html.H5('click to change '),
						dcc.Dropdown(id='filter',options=[{'label': 'VALUE', 'value':random.sample(range(30), 4)}],
							 multi=False)])) ,
					html.Div(id = 'chart', children = self.clsBuildGraph(self.y))
					])
		return self.layout
		
	#create callbacks dynamically
	def clsBuildCallbacks(self):
		@self.App.callback(
		Output(component_id='chart', component_property='children'),
			[ Input(component_id= 'filter', component_property='value')])
		def updatelayout(val1=None):
			val1 = random.sample(range(30), 4)
			return self.clsBuildGraph(val1)
	
	#build the final application
	#build the layout and build the callbacks
	def clsBuildApp(self):
		lay = self.clsBuildLayout()
		callb = self.clsBuildCallbacks()
		return lay

main option 1 : all is running well

if __name__=="__main__":
	appServer=dash.Dash(__name__)
	appServer.config['suppress_callback_exceptions']=True
	graph = clsSimpleGraph(appServer)
	appServer.layout = graph.clsBuildApp()	
	appServer.run_server(debug=True)

main option 2 : class callbacks don’t refresh the graph

if __name__=="__main__":
	appServer=dash.Dash(__name__)
	appServer.config['suppress_callback_exceptions']=True
	
	appServer.layout = html.Div([
	dcc.Location(id='url', refresh=False),
	html.Div(id='page-content')
	])

	@appServer.callback(Output('page-content', 'children'),[Input('url', 'pathname')])
	def display_page(pathname):
		if pathname:
			graph = clsSimpleGraph(appServer)
			lay = graph.clsBuildApp()	
			appServer.layout = lay
			return appServer.layout

	appServer.run_server(debug=True)

hi everyone,

any help?

rgds

When you make a Dash app, you must define all callbacks before you start the app. In the first version of you main conditional, that’s what your code does. However in the second version you create a new instance of clsSimpleGraph within every location change callback, which in turn, creates a new callback, but now this is after the app has been started, so I think this is causing problems.

I think you want to change your code so that you only every create a single instance of clsSimpleGraph. it’s probably also going to make your code easier to reason about if you only include the run_server invocation inside of the if __name__=="__main__": conditional, and move the rest of the logic outside. There’ no particular reason to be doing things like creating the Dash app instance inside that context.

thks for your reply :grinning:

Will give it a try but then, I also need to change all the logic of my main App.

The idea to build a graph class was that in my App, I will have more than 60 graphs to go through. Instead of creating an appX.py file fo each graphs, I was expecting a single app.py file, but this file will dynamically create the graphs, based on the parameters stored in a database : datasets, type of graph, and the list of “columns” that will be linked to components to interact with the graph. Which means potentialy, the components list is different between 2 graphs, so I need to build them dynamically, as well as their callbacks.

This process works fine (I have created 4 graphs this way) using the class and the parameters in DB. For my tests I just call the program manually, by changing the grpahID in parameter.
But in the website, I was thinking about working with the routes (dcc.location, dcc.link). Example :

 if pathname==1 : 
       Graph= clsGraph(1)
  elif pathname==2 
      Graph = clsGraph(2)
...

By doing this way, I think the maintenance of the python code is much more simple, avoiding to redo a lot of similar code, and the job is to make sure that the parameters in the database are correct.

But as you mentioned, the server should be started to create the links, which means before call the constructor…

Rgds

No worries :slight_smile:

My hunch is that you’ve over-complicated things a bit with the use of the class to try to do everything. It might be simpler to create the layout and define all the relevant callbacks up front not inside a class (as is done in the Dash docs) and then in the location callback you could have a function that builds the relevant graph given the current pathname. eg build_graph(pathname).

uhm

means I need to create 60 callbacks (with some of them multi-inputs), create all theses inputs first to get a unique ID for them, and display them only when necessary.

Might be overloaded the memory

Will try for fun

You do in fact need to create every callback that your app will ever use up front before it starts running. And yes, you’ll need to create unique IDs for the output elements. There’s been some discussion of this before on the forum. In particular, see Callback for Dynamically created Graph.

Right now, you need to create all of the callbacks up-front.

That doesn’t mean it’s smth that won’t be possible in the future though :slight_smile:

Hello guys

Have done it, it works, but the memory with 60 charts and on average 6 components is full.

I will find a way to rationalize this, no problem.

BTW, has anyone ever tried to render the dash web page(s) on android ? Which front end are you using ?

I would like to give a try with Kivy which is an amazing framework. But not sure how to manage the components. Using a webview might work, but their might be a better solution ?

Rgds

Could[quote=“jejesh26, post:9, topic:9260”]
BTW, has anyone ever tried to render the dash web page(s) on android ? Which front end are you using ?

I would like to give a try with Kivy which is an amazing framework. But not sure how to manage the components. Using a webview might work, but their might be a better solution ?
[/quote]

Could you create a new thread to discuss this? That way it’ll be more discoverable when people search for this question in the future.

1 Like