So, after lots of painful debugging in my app, I found that one of my callbacks, storing a simple binary value in dcc.Store
, is somehow interfering with another callback that relies on a dcc.Interval
component to fire it off every few seconds or so.
MWE:
import dash
import dash_core_components as dcc
import dash_html_components as html
import dash_table
from dash.dependencies import Input, Output, State
import datetime as dt
import pandas as pd
import numpy as np
import subprocess
import sys
import os
from pathlib import Path
###### Setup ######
## Pointing subprocess to active directory
os.chdir(sys.path[0])
app = dash.Dash(dev_tools_hot_reload=True)
app.scripts.config.serve_locally = True
app.config['suppress_callback_exceptions'] = True
fonts = '12px Verdana, Geneva, sans-serif'
###### Main code ######
app.layout = html.Div(children=[
html.H3('Twitter App'),
html.Div(children=[
html.Label('Twitter id(s)',
style={'grid-row':'1 / 2', 'grid-column':'1 / 2'}),
dcc.Input('ScreenName_Input', type='text',
style={'grid-row':'2 / 3', 'grid-column':'1 / 2'}),
html.Label('# of tweets',
style={'grid-row':'1 / 2', 'grid-column':'2 / 3'}),
dcc.Input(id='NumberOfTweets', value=10, type='number',
style={'grid-row':'2 / 3', 'grid-column':'2 / 3'}),
html.Button(id='screenNames_submit_button', children='Submit',
style={'grid-row':'2 / 3', 'grid-column':'3 / 4'}),
html.Button(id='time_Stream', children='Stream time',
style={'grid-row':'2 / 3', 'grid-column':'4 / 5'}),
html.Button(id='pauseStream', children='Pause collection',
style={'grid-row':'2 / 3', 'grid-column':'5 / 6'})
],
style={'display':'grid', 'grid-template-rows':'25px 25px 25px 25px',
'grid-auto-columns': '100px 100px 100px 100px 150px'}
),
html.Div(children=[
dash_table.DataTable(id='tweet_table', columns=[
{'name': 'Date', 'id': 'Date'},
{'name': 'Author', 'id': 'Author'},
{'name': 'Len', 'id': 'Length'},
{'name': 'Favs', 'id': 'Favorites'},
{'name': "Retw", 'id': 'Retweets'},
{'name': 'Text', 'id': 'Text'}],
style_cell = {'whiteSpace':'normal'},
## Left-aligning test, setting fonts, and setting some column widths
style_cell_conditional=[{'textAlign':'left',
'font': fonts},
{'if':{'column_id':'Date'},
'width':'40px'}],
## Bolding of column titles
style_header={'fontWeight':'bold'},
## Horizontal and vertical scrolling
style_table={'maxHeight': '400',
'overflowY': 'scroll',
'minWidth':'500',
'maxWidth':'800'},
## Enabling selection of multiple tweets for use in analysis on right
row_selectable = 'multi'
),
html.Div(children=[
html.H1(children='Analysis',
style={'margin-top':'-40px'}),
dcc.Graph(id='sentiment_graph',
figure={'data': [{'type':'histogram'}]}
)
])
],
style={'display':'grid', 'grid-template-columns': '1fr 1fr', 'column-gap':'20px',
'margin-top':'-25px'}),
## Saving most recent tweet from stream, as well as
## name of most recent csv created in quick cache
html.Div(children=[
dcc.Store(id='table_exists_or_not'),
dcc.Store(id='latest_tweet'),
html.Div(id='temp'),
html.Div(id='temp2'),
dcc.Interval(id='table_update', interval=3*1000, n_intervals=0)
])
])
###### Callbacks ######
## Random complicated callback simply printing time every 3 seconds
@app.callback(
Output(component_id='tweet_table', component_property='data'),
[Input(component_id='table_update', component_property='n_intervals')],
[State(component_id='ScreenName_Input', component_property='value'),
State(component_id='NumberOfTweets', component_property='value'),
State(component_id='table_exists_or_not', component_property='data')]
)
def tweet_table(update_interval, screen_names, number_tweets, table_exists_or_not):
print('tweet_table function ran at least, table_exists_or_not: {}'.format(table_exists_or_not))
print('Time is {}'.format(dt.datetime.now()))
return None
## Callback that stops the dcc.Interval working up above.
# @app.callback(
# Output(component_id='table_exists_or_not', component_property='data'),
# [Input(component_id='screenNames_submit_button', component_property='n_clicks_timestamp')],
# [State(component_id='tweet_table', component_property='data')]
# )
# def table_exists_or_not(submit, table_exists):
# if table_exists:
# print('Table seems to exist: {}'.format(table_exists))
# return 1
# else:
# print('Table does not exist: {}'.format(table_exists))
# return 0
if __name__ == '__main__':
app.run_server(debug=True)
tweet_table
only prints the time for me on an interval if the bottom callback is commented out (as it is currently).
How can I fix this bug, as I need both callbacks in my application?
Is this maybe occurring because table_exists_or_not
is an output of my second callback and a State
input of my first, so the default None
being sent into my first callback somehow screws up dcc.Interval
?