I have a graph that I update in 2 manners :
- When I press on the save button : the whole content is updated, I send a request back to the server, then the server asks the backend for data crunching and analysis, send back dictionaries to the front server, updates the content and does some other bunch of things (updating inner custom objects). In this callback, I update the “figure”. My figure is in two parts : a heavy scatter in the background and 6 very light line traces on top. I call a first file that loads my Scatter, this file calls a second file that loads the lines. I separated the lines in a second file for the next purpose. THIS WORKS FINE.
@app.callback(
Output("P_T", "figure"),
Output("heatmap", "figure"),
Output("ts","figure"),
Input('test-params', 'n_clicks'),
Input('save-params', 'n_clicks'),
[State(parameter["input"], "value") for parameter in app.structure["FLAT"].values() if parameter["type"] != "time"])
def update_graph(click_test,click_save, *parameters):
return [
scatter_power_temp(app.data["df"], *parameters), # this is the dcc.Graph I'm worried about
heatmap(app.data["df"].temperature.values,app.data["df"].unexplained.values),
plot_ts_full(app.data["df"]),
]
Initialization goes fine, my scatter shows, the default lines show (0 all over as intended). The following is the end of scatter_power_temp
fig = go.Figure(data=data,layout=layout)
traces=params_to_traces(*parameters)[0]
for i in range(6):
x=traces["x"][i]
y=traces["y"][i]
model_i=go.Scatter(x=x,y=y)
fig.add_trace(model_i)
return fig
and now the return of params_to_traces :
return dict(x=x,y=y),[1,2,3,4,5,6],7
Again, this works fine :
As you can see in the first callback, there are a bunch of parameters that will be converted to the 6 lines from params_to_traces. I want to make a clientside callback that instantly updates my 6 lines when someone changes one parameter, by using extendData. So I switch from State to Input on those parameters, and from “figure” to “extendData” and only call my params_to_traces. Also, I keep my first callback for general and obvious updating purposes of a Save button. But before I go on include a js file, I’m trying with the classic callback in python language.
@app.callback(
Output("P_T", "extendData"),
[Input(parameter["input"], "value") for parameter in app.structure["FLAT"].values() if parameter["type"] != "time"])
def extend_traces(*parameters):
traces=params_to_traces(*parameters)
print(traces)
return traces
The traces don’t show, instead I have an empty dcc.Graph. The print statement gives :
({'x': [[-10, 5, 10, 15, 20, 30, 40], [-10, 5, 10, 15, 20, 30, 40], [-10, 5, 10, 15, 20, 30, 40], [-10, 5, 10, 15, 20, 30, 40], [-10, 5, 10, 15, 20, 30, 40], [-10, 5, 10, 15, 20, 30, 40]], 'y': [[0.0, 0.0, 0.0, 0,
0, 0, 0], [0.0, 0.0, 0.0, 0, 0, 0, 0], [0.0, 0.0, 0.0, 0, 0, 0, 0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]]}, [1, 2, 3, 4, 5, 6], 7)
EDIT : I went on and wrote the javascript, the clientside_callback is the following :
app.clientside_callback(
"""
function params_to_traces( lo_slope, up_slope, T_pivot, P_pivot, partial_power, full_power, heating_power, sat_heat, set_heat, cooling_power, set_cool, sat_cool){
const temp=[-10,40,T_pivot,sat_heat,set_heat,sat_cool,set_cool];
temp.sort();
const refrigeration_powers = new Array(temp.length);
const partial_lighting = new Array(temp.length);
const full_lighting = new Array(temp.length);
const hvac_powers = new Array(temp.length);
const x = new Array(6);
const y = new Array(6);
for (let i = 0; i < 6; i++) {
x[i] = new Array(temp.length).fill(0);
y[i] = new Array(temp.length).fill(0);
}
for (let j = 0; j < temp.length; j++) {
refrigeration_powers[j] = P_pivot + (temp[j] - T_pivot) * lo_slope + Math.max(0.0, temp[j] - T_pivot) * up_slope;
partial_lighting[j] = partial_power;
full_lighting[j] = full_power;
hvac_powers[j] = heating_power * Math.min(1.0, Math.max(0.0, (temp[j] - sat_heat) / (set_heat - sat_heat)))
+ cooling_power * Math.min(1.0, Math.max(0.0, (temp[j] - sat_cool) / (set_cool - sat_cool)));
for (let i = 0; i < 6; i++){
x[i][j] = temp[j];
}
y[0][j] = refrigeration_powers[j];
y[1][j] = refrigeration_powers[j] + partial_lighting[j];
y[2][j] = refrigeration_powers[j] + partial_lighting[j] + full_lighting[j];
y[3][j] = refrigeration_powers[j] + hvac_powers[j];
y[4][j] = refrigeration_powers[j] + partial_lighting[j] + hvac_powers[j];
y[5][j] = refrigeration_powers[j] + partial_lighting[j] + full_lighting[j] + hvac_powers[j];
}
return [{x:x,y:y},[1,2,3,4,5,6],7]
}
""",
Output(Power_vs_Temperature, "extendData"),
[Input(parameter["input"], "value") for parameter in app.structure["FLAT"].values() if parameter["type"] != "time"],
)
About the Input array, I put some of the dash components in a dict in the app, this way I can have access to them in any python file and that let’s me rearrange and split my code in files and folders in a manner similar to my dash app layout. I wish there was something like a callbacks.init_app(dash_app) possibility, without having to import third party libraries with 1 or 2 stars, but in the meantime I use this architecture, and it works.
Except for this extendData stuff.