i'm new with Python & Dash and i don't know how to force my application to refresh the shown layout without use the callback; this is my code:
AppMain.py
app.layout = html.Div(
children=[
dcc.Location(
id=ID_URL,
refresh=False
),
html.Div(
[
dcc.Link(
'Test',
href=HREF_PAGE_TEST,
),
dcc.Link(
'Single',
href=HREF_PAGE_SINGLE,
),
dcc.Link(
'Common',
href=HREF_PAGE_COMMON,
),
]
),
html.Div(
id=ID_PAGE_CONTAINER,
),
),
#app.callback(
Output(ID_PAGE_CONTAINER, 'children'),
[Input(ID_URL, 'pathname')],
)
def clickOnPageToShow(pathname):
return findPageToShow(pathname)
def findPageToShow(pathname):
if pathname == HREF_PAGE_TEST:
PageTest.data_frame = shared_data_frame
return PageTest.layout
elif pathname == HREF_PAGE_SINGLE:
PageSingle.data_frame = shared_data_frame
return PageSingle.layout
elif pathname == HREF_PAGE_COMMON:
PageCommon.data_frame = shared_data_frame
return PageCommon.layout
return ""
Every page builds own layout and user choose which one shown clicking on the related dcc.Link; this procedure works fine and user can change page avery time he wants.
What i want now is to build and shown the PageSingle in programmatically way, so i tried to add:
AppMain.showPageSingle()
and
def showPageSingle():
page = findPageToShow(HREF_PAGE_SINGLE)
app.layout[ID_PAGE_CONTAINER].children = [page]
#app.layout[ID_PAGE_CONTAINER].children = page
But nothing happens.
What i do wrong?
Thanks
EDIT
How suggested i use a callback raised when use click on a cell of table created into page Common
PageCommon.py
#app.callback(
Output(ID_PAGE_CONTAINER, 'children'),
[
Input(ID_TABLE_COMMON, 'active_cell'),
Input(ID_TABLE_COMMON, 'data')
]
)
def clickOnCell(active_cell, data_table):
if active_cell is not None:
row_index = active_cell['row']
col_index = active_cell['column']
import AppMain
return AppMain.buildPageSingle()
return []
BUT in this case, application doesn't respond when i choose a page, maybe because the callback is not valid.
You can add an "interval" component in this way (in your layout):
dcc.Interval(
id='interval-component',
interval=60 * 1000, # in milliseconds , 20 seconds every refresh
n_intervals=0
And then, you can refer to that component.
#app.callback(Output('live-update-time', 'children'),
[Input('interval-component', 'n_intervals')])
def update_time(n):
current_time = datetime.datetime.now()
return html.P(f"Last Update at {current_time} ")
In the above example, i refresh my page every 20 second.
Related
I have a function that takes two inputs and a button to generate a table. It was working well until I placed the inputs and the button in a tab. Now the function does not recognize the button Id.
I get an error while loading the app. Function "displayDVFiltre" is not able to find component ids "VF_button" and "result". Most likely because they are generated during the "render_content" callback.
informations = html.Div([dcc.Tabs(id="tabs-styled-with-inline", value='VF', children[dcc.Tab(label='Ventes historiques', value='VF', disabled=False)], style=tabs_styles), html.Div(id='tabs-content inline')])
#app.callback(Output('tabs-content-inline', 'children'),
Input('tabs-styled-with-inline', 'value'))
def render_content(tab):
if tab == 'VF':
return VF_tab
VF_tab = dbc.Card(
dbc.CardBody(
[
html.H5("Historique des ventes immobilières"),
dbc.Input(id="cp", placeholder="Code postal", type="text"),
dbc.Input(id="cd", placeholder="Code cadastral", type="text"),
dbc.Button("Voir les ventes", id="VF_button", color="dark"),
html.Div(id="result"),
]
),
className="mt-3",
style=tab_style_inside
)
#app.callback(
Output("result", 'children'),
Input("VF_button","n_clicks"),
State("cp","value"),
State("cd","value")
)
def displayDVFiltre(click, codePostal, codeCadastral):
if click:
df = vf.valeurFonciereParCPetCodeCadastal(codePostal, codeCadastral)
print(df.columns)
return dash_table.DataTable(df.to_dict('records'), [{"name": i, "id": i} for i in df.columns])
else:
return {}
I have been trying to return the ‘href’ value from click event in python-Dash application. Below is my code snippet:
import dash
import dash_html_components as html
from dash.dependencies import Input, Output, State
app = dash.Dash(
__name__,
meta_tags=[{"name": "viewport", "content": "width=device-width, initial-scale=1"}],
)
def make_table(df, val):
table = []
for index, row in df.iterrows():
rows = []
html.Td([
html.Div([row["col1"]]),
html.A(id = 'link',href=row["file-link"], children=row["link-name"], target="_blank"),
])
table.append(html.Tr(rows))
return table
app.layout = html.Div([
html.Table(
id="table-element",
className="table__container",
)
],
className="six columns",
),
#app.callback(
Output("link", 'pathname'),
[Input('link', 'n_clicks')],
[State('link', 'href')]
)
def open_link(n_clicks, href):
enable_open_link(href) #enable_open_link function takes in the href string value (a local filesystem link) and opens it up in a new window.
#app.callback(
Output("table-element", 'children'),
[Input("input-1-submit", 'n_submit')],
[State('input-1-submit', 'value')]
)
def update_output(ns1,val):
table = make_table(df,val)
return table
This code works upto some extent i.e. it does returns a href value, but not the one I click. It always returns the last href value stored inside the html table.
Is there a way to fetch the href value when I click on the link?
I know I can use Jquery to fetch the correct href value… but I didn’t find a way to integrate javascript within the call-back function.
I think it's because all the links you'r creating have the same id ='link'.
You'll need to find a way around that. One possibilty would be to generate the id when creating it based on the index of your df, but then you'll also have to create the corresponding callbacks. This thread tells you how it could be done. plotly dash: create multiple callbacks (with loop?)
Taking a clue from thread provided by #Rudertier, I was able to get the solution.
Below is the updated code snippet:
def make_table(df, val):
table = []
for index, row in df.iterrows():
rows = []
html.Td([
html.Div([row["col1"]]),
html.A(id = 'link'+str(index),href=row["file-link"], children=row["link-name"], target="_blank"),
])
table.append(html.Tr(rows))
return table
app.layout = html.Div([
html.Table(
id="table-element",
className="table__container",
)
],
className="six columns",
),
links = ['link1','link2','link3','link4','link5','link6','link7','link8','link9','link10']
for link in links:
#app.callback(
Output('{}'.format(link), 'pathname'),
[Input('{}'.format(link), 'n_clicks')],
[State('{}'.format(link), 'href')]
)
def open_link(n_clicks, href):
enable_open_link(href) #enable_open_link function takes in the href string value (a local filesystem link) and opens it up in a new window.
#app.callback(
Output("table-element", 'children'),
[Input("input-1-submit", 'n_submit')],
[State('input-1-submit', 'value')]
)
def update_output(ns1,val):
table = make_table(df,val)
return table
I have a basic datatable in plotly/dash. My goal is to upload (or print for the sake of the example...) after I press the upload-button.
The issue is, that I can't figure out how to get the n_clicks attribute of the button back to zero. So what happens is that after I clicked the button for the first time it prints continuously whenever something changes (row added or number changed/added), but what I want is for it to print only once whenever I click the button.
This is the code:
import dash
from dash.dependencies import Input, Output, State
import dash_table
import dash_daq as daq
import dash_core_components as dcc
import dash_html_components as html
import pandas as pd
external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
df = pd.read_csv('.../dummy_csv.csv')
app = dash.Dash(__name__)
app.layout = html.Div([
html.Div(id='my-div'),
dash_table.DataTable(
id='adding-rows-table',
style_data={
'whiteSpace': 'normal',
'height': 'auto'
},
style_table={
'maxHeight': '800px'
, 'overflowY': 'scroll'
},
columns=[
{'name': i, 'id': i} for i in df.columns
],
data=df.to_dict('records'),
editable=True,
row_deletable=True
),
html.Button('+ Row', id='editing-rows-button', n_clicks=0),
html.Button('Update', id='btn-nclicks-1', n_clicks=0 ),
])
#app.callback(
Output(component_id='my-div', component_property='children'),
[Input('btn-nclicks-1', 'n_clicks'), Input('adding-rows-table', 'data')]
)
def update_output_div(n_clicks, data):
if n_clicks > 0:
print(data)
# n_clicks = 0
# return n_clicks
else:
print("else loop")
#app.callback(
Output('adding-rows-table', 'data'),
[Input('editing-rows-button', 'n_clicks')],
[State('adding-rows-table', 'data'),
State('adding-rows-table', 'columns')])
def add_row(n_clicks, rows, columns):
if n_clicks > 0:
rows.append({c['id']: '' for c in columns})
return rows
if __name__ == '__main__':
app.run_server(debug=True)
This the CSV:
a,b,c,d
1,1,5,1
2,2,5,1
2,33,6,2
3,4,6,2
And this is the "faulty" output.
You could use the dash.callback_context property to trigger the callback only when the number of clicks has changed rather than after the first click. See the section on "Determining which Button Changed with callback_context" in the Dash documentation. The following is an example of how you could update your callback.
#app.callback(Output(component_id='my-div', component_property='children'),
[Input('btn-nclicks-1', 'n_clicks'), Input('adding-rows-table', 'data')])
def update_output_div(n_clicks, data):
changed_id = [p['prop_id'] for p in dash.callback_context.triggered][0]
if 'btn-nclicks-1' in changed_id:
print(data)
# n_clicks = 0
# return n_clicks
else:
print("else loop")
You can reset the n_clicks=None attribute, and it won't trigger an infinite loop when used in combination with a PreventUpdate null condition that you are probably already using.
#callback(
Output('some_btn', 'n_clicks'),
Input('some_btn', 'n_clicks')
)
def upon_click(n_clicks):
if (n_clicks is None): raise PreventUpdate
print(n_clicks)#1
return None
It's a bit more explicit and faster than callback_context 🤷♂️ For example, my button's id contained a complex "pattern-matching callback" so I didn't want to parse the callback context for that id
An option that worked for me is: n_clicks = 0 in the button declaration.
create list and add click data, then check if next index is greater than current index.
An approximate example with your code would be:
list_clicks = []
#app.callback(
Output('adding-rows-table', 'data'),
[Input('editing-rows-button', 'n_clicks')],
[State('adding-rows-table', 'data'),
State('adding-rows-table', 'columns')])
def add_row(n_clicks, rows, columns):
list_clicks.append(n_clicks)
for index in range(len(list_clicks)-1):
if list_clicks[index + 1] > list_clicks[index]:
list_clicks.clear()
rows.append({c['id']: '' for c in columns})
return rows
clicking on the button n_clicks is increased by 1.
What the code block does is compare if the current index is greater than the previous one.
If it is greater, it means that the button was clicked.
list_clicks.clear() it is to clean the list and not iterate over the same passed values.
maybe you could adapt it better to your code!
I am trying to create dash table on Web using Inputs. However the issue is that the data is created from database from the callback and a priori,
I do not know the names of the columns unless the pandas dataframeis created using the callback function.
I have checked that I getting correct data. However not able to display it. I have used multiple output options (using Dash 0.41)
My code looks as follows: ( I have not provided the details of the function which generates the pandas dataframe in the callback someFunc,
as that was not important for the purpose of this Dash code TroubleShooting.
import dash_table as dt
def someFunc(ID, pattern_desc, file_path):
## do something
return df # pandas dataframe
#
external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
app = dash.Dash(__name__, external_stylesheets=external_stylesheets)
server = app.server
app = dash.Dash(__name__)
app.config.suppress_callback_exceptions = True
app.css.config.serve_locally = True
app.scripts.config.serve_locally = True
app.layout = html.Div(
children = [
html.Div(
id = 'title',
children = appTitle,
className = 'titleDiv'
),
html.Div(
children = [
html.Div(
children = "Enter ID:",
className = 'textDiv'
),
dcc.Input(
id = 'ID',
type = 'text',
value = 'ABCER1',
size = 8),
html.Div(
children = "Enter Test Pattern",
className = 'textDiv'
),
dcc.Input(
id = 'pattern_desc',
type = 'text',
value = 'Sample',
size = 20),
html.Div(
children = "Enter File OutPut Path:",
className = 'textDiv'
),
dcc.Input(
id = 'file_path',
type = 'text',
value = '',
size = 30),
html.Button(
id = 'submit',
n_clicks = 0,
children = 'Search'
)
]
),
html.Div(
id = 'tableDiv',
children = dash_table.DataTable(
id = 'table',
style_table={'overflowX': 'scroll'},
style_as_list_view=True,
style_header={'backgroundColor': 'white','fontWeight':
'bold'},
),
className = 'tableDiv'
)
]
)
# callback to update the table
#app.callback([Output('table', 'data'),Output('table', 'columns')]
[Input('submit', 'n_clicks')],
[State('ID', 'value'), State('pattern_desc', 'value'),
State('file_path', 'value')])
def update_table(n_clicks, ID, pattern_desc, file_path):
df = someFunc(ID, pattern_desc, file_path)
mycolumns = [{'name': i, 'id': i} for i in df.columns]
return html.Div([
dt.DataTable(
id='table',
columns=mycolumns,
data=df.to_dict("rows")
)
])
So in this case the function someFunc which takes the 3 input arguments returns a pandas dataframe which can have different columns based on the inputs. Thus the app layout should display
those columns as given by the output of the callback function dynamically based on the inputs.
I should be getting the webpage populated with table and columns, But instead getting an error. When I run this, I am getting the data generated through the function to the file, but dash is not able to
generated the table on webpage. I get the following error:
dash.exceptions.InvalidCallbackReturnValue: The callback ..table.data...table.columns.. is a multi-output.
Expected the output type to be a list or tuple but got Div([DataTable(columns=[{'name': 'pattern_desc', 'id': 'pattern_desc'}, ......
Not Sure How I can achieve that. Any help will be appreciated.
The error message is fairly straightforward. The decorator
`#app.callback([Output('table', 'data'),
Output('table', 'columns')], ...)`
specifies 2 outputs so the function should return a tuple (data, columns) rather than a html.Div(...) value.
What Yixing said above is correct - so if you want to just return the data and columns to the table Datatable your return statement should just be df.to_dict("rows"), mycolumns.
If you want to keep the return statement the way you have it now, then change your decorator to be
`#app.callback(Output('tableDiv', 'children'),
[Input('submit', 'n_clicks')],
[State('ID', 'value'),
State('pattern_desc', 'value'),
State('file_path', 'value')])`
I would say the first option is preferable.
I have app that imports layout from second module then renders it in tab as returning app.layout to this tab content. I have problem with thing that layout is imported with default values of inputs and when I have inputs filled with keys and change between tabs then when I click back to the inputs tab the values reset to default 'x'. I managed to solve this problem by declaring layout second time in a callback but it doesn't look good and would require to write layout 2 times for further functionalities. There is my code:
if 'manager' not in vars():
manager = Recognition('xx', 'xx')
print('defining manager')
lcheck = 0
while lcheck == 0:
layout = [
html.Div([
html.Div(
[
html.H3("Primary API key"),
dcc.Input(
id='primary-key',
value=manager.api_key,
placeholder='Enter a value...',
type='text',
style = {"width":"50%"}
),
html.H3("Private API key"),
dcc.Input(
id='private-key',
value=manager.api_secret,
placeholder='Enter a value...',
type='text',
style = {"width":"50%"}
),
],
),
html.Button('Submit', id='button', className='btn btn-primary'),
html.Div(id='output-hidden')
])
]
lcheck=1
#app.callback(
Output('primary-key', 'value'),
[Input('button', 'n_clicks')],
[State('primary-key', 'value'),
State('private-key', 'value')]
)
def update_output(n_clicks, value1, value2):
values = {'value1':value1, 'value2':value2}
global manager
global layout
manager.update(value1, value2)
layout = [
html.Div([
html.Div(
[
html.H3("Primary API key"),
dcc.Input(
id='primary-key',
value=manager.api_key,
placeholder='Enter a value...',
type='text',
style = {"width":"50%"}
),
html.H3("Private API key"),
dcc.Input(
id='private-key',
value=manager.api_secret,
placeholder='Enter a value...',
type='text',
style = {"width":"50%"}
),
],
),
html.Button('Submit', id='button', className='btn btn-primary'),
html.Div(id='output-hidden')
])
]
lcheck=0
return values['value1']
As you can see it saves new layout everytime I send new data with button but this solution looks terrible I would like to find better solution without chaos in code. Whole thing is to update value=manager.api_key, and value=manager.api_secret, in layout variable. I was thinking to get access of the values like from normal list but it is not possible since it is not nested as regular list. Is there any way to access the values for example layout.getid("primary-key").value or something like this? How to clean this code?
I have figured out how to solve this problem. As my app us rendering app.layout everytime I select a tab, so it always render the layout that was assigned during first import. Firstly I wanted to override layout everytime in a callback function but it has no sense to write layout= 2 times. So I found in dash docs a dash core component named dcc.Store (it stores json in hidden div). I used it like this:
html.Div([
dcc.Store(id='storage')
],
id="hiddendata",
style={"display": "none"},
),
This div is placed directly after 'Tabs' div and is there as a 'cache' memory for tabs to remember previous state. And finally I have updated callbacks to change data in dcc.Store everytime I send new keys so when I leave the tab, other tabs know what is there:
#app.callback(
Output('storage', 'data'),
[Input('button', 'n_clicks')],
[State('primary-key', 'value'),
State('private-key', 'value')]
)
def update_output(n_clicks, value1, value2):
values = {'value1':value1, 'value2':value2}
global manager
global layout
manager.update(value1, value2)
lcheck=0
return values
#app.callback(
Output('primary-key', 'value'),
[Input('storage', 'data')],
[State('storage', 'data')])
def update_values(values, value):
if values:
return value['value1']
#app.callback(
Output('private-key', 'value'),
[Input('storage', 'data')],
[State('storage', 'data')])
def update_values(values, value):
if values:
return value['value2']