jsexauer / networkx_viewer

Interactive GUI for NetworkX graphs
GNU General Public License v3.0
137 stars 27 forks source link

NetworkX Viewer

Introduction

NetworkX Viewer provides a basic interactive GUI to view networkx graphs. In addition to standard plotting and layout features as found natively in networkx, the GUI allows you to:

A typical usage might be:

import networkx as nx
from networkx_viewer import Viewer

G = nx.MultiGraph()
G.add_edge('a','b')
G.add_edge('b','c')
G.add_edge('c','a',0, fill='green')
G.add_edge('c','d')
G.add_edge('c','d',1, dash=(2,2))
G.nodes['a']['outline'] = 'blue'
G.nodes['d']['label_fill'] = 'red'

app = Viewer(G)
app.mainloop()

The result will be:

NetworkX Viewer Window

Installation

NetworkX Viewer is hosted on PyPI and can be installed by simply doing the following.

pip install networkx-viewer

NetworkX Viewer requires NetworkX version 3.2 or greater.

Using the GUI

The default layout for the nodes is to use nx.spring_layout. While this layout is pretty good, it is not perfect, so the GUI supports standard features like rearranging the nodes, panning, and zooming.

By default, the viewer will display the entire graph on initialization. However, most of the power in the GUI comes in showing a subset of the graph. You can specify a subgraph to display using:

app = Viewer(G, home_node='a', levels=1)

Constructing a plot

On the right of the screen is a box to enter node(s) to graph.

You may either "Build New" or "Add to Existing." If you choose to add to the existing plot, and a path exists between the new node and your existing display island, you will be prompted if you'd like the program to plot the intermediate nodes.

Right-click functionality

Several actions can be taken by right-clicking on nodes and edges, including

You can also simply hover over a node and press the shortcut key ("G" for grow, "H" for hide, etc...) to activate the action.

Filtering

You can filter the nodes to display based on the attributes a node possess. This is done in a simmilar manner to how Grow Until works, as described above. You must write a lambda function which accepts the following paramaters:

When this lambda function evaluates to False, the node is hidden, otherwise the node is displayed. Multiple Filters are ANDed together.

Node and Edge Attributes

The attributes (ie, the dictionary stored in G.node[u] and G.edge[u][v]) are displayed in the lower-right section of the screen.

At this time, neither the attributes not the graph's nodes/edges themselves are editable through the GUI. The GUI is read-only. You should programatically create/update the graph by doing the following:

G = app.canvas.dataG
# code to edit graph
app.canvas.refresh()

Using the Tk Pass-through

If the data dictionary stored in the graph for an edge or node contains a key that can be used by Tk, the token will be customized as such. Specifically,

Expanding and Customizing the GUI

The core Tk widget that is implemented by networkx_viewer is the GraphCanvas widget. If you simply wish to use the GUI as presented as part of a larger application, you can just instantiate the canvas, passing it the graph to display as an argument and pack or grid it into your Tk application like any other canvas widget.

If you wish to change the tokens used for edges or nodes, subclass NodeToken or EdgeToken and pass as an argument into the GraphCanvas as such. For example:

import tkinter as tk
import networkx as nx
from networkx_viewer import NodeToken, GraphCanvas

class CustomNodeToken(NodeToken):
    def render(self, data, node_name):
        """Example of custom Node Token
        Draw a circle if the node's data says we are a circle, otherwise
        draw us as a rectangle.  Also, if data contains a color key,
        use that as our color (default, red)
        """
        # For our convenience, the render method is called with the
        #  graph's data attributes and the name of the node in the graph

        # Note that NodeToken is a subclass of Tkinter.Canvas, so we
        #  simply draw on ourselves to create the apperance for the node.

        # Make us 50 pixles big
        self.config(width=50, height=50)

        # Set color and other options
        marker_options = {'fill':       data.get('color','red'),
                          'outline':    'black'}

        # Draw circle or square, depending on what the node said to do
        if data.get('circle', None):
            self.create_oval(0,0,50,50, **marker_options)
        else:
            self.create_rectangle(0,0,50,50, **marker_options)

class ExampleApp(tk.Tk):
    def __init__(self, graph, **kwargs):
        tk.Tk.__init__(self)

        self.canvas = GraphCanvas(graph, NodeTokenClass=CustomNodeToken,
            **kwargs)
        self.canvas.grid(row=0, column=0, sticky='NESW')

G = nx.path_graph(5)
G.nodes[2]['circle'] = True
G.nodes[3]['color'] = 'blue'

app = ExampleApp(G)
app.mainloop()

Development Status

As of May 2024, networkx_viewer is considered feature complete. No additional development is expected. Bugs or feature requests should be submitted to the github issue tracker.

Many thanks to Faith Eser for doing the majority of the development work to make this library work with networkx version 2.2+ and Phillip Feldman for networkx version 3.3+.