Hi @AIMPED
Thank you again for getting back to me. I have a few questions if that’s ok.
The main purpose of the app will be live monitoring with rows of machines, with multiple columns being updated/refreshed via MQTT messages.
I have a number of features I wish to include, the first being filtering the rows of machines.
What is the best practice when refreshing/updating multiple cells of the table via callbacks would you suggest a callback for each column or refresh the whole table?
i.e. The CPU usage changing but the status likely remaining the same
Do this hidden rows still refresh their values? Does this impact on performance?
Also, when the rows are unhidden what value would they show? The last updated value before being filtered? But I assume I could handle this by updating the pandas dataframe and querying it.
They certainly look a lot cleaner when passing in multiple ids rather than my list comprehension solution.
I guess they work the same when used as an input. Is there a best practice for only updating a value when it changes rather than updating all values at the same time?
Thank you for your code and helpful comments. I split the callback so the location filtering and status updating were separate, this made it so the status now refresh on app load and the location doesn’t require in interval input.
What do you think?
import dash
from dash import html, dcc, Input, Output, ALL, ctx
import dash_bootstrap_components as dbc
import pandas as pd
import uuid
data = {
"Location": ["Europe", "Europe", "Asia", "Europe", "America", "Asia", "Europe"],
"Name": [f"Test-PC-{number}" for number in range(7)],
"Status": [str(uuid.uuid4()) for _ in range(7)],
}
machines = pd.DataFrame(data)
locations = machines.Location.unique()
machine_names = machines.Name.unique()
initial_state = {name: state for name, state in zip(machine_names, machines.Status)}
dropdown = dbc.DropdownMenu(
id="location_selector",
label="Location",
children=[dbc.DropdownMenuItem(location, id=location) for location in locations]
)
table_header = [
html.Thead(html.Tr(
[html.Th("Name"), html.Th("Status")]))
]
def extract_indices(m_names):
return [int(num[-1]) for num in m_names]
def all_machines():
table_body = create_rows(machine_names)
return table_header + table_body
def filtered_by(location):
if location == 'Location':
return machines.Name
else:
return machines.Name.loc[(machines['Location'] == location)]
def create_rows(m_list):
# extract index from name
indices = extract_indices(m_list)
# set variable
rows = []
for idx, name in zip(indices, m_list):
row = html.Tr(
id={'type': 'row', 'index': idx},
children=[
html.Td(name, id={'type': 'Test-PC-', 'index': idx}),
html.Td(initial_state[name], id={'type': 'status_Test-PC-', 'index': idx})
]
)
rows.append(row)
table_body = [html.Tbody(rows)]
return table_body
# create table
table_content = all_machines()
table = dbc.Table(table_content, id="summary_table", bordered=True, hover=True, striped=True)
app = dash.Dash(external_stylesheets=[dbc.themes.BOOTSTRAP])
app.layout = dbc.Container(
[
dcc.Interval(id='update', n_intervals=0, interval=1000 * 5),
dropdown,
html.Br(),
table,
]
)
@app.callback(
Output('location_selector', 'label'),
[Input(location, 'n_clicks') for location in locations]
)
def update_dropdown(*args):
trigger = ctx.triggered_id
if args is None or not trigger:
return "Location"
return trigger
@app.callback(
Output({'type': 'row', 'index': ALL}, 'hidden'),
Input("location_selector", 'label'),
)
def update_filter(selected_location):
# get the machine names corresponding to the location
names = filtered_by(selected_location)
# index is the last char of the machine name
indices = extract_indices(names)
# set the current status to initial values
current_status = [initial_state[name] for name in machine_names]
# prepare list for row visibility
hide_rows = [True for _ in machine_names]
for idx, item in enumerate(current_status):
if idx in indices:
hide_rows[idx] = False
# this is actually just for startup: show all rows
if selected_location == 'Location':
hide_rows = [False for _ in hide_rows]
return hide_rows
@app.callback(
Output({'type': 'status_Test-PC-', 'index': ALL}, 'children'),
Input('update', 'n_intervals'),
Input("location_selector", 'label'),
)
def update_status(timer, location):
# get the machine names corresponding to the location
names = filtered_by(location)
# index is the last char of the machine name
indices = extract_indices(names)
# set the current status to initial values
current_status = [initial_state[name] for name in machine_names]
for idx, item in enumerate(current_status):
if idx in indices:
current_status[idx] = str(uuid.uuid4())
return current_status
if __name__ == '__main__':
app.run_server(debug=True)
Thank you once again for your time and continued help, it is greatly appreciated.