I have a problem with hiding and showing a graph in the Python framework, Dash.
I define a new graph:
html.Div([
dcc.Graph(id = 'graph'),
],),
After I update my trace data with a callback function I return it and it is shown in the graph, but if nothing is selected from my dropdown menu I do
if not input or input == []:
return {'display': 'none'}
so that my graph is not shown, but it doesn't work for some reason.
Is there a workaround?
Thank you in advance
You can set the id element for the Div holding your graph and hide the whole div by using #app.callback on the style element.
html.Div(id="graph-container", children= [
dcc.Graph(id = 'graph'),
]),
#app.callback(Output('graph-container', 'style'), [Input('input_widget','value')])
def hide_graph(input):
if input:
return {'display':'block'}
else:
return {'display':'none'}
To see this code in action you can test out my code:
import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output
app = dash.Dash()
app.layout = html.Div(children=[
html.H1(children='Hello Dash'),
html.Div(children='''
Dash: A web application framework for Python.
'''),
html.Label("Show?"),
dcc.Dropdown(
id="my-input",
options = [
{'label':'Yes', 'value':True},
{'label':'No', 'value':False}
],
value = True
),
html.Div(
id="graph-container",
children = [
dcc.Graph(
id='example-graph',
figure={
'data': [
{'x': [1, 2, 3], 'y': [4, 1, 2], 'type': 'bar', 'name': 'SF'},
{'x': [1, 2, 3], 'y': [2, 4, 5], 'type': 'bar', 'name': u'Montréal'},
],
'layout': {
'title': 'Dash Data Visualization'
}
}
)
])
])
#app.callback(Output('graph-container', 'style'), [Input('my-input', 'value')])
def hide_graph(my_input):
if my_input:
return {'display':'block'}
return {'display':'none'}
if __name__ == '__main__':
app.run_server(port=8125, debug = True)
Note I used the following versions:
python 3.6.4
dash 0.21.0
dcc 0.22.1
html 0.10.0
Thanks lwileczek for the answer. I wanted to provide an update as the code needs a few minor tweaks to work in the following environment:
python 3.7.4
plotly 4.1.1
dash 1.2.0
dcc 1.1.2
html 1.0.1
When I copied lwileczek's code into the above environment, dash through an error about passing a bad argument into the dropdown. After a couple of moments of trial and error, I was able to make the code work if I changed the values in the dropdown to strings and modified the conditional in the callback so that it was doing a string comparison as opposed to expecting the value to be a boolean.
The code for the dropdown looks like:
dcc.Dropdown(
id = 'my-input',
options = [
{'label':'Yes', 'value':'Yes'},
{'label':'No', 'value':'No'}
],
value='Yes'
),
The code for the conditional in the callback becomes:
if my_input == 'Yes':
return {'display': 'block'}
return {'display': 'none'}
(This should probably be a comment, but I don't have enough points to post a comment)
Related
I am new to Python Dash, so I am not sure what is possible. What I would like to accomplish is making a datatable where the user is able to manipulate data in the datatable which would result in changes for other graphs.
Here is an example of what I am trying to do:
I have information about people’s grocery lists and I would like to be able to change what is in their list
The data that I have is structured as such
Name
Item
Amount
Bob
Apple
1
Bob
Banana
2
Anna
Apple
2
Anna
Banana
1
I would like to be able to have a datatable where there is a drop down to select the person’s list so the datatable would just have their items and amounts. In the selected datatable, they should be able to add and delete rows, change the item with a dropdown in the cell, and change the amount by typing in the desired amount.
I have been able to create this somewhat where you can select a person’s list, and the item can be changed based on a list of foods given, but I cannot figure out how to do the rest. Here is what I have been able to put together for my example
from dash import dash_table as dt
from dash import dcc
from dash import html
from dash.dependencies import Input
from dash.dependencies import Output
import pandas as pd
df = pd.DataFrame({'Name' : ['Bob', 'Bob', 'Anna', 'Anna'],
'Item' : ['Apple', 'Banana', 'Apple', 'Banana'],
'Amount' : [1, 2, 2, 1]})
app = dash.Dash(__name__)
names = df['Name'].unique().tolist()
app.layout = html.Div(
children=[
dcc.Dropdown(
id="filter_dropdown",
options=[{"label": name, "value": name} for name in names],
placeholder="-Select a Person-",
multi=False,
value=df['Name'].values,
),
dt.DataTable(
id="table-container",
columns=[{'name': 'Item', 'id': 'Item', 'presentation':'dropdown'},
{'name': 'Amount', 'id': 'Amount'}],
data=df.to_dict("records"),
editable=True,
dropdown={
'Item' : {
'options': [
{'label': i, 'value': i}
for i in list(df['Item'].unique())
]
}
}
)
]
)
#app.callback(
Output("table-container", "data"),
Input("filter_dropdown", "value"))
def display_table(name):
dff = df[df['Name'].isin([name])]
return dff.to_dict("records")
if __name__ == "__main__":
app.run_server(debug=True)
Here is what it looks like in the browser
I can select an item from the dropdown in the cell and it will appear to change it, but when I switch between people it loses its change that I made. I also cannot change the amounts. The next step would be is if I can use all the data as well as the changes in the data in another graph (for example to show how many people have a certain item). If someone would be able to help, I would appreciate it.
Thanks
After doing some work, I do have it working. It is by means not the optimal or right way to do this probably, but it does what I want it to do. I ended up using a global variable for my data frame and whenever there was a change to the data table, I would filter out the data from the data frame and add the modified data from the data table.
To get a visual to work, I listen for changes to the data table. I know that there is probably a race between updating the global variable and returning the new visual, but I did not have to delay the visual being updated.
Below is the code that I came up with. I hope this helps others somehow if they wanted to make a simple "spreadsheet" that people can manipulate and result in a responsive visual related to the data.
import dash
import pandas as pd
from dash import dash_table as dt
from dash import dcc
from dash import html
from dash.dependencies import Input
from dash.dependencies import Output
import plotly.express as px
df = pd.DataFrame({'Name' : ['Bob', 'Bob', 'Anna', 'Anna'],
'Item' : ['Apple', 'Banana', 'Apple', 'Banana'],
'Amount' : [1, 2, 2, 1]})
app = dash.Dash(__name__)
names = df['Name'].unique().tolist()
app.layout = html.Div(
children=[
dcc.Dropdown(
id="filter_dropdown",
options=[{"label": name, "value": name} for name in names],
placeholder="-Select a Person-",
multi=False,
value=df['Name'].values,
),
dt.DataTable(
id="table-container",
columns=[{'name': 'Item', 'id': 'Item', 'presentation':'dropdown'},
{'name': 'Amount', 'id': 'Amount'}],
data=df.to_dict("records"),
editable=True,
row_deletable=True,
dropdown={
'Item' : {
'options': [
{'label': i, 'value': i}
for i in list(df['Item'].unique())
]
}
}
),
html.Button('Add', id='add_btn', n_clicks = 0),
dcc.Graph(id='visual')
]
)
#app.callback(
Output("table-container", "data"),
Input("filter_dropdown", "value"),
Input("table-container", "data"),
Input("table-container", "columns"),
Input("add_btn", 'n_clicks'))
def display_table(name, rows, columns, n_clicks):
e = dash.callback_context.triggered[0]['prop_id']
global df
if e in ["table-container.data", "table-container.columns"]:
temp = pd.DataFrame(rows, columns=[c['name'] for c in columns])
temp['Name'] = name
df = df[~df['Name'].isin([name])]
df = df.append(temp)
elif e == 'add_btn.n_clicks':
if n_clicks > 0:
df = df.append(pd.DataFrame({'Name':name, 'Item': '', 'Amount': 0}, index=[0]))
dff = df[df['Name'].isin([name])].to_dict("records")
return dff
#app.callback(Output('visual', 'figure'),
Input("table-container", "data"),
Input("table-container", "columns"))
def display_graph(rows, columns):
fig = px.bar(df, x='Item', y='Amount')
return fig
if __name__ == "__main__":
app.run_server(debug=True)
Just creating a simple Graph, from an excel file, it loads correctly but at the moment of updating the graph dash gives back this error:
Failed component prop type: Invalid component prop figure key props supplied to Graph.
Bad object: {
“props”: {
“id”: “average_country”,
“figure”: {
“data”: [
{
“x”: [
“DE”,
“ES”,
“FR”,
“IT”,
“UK”
],
“y”: [
[
2365.56,
4528.33875,
4851.085,
4325.14,
2107.921428571429
]
],
“type”: “bar”
}
],
“layout”: {
“title”: “Hyperzone”
}
}
},
“type”: “Graph”,
“namespace”: “dash_core_components”
}
Valid keys: [
“data”,
“layout”,
“frames”
]
I’m just using a pivot table to create the mean of data of some countries, here’s my code, the pivot table is correct and outputs a valid result with the values I want, just when I try to update the graph with the callback function it returns an Error message.
import pandas as pd
import dash
import dash_core_components as dcc
import dash_html_components as html
import plotly.graph_objs as go
from dash.dependencies import Input, Output
HP= pd.read_excel(r’C:\Users\salinejs\HP.xlsx’)
Columns = [‘Station’, ‘Week’, ‘Owner’, ‘Cost’,‘Country’]
HP_filtered = HP[Columns]
avaliable_weeks= HP_filtered[‘Week’].unique()
external_stylesheets = [‘https://codepen.io/chriddyp/pen/bWLwgP.css’]
app = dash.Dash(name, external_stylesheets=external_stylesheets)
app.layout = html.Div([
html.Div([
html.Div([
html.Div([
dcc.Dropdown(
id= 'Week_filter',
options = [{'label':j, 'value':j}for j in avaliable_weeks],
value= 'Week 42'
),
],
style= {'float':'left', 'display':'inline-block'}
),
html.Div([
dcc.Graph(
id='average_country',
)
],
)
]
),
],
)
#app.callback(
Output(‘average_country’, ‘figure’),
[Input(‘Week_filter’,‘value’)]
)
def update_value(week):
HP_filtered_week = HP_filtered[ HP_filtered['Week'] == week ]
pv = pd.pivot_table(HP_filtered_week, values='Cost', columns='Country', index='Week')
print(pv)
print(pv.columns)
return dcc.Graph(
id='average_country',
figure={
'data': [{'x': pv.columns , 'y' : pv.values,'type': 'bar'
}],
'layout':{
'title': 'Cost',
}
})
if name == ‘main’:
app.run_server(debug=True)
A bit late - you probably sorted this by now, but I just had the same error and an answer here would have saved me time.
It's because you are sending the callback output to figure but the function is returning the entire dcc.Graph() instead of just the figure.
change
return dcc.Graph(
id='average_country',
figure={
'data': [{'x': pv.columns , 'y' : pv.values,'type': 'bar'
}],
'layout':{
'title': 'Cost',
}
})
to
return {'data': [{'x': pv.columns , 'y' : pv.values,'type': 'bar'
}],
'layout':{
'title': 'Cost',},}
I was looking for a solution to this issue I encountered. There is a question almost similar to mine in plotly community (https://community.plot.ly/t/problem-with-densitymapbox-chart-type/28517), but still haven’t found a resolution. My dropdown menu consists of scattermapbox and densitymapbox as i wanted to juggle between these. However, when changing from scattermapbox to densitymapbox, it results to the image below:
densitymapbox after scattermapbox format
import dash
import copy
import pathlib
import dash
import numpy as np
import math
import datetime as dt
import pandas as pd
from dash.dependencies import Input, Output, State, ClientsideFunction
import dash_core_components as dcc
import dash_html_components as html
import dash_table
import plotly.graph_objs as go
# get relative data folder
PATH = pathlib.Path(__file__).parent
DATA_PATH = PATH.joinpath("data").resolve()
external_scripts = [
‘https://cdn.plot.ly/plotly-1.39.1.min.js’
]
external_stylesheets = [
‘https://codepen.io/etpinard/pen/wxwPXJ’
]
app = dash.Dash(
__name__,
external_scripts=external_scripts,
external_stylesheets=external_stylesheets
)
server = app.server
# Load data
df = pd.read_excel("Clean_TR(6.8.19).xlsx")
group_name = df['gname'].unique()
mapbox_access_token = <your token>
app.layout = html.Div(
[
dcc.Store(id = 'aggregate_data'),
dcc.Dropdown(
id = 'map_plot',
options = [{'label':i, "value":i} for i in ['Scatter', 'Density']],
value = ['Scatter']
),
dcc.Graph(id = 'mindanao-map')
]
)
#app.callback(
Output('mindanao-map', 'figure'),
[Input('map_plot', 'value')]
)
def update_map(map_plot):
if map_plot == "Density":
maptype = 'densitymapbox'
else:
maptype = 'scattermapbox'
return {
'data' : [{
'lat':df['latitude'],
'lon':df['longitude'],
'marker':{
'color': df['freq'],
'size': 8,
'opacity': 0.6
},
'customdata': df['idno'],
'type': maptype
}],
'layout': {
'mapbox': {
'accesstoken': mapbox_access_token,
'style':"light",
'center': dict(lon=123.30, lat= 7.50),
'zoom':'6',
},
'hovermode': 'closest',
'margin': {'l': 0, 'r': 0, 'b': 0, 't': 0}
}
}
if __name__ == "__main__":
app.run_server(debug=True)
But whenever I swap out the if-else ordering, i.e,
if map_plot == "Scatter":
maptype = 'scattermapbox'
else:
maptype = 'densitymapbox'
it results to density map showing, but scatter will not.
Do I need to separate these two instead of if-else? Any inputs will do. Thank you for your time!
This is a Plotly.js bug, and I've filed a report here: https://github.com/plotly/plotly.js/issues/4285
Edit: this bug is fixed in recent versions of Plotly.js and Plotly.py and Dash.
In the sample Dash application below, I am attempting to create a dynamic layout with a variable number of rows and columns. This dynamic grid-style layout will be populated with various graphs that can be modified by dropdowns, etc.
The main issue I have run into thus far pertains to viewport-units and attempting to style the individual graphs appropriately to accommodate the dynamic layout. For example, I am modifying the style of the dcc.Graph() components via viewport-units, where the dimensions (e.g. height and width may be either 35vw or 23vw depending on the number of columns). When I change the number of columns from 3 to 2, for example, the height and width of the dcc.Graph() component are clearly changed, however this change is not reflected in the actual rendered layout until the window is physically resized (see the images below the sample code).
How do I force the dcc.Graph() components to propagate these changes without having to resize the window?
import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output, State
external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
app = dash.Dash(__name__, external_stylesheets=external_stylesheets)
app.config.suppress_callback_exceptions = True
app.layout = html.Div([
html.Div(className='row', children=[
html.Div(className='two columns', style={'margin-top': '2%'}, children=[
html.Div(className='row', style={'margin-top': 30}, children=[
html.Div(className='six columns', children=[
html.H6('Rows'),
dcc.Dropdown(
id='rows',
options=[{
'label': i,
'value': i
} for i in [1,2,3,4]],
placeholder='Select number of rows...',
clearable=False,
value=2
),
]),
html.Div(className='six columns', children=[
html.H6('Columns'),
dcc.Dropdown(
id='columns',
options=[{
'label': i,
'value': i
} for i in [1,2,3]],
placeholder='Select number of columns...',
clearable=False,
value=3
),
])
]),
]),
html.Div(className='ten columns', id='layout-div', style={'border-style': 'solid', 'border-color': 'gray'}, children=[])
])
])
#app.callback(
Output('layout-div', 'children'),
[Input('rows', 'value'),
Input('columns', 'value')])
def configure_layout(rows, cols):
mapping = {1: 'twelve columns', 2: 'six columns', 3: 'four columns', 4: 'three columns'}
sizing = {1: '40vw', 2: '35vw', 3: '23vw'}
layout = [html.Div(className='row', children=[
html.Div(className=mapping[cols], children=[
dcc.Graph(
id='test{}'.format(i+1+j*cols),
config={'displayModeBar': False},
style={'width': sizing[cols], 'height': sizing[cols]}
),
]) for i in range(cols)
]) for j in range(rows)]
return layout
#Max layout is 3 X 4
for k in range(1,13):
#app.callback(
[Output('test{}'.format(k), 'figure'),
Output('test{}'.format(k), 'style')],
[Input('columns', 'value')])
def create_graph(cols):
sizing = {1: '40vw', 2: '35vw', 3: '23vw'}
style = {
'width': sizing[cols],
'height': sizing[cols],
}
fig = {'data': [], 'layout': {}}
return [fig, style]
if __name__ == '__main__':
app.server.run()
Relevant screenshots (Image 1 - page load, Image 2 - change columns to 2):
Here is how to proceed:
app.py must import:
from dash.dependencies import Input, Output, State, ClientsideFunction
let’s include the below Div somewhere in the Dash layout:
html.Div(id="output-clientside"),
asset folder must include either your own script, or the default script resizing_script.js, which contains:
if (!window.dash_clientside) {
window.dash_clientside = {};
}
window.dash_clientside.clientside = {
resize: function(value) {
console.log("resizing..."); // for testing
setTimeout(function() {
window.dispatchEvent(new Event("resize"));
console.log("fired resize");
}, 500);
return null;
},
};
Among your callbacks, put this one, without #:
app.clientside_callback(
ClientsideFunction(namespace="clientside", function_name="resize"),
Output("output-clientside", "children"),
[Input("yourGraph_ID", "figure")],
)
At this point, when you manually resize the window, in your browser, the resize function is triggered.
We aim to achieve the same result, but without manual window resizing. For instance, the trigger could be a className update.
So, we apply the following changes:
Step 1: unchanged
Step 2: unchanged
Step 3: let’s add a “resize2” function inside our javascript file, which takes 2 arguments:
if (!window.dash_clientside) {
window.dash_clientside = {};
}
window.dash_clientside.clientside = {
resize: function(value) {
console.log("resizing..."); // for testing
setTimeout(function() {
window.dispatchEvent(new Event("resize"));
console.log("fired resize");
}, 500);
return null;
},
resize2: function(value1, value2) {
console.log("resizingV2..."); // for testing
setTimeout(function() {
window.dispatchEvent(new Event("resize"));
console.log("fired resizeV2");
}, 500);
return value2; // for testing
}
};
Function “resize2” now takes 2 arguments, one for each Input defined in the below callback. It will return the value of “value2” in the Output, specified in this very same callback. You can set it back to “null”, it’s just to illustrate.
Step4: our callback now becomes:
app.clientside_callback(
ClientsideFunction(namespace="clientside", function_name="resize2"),
Output("output-clientside", "children"),
[Input("yourGraph_ID", "figure"), Input("yourDivContainingYourGraph_ID", "className")],
)
Finally, you need a button to trigger the event which will change the className of your container.
let’s say your have:
daq.ToggleSwitch(
id='switchClassName',
label={
'label':['Option1', 'Option2'],
},
value=False,
),
And the following callback:
#app.callback(Output("yourDivContainingYourGraph_ID", "className"),
[Input("switchClassName","value")]
)
def updateClassName(value):
if value==False:
return "twelve columns"
else:
return "nine columns"
Now, if you save everything, refresh, everytime you press on your toggleSwitch,it resizes the container, triggers the function, and refreshes the figure.
Given the way it’s done, I assume it must also be possible to run more Javascript functions, the same way, but I didnt check yet.
Hope it will help some
The behavior looks like a Plotly bug to me.
Here is a possible workaround/short-termin solution.
There is a nice library visdcc which allows callbacks with Javascript. You can install it via
pip install visdcc
Add it to your div:
visdcc.Run_js(id='javascript'),
and add a callback
#app.callback(
Output('javascript', 'run'),
[Input('rows', 'value'),
Input('columns', 'value')])
def resize(_, __):
return "console.log('resize'); window.dispatchEvent(new Event('resize'));"
Plotly will throw an error in the console after the resize event (this also happens when the windows is manually resized) but the plots are shown correctly.
Full code
import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output, State
import visdcc
SIZING = {1: '40vw', 2: '35vw', 3: '23vw'}
external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
app = dash.Dash(__name__, external_stylesheets=external_stylesheets)
app.config.suppress_callback_exceptions = True
app.layout = html.Div([
visdcc.Run_js(id='javascript'),
html.Div(className='row', children=[
html.Div(className='two columns', style={'margin-top': '2%'}, children=[
html.Div(className='row', style={'margin-top': 30}, children=[
html.Div(className='six columns', children=[
html.H6('Rows'),
dcc.Dropdown(
id='rows',
options=[{
'label': i,
'value': i
} for i in [1,2,3,4]],
placeholder='Select number of rows...',
clearable=False,
value=2
),
]),
html.Div(className='six columns', children=[
html.H6('Columns'),
dcc.Dropdown(
id='columns',
options=[{
'label': i,
'value': i
} for i in [1,2,3]],
placeholder='Select number of columns...',
clearable=False,
value=3
),
])
]),
]),
html.Div(className='ten columns', id='layout-div', style={'border-style': 'solid', 'border-color': 'gray'}, children=[])
])
])
#app.callback(
Output('layout-div', 'children'),
[Input('rows', 'value'),
Input('columns', 'value')])
def configure_layout(rows, cols):
mapping = {1: 'twelve columns', 2: 'six columns', 3: 'four columns', 4: 'three columns'}
layout = [html.Div(className='row', children=[
html.Div(className=mapping[cols], style={'width': SIZING[cols], 'height': SIZING[cols]}, children=[
dcc.Graph(
id='test{}'.format(i+1+j*cols),
config={'displayModeBar': False},
style={'width': SIZING[cols], 'height': SIZING[cols]}
),
]) for i in range(cols)
]) for j in range(rows)]
return layout
#app.callback(
Output('javascript', 'run'),
[Input('rows', 'value'),
Input('columns', 'value')])
def resize(_, __):
return "console.log('resize'); window.dispatchEvent(new Event('resize'));"
#Max layout is 3 X 4
for k in range(1,13):
#app.callback(
[Output('test{}'.format(k), 'figure'),
Output('test{}'.format(k), 'style')],
[Input('columns', 'value')])
def create_graph(cols):
style = {
'width': SIZING[cols],
'height': SIZING[cols],
}
fig = {'data': [], 'layout': {}}
return [fig, style]
if __name__ == '__main__':
app.server.run()
I want to export the site I've made in dash into a static PDF. Here is the code for my site (it's just a chart with 3 columns):
import dash
import dash_core_components as dcc
import dash_html_components as html
import pdfkit
from flask import Flask, render_template, make_response
app = dash.Dash()
app.layout = html.Div(
className="three columns",
children=html.Div([
dcc.Graph(
id='right-top-graph',
figure={
'data': [{
'x': [1, 2, 3],
'y': [3, 1, 2],
'type': 'bar'
}],
'layout': {
'height': 400,
'margin': {'l': 10, 'b': 20, 't': 0, 'r': 0}
}
}
),
])
)
app.css.append_css({
'external_url': 'https://codepen.io/chriddyp/pen/bWLwgP.css'
})
if __name__ == '__main__':
app.run_server(debug=True)
I tried using pdfkit by adding this code to my script, but it didn't work (received an error telling me that render_template() takes 1 positional argument but 2 were given):
rendered = render_template('pdf_template.html',app)
pdf = pdfkit.from_string(rendered, False)
response = make_response(pdf)
response.headers['Content-Type'] = 'application/pdf'
response.headers['Content-Disposition'] = 'attachment; filename=output.pdf'
Does anyone have any idea as to how I can convert my dash site into a PDF?
Thanks in advance.
You can use the print function of whatever browser you are using (usually control + p) and save it as PDF if all you are looking for is the static PDF file.
If you want more enhanced functionality you can add a print to PDF button like the one in one of the dash examples. It uses a js file to invoke the browser print functionality, see more detail. This way you can also use CSS to define the way the PDF output looks
The problem with using the python file to generate pdf directly is that dash only creates a JSON representation of the layout tree which is then assembled in the browser itself, see more.
You can use pdfkit in the following way:
import pdfkit
pdfkit.from_url('http://local.dash.site', 'out.pdf')
The big difference from what you posted is that you could use the local web server to render the page.
As an alternative, you could also use https://wkhtmltopdf.org/
This is the lib underneath pdfkit.
You can use the directions from the following link to create pdf:
http://flask.pocoo.org/snippets/68/
render_template accepts only one positional arguments, the rest must be keyword arguments.
render_template takes only one positional argument.
Can you try below?
options = {
'margin-top': '0.15in',
'margin-right': '0.15in',
'margin-bottom': '0.2in',
'margin-left': '0.15in',
'encoding': "UTF-8",
}
css = 'path to your .css file'
html = render_template('a_html.html')
pdf = pdfkit.from_string(html, False, options=options, css=css)
Have you tried running your script in the following way (I've just pasted the pdf creation part of your script into where the dash site is rendered):
import dash
import dash_core_components as dcc
import dash_html_components as html
import pdfkit
from flask import Flask, render_template, make_response
app = dash.Dash()
app.layout = html.Div(
className="three columns",
children=html.Div([
dcc.Graph(
id='right-top-graph',
figure={
'data': [{
'x': [1, 2, 3],
'y': [3, 1, 2],
'type': 'bar'
}],
'layout': {
'height': 400,
'margin': {'l': 10, 'b': 20, 't': 0, 'r': 0}
}
}
),
])
)
app.css.append_css({
'external_url': 'https://codepen.io/chriddyp/pen/bWLwgP.css'
})
rendered = render_template('pdf_template.html',app)
pdf = pdfkit.from_string(rendered, False)
response = make_response(pdf)
response.headers['Content-Type'] = 'application/pdf'
response.headers['Content-Disposition'] = 'attachment; filename=output.pdf'
if __name__ == '__main__':
app.run_server(debug=True)