Cytoscape
Element
layout
- present, organize the nodes according to the positions specified
- random
- grid
- circle
- concentric
- breadthfirst
- cose
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
- Group selector
- node, for nodes
- edge, for edges
- *, for all
- Class selector
- matches elements that have the specified class
- ID selector
- #id, matches element with the matching ID
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
- cose-bilkent, cola, euler, spread, dagre, klay
# 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
- 'selector', select nodes
- 'selector': '[weight > 3]',
- 'selector': '[firstname *= "ert"]', 'firstname' contains the substring 'ert'
- 'selector': '[firstname !*= "ert"]', 'firstname' does not contain 'ert'
- 'selector': '[firstname ^= "Alb"]', select the prefix
- 'selector': '[firstname @^= "alb"]', matched case insensitive
- 'style', width, height, color of a node, shape of the arrow on an edge, or many more
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