I have a Plotly-Dash dashboard here that is updated using the variable inputs on the left-hand side, as well as a few "fixed" variables.
These fixed variables are shown in the dash_table.DataTable at the bottom of image. Should the user choose to alter the value in the DataTable, I would like to update the callbacks that used these fixed values.
As of now, the callbacks use the the dropdown and numeric inputs as [Input(' ', ' ')] and the fixed variables are stored as variables, and used in the relevant equations.
Is there a way to either:
A bit of a vague question I know, I've googled this just about every which way and most of the info has to do with filtering dataTables and displaying rows, less so selecting and storing cell values as variables.
All I really need is an example of taking a cell numeric value, using as a callback [Input()], and that callback using the input in a basic algebraic formula.
I have attached the code, you will see I've been tinkering with the first callback, the rest of the code works fine.
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)
Alright so easy fix, nothing a few print() test statements can't fix lol. Essentially depending on the type of callback input you use for Input('machine_spec_table','data')
Here I used 'data', there are plenty of others available and explained in documentation,
the input to the function is as follows:
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}
]
A list of dictionaries! Easy to access, something along the lines of table[0]['Values]
does just fine :) (0 being a the list index,'Values' the dictionary key).
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.