Firstly you need to make a choice as by default Dash doesn’t support appending data to props, so you can either:
- Read the whole file every time there is an update
- Or send the whole text between the server and the client and append on to the end of it
I’m going to give a simple example of 1. To make it a little more efficent I’m going to store the mtime of the file and only send an update when the mtime changes. For this you need to understand:
- dcc.Interval to refresh the page
- dcc.Store to store user side state rather than saving as a global variable
- Dash callback_context to figure out which button was pressed
- How to use multiple outputs
Here is the example of reading a file in the same directory as the script called “test.txt”:
# Standard Library
import os
# Third Party Libraries
import dash
import dash_html_components as html
import dash_core_components as dcc
current_directory = os.path.dirname(os.path.abspath(__file__))
file = os.path.join(current_directory, 'test.txt')
external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
app = dash.Dash(__name__, external_stylesheets=external_stylesheets)
app.layout = html.Div([
html.Button('Pause', id='pause'),
html.Button('Resume', id='resume'),
html.Div(id='output-container-button',
children='Enter a value and press submit'),
dcc.Interval(id='refresh-interval', disabled=True, interval=1000),
dcc.Store(id='mtime', storage_type='memory'),
html.Pre(id='output'),
])
@app.callback(
output=[dash.dependencies.Output('output', 'children'),
dash.dependencies.Output('refresh-interval', 'disabled'),
dash.dependencies.Output('mtime', 'data')],
inputs=[dash.dependencies.Input('pause', 'n_clicks'),
dash.dependencies.Input('resume', 'n_clicks'),
dash.dependencies.Input('refresh-interval', 'n_intervals')],
state=[dash.dependencies.State('mtime', 'data')]
)
def update_output(pause_n_clicks, resume_n_clicks, refresh_n_intervals, remote_mtime):
if pause_n_clicks is None and resume_n_clicks is None:
raise dash.exceptions.PreventUpdate
# Get the prop triggering the callback so we know what to do
triggered_prop = dash.callback_context.triggered[0]['prop_id']
# Pause means no update to text, no update to mtime, and disable the refresh
if triggered_prop == 'pause.n_clicks':
return dash.no_update, True, dash.no_update
# Resume or interval firing means check mtime and then read file
if triggered_prop == 'resume.n_clicks' or triggered_prop == 'refresh-interval.n_intervals':
# Get mtime of file and check if it's same as the one user already has
local_mtime = os.path.getmtime(file)
if local_mtime == remote_mtime:
return dash.no_update, False, dash.no_update
# Local and Remote mtime didn't match so send file contents
return open(file, 'r').read(), False, local_mtime
raise ValueError(f'Unknown prop: {triggered_prop}')
if __name__ == '__main__':
app.run_server()