I am working on an app with multi pages feature in Plotly Dash that uses this schema:
|app.py
| pages
|
| games_
*dashboard.py
|
|channels_dashboard.py
|
| *videos_dashboard.py
The project is talking about youtube gaming analysis (videos, channels, comments) data and each file of the pages folder file contains layout in a layout variable and some callbacks that are also in a variable here's some example code:
layout = html.Div(children= [
html.Center(children= html.H1(children= ["Analysis for specfic", html.Span(
" Games ", style= {"color": THEME_COLORS[1]}), "Videos"])),
dcc.Graph(id= "video_stats_per_game",
figure= video_stats_per_game.update_layout(width= 1240, height= 500),
style= {'position': 'absolute', 'border': f'2px solid {THEME_COLORS[0]}','left': '10px',
'display': 'inline-block'}),
html.Div(["Choose a game:",
dcc.Dropdown(GAMES, "Minecraft" ,id= "game_dropdown")],
style= {'top': '600px', 'position': 'absolute', 'display': 'inline-block',
'width': '1230px'}), #Bla bla bla
and here's the callback varible for the same file:
callback_1 = [Output('duration_vs_view', 'figure'), # this `duration_vs_view` is a plot name.
[Input('game_dropdown', 'value')]]
def duration_vs_view(value): # Some unreadable code ...
callback_2 = [Output('stats_growth', 'figure'),
[Input('game_dropdown', 'value')]]
def stats_growth(value): # bla bla bla
callbacks = callback_1 + callback_2 + callback_3
Now what I want to do is implement the callbacks into my app.py
page_container
and here's some code from app.py
:
# --------------------Creating the main app----------------------
app = Dash(__name__, use_pages=True,
external_stylesheets= EXERNAL_STYLESHEETS)
app.layout = html.Div([
html.Center(html.H1([html.Span("Youtube",
style= {"color": THEME_COLORS[1]}), ' gaming analysis'])),
html.Button('Channels dashboard', id='channels_dashboard_button'),
html.Button('Games dashboard', id='games_dashboard_button'),
html.Center(html.Button('Videos dashboard', id='videos_dashboard_button',)),
html.Div(id='page-content', children=dash.page_container)])
# ---------------------------------------------------------------
@app.callback(Output('page-content', 'children'),
[Input('channels_dashboard_button', 'n_clicks'),
Input('games_dashboard_button', 'n_clicks'),
Input('videos_dashboard_button', 'n_clicks')])
def display_page(channels_clicks, games_clicks, videos_clicks):
if channels_clicks:
return (pages.channels_dashboard.layout,
pages.channels_dashboard.callbacks)
elif games_clicks:
return (pages.games_dashboard.layout,
pages.games_dashboard.callbacks) # ...
else:
return ''
The plotly dash app returns a callback
error when I click on any button that leads me to a dashbord with callbcks and here's the error message:
dash.exceptions.InvalidCallbackReturnValue: The callback for `<Output `page-content.children`>`
returned a value having type `tuple`
which is not JSON serializable.
The value in question is either the only value returned,
or is in the top level of the returned list,
I expected that I will get the dashboard just as fine as I was running them without the muli pages functionality (I used before the normal call back way for single variables)
please if you can help me by telling me a totally another way please do that like to use links to each dashboard or something and if you have any idea about how can I fix this please also do that if the problem needs more code or screenshots to put also tell me,Thanks in advance
As mentioned in the comments, the InvalidCallbackReturnValue
exception you got is because return statements like this:
return (pages.channels_dashboard.layout, pages.channels_dashboard.callbacks)
For your Output
: Output('page-content', 'children')
a valid return value could be:
return pages.channels_dashboard.layout
But you can't setup the page callbacks this way.
I think your display_page
callback, the passing of layout and callback variables are all unnecessary. I think it's working against the dash pages feature.
You can implement the callbacks in the pages (.py files) themselves.
Here's an example of a simple page with a callback:
# File: pages/page1.py
from dash import html, callback, Input, Output, register_page
register_page(__name__)
layout = html.Div([
html.Button(id="page1-input", children="Click me"),
html.Div(id="page1-output")
])
@callback(
Output("page1-output", "children"),
Input("page1-input", "n_clicks")
)
def update_city_selected(n_clicks):
return n_clicks
Instead of using inputs like Input('channels_dashboard_button', 'n_clicks')
to go to a certain page you can just use links, no callback required:
dcc.Link("Link", href="/page1")
If you need the link to look/act like a button you can wrap a button in a link.
Example app.py
:
from dash import Dash, html, dcc
import dash
app = Dash(__name__, use_pages=True)
app.layout = html.Div([
html.Div(
[
html.Div(dcc.Link(f"{page['name']} - {page['path']}", href=page["relative_path"]))
for page in dash.page_registry.values()
]
),
dash.page_container
])
if __name__ == '__main__':
app.run_server(debug=True)