Cytoscape
Element
  • layout
  • data, contains 'id' and 'label'
  • position, contains 'x' and 'y'
  • locked, lock node, can be selected and moved or not
  • selected, select node, has been selected or not
  • selectable, decide if the node is selectable
  • grabbable, decide if the node is movable
  • classes, define classes for each node
  • stylesheet, define a stylesheet
  • import dash
    import dash_cytoscape as cyto
    import dash_html_components as html
    
    app = dash.Dash(__name__)
    
    my_stylesheet = [
        # Group selectors
        {
            'selector': 'node',
            'style': {
                'content': 'data(label)'
            }
        },
    
        # Class selectors
        {
            'selector': '.red',
            'style': {
                'background-color': 'red',
                'line-color': 'red'
            }
        },
        {
            'selector': '.triangle',
            'style': {
                'shape': 'triangle'
            }
        },
    
        # ID selector
        {
            'selector': '#edgeid',
            'style': {
                'line-color': 'yellow'
            }
        },
    ]
    
    app.layout = html.Div([
        cyto.Cytoscape(
        id='cytoscape-elements-classes',
        layout={'name': 'preset'},
        style={'width': '100%', 'height': '400px'},
        stylesheet=my_stylesheet,
        elements=[
            {
                'data': {'id': 'one', 'label': 'Modified Color'},
                'position': {'x': 75, 'y': 75},
                'classes': 'red' # Single class
            },
            {
                'data': {'id': 'two', 'label': 'Modified Shape'},
                'position': {'x': 75, 'y': 200},
                'classes': 'triangle' # Single class
            },
            {
                'data': {'id': 'three', 'label': 'Both Modified'},
                'position': {'x': 200, 'y': 75},
                'classes': 'red triangle' # Multiple classes
            },
            {
                'data': {'id': 'four', 'label': 'Regular'},
                'position': {'x': 200, 'y': 200}
            },
            {'data': {'source': 'one', 'target': 'two'}, 'classes': 'red'},
            {'data': {'source': 'two', 'target': 'three'}},
            {'data': {'source': 'three', 'target': 'four'}, 'classes': 'red'},
            {'data': {'id': 'edgeid', 'source': 'two', 'target': 'four'}},
        ]
    )
    ])
    
    if __name__ == '__main__':
        app.run_server(debug=True)
    		
    Compound Nodes
  • compound nodes are nodes that contain (parent), or are contained (child) inside another node
  • A parent node does not have have a position nor a size, since those values are automatically calculated based on how the children nodes are configured
  • # Parent Nodes
    {
        'data': {'id': 'us', 'label': 'United States'}
    },
    # Children Nodes
    {
        'data': {'id': 'nyc', 'label': 'New York', 'parent': 'us'},
        'position': {'x': 100, 'y': 100}
    }
    		
    Layout
  • null, puts all nodes at (0, 0)
  • preset
  • circle, radius, startAngle, sweep
  • grid, rows, cols
  • breadthfirst, roots
  • external layouts
  • # Load extra layouts
    cyto.load_extra_layouts()
    		
    import dash
    import dash_cytoscape as cyto
    import dash_html_components as html
    
    app = dash.Dash(__name__)
    
    nodes = [
        {
            'data': {'id': short, 'label': label},
            'position': {'x': 20*lat, 'y': -20*long}
        }
        for short, label, long, lat in (
            ('la', 'Los Angeles', 34.03, -118.25),
            ('nyc', 'New York', 40.71, -74),
            ('to', 'Toronto', 43.65, -79.38),
            ('mtl', 'Montreal', 45.50, -73.57),
            ('van', 'Vancouver', 49.28, -123.12),
            ('chi', 'Chicago', 41.88, -87.63),
            ('bos', 'Boston', 42.36, -71.06),
            ('hou', 'Houston', 29.76, -95.37)
        )
    ]
    
    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')
        )
    ]
    
    elements = nodes + edges
    
    app.layout = html.Div([
        cyto.Cytoscape(
            id='cytoscape-compound',
    	layout={'name': 'breadthfirst', 'roots': '#nyc, #hou'},
            style={'width': '100%', 'height': '450px'},
            elements=elements
            )
    ])
    
    if __name__ == '__main__':
        app.run_server(debug=True)
    		
    Styling
  • stylesheet
  • content, value to display above or next to the element on the screen
    import dash
    import dash_cytoscape as cyto
    import dash_html_components as html
    import math
    
    app = dash.Dash(__name__)
    
    weighted_elements = [
        {'data': {'id': 'A'}},
        {'data': {'id': 'B'}},
        {'data': {'id': 'C'}},
        {'data': {'id': 'D'}},
        {'data': {'id': 'E'}},
    
        {'data': {'source': 'A', 'target': 'B', 'weight': 1}},
        {'data': {'source': 'A', 'target': 'C', 'weight': 2}},
        {'data': {'source': 'B', 'target': 'D', 'weight': 3}},
        {'data': {'source': 'B', 'target': 'E', 'weight': 4}},
        {'data': {'source': 'C', 'target': 'E', 'weight': 5}},
        {'data': {'source': 'D', 'target': 'A', 'weight': 6}}
    ]
    
    cyto.load_extra_layouts()
    
    app.layout = html.Div([
        cyto.Cytoscape(
        id='cytoscape-styling-2',
        layout={'name': 'circle'},
        style={'width': '100%', 'height': '400px'},
        elements=weighted_elements,
        stylesheet=[
            {
                'selector': 'edge',
                'style': {
                    'label': 'data(weight)'
                }
            },
            {
                'selector': '[weight > 3]',
                'style': {
                    'line-color': 'blue'
                }
            }
        ]
        )
    ])
    
    if __name__ == '__main__':
        app.run_server(debug=True)
    		
  • import dash
    import dash_cytoscape as cyto
    import dash_html_components as html
    import math
    
    app = dash.Dash(__name__)
    
    directed_edges = [
        {'data': {'id': src+tgt, 'source': src, 'target': tgt}}
        for src, tgt in ['BA', 'BC', 'CD', 'DA']
    ]
    
    directed_elements = [{'data': {'id': id_}} for id_ in 'ABCD'] + directed_edges
    
    app.layout = html.Div([
        cyto.Cytoscape(
        id='cytoscape-styling-2',
        layout={'name': 'circle'},
        style={'width': '100%', 'height': '400px'},
        elements=directed_elements,
        stylesheet=[
            {
                'selector': 'node',
                'style': {
                    'label': 'data(id)'
                }
            },
            {
                'selector': 'edge',
                'style': {
                    # The default curve style does not work with certain arrows
                    'curve-style': 'bezier'
                }
            },
            {
                'selector': '#BA',
                'style': {
                    'source-arrow-color': 'red',
                    'source-arrow-shape': 'triangle',
                    'line-color': 'red'
                }
            },
            {
                'selector': '#DA',
                'style': {
                    'target-arrow-color': 'blue',
                    'target-arrow-shape': 'vee',
                    'line-color': 'blue'
                }
            },
            {
                'selector': '#BC',
                'style': {
                    'mid-source-arrow-color': 'green',
                    'mid-source-arrow-shape': 'diamond',
                    'mid-source-arrow-fill': 'hollow',
                    'line-color': 'green',
                }
            },
            {
                'selector': '#CD',
                'style': {
                    'mid-target-arrow-color': 'black',
                    'mid-target-arrow-shape': 'circle',
                    'arrow-scale': 2,
                    'line-color': 'black',
                }
            }
        ]
        )
    ])
    
    if __name__ == '__main__':
        app.run_server(debug=True)
    		
    Callbacks
    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__)
    
    nodes = [
        {
            'data': {'id': short, 'label': label},
            'position': {'x': 20*lat, 'y': -20*long}
        }
        for short, label, long, lat in (
            ('la', 'Los Angeles', 34.03, -118.25),
            ('nyc', 'New York', 40.71, -74),
            ('to', 'Toronto', 43.65, -79.38),
            ('mtl', 'Montreal', 45.50, -73.57),
            ('van', 'Vancouver', 49.28, -123.12),
            ('chi', 'Chicago', 41.88, -87.63),
            ('bos', 'Boston', 42.36, -71.06),
            ('hou', 'Houston', 29.76, -95.37)
        )
    ]
    
    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')
        )
    ]
    
    elements = nodes + edges
    
    app.layout = html.Div([
        cyto.Cytoscape(
        id='cytoscape-update-layout',
        elements=elements,
        style={'width': '100%', 'height': '400px'},
        layout={
            'name': 'grid'
        }
        ),
        dcc.Dropdown(
        id='dropdown-update-layout',
        value='grid',
        clearable=False,
        options=[
            {'label': name.capitalize(), 'value': name}
            for name in ['grid', 'random', 'circle', 'cose', 'concentric']
        ]
        )
    ])
    
    @app.callback(Output('cytoscape-update-layout', 'layout'),
                  Input('dropdown-update-layout', 'value'))
    def update_layout(layout):
        return {
            'name': layout,
            'animate': True
        }
    
    if __name__ == '__main__':
        app.run_server(debug=True)
    		
    Interactions
    import json
    
    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
    from dash.exceptions import PreventUpdate
    
    app = dash.Dash(__name__)
    
    styles = {
        'pre': {
            'border': 'thin lightgrey solid',
            'overflowX': 'scroll'
        }
    }
    
    
    nodes = [
        {
            'data': {'id': short, 'label': label},
            'position': {'x': 20*lat, 'y': -20*long}
        }
        for short, label, long, lat in (
            ('la', 'Los Angeles', 34.03, -118.25),
            ('nyc', 'New York', 40.71, -74),
            ('to', 'Toronto', 43.65, -79.38),
            ('mtl', 'Montreal', 45.50, -73.57),
            ('van', 'Vancouver', 49.28, -123.12),
            ('chi', 'Chicago', 41.88, -87.63),
            ('bos', 'Boston', 42.36, -71.06),
            ('hou', 'Houston', 29.76, -95.37)
        )
    ]
    
    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-event-callbacks-1',
            layout={'name': 'preset'},
            elements=edges+nodes,
            stylesheet=default_stylesheet,
            style={'width': '100%', 'height': '450px'}
        ),
        html.Pre(id='cytoscape-tapNodeData-json', style=styles['pre'])
    ])
    
    
    @app.callback(Output('cytoscape-tapNodeData-json', 'children'),
                  Input('cytoscape-event-callbacks-1', 'tapNodeData'))
    def displayTapNodeData(data):
        if data is None:
            raise PreventUpdate
        return data['id']+' '+data['label']
    
    
    if __name__ == '__main__':
        app.run_server(debug=True)
    		
    Selecting Multiple Elements
  • Select by shift+click or drag
  • import json
    
    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
    from dash.exceptions import PreventUpdate
    
    app = dash.Dash(__name__)
    
    styles = {
        'pre': {
            'border': 'thin lightgrey solid',
            'overflowX': 'scroll'
        }
    }
    
    
    nodes = [
        {
            'data': {'id': short, 'label': label},
            'position': {'x': 20*lat, 'y': -20*long}
        }
        for short, label, long, lat in (
            ('la', 'Los Angeles', 34.03, -118.25),
            ('nyc', 'New York', 40.71, -74),
            ('to', 'Toronto', 43.65, -79.38),
            ('mtl', 'Montreal', 45.50, -73.57),
            ('van', 'Vancouver', 49.28, -123.12),
            ('chi', 'Chicago', 41.88, -87.63),
            ('bos', 'Boston', 42.36, -71.06),
            ('hou', 'Houston', 29.76, -95.37)
        )
    ]
    
    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-event-callbacks-1',
            layout={'name': 'preset'},
            elements=edges+nodes,
            stylesheet=default_stylesheet,
            style={'width': '100%', 'height': '450px'}
        ),
        html.Div(id='output')
    ])
    
    
    @app.callback(Output('output', 'children'),
                  Input('cytoscape-event-callbacks-1', 'selectedNodeData'))
    def displayTapNodeData(data_list):
        if data_list is None:
            raise PreventUpdate
        return str(data_list)
    
    if __name__ == '__main__':
        app.run_server(debug=True)
    		
    Reference
  • Cytoscape Manual
  • Cytoscape Reference
  • Layout
  • Cytoscape.js
  • Dash Cytoscape