Git Product home page Git Product logo

plotly / dash.jl Goto Github PK

View Code? Open in Web Editor NEW
480.0 17.0 41.0 78.59 MB

Dash for Julia - A Julia interface to the Dash ecosystem for creating analytic web applications in Julia. No JavaScript required.

License: MIT License

Julia 85.69% JavaScript 0.79% Python 13.28% CSS 0.05% Dockerfile 0.19%
dash plotly julia dashboard data-visualization data-science gui-framework react finance bioinformatics

dash.jl's Introduction

Dash for Julia

Juila tests CircleCI GitHub GitHub commit activity

Create beautiful, analytic applications in Julia

Built on top of Plotly.js, React and HTTP.jl, Dash ties modern UI elements like dropdowns, sliders, and graphs directly to your analytical Julia code.

Just getting started? Check out the Dash for Julia User Guide! If you can't find documentation there, then check out the unofficial contributed examples or check out source code from demo applications in Python and then reference the Julia syntax style.

Other resources

Project Status

Julia components can be generated in tandem with Python and R components. Interested in getting involved with the project? Sponsorship is a great way to accelerate the progress of open source projects like this one; please feel free to reach out to us!

Installation

To install the most recently released version:

pkg> add Dash

To install the latest (stable) development version instead:

pkg> add Dash#dev

Usage

Basic application

using Dash

app = dash(external_stylesheets = ["https://codepen.io/chriddyp/pen/bWLwgP.css"])

app.layout = html_div() do
    html_h1("Hello Dash"),
    html_div("Dash.jl: Julia interface for Dash"),
    dcc_graph(id = "example-graph",
              figure = (
                  data = [
                      (x = [1, 2, 3], y = [4, 1, 2], type = "bar", name = "SF"),
                      (x = [1, 2, 3], y = [2, 4, 5], type = "bar", name = "Montréal"),
                  ],
                  layout = (title = "Dash Data Visualization",)
              ))
end

run_server(app)

then go to http://127.0.0.1:8050 in your browser to view the Dash app!

Basic Callback

using Dash

app = dash(external_stylesheets = ["https://codepen.io/chriddyp/pen/bWLwgP.css"])

app.layout = html_div() do
    dcc_input(id = "my-id", value = "initial value", type = "text"),
    html_div(id = "my-div")
end

callback!(app, Output("my-div", "children"), Input("my-id", "value")) do input_value
    "You've entered $(input_value)"
end

run_server(app)
  • You can make your Dash app interactive by registering callbacks with the callback! function.
  • Outputs and inputs (and states, see below) of callback can be Output, Input, State objects or splats / vectors of this objects.
  • Callback functions must have the signature (inputs..., states...), and provide a return value with the same number elements as the number of Outputs to update.

States and Multiple Outputs

using Dash

app = dash(external_stylesheets = ["https://codepen.io/chriddyp/pen/bWLwgP.css"])

app.layout = html_div() do
    dcc_input(id = "my-id", value = "initial value", type = "text"),
    html_div(id = "my-div"),
    html_div(id = "my-div2")
end

callback!(app,
          Output("my-div","children"),
          Output("my-div2","children"),
          Input("my-id", "value"),
          State("my-id", "type")) do input_value, state_value
    return ("You've entered $(input_value) in input with type $(state_value)",
            "You've entered $(input_value)")
end

run_server(app)

Comparison with original Python syntax

Component naming

  • Python:
import dash

dash.html.Div
dash.dcc.Graph
dash.dash_table.DataTable
  • Dash.jl:
using Dash

html_div
dcc_graph
dash_datatable

Component creation

Just as in Python, functions for declaring components have keyword arguments, which are the same as in Python. html_div(id = "my-id", children = "Simple text").

For components which declare children, two additional signatures are available:

  • (children; kwargs..) and
  • (children_maker::Function; kwargs...)

So one can write html_div("Simple text", id = "my-id") for simple elements, or choose an abbreviated syntax with do syntax for complex elements:

html_div(id = "outer-div") do
    html_h1("Welcome"),
    html_div(id = "inner-div") do
        #= inner content =#
    end
end

Application and layout

  • Python:
app = dash.Dash(external_stylesheets=["https://codepen.io/chriddyp/pen/bWLwgP.css"])

app.layout = html.Div(children=[....])
  • Dash.jl:
app = dash(external_stylesheets = ["https://codepen.io/chriddyp/pen/bWLwgP.css"])

app.layout = html_div() do
    #= inner content =#
end

Callbacks

  • Python:
@app.callback(Output('output', 'children'),
              Input('submit-button', 'n_clicks')],
              State('state-1', 'value'),
              State('state-2', 'value'))
def update_output(n_clicks, state1, state2):
    # logic
  • Dash.jl:
callback!(app,
          Output("output", "children"),
          Input("submit-button", "n_clicks")],
          State("state-1", "value"),
          State("state-2", "value")) do n_clicks, state1, state2
    # logic
end

JSON

Dash apps transfer data between the browser (aka the frontend) and the Julia process running the app (aka the backend) in JSON. Dash.jl uses JSON3.jl for JSON serialization/deserialization.

Note that JSON3.jl converts

  • Vectors and Tuples to JSON arrays
  • Dicts and NamedTuples to JSON objects

dash.jl's People

Contributors

alexcjohnson avatar amanbh avatar beastyblacksmith avatar bolognam avatar chrisrackauckas avatar csertegt3 avatar ericforgy avatar etpinard avatar felix-gauthier avatar github-actions[bot] avatar jackparmer avatar jbampton avatar kimttfung avatar logankilpatrick avatar michaelhatherly avatar mind6 avatar nandvinchhi avatar pallharaldsson avatar rpkyle avatar sglyon avatar sjkelly avatar waralex avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

dash.jl's Issues

Update "min" and "max" of Ranged Slider via "relayoutData" of Figure not working

Hi ,

i am quite new to Julia and especially Dash. So maybe there is an easy fix to my problems.

I want to plot a heatmap and then add via callback vlines to the heatmap. That is working quite well.
In another callback i want to be able to change the position of the vlines with a ranged slider. This is not working quite well, since we cant just add a trace to an existing figure. I tried a workaround with using the figure as Input in a callback and then just use addtraces! from the PlotlyJS package. This works, but the updates are quite slow, due the complexity of the origninal heatmap. There is no Dash function for just adding traces to an existing figure without recomputing the whole original figure, right?

Now to my Issue thats not working:

I want to use the slider for the visualisation of the slider values in my heatmap. Therefore i want to be able to zoom into my heatmap to increase the accuracy of my vline position. With the "relayoutData" attribute of my Heatmap i can access the current xlim and ylim positions of my Heatmap. I want to use these to update the "min" and "max" attribute of the ranged slider corresponding to my "zoom" in my Heatmap.

This should work right?

I am currently using Julia 1.5.3:

(@v1.5) pkg> st
Status `~\.julia\environments\v1.5\Project.toml`
  [1b08a953] Dash v0.1.3
  [1b08a953] DashCoreComponents v1.12.0
  [1b08a953] DashHtmlComponents v1.1.1
  [7073ff75] IJulia v1.23.0
  [f0f68f2c] PlotlyJS v0.14.0

I tried to make an easy example that illustrates my Issue.

Thank you!

using Dash, DashHtmlComponents, DashCoreComponents,PlotlyJS

app = dash(external_stylesheets = ["https://codepen.io/chriddyp/pen/bWLwgP.css"])

xdata= rand(1:10,3)
ydata= rand(1:10,3)
matrix= rand(1:10,3,3)



function vline(xposition,ydata)

    scatter(Dict(
                :mode => "lines",  
                :x => fill(xposition,2), 
                :y => [minimum(ydata),maximum(ydata)],
            ),
            showlegend=false,
            marker_line_width = 0.5,
            marker_opacity = 0.8,
            line_color= "#da0a72b6"
    ) 
end

                                        #Main layout

app.layout =    html_div() do
    dcc_graph(  
        id="Hidden-Heatmap",
        figure = (
            Plot(
                heatmap(
                    Dict( 
                        :type => "heatmap", 
                        :x => xdata, 
                        :y => ydata,
                        :z => [matrix[i,:] for i in 1:size(matrix)[1]]
                    ),
                    zmax= 10,
                    zmin= 0 ,
                    colorscale= "Viridis"
                ),
                Layout(
                    uirevision = "zoom"
                )
            )
        ),
        style = Dict(
            "display" => "none"
        )
     ),
    html_div(
        children =[
            dcc_graph(id="Heatmap",
            ),
            html_div(
                id="Slider Div",
                dcc_rangeslider(
                    id = "Ranged Slider",
                    step = 0.01,
                    min=0,
                    max=10,
                    value=[1,2],
                    persistence=true ,
                    allowCross=false,          
                ),
                style = Dict(
                    :display => "none"
                )
            ),
            html_div(
                children =[
                    html_div(
                        id= "Input Div",
                        children = [
                            "Number of Sliders",   
                            dcc_input(
                                id="Input",
                                type = "number",
                                step = 1,
                                min = 1,
                                value = 1,
                                style = Dict(
                                    :width => "3%",
                                )
                            ),
                        ],
                        style= Dict(
                                :display => "none"
                        )
                    ),
                    html_button(
                        "Manual Button", 
                        id="Manual Button", 
                        n_clicks=1,
                    )
                ]
            )
        ]
    ) 
end

# Vline into Graph from Hidden Graph figure
callback!(app,
Output("Heatmap","figure"),
Input("Ranged Slider","value"),
Input("Manual Button","n_clicks"),
State("Hidden-Heatmap","figure")) do value, button_click,current_fig




    if  isodd(button_click) == true
        return current_fig
    
    elseif iseven(button_click) == true
        type = current_fig[:data][1][:type]
        zmax = current_fig[:data][1][:zmax]
        zmin = current_fig[:data][1][:zmin]
        
        xdata = current_fig[:data][1][:x]
        ydata = current_fig[:data][1][:y]
        zdata = current_fig[:data][1][:z]
    
        cur_plot =  Plot(
                        heatmap(
                            Dict( 
                                :type => type, 
                                :x    => xdata, 
                                :y    => ydata,
                                :z    => zdata
                            ),
                            zmax = 10,
                            zmin = 1,
                            colorscale= "Viridis",
                            uirevision = "zoom"
                        ),
                        Layout(uirevision = "zoom"
                        )
                    )  
        for i in 1:length(value)     
            addtraces!(cur_plot,vline(value[i],ydata))
        end
        return figure=(cur_plot)
    end
    
end



## Change Button Color on Click
callback!(app,
Output("Manual Button","style"),
Input("Manual Button","n_clicks")) do button_click
    if iseven(button_click) == false
        style= Dict(
            :backgroundColor => "white"
        )
    elseif iseven(button_click) == true
        style= Dict(
            :backgroundColor => "#2cb6f6b6"
        )
    end
end


# Toogle visibility of Input of Number of Resonances
callback!(app,
Output("Input Div","style"),
Input("Manual Button","n_clicks")) do button_click
    if iseven(button_click) == false
        style= Dict(
            :display => "none"
        )
    elseif iseven(button_click) == true
        style= Dict(
            :display => "block"
        )
    end
end

# Toogle visibility of Manual Integration Slider
callback!(app,
Output("Slider Div","style"),
Input("Input Div","style"),
Input("Manual Button","n_clicks")) do input_div_style,button_click
    if iseven(button_click) == false
        style= Dict(
            :display => "none"
        )
    elseif iseven(button_click) == true && last(input_div_style) !== "none"
        style= Dict(
            :display => "block"
        )
    end
end


# Number of Ranged Sliders
## randonmly populate the slider with values between min and max
callback!(app,
Output("Ranged Slider","value"),
Input("Input Div","style"),
Input("Manual Button","n_clicks"),
Input("Input","value"),
State("Ranged Slider","min"),
State("Ranged Slider","max"))  do input_div_style,button_click, input_value,min,max

    if (iseven(button_click) == true) && typeof(input_value) == Int64 && last(input_div_style) !== "none"
        return value  = collect(min:(abs(min)+abs(max)/(2*input_value-1)):max)
    end
end

# Autosize range of ranged sliders to Zoom of Heatmap
callback!(app,
Output("Ranged Slider","min"),
Output("Ranged Slider","max"),
Input("Slider Div","style"),
Input("Heatmap","relayoutData"),
State("Heatmap","figure"))  do  slider_div_style, range,figure 

    # Only set ranges if RangedSliders are visible
    ## if relayoutData is at default
    if last(slider_div_style) !== "none" && (try range[:autosize]  catch end !== nothing)
        xdata_fig = figure[:data][1][:x]
        min = minimum(xdata_fig)
        max = maximum(xdata_fig)

            return (min,max)
    ## changed the zoom
    elseif last(slider_div_style) !== "none" && try range[:autosize]  catch end === nothing && typeof(range) == (NamedTuple{(Symbol("xaxis.range[0]"), Symbol("xaxis.range[1]"), Symbol("yaxis.range[0]"), Symbol("yaxis.range[1]")),NTuple{4,Float64}})
        min = range[1]
        max = range[2]
    
            return min,max
    ## set back to autosize
    elseif last(slider_div_style) !== "none" && try range[:autosize]  catch end === nothing && typeof(range) == (NamedTuple{(Symbol("xaxis.autorange"), Symbol("yaxis.autorange")),Tuple{Bool,Bool}})
        xdata_fig = figure[:data][1][:x]
        min = minimum(xdata_fig)
        max = maximum(xdata_fig)    

            return (min,max)
    else 
        return (Dash.no_update(),Dash.no_update())
    end
end


run_server(app, "0.0.0.0", debug=true)

Hot reloading support in Dash.jl

Hey folks, I am just trying to start using Plotly Dash, just to get a feel for it. So I am just logging issues you probably know about, but just in case others run into the same issues they can reference the issues.

I notice that the hot reloading does not seem to work. When I run:

run_server(app, "0.0.0.0", 8080, debug=true)

the code does not seem to reload upon update to the original file. That might be related to the recompilation that julia usually does. Is there some planned workaround for this?

Thanks again for all the work you are all doing.

Hot reloading performance improvements

An initial experimental implementation of hot reloading in Dash.jl provided in #25 is essentially complete, and offers the same general level of functionality as found in Dash for Python or Dash for R.

However, the cycle time -- the interval between detecting changes to assets or app code, launching a new Julia process, bringing the current HTTP.jl server down, precompiling code in the new process, and relaunching the HTTP.jl server -- is noticeably longer than for either Python or R.

In tests running a very simple sample app, it takes roughly 30-40 seconds to start the HTTP.jl process and see the following status message appear indicating that the server is ready:

Simple sample app
using Dash
using DashHtmlComponents

app = dash()
app.layout = html_div(id="outer-div") do
    html_div("Hello Julia!", id="page-content")
end

run_server(app, debug=true, dev_tools_hot_reload=true)
rpkyle$ julia /tmp/testapp/test_reload.jl 
[ Info: Running on http://127.0.0.1:8050

It can then take approximately 10-15 seconds upon making a change to the "Hello Julia!" string in the html_div for Julia + HTTP.jl + any other loaded packages to complete precompilation and finish restarting, and for the page to refresh. During this interval, the page/server are unresponsive:

rpkyle$ julia /tmp/testapp/test_reload.jl 
[ Info: Running on http://127.0.0.1:8050
[ Info: Running on http://127.0.0.1:8050

Improving startup time required to launch Julia or load packages is an active area of development in the language (see here or issues labeled as "latency" for background).

This issue is a placeholder for further discussion on this issue, and potential improvements to reduce the time required between page refreshes when "hard" (updates to non-CSS assets or code) reloads are performed.

Clientside callbacks

As with Dash for Python and R, it would be great if Dash.jl supported clientside callbacks in JavaScript.

Ideally this would involve allowing JavaScript-as-strings within the callback, as well as reference to JavaScript functions loaded from assets.

The implementation in R is slightly different from Python (there is no specific ClientsideCallback method), but supporting both types of clientside callbacks from callback! would be preferable to adding a second function just for JavaScript callbacks.

Ability to version-lock Dash.jl and required core component libraries

In both Dash for R and Python, dash requires the three core component libraries be loaded first. In Dash.jl, the reverse is currently (as of this writing) true.

While there are multiple possible solutions to "locking" release versions of Dash.jl with DashTable, DashCoreComponents, and DashHtmlComponents, the following conditions must be resolved before this issue can be considered closed:

  • when you install Dash.jl, the core component libraries DashTable, DashCoreComponents and DashHtmlComponents should automatically be downloaded and installed
  • the versions of the core component libraries which are installed must be compatible with the installed version of Dash.jl
  • upgrading Dash.jl automatically triggers an update to the three installed core component libraries
  • regardless of the chosen approach, we should try to avoid warning messages/errors

A relevant discussion about addressing "reverse dependency" issues may be found here: plotly/dash#1062 (comment)

Put exceptions in order

Currently, most errors in the dash logic cause ErrorException to be thrown. This is not very informative and does not allow application to react differently to different types of errors. I need to make dash-specific exceptions and use them. Where this is justified, I should replace ErrorException with more accurate standard exceptions like WrongArgumentException. I also need to put in order the tests that check for throwing exceptions.

Add option to run a non-blocking server

Heya,
The blocking nature of run_server makes this difficult to use from the REPL. REPL use seems to be the recommended way of getting around many of Julia's pain points, so it would be great for this to be easy.

Would you accept a PR to basically make start_server a public part of the API again?

Cheers

Add local image to DASH

Hi,

I am trying to add in a div a local image. Looking at documentation, it seems that the way to do it is to use get_url_asset. But in my case I get this error
ERROR: type DashApp has no field get_asset_url
Is there a way to do it in jula version of Dash ?

Here is the documentation page for static assets https://dash.plotly.com/dash-enterprise/static-assets
Here is another discussion that mention the get_asset_url solution https://community.plotly.com/t/adding-local-image/4896

I use the 0.1.3 version of Dash

Thank you for your help (and for the work on dash for julia :))

Multi-output callback error in tutorial

On running the tutorial example on callbacks, I run into the following error:

┌ Error: error handling request │ exception = │ Invalid callback return value: The callback graph.figure is a multi-output. │ Expected the output type to be a list or tuple but got: │ {"layout":{"xaxis1":{"type":"log","title":"GDP Per Capita", [truncated because of long output] │ Stacktrace: │ [1] validate_callback_return(::Array{NamedTuple{(:id, :property),Tuple{String,String}},1}, ::Plot{Array{AbstractTrace,1},Layout{Dict{Symbol,Any}},Array{PlotlyFrame,1}}, ::Symbol) at C:\Users\Jakob\.julia\packages\Dash\OP7s2\src\handler\processors\callback.jl:81

Here's the code:

using DataFrames, Dash, DashHtmlComponents, DashCoreComponents, PlotlyJS, UrlDownload


df1 = DataFrame(urldownload("https://raw.githubusercontent.com/plotly/datasets/master/gapminderDataFiveYear.csv"))

years = unique(df1[!, :year])

app = dash()

app.layout = html_div() do
    dcc_graph(id = "graph"),
    dcc_slider(
        id = "year-slider-1",
        min = minimum(years),
        max = maximum(years),
        marks = Dict([Symbol(v) => Symbol(v) for v in years]),
        value = minimum(years),
        step = nothing,
    )
end

callback!(
    app,
    Output("graph", "figure"),
    Input("year-slider-1", "value"),
) do selected_year
    return Plot(
        df1[df1.year .== selected_year, :],
        Layout(
            xaxis_type = "log",
            xaxis_title = "GDP Per Capita",
            yaxis_title = "Life Expectancy",
            legend_x = 0,
            legend_y = 1,
            hovermode = "closest",
            transition_duration = 500
        ),
        x = :gdpPercap,
        y = :lifeExp,
        text = :country,
        group = :continent,
        mode = "markers",
        marker_size = 15,
        marker_line_color = "white",
    )
end

run_server(app, "0.0.0.0", debug = true)

I'm on Julia 1.5 and here's my environment status:

[1b08a953] Dash v0.1.3 [a03496cd] PlotlyBase v0.4.1 [f0f68f2c] PlotlyJS v0.14.0

Refactor Dash.jl to support same parameters as Dash for Python & R

The current definition of the DashApp struct should be made comparable to the Dash class in R and Python (it can remain a struct, but the interface provided to users should be more or less identical):

Dash.jl/src/Dash.jl

Lines 98 to 119 in 2707170

struct DashApp
name ::String
layout ::Component
callbacks ::Dict{Symbol, Callback}
external_stylesheets ::Vector{String}
external_scripts ::Vector{String}
url_base_pathname ::String
assets_folder ::String
callable_components ::Dict{Symbol, Component}
type_links ::Dict{Symbol, Dict{Symbol, Type}}
function DashApp(name::String, layout::Component;
external_stylesheets ::Vector{String} = Vector{String}(),
external_scripts ::Vector{String} = Vector{String}(),
url_base_pathname="/",
assets_folder::String = "assets")
new(name, layout, Dict{Symbol, Callback}(),
external_stylesheets, external_scripts,
url_base_pathname, assets_folder,
Components.collect_with_ids(layout), Dict{Symbol, Dict{Symbol, Type}}()
)
end
end

For parity, if possible, we should use similar names across all backends

  • Rename callbacks to callback

The following initial additions to the Dash.jl API are proposed:

  • Add serve_locally parameter
  • Add suppress_callback_exceptions parameter
  • Add assets_ignore parameter
  • Add assets_url_path parameter
  • Add layout_get method

We should consider removing the following elements:
- [ ] Remove callable_components, any required logic can be rolled into internal component handling, in similar fashion to Python and R
- [ ] Remove type_links

  • Deprecate and eventually remove name with a warning message as in Dash for R, and suggest using title instead.

A full list of current Dash parameters appears below; eventually all three backends should support these.


🏁 Pull request Element Python R Julia
✔️ name ✔️ ✔️ ✔️
✔️ assets_folder ✔️ ✔️ ✔️
✔️ url_base_pathname ✔️ ✔️ ✔️
✔️ external_scripts ✔️ ✔️ ✔️
✔️ external_stylesheets ✔️ ✔️ ✔️
✔️ assets_url_path ✔️ ✔️ ✔️
✔️ meta_tags ✔️ ✔️ ✔️
✔️ requests_pathname_prefix ✔️ ✔️ ✔️
✔️ routes_pathname_prefix ✔️ ✔️ ✔️
✔️ suppress_callback_exceptions ✔️ ✔️ ✔️
✔️ index_string ✔️ ✔️ ✔️
✔️ show_undo_redo ✔️ ✔️ ✔️
✔️ compress ✔️ ✔️ ✔️
✔️ assets_ignore ✔️ ✔️ ✔️
✔️ serve_locally ✔️ ✔️ ✔️
✔️ eager_loading ✔️ ✔️ ✔️
include_assets_files ✔️ ✔️
assets_external_path ✔️

UndefVarError: html_div not defined

Hi all,

I have added the Dash package to Julia and I am just trying to run the "Basic application" you shared in the readme.md but it fails with "UndefVarError: html_div not defined". I have tried the stable version as well. What am I possibly missing here?

Thanks,
Daniel

Parity with existing Python-based component generation pipeline

Currently, components in Dash.jl are generated via macros, which populates the component template from within Julia. This differs from the way components are currently generated for Plotly's existing Dash backends -- these are created using (Python) scripts for Python and R alike.

plotly/dash#1197 proposes to add similar functionality for Julia component generation. Doing so would allow us to

  • update Julia components at each release cycle
  • standardize documentation and component generation practices
  • permit non-Julia users to easily build Dash components for this user community
  • implement the same structure we currently use for component build tests in other repositories

@waralex has noted that switching to pre-generated components may lead to scoping issues that require some refactoring within Dash.jl; specifically, we want to ensure that generated components have global scope, so that they are available to Dash apps once the module is loaded.

I've noticed that the Project.toml file is generated within Julia, but I'm curious if there's any way to obtain this metadata outside of Julia's REPL for component modules. This will be a minor obstacle as we work to create component libraries for Julia, since we try not to force Python users to do any component generation work in R, for example.

@mbauman if you have any suggestions related to scoping or generating Project.toml outside of Julia, your insights would be much appreciated 🙂

Assert and validate Dash component functionality

Both the R and Python implementations of the Dash backend perform various tests for component functionality, in R, these occur within assert_valid_callbacks. We should implement the following tests within Dash.jl in similar fashion:

  • Verify that no output arguments are duplicated
  • Verify that parameters contain only input or state arguments
  • Verify that all input arguments precede state arguments, when latter are present
  • Verify that input arguments are a nested list of dictionaries (or whatever data structure makes the most sense in Julia)
  • Verify that state arguments are a nested list of dictionaries, if they exist
  • Verify that at least one valid input has been provided
  • Verify that no callback input arguments are identical to output arguments (in Dash, we call these "circular callbacks" and they are not allowed)

Rendering issue

Hi Dash.jl devs. I have found a bug. Parts of the webpage are not rendered. Plots and dash table are affected. Dash html components are working OK.

The following error is printed in the javascript console. The Julia code compiles without any issue.

react-dom@16.v1_2_2m1603136737.13.0.min.js:125 TypeError: Cannot create property 'editable' on string 'date'
    at Sanitizer.ts:31
    at r (_map.js:7)
    at map.js:62
    at _dispatchable.js:44
    at Object.t [as a] (_curry2.js:28)
    at v (Sanitizer.ts:29)
    at e.applyDefaultsToColumns (memoizer.ts:7)
    at e.value (Sanitizer.ts:58)
    at i.value (DataTable.js:21)
    at Ie (react-dom@16.v1_2_2m1603136737.13.0.min.js:104)
Me @ react-dom@16.v1_2_2m1603136737.13.0.min.js:125
Jh.c.payload @ react-dom@16.v1_2_2m1603136737.13.0.min.js:138
Qb @ react-dom@16.v1_2_2m1603136737.13.0.min.js:66
rh @ react-dom@16.v1_2_2m1603136737.13.0.min.js:101
zj @ react-dom@16.v1_2_2m1603136737.13.0.min.js:228
Th @ react-dom@16.v1_2_2m1603136737.13.0.min.js:152
tj @ react-dom@16.v1_2_2m1603136737.13.0.min.js:152
Te @ react-dom@16.v1_2_2m1603136737.13.0.min.js:146
(anonymous) @ react-dom@16.v1_2_2m1603136737.13.0.min.js:61
unstable_runWithPriority @ react@16.v1_2_2m1603136737.13.0.min.js:25
Da @ react-dom@16.v1_2_2m1603136737.13.0.min.js:60
Pg @ react-dom@16.v1_2_2m1603136737.13.0.min.js:61
U @ react@16.v1_2_2m1603136737.13.0.min.js:16
B.port1.onmessage @ react@16.v1_2_2m1603136737.13.0.min.js:24
dash_renderer.v1_5_0m1603136737.min.js:20 TypeError: Cannot create property 'editable' on string 'date'
    at Sanitizer.ts:31
    at r (_map.js:7)
    at map.js:62
    at _dispatchable.js:44
    at Object.t [as a] (_curry2.js:28)
    at v (Sanitizer.ts:29)
    at e.applyDefaultsToColumns (memoizer.ts:7)
    at e.value (Sanitizer.ts:58)
    at i.value (DataTable.js:21)
    at Ie (react-dom@16.v1_2_2m1603136737.13.0.min.js:104)

The relevant Julia code is

using Dash
using DashHtmlComponents
using DashCoreComponents
using DashTable

# typeof(interesting) is Array{String,2}
 dash_datatable(
        columns = ["date", "type"],
        data = DataFrame(interesting, ["date", "type"])
    )
# this is in a separate function
dcc_graph(
            id = "open-prices-graph",
            figure = price_plot_fig)

The versions are

  "DashTable"          => v"4.10.1"
  "DashCoreComponents" => v"1.12.0"
  "Dash"               => v"0.1.3"
  "DashHtmlComponents" => v"1.1.1"
  "DataFrames"         => v"0.21.8"

Happens in both Chromium (85) and Firefox (81)

Please tell me if I can provide some more details.

Kind regards.

IOError: stream is closed or unusable

I started to experiment with Dash.jl and tried to run the examples. When I run

app = dash(external_stylesheets = ["https://codepen.io/chriddyp/pen/bWLwgP.css"])

app.layout = html_div() do
    dcc_input(id = "my-id", value="initial value", type = "text"),
    html_div(id = "my-div")
end

callback!(app, Output("my-div", "children"), Input("my-id", "value")) do input_value
    "You've entered $(input_value)"
end

run_server(app)

I can load the page and the plot is displayed but the REPL shows an error message:

[ Info: Listening on: 127.0.0.1:8050
┌ Error: error handling request
│   exception =
│    IOError: stream is closed or unusable
│    Stacktrace:
│     [1] check_open at .\stream.jl:328 [inlined]
│     [2] uv_write_async(::Sockets.TCPSocket, ::Ptr{UInt8}, ::UInt64) at .\stream.jl:959
│     [3] uv_write(::Sockets.TCPSocket, ::Ptr{UInt8}, ::UInt64) at .\stream.jl:922     
│     [4] unsafe_write(::Sockets.TCPSocket, ::Ptr{UInt8}, ::UInt64) at .\stream.jl:1005│     [5] unsafe_write at C:\Users\Mario\.julia\packages\HTTP\IAI92\src\ConnectionPool.jl:171 [inlined]
│     [6] unsafe_write at .\io.jl:622 [inlined]
│     [7] write at .\io.jl:645 [inlined]
│     [8] startwrite(::HTTP.Streams.Stream{HTTP.Messages.Request,HTTP.ConnectionPool.Transaction{Sockets.TCPSocket}}) at C:\Users\Mario\.julia\packages\HTTP\IAI92\src\Streams.jl:87
│     [9] handle(::HTTP.Handlers.RequestHandlerFunction{Dash.HttpHelpers.var"#4#5"{Array{String,1},Int64,HTTP.Handlers.RequestHandlerFunction{Dash.HttpHelpers.var"#7#8"{Dash.var"#65#67"{Dash.DashApp},HTTP.Handlers.RequestHandlerFunction{Dash.HttpHelpers.var"#1#2"{Dash.HttpHelpers.Router,Dash.HandlerState}}}}}}, ::HTTP.Streams.Stream{HTTP.Messages.Request,HTTP.ConnectionPool.Transaction{Sockets.TCPSocket}}) at C:\Users\Mario\.julia\packages\HTTP\IAI92\src\Handlers.jl:278
│     [10] #4 at C:\Users\Mario\.julia\packages\HTTP\IAI92\src\Handlers.jl:345 [inlined]
│     [11] macro expansion at C:\Users\Mario\.julia\packages\HTTP\IAI92\src\Servers.jl:367 [inlined]
│     [12] (::HTTP.Servers.var"#13#14"{HTTP.Handlers.var"#4#5"{HTTP.Handlers.RequestHandlerFunction{Dash.HttpHelpers.var"#4#5"{Array{String,1},Int64,HTTP.Handlers.RequestHandlerFunction{Dash.HttpHelpers.var"#7#8"{Dash.var"#65#67"{Dash.DashApp},HTTP.Handlers.RequestHandlerFunction{Dash.HttpHelpers.var"#1#2"{Dash.HttpHelpers.Router,Dash.HandlerState}}}}}}},HTTP.ConnectionPool.Transaction{Sockets.TCPSocket},HTTP.Streams.Stream{HTTP.Messages.Request,HTTP.ConnectionPool.Transaction{Sockets.TCPSocket}}})() at .\task.jl:356└ @ HTTP.Servers C:\Users\Mario\.julia\packages\HTTP\IAI92\src\Servers.jl:373

This happens to several examples I have found. So it seems to be a generel problem with my setup/installation. However, the dashboard stays responsive and the callback still works.

In case it helps, my installed versions are:

  [1b08a953] Dash v0.1.3
  [1b08a953] DashCoreComponents v1.12.0
  [1b08a953] DashHtmlComponents v1.1.1

Contributed Examples

As well as Interact.jl Contributed Examples JuliaGizmos/Interact.jl#36 , it is good idea to collect examples using Dash.jl.

Here is my example

Code

# Usage: just run this code 
# tested on Julia 1.4 and 1.5 and 1.6-DEV

using Dash
using DashHtmlComponents
using DashCoreComponents
using Plots

function powplot(n)
    p = plot(x -> x^n, label = "y=x^$n", xlims=[0,1])
    figure = (data = Plots.plotly_series(p), layout = Plots.plotly_layout(p))
    figure
end

app =
    dash(external_stylesheets = ["https://codepen.io/chriddyp/pen/bWLwgP.css"])

app.layout = html_div(style = Dict(:width => "50%")) do
    html_h1("Hello Dash"),
    html_div() do
        html_div("slider", style = (width = "10%", display = "inline-block")),
        html_div(dcc_slider(
            id = "slider",
            min = 0,
            max = 9,
            marks = Dict(i => "$i" for i = 0:9),
            value = 1,
        ),style = (width = "70%", display = "inline-block"))
    end,
    html_br(),
    dcc_graph(id = "power", figure = powplot(1))
end

callback!(app, Output("power", "figure"), Input("slider", "value")) do value
    powplot(value)
end

run_server(app)

Result

powslider

Updated 2022/3/5

It seems the current Dash.jl does not work the example above.
I've fixed the example as below:

using Dash
using PlotlyJS

#      Status `~/.julia/environments/v1.7/Project.toml`
#  [1b08a953] Dash v1.1.2

function powplot(n)
    x = 0:0.01:1
    y = x .^ n
    p = plot(x, y, mode="lines")
    p.plot 
end

app =
    dash(external_stylesheets = ["https://codepen.io/chriddyp/pen/bWLwgP.css"])

app.layout = html_div(style = Dict(:width => "50%")) do
    html_h1("Hello Dash"),
    html_div() do
        html_div("slider", style = (width = "10%", display = "inline-block")),
        html_div(dcc_slider(
            id = "slider",
            min = 0,
            max = 9,
            marks = Dict(i => "$i" for i = 0:9),
            value = 1,
        ),style = (width = "70%", display = "inline-block"))
    end,
    html_br(),
    dcc_graph(id = "power", figure = powplot(1))
end

callback!(app, Output("power", "figure"), Input("slider", "value")) do value
    powplot(value)
end

run_server(app)

Dash.jl should automatically set port if corresponding PORT environment variable exists

Currently, Dash.jl does not attempt to detect whether a PORT environment variable has been set before launching the server, so using a statement such as

run_server(app, "0.0.0.0")`

when deploying the app may fail to bind to the correct port. This may cause issues with Dash Enterprise or Heroku unless a workaround is used, e.g.

port = parse(Int64, ENV["PORT"])

run_server(app, "0.0.0.0", port)

Add support for HTTP compression in Dash.jl

As with the R and Python versions of the Dash backend, we should provide support for compression of responses within Dash for Julia.

The CodecZlib package is a logical place to start, and it includes a GzipCompressor codec which can be used as a drop-in with transcode to pass the compressed body into HTTP.Response.

For unit testing and decompressing of the responses outside of the browser, we can pull in Inflate within the test environment.

Can't Download Dash-core-components In Julia

My OS Version: MacOS X 10.15
My Julia Version: V1.5
Using REPL Interface,
After Typing using Pkg; Pkg.add(PackageSpec(url="https://github.com/plotly/dash-core-components.git", rev="jl"))
These Error Messages Came Out:
ERROR: failed to fetch from https://github.com/plotly/dash-core-components.git, error: GitError(Code:ERROR, Class:Net, Error sending data: Broken pipe)
GitError(Code:ERROR, Class:Net, Error sending data: Broken pipe)

Question: Comparison between Python and Julia Dash

Hello. Sorry for posting a question here, but not sure where else to ask. I just went through the Python Plotly Dash guide, which is pretty good and comprehensive. I was just wondering what the difference in functionality is between the Python version and the Julia version.

Specifically, I was wondering about the multiple outputs feature, the cross-filtering features, etc. I am not looking for anything too complicated, just wondering how much of the python functionality is implemented.

Thanks so much for your hard work on this. I think it is great to have Plotly Dash on Julia.

Children should never be undefined in JSON passed to dash-renderer; results in reducer "layout" error

A simple test app with two html_div components declared and one html_br in between them fails to render anything in the browser window:

using Dash
using DashHtmlComponents

app = dash()
app.layout = html_div(id="outer-div") do
    html_div("A div", id="first-inner-div"),
    html_br(),
    html_div("Another div", id="second-inner-div")
end

run_server(app)

In the JavaScript console, the following error appears:
image

However, if you pass a blank space -- e.g. " " into the children property, it works. The problem may appear for components with all default arguments passed, that are nested inside another component, though this hypothesis has not been extensively tested.

The behaviour is reproducible if html_br() is substituted with html_div() or html_p().

@waralex @Marc-Andre-Rivet

Implement run_server for setting host and port in Dash.jl

In Python, the Flask server is launched via app.run_server; R launches the Fiery server in almost identical fashion (replacing the . with a $):

    app.run_server()

Dash.jl should have a run_server interface that launches the HTTP server, which initially supports the following two parameters:

  • host = a character string which specifies a valid IPv4 address for the HTTP server
  • port = an integer which specifies the port number on which to listen for HTTP traffic

We should remove make_handler and move this logic into the Dash.jl internals. We should also move these existing Dash.jl arguments to run_server:

  • debug

Eventually we will need to support other arguments to run_server, but we can discuss that further once these tasks are completed.

Relocate labeler from GitHub workflow to CircleCI configuration

This repo currently uses "Labeler", which runs via .github/labeler.yml. The Dash team has historically supported testing via CircleCI workflows.

In the interests of parsimony and ease of maintenance, we should relocate the "Labeler" workflow to the CircleCI config within .circleci/config.yml.

LaTeX rendering

Thanks for the package! Would be really great if rendering of LaTeX equations was possible out of the box, perhaps by passing a LaTeXString object as the children of a node. If this isn't suitable for inclusion in the core packages could I get some hints regarding building/using custom components with Dash.jl? I'm happy to contribute this back to the package if it's doable.

Examples need fixing

I know they were ok, as I fixed them at some point, but it's not enough to go back to that revision:

Pkg.add(PackageSpec(url="https://github.com/plotly/Dash.jl.git", rev="b302856"))

I DO have:

(@v1.5) pkg> st Dash
Status `~/.julia/environments/v1.5/Project.toml`
  [1b08a953] Dash v0.1.0 `https://github.com/plotly/Dash.jl.git#master`

still got/get:

julia> Pkg.add(PackageSpec(url="https://github.com/plotly/Dash.jl.git", rev="dev"))
   Updating git-repo `https://github.com/plotly/Dash.jl.git`
  Resolving package versions...
ERROR: Unsatisfiable requirements detected for package CodecZlib [944b1d66]:
 CodecZlib [944b1d66] log:
 ├─possible versions are: [0.4.4, 0.5.0-0.5.2, 0.6.0, 0.7.0] or uninstalled
 ├─restricted to versions 0.7 by Dash [1b08a953], leaving only versions 0.7.0
 │ └─Dash [1b08a953] log:
 │   ├─possible versions are: 0.1.0 or uninstalled
 │   └─Dash [1b08a953] is fixed to version 0.1.0
 └─restricted by compatibility requirements with CSVFiles [5d742f6a] to versions: [0.5.2, 0.6.0] — no versions left
   └─CSVFiles [5d742f6a] log:
     ├─possible versions are: [0.9.0-0.9.1, 0.10.0, 0.11.0, 0.12.0, 0.13.0, 0.14.0, 0.15.0, 0.16.0-0.16.1, 1.0.0] or uninstalled
     └─restricted to versions 1 by Queryverse [612083be], leaving only versions 1.0.0
       └─Queryverse [612083be] log:
         ├─possible versions are: 0.6.3 or uninstalled
         └─Queryverse [612083be] is fixed to version 0.6.3-DEV

[Question] Can we display heatmap ?

I would like to draw heatmap using Dash.jl like

http://juliaplots.org/PlotlyJS.jl/stable/examples/heatmaps/

The following code only displays diagonal elements

image

Here is my code to reproduce this issue

using Dash
using DashCoreComponents
using DashHtmlComponents
app =
    dash(external_stylesheets = ["https://codepen.io/chriddyp/pen/bWLwgP.css"])
app.layout = html_div() do
    html_h1("Hello Dash"),
    html_div("Dash.jl: Julia interface for Dash"),
    dcc_graph(
        id = "example-graph",
        figure = (
            data = [
                (x = [1, 2, 3], y = [1, 2, 3], z = rand(3,3), type = "heatmap", name = "SF"),
            ],
            layout = (title = "Dash Data Visualization",),
        ),
    )
end

run_server(app, "127.0.0.1", 8050, debug = true)

"Dash" The following package names could not be resolved

When I follow the first step in instruction:

import Pkg; Pkg.add("Dash")

Return error code:

_The following package names could not be resolved:

  • Dash (not found in project, manifest or registry)
    Please specify by known name=uuid.

Stacktrace:
[1] pkgerror(::String) at D:\buildbot\worker\package_win64\build\usr\share\julia\stdlib\v1.3\Pkg\src\Types.jl:113
[2] #ensure_resolved#101(::Bool, ::typeof(Pkg.Types.ensure_resolved), ::Pkg.Types.EnvCache, ::Array{Pkg.Types.PackageSpec,1}) at D:\buildbot\worker\package_win64\build\usr\share\julia\stdlib\v1.3\Pkg\src\Types.jl:936
[3] #ensure_resolved at .\none:0 [inlined]
[4] #add#25(::Bool, ::Pkg.BinaryPlatforms.Windows, ::Base.Iterators.Pairs{Union{},Union{},Tuple{},NamedTuple{(),Tuple{}}}, ::typeof(Pkg.API.add), ::Pkg.Types.Context, ::Array{Pkg.Types.PackageSpec,1}) at D:\buildbot\worker\package_win64\build\usr\share\julia\stdlib\v1.3\Pkg\src\API.jl:97
[5] add(::Pkg.Types.Context, ::Array{Pkg.Types.PackageSpec,1}) at D:\buildbot\worker\package_win64\build\usr\share\julia\stdlib\v1.3\Pkg\src\API.jl:72
[6] #add#24 at D:\buildbot\worker\package_win64\build\usr\share\julia\stdlib\v1.3\Pkg\src\API.jl:69 [inlined]
[7] add at D:\buildbot\worker\package_win64\build\usr\share\julia\stdlib\v1.3\Pkg\src\API.jl:69 [inlined]
[8] #add#21 at D:\buildbot\worker\package_win64\build\usr\share\julia\stdlib\v1.3\Pkg\src\API.jl:67 [inlined]
[9] add at D:\buildbot\worker\package_win64\build\usr\share\julia\stdlib\v1.3\Pkg\src\API.jl:67 [inlined]
[10] #add#20(::Base.Iterators.Pairs{Union{},Union{},Tuple{},NamedTuple{(),Tuple{}}}, ::typeof(Pkg.API.add), ::String) at D:\buildbot\worker\package_win64\build\usr\share\julia\stdlib\v1.3\Pkg\src\API.jl:66
[11] add(::String) at D:\buildbot\worker\package_win64\build\usr\share\julia\stdlib\v1.3\Pkg\src\API.jl:66
[12] top-level scope at In[9]:1_

My Env. Background:
Julia Version 1.3.1
Commit 2d5741174c (2019-12-30 21:36 UTC)
Platform Info:
OS: Windows (x86_64-w64-mingw32)
CPU: AMD Ryzen 5 3600X 6-Core Processor
WORD_SIZE: 64
LIBM: libopenlibm
LLVM: libLLVM-6.0.1 (ORCJIT, znver1)

Update slider's marks by callback

Hello,

The following code demonstrates that there is an issue with updating the slider's marks using a callback. If you run this code and press the "Test Slider" button twice, then the marks, which are set up while creating a slider, will be erased by the callback function:

update_slider_callback_function
marks:(1 = "1", 2 = "2")
update_slider_callback_function
marks:1

The code:
`
using Dash, DashHtmlComponents, DashCoreComponents, DashTable, Printf

app = dash(; suppress_callback_exceptions=true)

app.layout = html_div() do
children=[
html_button("Test Slider", id="test"),
dcc_slider(id="range_test", min=1, max=2, marks=Dict(1 => "1", 2 => "2"))
]
end

updateSliderState =
[
State("range_test", "marks")
]
updateSliderInput =
[
Input("test", "n_clicks")
]
updateSliderOutput =
[
Output("range_test", "marks")
]
function update_slider_callback_function(
n_clicks, marksCurrent
)
println("update_slider_callback_function")
println("marks:", marksCurrent)
Dict(1 => "1", 2 => "2")
end

callback!(
update_slider_callback_function,
app,
updateSliderOutput,
updateSliderInput,
updateSliderState
)

run_server(app, "0.0.0.0", 8080)
`

I really need this feature, so what would you recommend me as a workaround?
Thanks.

Make dash_bootstrap_components.themes available in Dash.jl

Hi all,

First, let me tell you how fantastic the work you've done so far is. I've been playing with Dash.jl for a few days now and it's incredible how seamless the experience is. Also, I think it's great you've added the components from the dbc library.

However, those components only work properly if you include a Bootstrap v4 stylesheet from BootstrapCDN. For this reason, the Python library provides the dash_bootstrap_components.themes module, with a number of free stylesheets from Bootstrap and from the Bootswatch gallery (see https://dash-bootstrap-components.opensource.faculty.ai/docs/themes/ for more info). It's a simple list of URL constants, but it's very practical as you don't have to hunt for the URLs yourself for at least that selection of stylesheets.

So I think it'd be great if Dash.jl could provide the same facility, maybe as a dbc_themes struct or NamedTuple, or whatever you feel fits best with how Dash.jl is supposed to work (I'm unfortunately not yet knowledgeable enough about Julia and the specific implementation of this library to be able to implement this myself).

What do you think?

Cycllic callbacks

I was wondering whether the following code should work:

`using Dash, DashHtmlComponents, DashCoreComponents, DashTable

app = dash(; suppress_callback_exceptions=true)

app.layout = html_div() do
children=[
html_h4(children="Excel-like checkboxes"),
dcc_checklist(
id="all",
options=[Dict("label" => "all", "value" => "all")],
value=[]
),
dcc_checklist(
id="cities",
options=[
Dict("label" => "New York City", "value" => "NYC"),
Dict("label" => "Montréal", "value" => "MTL"),
Dict("label" => "San Francisco", "value" => "SF")
],
value=["MTL", "SF"]
),
html_div(id="loop_breaker_container", children=[])
]
end

callback!(app, callid"all.value => cities.value") do inputs
if length(inputs) == 0
return []
else
return ["NYC", "MTL", "SF"]
end
end

callback!(app, callid"{all.value} cities.value => loop_breaker_container.children") do all_value, inputs
if length(inputs) == 3 && all_value == []
return [html_div(id="loop_breaker", children=true)]
elseif length(inputs) == 0 && all_value == ["all"]
return [html_div(id="loop_breaker", children=false)]
else
return []
end
end

callback!(app, callid" loop_breaker_container.children => all.value") do all_true
if all_true
return ["all"]
else
return []
end
end

run_server(app, "0.0.0.0", 8080)
`

By not it gives me an error regarding dependencies.
Thanks.

Rename id argument of callback! to params to match Dash convention

Following discussion offline with @alexcjohnson, we should rename the id argument of callback! to params, which aligns with current Dash convention. We already use id as a property within Dash components, so this usage is potentially confusing as-is.

This is a breaking change in the Dash.jl API, and we should probably make the modification sooner rather than later.

function callback!(func::Function, app::DashApp, id::CallbackId)

This likely implies that we should consider renaming CallbackId to Params (or SetParams or Setparms or setparms) and callid_str to params_str (and, callid also):

struct CallbackId

macro callid_str(s)

Contour plot has no selection tools

Hello,

I adapted the example from #60 using the JSON workaround to render a contour plot. As can be seen in the photo there are no rectangle or lasso select tools. Is there an option to enable selection tools somehow?

using Dash
using DashCoreComponents
using DashHtmlComponents
app = dash(external_stylesheets = ["https://codepen.io/chriddyp/pen/bWLwgP.css"])
app.layout = html_div() do
    html_h1("Hello Dash"),
    html_div("Dash.jl: Julia interface for Dash"),
    dcc_graph(
        id = "example-graph",
        figure = (
            data = [
                (x = [1, 2, 3], y = [1, 2, 3], z = [rand(3) for _ in 1:3], type = "contour", name = "SF"),
            ],
            layout = (title = "Dash Data Visualization",),
        ),
    )
end

run_server(app, "127.0.0.1", 8050, debug = true)

contour

Support for user-defined routes

As described in https://dash.plot.ly/integrating-dash and seen in code examples posted to our community forum, Dash for Python permits app developers to leverage Flask support for adding new routes.

Dash.jl currently lacks this functionality; sample code in Dash for Python looks like this:

@app.callback(Output('my-link', 'href'), [Input('my-dropdown', 'value')])
def update_link(value):
    return '/dash/urlToDownload?value={}'.format(value)

@app.server.route('/dash/urlToDownload') 
def download_csv():
    value = flask.request.args.get('value')
    # create a dynamic csv or file here using `StringIO` 
    # (instead of writing to the file system)
    strIO = StringIO.StringIO()
    strIO.write('You have selected {}'.format(value)) 
    strIO.seek(0)    
    return send_file(strIO,
                     mimetype='text/csv',
                     attachment_filename='downloadFile.csv',
                     as_attachment=True)

User-defined routes are helpful generally, but particularly for documentation and applications where URL redirects are required.

Define layouts in Dash.jl in same manner as Dash for Python and R

In Python, the app object has a layout method, for example:

app.layout = html.Div(children=[
    dcc.Input(id="graphTitle",
                     value="Let's Dance",
                     type="text"),

    html.Div(id="outputID"),

    dcc.Graph(
        id='graph',
        figure={
            'data': [
                {'x': [1, 2, 3], 'y': [3, 2, 8], 'type': 'bar'}
            ],
            'layout': {
                'title': 'Graph'
            }
        }
    )
])

In R, the syntax is nearly identical, with minor differences when declaring lists vs. dicts, and omission of dots within function names (e.g. dccInput instead of dcc.Input). In Julia, the layout is implicit when declaring the app object:

app = Dash("Test", external_stylesheets=["https://codepen.io/chriddyp/pen/bWLwgP.css"]) do
    html_div() do
        dcc_input(id="graphTitle", value="Let's Dance!", type = "text"),
        html_div(id="outputID"),    
        dcc_graph(id="graph",
            figure = ( 
                data = [(x = [1,2,3], y = [3,2,8], type="bar")],
                layout = Dict(:title => "Graph")
            )   
        )   
    
    end 
end
  • We should add a way of setting the layout in Dash for Julia which mimics that of the Python and R versions, and separate this from the initial declaration of app.
  • After refactoring to add layout, remove 🔪 layout_maker function
  • Provide a layout_get method within the Dash struct in Dash.jl

HTML templating style/API: varargs instead of do blocks?

I've really been enjoying Dashboards.jl — and am thrilled to see this becoming Dash.jl! I've been brainstorming a bit on how to make the HTML templating a bit simpler (and closer to both HTML & Julia syntaxes). One thought I've had is to de-emphasize (or maybe even deprecate/remove) the do block form and instead allow passing children as varargs to the components. Since we can pass keyword arguments before positional arguments, we can still have a decent structure.

As an example, DashboardsExamples/dash_tutorial/5_interactive_graphing_1.jl becomes:

html_div(
    # ...
    html_div(className="three columns",
        dcc_markdown(
            """**Hover Data**
            Mouse over values in the graph."""
        ),
        html_pre(id="hover-data", style=styles.pre)
    ),
    html_div(className="three columns",
        dcc_markdown(
            """**Click Data**
            Click on points in the graph."""
        ),
        html_pre(id="click-data", style=styles.pre)
    ),
    # ...
)

The biggest advantage over the do block form is that it's easier to catch mistakes. Forgetting a comma at the end of a line in a do block just simply ignores everything that precedes it! It's an error like this. I also think this matches Julia's style a bit closer while still allowing you to put ids and classes right up front (like in HTML).

Docstrings for online help render oddly when props that are lists with nested elements exist

The docstrings flow oddly in some Julia help pages -- this one, for dcc_link, renders is_loading, prop_name, and component_name as if they were top-level props themselves. Simultaneously, title and target appear to be missing.

help?> dcc_link
search: dcc_link dcc_loading dcc_location dcc_slider dcc_checklist dcc_rangeslider dcc_confirmdialogprovider

  dcc_link(;kwargs...)
  dcc_link(children::Any;kwargs...)
  dcc_link(children_maker::Function;kwargs...)

  A Link component. Link allows you to create a clickable link within a multi-page app.

  For links with destinations outside the current app, html.A is a better component to use. Keyword arguments:

    •    children (a list of or a singular dash component, string or number; optional): The children of this component

    •    id (String; optional): The ID of this component, used to identify dash components

  in callbacks. The ID needs to be unique across all of the components in an app.

    •    href (String; optional): The URL of a linked resource.

    •    refresh (Bool; optional): Controls whether or not the page will refresh when the link is clicked

    •    className (String; optional): Often used with CSS to style elements with common properties.

    •    style (Dict; optional): Defines CSS styles which will override styles previously set.

    •    loading_state (optional): Object that holds the loading state object coming from dash-renderer. loadingstate has the following type: lists containing elements 'isloading', 'propname', 'componentname'.

  Those elements have the following types:

    •    is_loading (Bool; optional): Determines if the component is loading or not

    •    prop_name (String; optional): Holds which property is loading

    •    component_name (String; optional): Holds the name of the component that is loading

Callback context

Dash.jl should support a callback_context method which will enable retrieving the inputs which triggered the firing of a given callback, and which also allows introspecting the input/state values given their names. It should only be available from within a callback, and it should present a warning message if a developer attempts to use it from outside of a callback.

This feature is described in more detail in plotly/dash#608 and plotly/dash-renderer#124.

Periodic "IOError: stream is closed or unusable" message appears when responding to web request

On several occasions I have seen the following message appear in the REPL, even as the Dash app continues to be accessible from the browser:

┌ Error: error handling request
│   exception =
│    IOError: stream is closed or unusable
│    Stacktrace:
│     [1] unsafe_write at /home/rpkyle/.julia/packages/HTTP/IAI92/src/ConnectionPool.jl:171 [inlined]
│     [2] unsafe_write(::HTTP.Streams.Stream{HTTP.Messages.Request,HTTP.ConnectionPool.Transaction{Sockets.TCPSocket}}, ::Ptr{UInt8}, ::UInt64) at /home/rpkyle/.julia/packages/HTTP/IAI92/src/Streams.jl:98
│     [3] unsafe_write at ./io.jl:622 [inlined]
│     [4] write at ./io.jl:645 [inlined]
│     [5] handle(::HTTP.Handlers.RequestHandlerFunction{Dash.HttpHelpers.var"#4#5"{Array{String,1},Int64,HTTP.Handlers.RequestHandlerFunction{Dash.HttpHelpers.var"#7#8"{Dash.var"#65#67"{Dash.DashApp},HTTP.Handlers.RequestHandlerFunction{Dash.HttpHelpers.var"#1#2"{Dash.HttpHelpers.Router,Dash.HandlerState}}}}}}, ::HTTP.Streams.Stream{HTTP.Messages.Request,HTTP.ConnectionPool.Transaction{Sockets.TCPSocket}}) at /home/rpkyle/.julia/packages/HTTP/IAI92/src/Handlers.jl:279
│     [6] #4 at /home/rpkyle/.julia/packages/HTTP/IAI92/src/Handlers.jl:345 [inlined]
│     [7] macro expansion at /home/rpkyle/.julia/packages/HTTP/IAI92/src/Servers.jl:367 [inlined]
│     [8] (::HTTP.Servers.var"#13#14"{HTTP.Handlers.var"#4#5"{HTTP.Handlers.RequestHandlerFunction{Dash.HttpHelpers.var"#4#5"{Array{String,1},Int64,HTTP.Handlers.RequestHandlerFunction{Dash.HttpHelpers.var"#7#8"{Dash.var"#65#67"{Dash.DashApp},HTTP.Handlers.RequestHandlerFunction{Dash.HttpHelpers.var"#1#2"{Dash.HttpHelpers.Router,Dash.HandlerState}}}}}}},HTTP.ConnectionPool.Transaction{Sockets.TCPSocket},HTTP.Streams.Stream{HTTP.Messages.Request,HTTP.ConnectionPool.Transaction{Sockets.TCPSocket}}})() at ./task.jl:356

Not sure about the cause, but seems to be happening reasonably often (several times per day) for me both on Mac OS and on my Linux workstation, so mentioning here.

@alexcjohnson

Logging capability for debugging purposes

In Dash for R and Dash for Python, we currently provide logging output for requests and responses. In R, it looks like this (the colours are missing, but successful requests are green, failed requests are red, and warnings are yellow):

> app$run_server()
Fire started at 127.0.0.1:8050
start: 127.0.0.1:8050
request: 127.0.0.1 - ID_127.0.0.1 [25/Apr/2020:14:47:38 -0400] "GET / HTTP/1.1" 200 591 "" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.122 Safari/537.36"
error: Script or CSS dependencies with NULL href fields must include a file path, dependency name, and R package name.
request: 127.0.0.1 - ID_127.0.0.1 [25/Apr/2020:14:47:40 -0400] "GET /_dash-component-suites/dash_core_components/dash_core_components.v1_9_0m1586465028.min.js?v=1.9.0&m=1586465028 HTTP/1.1" 500 0 "http://127.0.0.1:8050/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.122 Safari/537.36"

In addition to a logging facility, it would be good to trap errors as warnings, so that the backend doesn't halt when a (recoverable) problem is encountered. Particularly in multi-user contexts, this prevents a simple/transient problem from taking down the application completely.

Can't install in Julia 1.5.1

Even with empty .julia on Julia 1.5.1:

julia> using Pkg; Pkg.add(PackageSpec(url="https://github.com/plotly/Dash.jl.git"))
    Cloning git-repo `https://github.com/plotly/Dash.jl.git`
[..]

      Added registry `General` to `~/.julia/registries/General`
ERROR: Unsatisfiable requirements detected for package DashHtmlComponents [1b08a953]:
 DashHtmlComponents [1b08a953] log:
 ├─DashHtmlComponents [1b08a953] has no known versions!
 └─restricted to versions 1.0.3-1 by Dash [1b08a953] — no versions left
   └─Dash [1b08a953] log:
     ├─possible versions are: 0.1.0 or uninstalled
     └─Dash [1b08a953] is fixed to version 0.1.0
Stacktrace:

EDIT: The other installation instructions in the README DO work, and then finally the examples.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.