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)
I opened a topic on discourse.julialang regarding this topic: