I have a line chart where the user can select the x range. A callback then requests data for the given x range from a database and displays it.
However, there may not be any data for the chosen x range, resulting in an empty graph, which looks a bit clumsy. What would be a good way to signal the user that there is no data for the selected x range?
Here is a small example I put together. Selecting some x range between 4 and 11 (exlusive) by drag-selection on the graph will result in an empty graph.
import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output
import plotly.graph_objects as go
app = dash.Dash()
app.layout = html.Div([dcc.Graph(id='my-graph'), dcc.Store(id='my-data')])
@app.callback(Output('my-data', 'data'),
Input('my-graph', 'relayoutData'),)
def update_data(relayout_data):
start, end = 1, 14
if isinstance(relayout_data, dict):
if 'xaxis.range[0]' in relayout_data and 'xaxis.range[1]' in relayout_data:
start = relayout_data['xaxis.range[0]']
end = relayout_data['xaxis.range[1]']
return pull_data(start, end)
def pull_data(start, end): # imagine this being a DB request or similar
x = [1,2,3,4,11,12,13,14]
x = [i for i in x if start <= i <= end]
y = [i**2 for i in x]
return [x,y]
@app.callback(Output('my-graph', 'figure'),
Input('my-data', 'data'),)
def update_graph(data):
print("selected data:", data)
x, y = data
fig = go.Figure(data=go.Scatter(x=x, y=y))
fig.update_yaxes(fixedrange=True)
if not x:
pass # what could I do here?
return fig
if __name__ == '__main__':
app.run_server(debug=True)
One way I found for now is to add a very visible annotation to inform the user about the missing data, like so:
if not x:
fig.add_annotation(text='No data in chosen x range',
xref='paper', yref='paper', x=0.5, y=0.5, showarrow=False,
font=dict(size=24), bgcolor="rgba(255,127,14,0.5)",)
The exact position (x
and y
), the font
size
and the bgcolor
can be adjusted of course. One could also use the visible
argument of fig.add_annotation
for further control.
It's still a bit clumsy for the example chosen in the question, because the x axis range won't be accurate, but at least the user is told what is going on.
Edit: Having an accurate x axis range was important for me in a later use case, where the x axis was of type date. Luckily, that's easy to set manually if the start
and end
of the chosen range are also stored, like so: fig.update_xaxes(range=[start,end])