come modificare una cella dash_table.DataTable e aggiornare altri callback
Ho un dashboard Plotly-Dash qui che viene aggiornato utilizzando gli input delle variabili sul lato sinistro, nonché alcune variabili "fisse".
Queste variabili fisse sono mostrate in dash_table.DataTable nella parte inferiore dell'immagine. Se l'utente sceglie di modificare il valore in DataTable, vorrei aggiornare i callback che utilizzavano questi valori fissi.
A partire da ora, i callback utilizzano il menu a discesa e gli input numerici come [Input(' ', ' ')] e le variabili fisse sono memorizzate come variabili e utilizzate nelle equazioni pertinenti.
C'è un modo per:
- utilizzare un callback per definire questi valori variabili fissi. E quando la tabella viene modificata, lo sono anche queste variabili "fisse", ..
- oppure, utilizzare un callback per cercare nella tabella e, se vengono apportate modifiche, aggiornare i valori delle variabili fisse.
Una domanda un po 'vaga che so, l'ho cercato su Google praticamente in ogni modo e la maggior parte delle informazioni ha a che fare con il filtraggio di tabelle dati e la visualizzazione di righe, meno selezionando e memorizzando i valori delle celle come variabili.
Tutto ciò di cui ho veramente bisogno è un esempio di prendere un valore numerico di cella, usando come callback [Input()], e quel callback usando l'input in una formula algebrica di base.
Ho allegato il codice, vedrai che ho armeggiato con la prima richiamata, il resto del codice funziona bene.
import dash
import dash_design_kit as ddk
import dash_core_components as dcc
import dash_html_components as html
import plotly.graph_objects as go
import pandas as pd
import dash_daq as daq
import dash_table
from dash.dependencies import Input, Output
import math
import pandas as pd
import pathlib
from crunch_numbers import *
num_datacards = 5
# theme.js supplies some additional styling, generated using editor
# REQUIRED FOR DEPLOYMENT
app = dash.Dash(__name__, suppress_callback_exceptions=True) # keep suppress_ in production code
server = app.server # expose server variable for Procfile
app.layout = ddk.App(show_editor=True, children=[
ddk.Header([
ddk.Logo("assets/komatsuLogo.png",
style={
"height": "30px",
"margin-right":"0px",
"width": "auto",
},
),
ddk.Title('Drivetrain Selection'),
]), # end of ddk.Header
ddk.Block(width=20,children=[ # left-side (inputs)
ddk.Card([ # machine Configuration
ddk.CardHeader("Machine Configuration"),
html.Br(),
dcc.Dropdown(
id='Platform',
options=[
{'label': 'Badger', 'value': 'Badger'},
{'label': 'Weasel', 'value': 'Weasel'},
],
value='Badger',
clearable=False,
placeholder="Select Machine",
),
html.Br(),
dcc.Dropdown(
id='battery_size',
options=[
{'label': '5S1P ( 66kWh)', 'value': 66},
{'label': '5S2P (132kWh)', 'value': 132},
],
value=66,
clearable=False,
#placeholder="Battery Size (kWh)",
),
html.Br(),
dcc.Dropdown(
id='Motor Selection',
options=[
{'label': 'MD 2200', 'value': 'sumo_md_2200'},
],
value='sumo_md_2200',
clearable=False,
placeholder="Motor Selection",
),
]), # end of Machine Configuration
ddk.Card([ # "Inputs"
ddk.CardHeader("Inputs"),
daq.NumericInput(
id='ramp_angle',
label='% Grade',
labelPosition='top',
value=0,
min=0,
max=18,
size='auto',
),
html.Br(),
daq.NumericInput(
id='ground_speed',
label='Speed (kph)',
labelPosition='top',
value=0,
min=0,
max=15,
size='auto',
),
html.Br(),
daq.NumericInput(
id='parasitics',
label='Parasitic Loads (kw)',
labelPosition='top',
value=0,
min=0,
max=30,
size='auto',
),
#html.Br(),
]), # end of "Inputs"
]), # end of left-side
ddk.Block(width=80, children=[ # right side block
ddk.Card([ # datacards and plot
ddk.DataCard(
width=100/num_datacards, # num_datacards is defined at top of file
id='motor_speed',
value=0,
label="(RPM)",
),
ddk.DataCard(
width=100/num_datacards,
id='motor_torque',
value=0,
label="(NM)",
),
ddk.DataCard(
width=100/num_datacards,
id='traction_efficiency',
value=0,
label="(Tot. %)",
),
ddk.DataCard(
width=100/num_datacards,
id='total_power',
value=0,
label="(kW)",
),
ddk.DataCard(
width=100/num_datacards,
id='autonomy',
value=0,
label="(km)",
),
dcc.Graph(id='plot'),
]), # end datacards and plot
ddk.Card(width=100,children=[ # table card
ddk.CardHeader("Machine Characteristics"),
dcc.Markdown(
"""
Update values in the table to modify machine performance.
""",
style={'textAlign': 'justify'}
),
dash_table.DataTable(
id='machine_spec_table',
data=Badger.to_dict("rows"),
columns=[ # only 'Values' and 'Mechanical Efficiency' are editable!
{"name": i, "id": i,"editable":False,"selectable":True}
if i == "Description" or i == "Units"
else {"name": i, "id": i,"selectable":True}
for i in Badger.columns
],
style_as_list_view=True,
style_header={"fontWeight": "bold", "textTransform": "capitalize"},
style_data_conditional=[
{
"if": {"row_index": "even"},
"backgroundColor": "var(--report_background_page)",
}
],
editable=True,
),
]) # end of table card
]) # end of right side block
]) # end of ddk.App
"""
Example of how to manage column width, should the need arise
style_cell_conditional=[
{
'if': {'column_id': 'Units'},
'width': 25
} for c in ['Units']
],
"""
############################# TABLE CALLBACKS ##################################################
################################################################################################
def find_fixed_variables(dict_list,var):
return dict_list[]
############################# DRIVETRAIN SELECTION CALLBACKS ###################################
################################################################################################
@app.callback(
Output('motor_speed', 'value'),
[Input('ground_speed', 'value'),
Input('machine_spec_table','data')] # , Input('tire_rr', 'value'), Input('diff_ratio', 'value'), Input('transfer_ratio', 'value')
)
def update_output(ground_speed,dict_list): #tire_rr, diff_ratio, transfer_ratio
return math.floor((ground_speed*1000)/60/(2*math.pi*tire_rr)*diff_ratio*transfer_ratio)
@app.callback(
Output('total_power', 'value'),
[Input('ground_speed', 'value'),
Input('ramp_angle', 'value')] #, Input('parasitics', 'value')] # Input('GVW', 'value'), Input('RR', 'value'),, Input('traction_efficiency', 'value')
)
def update_output(ground_speed, ramp_angle): #, traction_efficiency
power = math.floor(((RR/100)*(ground_speed*0.278) * GVW * gravity_cnst * math.cos(math.atan(ramp_angle/100))
/ 0.9 / 1000)
+ ((ground_speed * 0.278) * GVW * gravity_cnst * math.sin(math.atan(ramp_angle / 100))
/ 0.9 / 1000)
)
if ground_speed == 0:
return 0
else:
return power
@app.callback(
Output('motor_torque', 'value'),
[Input('ground_speed', 'value'),
Input('motor_speed', 'value'),
Input('total_power', 'value'),]
)
def update_output(ground_speed, motor_speed, total_power):
if ground_speed == 0:
return 0
elif math.floor(9.5488*total_power*1000/motor_speed) < 50:
return 50
else:
return math.floor(9.5488*total_power*1000/motor_speed)
@app.callback(
Output('plot', 'figure'),
[Input('motor_speed', 'value'),
Input('motor_torque', 'value')] #Input('Motor Selection', 'value')
)
def update_output(motor_speed, motor_torque): # , Motor_Selection
fig = go.Figure(
layout=go.Layout(
# title="Motor Efficiency Plot",
# autosize=False,
# width=500,
paper_bgcolor="rgba(0,0,0,0)",
plot_bgcolor="rgba(0,0,0,0)",
yaxis=dict(title="Motor Torque (Nm)"),
xaxis=dict(title="Motor Speed (RPM)"),
)
)
fig.update_layout(legend=dict(
orientation="h",
yanchor="bottom",
y=1,
xanchor="left",
x=0
)
)
fig.add_trace(go.Scatter(
x=TM4_BoundaryCurve['Speed (rpm)'],
y=TM4_BoundaryCurve['Peak Torque (Nm)'],
name="Peak Torque",
)
)
fig.add_trace(go.Scatter(
x=TM4_BoundaryCurve['Speed (rpm)'],
y=TM4_BoundaryCurve['Continuous Torque (Nm)'],
name="Cont. Torque",
)
)
fig.add_trace(go.Contour(
z=[TM4_EfficiencyMap['0'], TM4_EfficiencyMap['280'], TM4_EfficiencyMap['420'], TM4_EfficiencyMap['560'],
TM4_EfficiencyMap['700'],
TM4_EfficiencyMap['840'], TM4_EfficiencyMap['980'], TM4_EfficiencyMap['1120'], TM4_EfficiencyMap['1260'],
TM4_EfficiencyMap['1400'],
TM4_EfficiencyMap['1540'], TM4_EfficiencyMap['1680'], TM4_EfficiencyMap['1820'], TM4_EfficiencyMap['1960'],
TM4_EfficiencyMap['2100'],
TM4_EfficiencyMap['2240'], TM4_EfficiencyMap['2380'], TM4_EfficiencyMap['2520'], TM4_EfficiencyMap['2660'],
TM4_EfficiencyMap['2800'],
TM4_EfficiencyMap['2940'], TM4_EfficiencyMap['3080'], TM4_EfficiencyMap['3220'], TM4_EfficiencyMap['3360'],
TM4_EfficiencyMap['3500'], ],
x=TM4_EfficiencyMap['Speed'],
y=TM4_EfficiencyMap['Torque'],
transpose=True,
colorscale='Blues',
ncontours=20,
opacity=0.5,
showscale=False,
contours=dict(
showlabels=True, # show labels on contours
labelfont=dict( # label font properties
size=12,
color='white',
)
)
)
)
fig.add_trace(go.Scatter(
x=[motor_speed],
y=[motor_torque],
name="Actual",
mode="markers",
marker=dict(size=20, color='black', symbol="x"),
)
)
return fig
@app.callback(
Output('autonomy', 'value'),
[Input('ground_speed', 'value'),
Input('total_power', 'value'),
Input('battery_size', 'value')]
)
def update_output(ground_speed, total_power, battery_size):
if ground_speed == 0 or total_power == 0:
return 0
else:
return round((battery_size * DOD / total_power) * ground_speed, 2)
@app.callback(
Output('traction_efficiency', 'value'),
[Input('motor_speed', 'value'),
Input('motor_torque', 'value')]
)
def update_output(motor_speed, motor_torque):
df = pd.DataFrame(TM4_EfficiencyMap)
if motor_speed <= 280:
speed = str(0)
torque = 50
else:
speed = str(int((round(motor_speed / 140, 0) / 2) * 280))
torque = round(motor_torque / 50, 0) * 50
z = sum(round(df.loc[df['Torque'] == torque, speed] / 100 * diff_eff * transfer_eff * driveshaft_mt * driveshaft_td, 2))
return z
################################# MANDATORY SERVER CODE ##################################
if __name__ == '__main__':
app.run_server(debug=True)
Risposte
Va bene, soluzione così semplice, niente che alcune istruzioni di test print() non possano risolvere lol. Essenzialmente a seconda del tipo di input di callback utilizzato per Input('machine_spec_table','data')
Qui ho usato "dati", ce ne sono molti altri disponibili e spiegati nella documentazione,
l'input per la funzione è il seguente:
your_var = [
{'Description': 'Gross Vehicle Weight', 'Values': 29500, 'Units': 'kg', 'Mechanical Efficiency': '-'},
{'Description': 'Weight Distribution', 'Values': '60/40', 'Units': '', 'Mechanical Efficiency': '-'},
{'Description': 'Tire Rolling Radius', 'Values': 0.589, 'Units': 'm', 'Mechanical Efficiency': '-'},
{'Description': 'Differential Ratio', 'Values': 20.65, 'Units': '', 'Mechanical Efficiency': 0.93},
{'Description': 'Transfer Case Ratio', 'Values': 2.48, 'Units': '', 'Mechanical Efficiency': 0.98},
{'Description': 'Rolling Resistance', 'Values': 0.02, 'Units': '', 'Mechanical Efficiency': '-'},
{'Description': 'Drive Shaft', 'Values': '-', 'Units': '', 'Mechanical Efficiency': 0.98}
]
Un elenco di dizionari! Facile da accedere, qualcosa sulla falsariga di table[0]['Values]va bene :) (0 è l'indice dell'elenco, 'Valori' la chiave del dizionario).