Skip to content

Hot reloading #66

Closed
Closed

Description

@nicolaskruchten

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 ;)

Activity

  1. chriddyp commented on Jul 4, 2017

    @chriddyp
    Member

    Definitely! Would love to add this.

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

  2. chriddyp commented on Jul 6, 2017

    @chriddyp
    Member

    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)
  3. nicolaskruchten commented on Jul 6, 2017

    @nicolaskruchten
    ContributorAuthor

    oooh nice :)

  4. nicolaskruchten commented on Jul 6, 2017

    @nicolaskruchten
    ContributorAuthor

    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.
  5. chriddyp commented on Jul 6, 2017

    @chriddyp
    Member

    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.

  6. nicolaskruchten commented on Jul 6, 2017

    @nicolaskruchten
    ContributorAuthor

    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)
  7. chriddyp commented on Jun 8, 2018

    @chriddyp
    Member

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

  8. T4rk1n commented on Jul 5, 2018

    @T4rk1n
    Contributor

    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.

  9. nicolaskruchten commented on Jul 5, 2018

    @nicolaskruchten
    ContributorAuthor

    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?

  10. added a commit that references this issue on Apr 23, 2019
    0ea7d96
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions