cytoscape / ipycytoscape

A Cytoscape Jupyter widget
https://ipycytoscape.readthedocs.io/en/master/
BSD 3-Clause "New" or "Revised" License
269 stars 62 forks source link

Add a default style for directed graphs #48

Closed marimeireles closed 4 years ago

marimeireles commented 4 years ago

As suggested by @ianhi in https://github.com/QuantStack/ipycytoscape/issues/46 in his second point.

One idea is to add a flag that can be activated if the graph is directed in the add_graphs methods:

https://github.com/QuantStack/ipycytoscape/blob/59e3440d8a77efb3ecae4867e1992d400be5b96c/ipycytoscape/cytoscape.py#L184

https://github.com/QuantStack/ipycytoscape/blob/59e3440d8a77efb3ecae4867e1992d400be5b96c/ipycytoscape/cytoscape.py#L204

https://github.com/QuantStack/ipycytoscape/blob/59e3440d8a77efb3ecae4867e1992d400be5b96c/ipycytoscape/cytoscape.py#L225

Once this flag is activated it calls a function that changes the style of the graph, you can either use the set_style() or copy the style in the dagre example and apply it to the graph.

MridulS commented 4 years ago

A design choice: should the directed be a graph property or an edge property? https://github.com/QuantStack/ipycytoscape/pull/62 assumes it as a graph property right now.

By being a graph property we can have 2 types of graphs, undirected and directed. By being an edge property, this will also work for mixed graph (undirected and directed edges in one single graph).

ianhi commented 4 years ago

:+1: to mixed graphs! Edge properties seems like a great way to go towards that end.

I poked around a bit and it seems as though using the classes syntax in cytoscapejs would be a good way to do this. i.e. use Edge(classes='directed') and add use the selector .directed in the style. (Beware that to use selectors based on classes you need the changes in https://github.com/QuantStack/ipycytoscape/pull/63)

Nodes and edges in networkx can have data associated with them in a dictionary which is accessible by passing data=True to the call to edges() or nodes(). If the styling is accomplished by a classes selector then the add_graph_from_* functions could all look for the classes attribute of data and add it to the Edge or Node. This would give the user greater control of styling of the minutiae of styling if desired. This diff may be a start in that direction:

diff --git a/ipycytoscape/cytoscape.py b/ipycytoscape/cytoscape.py
index 8e1a2a6..67738f0 100644
--- a/ipycytoscape/cytoscape.py
+++ b/ipycytoscape/cytoscape.py
@@ -191,14 +191,18 @@ class Graph(Widget):
             receives a generic NetworkX graph. more info in
             https://networkx.github.io/documentation/
         """
-        for node in g.nodes():
+        for node, data in g.nodes(data=True):
             node_instance = Node()
-            node_instance.data = {'id': int(node)}
+            node_instance.data = {'id': node}
+            if 'classes' in data:
+                node_instance.classes = data['classes']
             self.nodes.append(node_instance)

-        for edge in g.edges():
+        for source, target, edge in g.edges(data=True):
             edge_instance = Edge()
-            edge_instance.data = {'source': edge[0],'target': edge[1]}
+            edge_instance.data = {'source': source, 'target': target}
+            if 'classes' in data:
+                edge_instance.classes = data['classes']
             self.edges.append(edge_instance)

     def add_graph_from_json(self, json_file):

But it would still be good to add a directed argument, to ensure the class is added.

@MridulS I think you are more knowledgeable about networkx than I am, is there an easy way to add to the data of nodes/edges after they've been created? I was moderately frustrated trying to use G = nx.complete_graph(5) and then trying to modify the data of individual nodes and edges after that.