Cytoscape Elements

Element Declaration

Each element is defined by a dictionary declaring its purpose and describing its properties. Usually, you specify what group the element belongs to (i.e., if it's a node or an edge), indicate what position you want to give to your element (if it's a node), or what data it contains. In fact, the data and position keys are themselves mapped to dictionaries, where each item specify an aspect of the data or position.

In the case of data, the typical keys fed to the dictionaries are:

  • id: The index of the element, useful when you want to reference it
  • label: The name associated with the element if you wish to display it

If your element is an edge, the following keys are required in your data dictionary:

  • source: The id of the source node, which is where the edge starts
  • target: The id of the target node, where the edge ends

The position dictionary takes as items the x and y position of the node. If you use any other layout than preset, or if the element is an edge, the position item will be ignored.

If we want a graph with two nodes, and an edge connecting those two nodes, we effectively need three of those element dictionaries, grouped as a list:

using Dash, DashCytoscape

app = dash()

app.layout = cyto_cytoscape(
    id="cytoscape-elements-basic",
    layout=Dict("name" => "preset"),
    style=Dict("width" => "100%", "height" => "400px"),
    elements=[
        # The nodes elements
        Dict("data" => Dict("id" => "one", "label" => "Node 1"),
         "position" => Dict("x" => 50, "y" => 50)),
        Dict("data" => Dict("id" => "two", "label" => "Node 2"),
         "position" => Dict("x" => 200, "y" => 200)),

        # The edge elements
        Dict("data" => Dict("source" => "one", "target" => "two", "label" => "Node 1 to 2"))
    ]
)

run_server(app, "0.0.0.0", debug=true)

Notice that we also need to specify the id, the layout, and the style of Cytoscape. The id parameter is needed for assigning callbacks, style lets you specify the CSS style of the component (similarly to core components), and layout tells you how to arrange your graph. It is described in depth in part 2, so all you need to know is that 'preset' will organize the nodes according to the positions you specified.

The official Cytoscape.js documentation nicely outlines the JSON format for declaring elements.

Boolean Properties

In addition to the properties presented above, the element dictionary can also accept boolean items that specify its state. We extend the previous example in the following way:

using Dash, DashCytoscape
app = dash()

app.layout = cyto_cytoscape(
    id="cytoscape-elements-boolean",
    layout=Dict("name" =>  "preset"),
    style=Dict("width" =>  "100%", "height" =>  "400px"),
    elements=[
        Dict(
            "data" =>  Dict("id" =>  "one", "label" =>  "Locked"),
            "position" =>  Dict("x" =>  75, "y" =>  75),
            "locked" =>  true
        ),
        Dict(
            "data" =>  Dict("id" =>  "two", "label" =>  "Selected"),
            "position" =>  Dict("x" =>  75, "y" =>  200),
            "selected" =>  true
        ),
        Dict(
            "data" =>  Dict("id" =>  "three", "label" =>  "Not Selectable"),
            "position" =>  Dict("x" =>  200, "y" =>  75),
            "selectable" =>  false
        ),
        Dict(
            "data" =>  Dict("id" =>  "four", "label" =>  "Not grabbable"),
            "position" =>  Dict("x" =>  200, "y" =>  200),
            "grabbable" =>  false
        ),
        Dict("data" =>  Dict("source" =>  "one", "target" =>  "two")),
        Dict("data" =>  Dict("source" =>  "two", "target" =>  "three")),
        Dict("data" =>  Dict("source" =>  "three", "target" =>  "four")),
        Dict("data" =>  Dict("source" =>  "two", "target" =>  "four")),
    ]
)

run_server(app, "0.0.0.0", debug=true)

Note that those boolean properties can be overwritten by certain Cytoscape parameters such as autoungrabify or autounselectify. Please refer to the reference for more information.

Classes

Similarly to CSS classes, element classes are used to style groups of elements using a selector. We modify the previous example by giving the elements a class or multiple classes (separated by a space), and define a stylesheet that modifies the elements based on those classes.

using Dash, DashCytoscape

app = dash()

my_stylesheet = [
    # Group selectors
    Dict(
        "selector" =>  "node",
        "style" =>  Dict(
            "content" =>  "data(label)"
        )
    ),

    # Class selectors
    Dict(
        "selector" =>  ".red",
        "style" =>  Dict(
            "background-color" =>  "red",
            "line-color" =>  "red"
        )
    ),
    Dict(
        "selector" =>  ".triangle",
        "style" =>  Dict(
            "shape" =>  "triangle"
        )
    )
]

app.layout = cyto_cytoscape(
    id="cytoscape-elements-classes",
    layout=Dict("name" =>  "preset"),
    style=Dict("width" =>  "100%", "height" =>  "400px"),
    stylesheet=my_stylesheet,
    elements=[
        Dict(
            "data" =>  Dict("id" =>  "one", "label" =>  "Modified Color"),
            "position" =>  Dict("x" =>  75, "y" =>  75),
            "classes" =>  "red" # Single class
        ),
        Dict(
            "data" =>  Dict("id" =>  "two", "label" =>  "Modified Shape"),
            "position" =>  Dict("x" =>  75, "y" =>  200),
            "classes" =>  "triangle" # Single class
        ),
        Dict(
            "data" =>  Dict("id" =>  "three", "label" =>  "Both Modified"),
            "position" =>  Dict("x" =>  200, "y" =>  75),
            "classes" =>  "red triangle" # Multiple classes
        ),
        Dict(
            "data" =>  Dict("id" =>  "four", "label" =>  "Regular"),
            "position" =>  Dict("x" =>  200, "y" =>  200)
        ),
        Dict("data" =>  Dict("source" =>  "one", "target" =>  "two"), "classes" =>  "red"),
        Dict("data" =>  Dict("source" =>  "two", "target" =>  "three")),
        Dict("data" =>  Dict("source" =>  "three", "target" =>  "four"), "classes" =>  "red"),
        Dict("data" =>  Dict("source" =>  "two", "target" =>  "four")),
    ]
)

run_server(app, "0.0.0.0", debug=true)

The stylesheet parameter will be described in depth in part 3 of this guide. We will show extensive examples of using selectors to style groups, classes, and data values. Expand below if you still want to take a look at the stylesheet used previously.

View the Stylesheet

my_stylesheet = [
    # Group selectors
    Dict(
        "selector" =>  "node",
        "style" =>  Dict(
            "content" =>  "data(label)"
        )
    ),

    # Class selectors
    Dict(
        "selector" =>  ".red",
        "style" =>  Dict(
            "background-color" =>  "red",
            "line-color" =>  "red"
        )
    ),
    Dict(
        "selector" =>  ".triangle",
        "style" =>  Dict(
            "shape" =>  "triangle"
        )
    )
]

Compound Nodes

A concept introduced in Cytoscape.js, 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.

Here is the example of an app using compound nodes:

using Dash, DashCytoscape

app = dash()

app.layout = html_div([
    cyto_cytoscape(
        id="cytoscape-compound",
        layout=Dict("name" =>  "preset"),
        style=Dict("width" =>  "100%", "height" =>  "450px"),
        stylesheet=[
            Dict(
                "selector" =>  "node",
                "style" =>  Dict("content" =>  "data(label)")
            ),
            Dict(
                "selector" =>  ".countries",
                "style" =>  Dict("width" =>  5)
            ),
            Dict(
                "selector" =>  ".cities",
                "style" =>  Dict("line-style" =>  "dashed")
            )
        ],
        elements=[
            # Parent Nodes
            Dict(
                "data" =>  Dict("id" =>  "us", "label" =>  "United States")
            ),
            Dict(
                "data" =>  Dict("id" =>  "can", "label" =>  "Canada")
            ),

            # Children Nodes
            Dict(
                "data" =>  Dict("id" =>  "nyc", "label" =>  "New York", "parent" =>  "us"),
                "position" =>  Dict("x" =>  100, "y" =>  100)
            ),
            Dict(
                "data" =>  Dict("id" =>  "sf", "label" =>  "San Francisco", "parent" =>  "us"),
                "position" =>  Dict("x" =>  100, "y" =>  200)
            ),
            Dict(
                "data" =>  Dict("id" =>  "mtl", "label" =>  "Montreal", "parent" =>  "can"),
                "position" =>  Dict("x" =>  400, "y" =>  100)
            ),

            # Edges
            Dict(
                "data" =>  Dict("source" =>  "can", "target" =>  "us"),
                "classes" =>  "countries"
            ),
            Dict(
                "data" =>  Dict("source" =>  "nyc", "target" =>  "sf"),
                "classes" =>  "cities"
            ),
            Dict(
                "data" =>  Dict("source" =>  "sf", "target" =>  "mtl"),
                "classes" =>  "cities"
            )
        ]
    )
])

run_server(app, "0.0.0.0", debug=true)