Search code examples
pythonnetworkxplotly-dashplotly-pythondash-cytoscape

Correct callback for networkx dash connectivity


I am trying to create a simple networkx and dash dashboard with a dropdown to select either successors, predecessors, or connected then when I click on a node it will return that info.

For example, if I select predecessors and then click on Texas, it will provide US, but if I select successors and click on it, it will show Houston.

If I select connected and click on Texas, it will respond with US and Houston.

Would anyone know the correct callback function that I would need to create to accomplish this?

import dash
import dash_core_components as dcc
import dash_html_components as html
import dash_cytoscape as cyto
from dash.dependencies import Input, Output
import plotly.express as px

app = dash.Dash(__name__)

app.layout = html.Div([
    html.P("Dash Cytoscape:"),
    cyto.Cytoscape(
        id='cytoscape',
        elements=[
            {'data': {'id': 'ca', 'label': 'Canada'}}, 
            {'data': {'id': 'on', 'label': 'Ontario'}}, 
            {'data': {'id': 'qc', 'label': 'Quebec'}},
            {'data': {'id': 'us', 'label': 'US'}},
            {'data': {'id': 'ny', 'label': 'New York'}},
            {'data': {'id': 'tx', 'label': 'Texas'}},
            {'data': {'id': 'fl', 'label': 'Florida'}},
            {'data': {'id': 'mia', 'label': 'Miami'}},
            {'data': {'id': 'hou', 'label': 'Houston'}},
            {'data': {'source': 'ca', 'target': 'on'}}, 
            {'data': {'source': 'ca', 'target': 'qc'}},
            {'data': {'source': 'us', 'target': 'ny'}},
            {'data': {'source': 'us', 'target': 'tx'}},
            {'data': {'source': 'us', 'target': 'fl'}},
            {'data': {'source': 'tx', 'target': 'hou'}},
            {'data': {'source': 'fl', 'target': 'mia'}}
        ],
        layout = {'name':'breadthfirst', 'directed':True},
           style={'width': '400px', 'height': '500px'}
    )
])



@app.callback(Output('cytoscape-tapNodeData-output', 'children'),
Input('cytoscape-event-callbacks-2', 'tapNodeData'))
def displayTapNodeData(data):
    if data:
        return 

app.run_server(debug=True)

Solution

  • I add the necessary callback to make the dropdown menu working along with the clicking on the nodes. You need only to manipulate how to extract the data from the list edges and nodes in the callback function, and it will be pure python problem, and you can use directly the edges and nodes list inside the callback function without passing them as parameters.

    import dash
    import dash_cytoscape as cyto
    import dash_html_components as html
    import dash_core_components as dcc
    from dash.dependencies import Input, Output
    
    app = dash.Dash(__name__)
    
    styles = {
        'pre': {
            'border': 'thin lightgrey solid',
            'overflowX': 'scroll'
        }
    }
    
    
    nodes = [
        {
            'data': {'id': short, 'label': label},
        }
        for short, label in (
            ('la', 'Los Angeles'),
            ('nyc', 'New York'),
            ('to', 'Toronto'),
            ('mtl', 'Montreal'),
            ('van', 'Vancouver'),
            ('chi', 'Chicago'),
            ('bos', 'Boston'),
            ('hou', 'Houston')
        )
    ]
    
    edges = [
        {'data': {'source': source, 'target': target}}
        for source, target in (
            ('van', 'la'),
            ('la', 'chi'),
            ('hou', 'chi'),
            ('to', 'mtl'),
            ('mtl', 'bos'),
            ('nyc', 'bos'),
            ('to', 'hou'),
            ('to', 'nyc'),
            ('la', 'nyc'),
            ('nyc', 'bos')
        )
    ]
    
    
    default_stylesheet = [
        {
            'selector': 'node',
            'style': {
                'background-color': '#BFD7B5',
                'label': 'data(label)'
            }
        }
    ]
    
    
    app.layout = html.Div([
        cyto.Cytoscape(
            id='cytoscape',
            layout={'name':'breadthfirst','directed':True},
            elements=edges+nodes,
            stylesheet=default_stylesheet,
            style={'width': '100%', 'height': '450px'}
        ),
        html.Div([
        dcc.Dropdown(['predecessors', 'successors', 'connected'], 'predecessors', id='cyto-dropdown')
        ]),
        html.Div(id='my-output'),
    ])
    
    
    @app.callback(Output('my-output', 'children'),
                  [Input('cyto-dropdown', 'value'),
                  Input('cytoscape', 'tapNodeData')])
    
    def displayTapNodeData(value, data):
        if data:
            if value == 'successors':
                return "The successor node(s): " + data['id']
            elif value == 'predecessors':
                return "The predecessor node(s) " + data['id']
            elif value == 'connected':
                return "The connected node(s): " + data['id']
    
    
    
    if __name__ == '__main__':
        app.run_server(debug=True, use_reloader=False)
    

    enter image description here