Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Hot reloading #66

Closed
nicolaskruchten opened this issue Jul 4, 2017 · 9 comments
Closed

Hot reloading #66

nicolaskruchten opened this issue Jul 4, 2017 · 9 comments
Assignees

Comments

@nicolaskruchten
Copy link
Contributor

I love that the Python module reloads automatically on save but I still need to refresh my browser when I make a change to the Python code, which means I lose the state I was in and need to fiddle with the UI to get the view back to how it was before I made the change. It would be great if we could leverage a system like React Hot Loader or something similar for totally seamless multi-monitor development. Just type code on one monitor, save, and turn your head to to see the changes on the other monitor ;)

@chriddyp
Copy link
Member

chriddyp commented Jul 4, 2017

Definitely! Would love to add this.

If any company out there would like to help sponsor and prioritize this work, please reach out.

@chriddyp
Copy link
Member

chriddyp commented Jul 6, 2017

I thought about this a little bit more last night and I think we can get pretty close with just dash_core_components.Interval and debug=True. Check this out 😺

live-reload

Here's the code:

import dash
from dash.dependencies import Event, Input, Output
import dash_core_components as dcc
import dash_html_components as html

app = dash.Dash()

# Edit this object!
layout = html.Div([
    html.H1('Hello Dash!'),
    html.H3('Dash with live-reload'),
])

# Barebones layout
app.layout = html.Div([
    dcc.Interval(id='refresh', interval=200),
    html.Div(id='content', className="container")
])

# Update the `content` div with the `layout` object.
# When you save this file, `debug=True` will re-run
# this script, serving the new layout
@app.callback(
    Output('content', 'children'),
    events=[Event('refresh', 'interval')])
def display_layout():
    return layout

app.css.append_css({"external_url": "https://codepen.io/chriddyp/pen/bWLwgP.css"})

if __name__ == '__main__':
    app.run_server(debug=True)

@nicolaskruchten
Copy link
Contributor Author

oooh nice :)

@nicolaskruchten
Copy link
Contributor Author

OK so I tried this with some actual statefulness in the app (sliders etc) and a couple of notes:

  1. I had to add app.config.supress_callback_exceptions=True as per the helpful warning :)
  2. It doesn't seem like this approach preserves/respects the state of the sliders in my little app, which is a large part of the value of hot-reload (although quick-preview of default state is already a big step!) ... am I interpreting this wrong? I'll try to make a toy example.

@chriddyp
Copy link
Member

chriddyp commented Jul 6, 2017

It doesn't seem like this approach preserves/respects the state of the sliders in my little app, which is a large part of the value of hot-reload (although quick-preview of default state is already a big step!) ... am I interpreting this wrong? I'll try to make a toy example.

Yes, I think that is true. Dang! Almost there. It sounds like there is still precedent for a proper hot-reload.

@nicolaskruchten
Copy link
Contributor Author

Here's a stripped-down example that shows the issue... I should add that it blinks/flickers pretty noticeably on my screen, also :) But it's a good start!

# -*- coding: utf-8 -*-
import dash, dash_core_components as dcc, dash_html_components as html
import plotly.graph_objs as go
import math, pandas as pd, numpy as np

data = np.random.normal(0,1,10000)

layout = html.Div([
        dcc.Slider(id='spread', min=0.5, max=5.0, value=2.5, step=0.5),
        dcc.Graph(id='hist'),
])

app = dash.Dash()
app.config.supress_callback_exceptions=True

app.layout = html.Div([
    dcc.Interval(id='refresh', interval=200),
    html.Div(id='content', className="container")
])

@app.callback(
    dash.dependencies.Output('content', 'children'),
    events=[dash.dependencies.Event('refresh', 'interval')])
def display_layout():
    return layout

@app.callback(
    dash.dependencies.Output('hist', 'figure'),
    [dash.dependencies.Input('spread', 'value')])
def update_hist(spread):
    kwargs = dict( opacity=0.75, autobinx=False,
        xbins=dict( start=-10.0, end=10, size=0.1 )
        )
    return dict(
        data = [
            go.Histogram(x=data, **kwargs),
            go.Histogram(x=data*spread, **kwargs)
        ],
        layout = go.Layout(
            barmode='overlay',
            xaxis=dict( range=[-10, 10] ),
            yaxis=dict( range=[0, 500] )
        )
    )

if __name__ == '__main__':
    app.run_server(debug=True)

@chriddyp
Copy link
Member

chriddyp commented Jun 8, 2018

Good news! The development for this issue has been sponsored by an organization as part of our "Pleasant and Productive Developer Experience" initiative.

@T4rk1n
Copy link
Contributor

T4rk1n commented Jul 5, 2018

I came across this issue and had an idea on how to implement it.

I thought that flask already reload itself when it detect changes in debug mode.
Let's use that with a hash generated at startup and periodically request the
server for the hash, if it has changed, it's time to reload. Put that request loop code
in a webworker so it won't tamper with the rest of the app.

So I made a quick prototype with flask only, it worked. But I needed to change
the index rendering of dash to use jinja2 so I could put a context and load the
worker only if hot-reload is enabled. This didn't take long and I had a working
prototype of dash with hot-reload.

I now plan to separate the integration of hot-reload into several components which
can be useful in other parts of dash.

@nicolaskruchten
Copy link
Contributor Author

I'm curious about the coupling of your solution to Jinja... is there a specific dependency on Jinja or do you just need some kind of access to the index.html, i.e. by overriding Dash's index() function?

@T4rk1n T4rk1n mentioned this issue Aug 31, 2018
byronz pushed a commit that referenced this issue Apr 23, 2019
@waralex waralex mentioned this issue May 19, 2020
7 tasks
HammadTheOne pushed a commit to HammadTheOne/dash that referenced this issue May 22, 2021
AnnMarieW pushed a commit to AnnMarieW/dash that referenced this issue Jan 6, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants