plotly / Dash.jl

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

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

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

lambcount opened this issue · comments

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)