Styling Graph Nodes & Edges in Cytoscape Based On The Weight Attribute

I’m trying to use dash-cytoscape to visualize network connections, and in order to do so I’m trying to adjust the value of the weight key in the data argument using callbacks.

The idea is that if someone selects a node, adjacent edges or nodes will have the value of weight change to determine if they’re a neighboring connection or not. Then I’ll use a selector to change that section’s opacity to make it fade out.

I’m trying to go off the documentation listed here: https://dash.plotly.com/cytoscape

Here’s some sample code:

import dash
import dash_cytoscape as cyto
import dash_html_components as html
from dash.dependencies import Input, Output

app = dash.Dash(__name__)

health_states = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H']
graph_data = {'A': ['B', 'F'],
          'B': ['C'],
          'C': ['D', 'E'],
          'D': ['A'],
          'E': ['G', 'H'],
          'F' : ['A', 'H']}
 # create nodes for graph
 nodes = [{'data': {'id': str(key), 'label': str(key), 'weight': 1}} for key in health_states]
# create edges
edges = [{'data': {'source': str(key), 'target': str(item), 'weight': 0}} for key in 
graph_data.keys() for item in graph_data[key]]

app.layout = html.Div([
cyto.Cytoscape(
    id='cytoscape-two-nodes',
    layout={'name': 'circle'},
    style={'width': '80%', 'height': '400px'},
    elements= nodes + edges,
    stylesheet=[
            {'selector': 'node',
             'style'   : {'opacity': 0.7, 'content': 'data(label)'}},
            {'selector': '[weight == 0]',
             'style': {'opacity': 0.2}}
            ]),
  html.Pre(id='output')
])

def find_connected_nodes(clicked_id):
  try:
    edges = graph_data[clicked_id]
    return edges
  except KeyError:
    return []

@app.callback([Output('cytoscape-two-nodes', 'elements'),
           Output('cytoscape-two-nodes', 'stylesheet')], 
          [Input('cytoscape-two-nodes', 'tapNodeData')])
def update_graph(node_val):
  existing_nodes = [{'data': {'id': str(key), 'label': str(key)}} for key in health_states]
  existing_edges = [{'data': {'source': str(key), 'target': str(item), 'weight': 0}} for key in 
graph_data.keys() for item in graph_data[key]]
current_elements = existing_nodes + existing_edges
current_styles = [
            {'selector': 'node',
             'style'   : {'opacity': 0.9, 'content': 'data(label)'}},
            {'selector': '[weight == 0]',
             'style': {'opacity': 0.2}}
            ]
if node_val:
    graph_edges   = find_connected_nodes(node_val['id'])
    if graph_edges:
        new_nodes = [{'data': {'id': str(key), 'label': str(key)}} for key in health_states]
        new_edges = [{'data': {'source': str(key), 'target': str(item), 'weight': 1 if item in graph_edges else 0}} for key in graph_data.keys() for item in graph_data[key]]
        print(new_edges)
        new_elements = new_nodes + new_edges
        new_styles = [
                {'selector': '[weight == 0]',
                 'style': {'opacity': 0.3},
                 }
                ]
        return new_elements, new_styles
return current_elements, current_styles

if __name__ == '__main__':
  app.run_server(debug=False)

What I’m hoping to have happen is that when you click on a node, the neighboring edges fade out into the background. I’d like to also do this with the nodes as well, but for now I’d just like to get some of the basic logic working.

This is not returning error messages…there’s just no opacity change based off the value of weight.

Thank you for your help.

2 Likes