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

Multiple instances of JupyterDash #40

Closed
Davide-sd opened this issue Oct 23, 2020 · 1 comment
Closed

Multiple instances of JupyterDash #40

Davide-sd opened this issue Oct 23, 2020 · 1 comment

Comments

@Davide-sd
Copy link

Hello,
I’m trying to leverage Dash to create interactive plots of mathematical functions inside Jupyter Notebook. Therefore, I’ve embedded the Dash app logic into MyClass. Whenever I need to plot a mathematical function, I create a Python function which is going to update the traces of the figure, and pass it to the constructor of MyClass.

This is my code:

import numpy as np
import plotly.graph_objects as go
from jupyter_dash import JupyterDash
import dash_core_components as dcc
import dash_html_components as html
import dash_bootstrap_components as dbc
from dash.dependencies import Input, Output, State, ALL
from dash.development.base_component import Component
from datetime import datetime

class MyClass:

    def __init__(self, user_func):
        
        timestamp = int(datetime.now().timestamp() * 1000)
        self.unique_id = str(timestamp)

        self.fig = go.Figure()

        childrens = []
        childrens.append(
            dbc.Container([
                self._create_slider(0, "Par a", 1, 5, 2.5),
                self._create_slider(1, "Par b", 0, 1, 0.15),
            ])
        )

        # Add a Plotly Graph Object
        childrens.append(
            dcc.Graph(id=self.unique_id + "_my_plot", figure={})
        )
        
        # create the Dash app applying the specified CSS and JS
        self.app = JupyterDash(
            __name__,
            external_stylesheets=[dbc.themes.SLATE],
        )
        # add the controls
        self.app.layout = html.Div(id=self.unique_id, children=childrens)

        # Create the callback, specifying the input/output controls
        @self.app.callback(
            [Output(self.unique_id + "_my_plot", "figure"),
             *[Output(self.unique_id + '_value_slider_{}'.format(j), 'children') for j in range(2)]],
            [Input({'type': self.unique_id + "_", 'index': ALL}, 'value')]
        )
        def func(values):
            user_func(self.fig, values)
            return [self.fig, *values]
        
        # execute the app in the Jupyter environment
#         self.app.run_server()
        self.app.run_server(mode="inline")

    def _create_slider(self, i, _name, _min, _max, _val):
        return dbc.Row(
                children = [
                    # this shows the label
                    dbc.Col(html.Div(_name), 
                            width=1, style = {"text-align": "right"}),
                    # this will show the actual value of the slider
                    dbc.Col(html.Div(id=self.unique_id + "_value_slider_" + str(i), children={}), 
                            width=1, style = {"text-align": "left"}),
                    dbc.Col(dcc.Slider(
                        id = {
                            "type": self.unique_id + "_",
                            "index": i
                        },
                        min = _min,
                        max = _max,
                        step = (_max - _min) / 50,
                        value = _val,
                    )),
                ]
            )

Now, let’s suppose I want to plot two different mathematical functions. I create a first instance, everything works ok in the first app:

def my_sin_func(fig, values):
    a, b = values
    fig.data = []
    xx = np.linspace(0, 20, 200)
    yy = a * np.sin(xx) * np.exp(-xx * b)
    fig.add_trace(
        go.Scatter(x=xx, y=yy, mode="lines")
    )
    fig.add_trace(
        go.Scatter(x=xx, y=np.sin(xx), mode="lines")
    )

a = MyClass(my_sin_func)

Then. I create the second instance, everything works ok on the second app:

def my_cos_func(fig, values):
    a, b = values
    fig.data = []
    xx = np.linspace(0, 20, 200)
    yy = a * np.cos(xx) * np.exp(-xx * b)
    fig.add_trace(
        go.Scatter(x=xx, y=yy, mode="lines")
    )
    fig.add_trace(
        go.Scatter(x=xx, y=np.cos(xx), mode="lines")
    )

b = MyClass(my_cos_func)

But as soon as I move back to the first app and change a slider, an error happens in the second app: KeyError: '..1603462668915_my_plot.figure...1603462668915_value_slider_0.children...1603462668915_value_slider_1.children..'

As I understand, JupyterDash ran both app in the same server, http://127.0.0.1:8050/. I tried to change the server_url option to use a different port, but the server always start at http://127.0.0.1:8050/. What can I do to have two instances working in parallel?

@Davide-sd
Copy link
Author

Ok, solved.

By looking at the source code, the run_server method can also accepts the options host and port. Brilliant!

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

1 participant