I am very new to Dash and am trying to create a directed graph and allow the user to highlight descendants and ancestors paths from selected nodes. My end result will have two graphs next to each connected to the same dropdowns, but below is the code for one. How would I highlight the paths selected in the existing graph?
Ideally the user would click the node, but i’ll get there later, I hope, for now I’m trying to figure out how to highlight the selected paths, is this something anyone can shed any light on? Sorry for the sloppy code and thank you in advance.
import plotly.graph_objects as go
import networkx as nx
import dash
from dash import Dash, html, dcc, Input, Output
import pandas as pd
from addEdge import addEdge
# Controls for how the graph is drawn
nodeColor = 'Pink'
nodeSize = 20
lineWidth = 2
lineColor = '#000000'
df_dict= {'From': {0: 'A', 1: 'B', 2: 'C', 3: 'A', 4: 'B', 5: 'C'}, 'To': {0: 'B', 1: 'C', 2: 'D', 3: 'A1', 4: 'B1', 5: 'C1'}, 'weight': {0: 1.0, 1: 1.0, 2: 1.0, 3: 0.5, 4: 0.5, 5: 0.5}, 'Cost': {0: 1, 1: 1, 2: 1, 3: 2, 4: 2, 5: 2}}
df = pd.DataFrame.from_dict(df_dict)
G=nx.from_pandas_edgelist(df, "From", 'To', ['weight', 'Cost'], create_using=nx.DiGraph())
pos = nx.layout.spring_layout(G)
for node in G.nodes:
G.nodes[node]['pos'] = list(pos[node])
# Make list of nodes for plotly
node_x = []
node_y = []
text = []
for node in G.nodes():
x, y = G.nodes[node]['pos']
node_x.append(x)
node_y.append(y)
text.append(node)
edge_x = []
edge_y = []
for edge in G.edges():
start = G.nodes[edge[0]]['pos']
end = G.nodes[edge[1]]['pos']
edge_x, edge_y = addEdge(start, end, edge_x, edge_y, .8, 'end', .04, 30, nodeSize)
edge_trace = go.Scatter(x=edge_x, y=edge_y, line=dict(width=lineWidth, color=lineColor), hoverinfo='none', mode='lines', text=text)
node_trace = go.Scatter(x=node_x, y=node_y, text=text,mode='markers+text', hoverinfo='text', marker=dict(showscale=False, color = nodeColor, size=nodeSize))
fig = go.Figure(data=[edge_trace, node_trace],
layout=go.Layout(
showlegend=False,
hovermode='closest',
margin=dict(b=20,l=5,r=5,t=40),
xaxis=dict(showgrid=False, zeroline=False, showticklabels=False),
yaxis=dict(showgrid=False, zeroline=False, showticklabels=False))
)
# Note: if you don't use fixed ratio axes, the arrows won't be symmetrical
fig.update_layout(yaxis = dict(scaleanchor = "x", scaleratio = 1), plot_bgcolor='rgb(255,255,255)')
app = dash.Dash()
app.layout = html.Div([
html.Div([
html.H1(children='Hello Dash'),
html.Div(children='''
Directed Water and Orange Juice Graph.
'''),
html.Div( children=[
dcc.Dropdown([{'label': 'Connected', 'value': 'con'},{'label': 'Ascending', 'value': 'asc'},{'label': 'Descending', 'value': 'desc'}], '', id='typ-dropdown'),
html.Div(id='dd-typ-container'),
html.Div(html.Br()),
dcc.Dropdown([{'label': 'Node A', 'value': 0},{'label': 'Node B', 'value': 1},{'label': 'Node C', 'value': 2},{'label': 'Node D', 'value': 3},{'label': 'Node A1', 'value': 4},{'label': 'Node B1', 'value': 5},{'label': 'Node C1', 'value': 6}], '', id='node-dropdown'),
html.Div(id='dd-out-container')
]),
html.Div([
html.H3('Water Network'),
dcc.Graph(id='example-graph1', figure=fig),
# dcc.Graph(id='example-graph', figure=fig)
], className="six columns"),
html.Div([
# dcc.Graph(id='example-graph2', figure=fig)
], className="six columns"),
], className="row")
])
# @app.callback(
# Output('dd-out-container', 'children'),
# Input('fig-dropdown', 'value')
# )
# def update_output(value):
# return f'you have selected {value}'
@app.callback(
Output('example-graph1', 'children'),
Input('typ-dropdown', 'value'),
Input('node-dropdown', 'value')
)
def connected_graph(contyp, value):
desc = nx.descendants(G,value)
anc = nx.ancestors(G,value)
if contyp == 'desc':
paths = [path for p in desc for path in nx.all_simple_paths(G,value,p)]
elif contyp == 'anc':
paths = [path for p in anc for path in nx.all_simple_paths(G,value,p)]
elif contyp == 'con_':
paths = [path for p in desc for path in nx.all_simple_paths(G,value,p)]
paths.extend([path for p in anc for path in nx.all_simple_paths(G,p,value)])
Q = nx.DiGraph()
for p in paths:
nx.add_path(Q,p)
return Q
app.run_server(debug=True)