#-----------------------------------------------------------------------------# Copyright (c) Anaconda, Inc., and Bokeh Contributors.# All rights reserved.## The full license is in the file LICENSE.txt, distributed with this software.#-----------------------------------------------------------------------------#-----------------------------------------------------------------------------# Boilerplate#-----------------------------------------------------------------------------from__future__importannotationsimportlogging# isort:skiplog=logging.getLogger(__name__)#-----------------------------------------------------------------------------# Imports#-----------------------------------------------------------------------------# Standard library importsfromtypingimportTYPE_CHECKING,Any,Sequence# Bokeh importsfrom..models.graphsimportStaticLayoutProviderfrom..models.renderersimportGraphRendererfrom..util.warningsimportwarnifTYPE_CHECKING:importnetworkxasnx#-----------------------------------------------------------------------------# Globals and constants#-----------------------------------------------------------------------------__all__=("from_networkx",)#-----------------------------------------------------------------------------# General API#-----------------------------------------------------------------------------
[docs]deffrom_networkx(graph:nx.Graph,layout_function:dict[int|str,Sequence[float]],**kwargs:Any)->GraphRenderer:''' Generate a ``GraphRenderer`` from a ``networkx.Graph`` object and networkx layout function. Any keyword arguments will be passed to the layout function. Only two-dimensional layouts are supported. Args: graph (networkx.Graph) : a networkx graph to render layout_function (function or dict) : a networkx layout function or mapping of node keys to positions. The position is a two element sequence containing the x and y coordinate. Returns: instance (GraphRenderer) .. note:: Node and edge attributes may be lists or tuples. However, a given attribute must either have *all* lists or tuple values, or *all* scalar values, for nodes or edges it is defined on. .. warning:: Node attributes labeled 'index' and edge attributes labeled 'start' or 'end' are ignored. If you want to convert these attributes, please re-label them to other names. Raises: ValueError '''# Handles nx 1.x vs 2.x data structure change# Convert node attributesnode_dict=dict()node_attr_keys=[attr_keyfornodeinlist(graph.nodes(data=True))forattr_keyinnode[1].keys()]node_attr_keys=list(set(node_attr_keys))forattr_keyinnode_attr_keys:values=[node_attr[attr_key]ifattr_keyinnode_attr.keys()elseNonefor_,node_attringraph.nodes(data=True)]values=_handle_sublists(values)node_dict[attr_key]=valuesif'index'innode_attr_keys:warn("Converting node attributes labeled 'index' are skipped. ""If you want to convert these attributes, please re-label with other names.")node_dict['index']=list(graph.nodes())# Convert edge attributesedge_dict=dict()edge_attr_keys=[attr_keyforedgeingraph.edges(data=True)forattr_keyinedge[2].keys()]edge_attr_keys=list(set(edge_attr_keys))forattr_keyinedge_attr_keys:values=[edge_attr[attr_key]ifattr_keyinedge_attr.keys()elseNonefor_,_,edge_attringraph.edges(data=True)]values=_handle_sublists(values)edge_dict[attr_key]=valuesif'start'inedge_attr_keysor'end'inedge_attr_keys:warn("Converting edge attributes labeled 'start' or 'end' are skipped. ""If you want to convert these attributes, please re-label them with other names.")edge_dict['start']=[x[0]forxingraph.edges()]edge_dict['end']=[x[1]forxingraph.edges()]graph_renderer=GraphRenderer()graph_renderer.node_renderer.data_source.data=node_dictgraph_renderer.edge_renderer.data_source.data=edge_dictifcallable(layout_function):graph_layout=layout_function(graph,**kwargs)else:graph_layout=layout_functionnode_keys=graph_renderer.node_renderer.data_source.data['index']ifset(node_keys)!=set(layout_function.keys()):warn("Node keys in 'layout_function' don't match node keys in the graph. ""These nodes may not be displayed correctly.")graph_renderer.layout_provider=StaticLayoutProvider(graph_layout=graph_layout)returngraph_renderer
#-----------------------------------------------------------------------------# Dev API#-----------------------------------------------------------------------------#-----------------------------------------------------------------------------# Private API#-----------------------------------------------------------------------------def_handle_sublists(values):# if any of the items is non-scalar, they all must beifany(isinstance(x,(list,tuple))forxinvalues):ifnotall(isinstance(x,(list,tuple))forxinvaluesifxisnotNone):raiseValueError("Can't mix scalar and non-scalar values for graph attributes")return[[]ifxisNoneelselist(x)forxinvalues]returnvalues#-----------------------------------------------------------------------------# Code#-----------------------------------------------------------------------------