简体   繁体   中英

how to alter a dash_table.DataTable cell and update other callbacks

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:

  • use a callback to define these fixed variable values. And when the table is changed, so are these "fixed" variables,..
  • or, use a callback to search the table and if changes are made, update the fixed variable value(s).

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM