Callback unable to update edge color

The objective is to have a dropdown menu that, when a certain item is selected, the edge colors of the graph object (networkx) are changed to a specified color that is built into the callback. The way I am attempting to achieve this is by first setting the glyph color to be derived from the edge attribute “edge_color”

network_graph.edge_renderer.glyph = MultiLine(line_color="edge_color", line_alpha=0.8)

The callback essentially loops over each element of “edge_color” from the data source and replaces it with a corresponding color.

def color_edges(arg: dict):
    callback = CustomJS(args=arg, code="""
        var edge_data = source.edge_renderer.data_source.data;
        var f = this.item
        var c = edge_data['start']
        for (var i = 0; i < c.length; i++)
        {
            if (f  == "cnv_color")
            {
                edge_data['edge_color'][i] = edge_data['edge_color_cnv'][i]
            }
            else if (f == "grey")
            {
                edge_data['edge_color'][i] = "grey"
            }
            else if (f == "black")
            {
                edge_data['edge_color'][i] = "black"
            }
            else if (f == "chrm_color")
            {
                edge_data['edge_color'][i] = edge_data['edge_color_chrm'][i]
            }
        }
        source.change.emit();
    """)
    return callback

I can confirm through the console that indeed the color value is being changed in the data source. Is there something about setting the edge color with MultiLine that prevents the color from being updated?

Any help would be much appreciated,

Best

The solution that is working seems to be directly modifying the line_color attribute in the glyph

source.edge_renderer.glyph.line_color = {'field': 'edge_color_turbo'}

That also means there is no need to iterate across the data

I’m curious, though why the same method doesn’t work with nodes e.g.

source.node_renderer.glyph.fill_color = {‘field’: ‘node_color_turbo’}

@mapQzero It’s not really possible to speculate with only the information given. If you can provide a simpler Minimal Reproducible Example then we could investigate.

That shouldn’t be too hard. I’ve got one right now for the edge coloring. I’ll try and post that later today.

1 Like

Since my minimal example seems to work without any problems I’m pretty sure there must be a bug in the production code.

Source = np.random.choice(list('ABCDEF'), size=10)
Target = np.random.choice(list('ABCDEF'), size=10)
Weight = np.random.randint(10, size=10)

g_df = pd.DataFrame({'Source':Source, 'Target': Target, 'Weight': Weight})
G = networkx.from_pandas_edgelist(g_df, 'Source', 'Target', 'Weight')

edge_attrs = {}
for start_node, end_node, _ in G.edges(data=True):
    # flip a coin to determine the color
    edge_attrs[(start_node, end_node)] = "yellow"
networkx.set_edge_attributes(G, edge_attrs, "edge_color")

node_attrs = {}
for node in G.nodes(data=True):
    node_attrs[node[0]] = "#"+''.join(list(np.random.choice(list('0123456789ABCDEF'), size=6)))
networkx.set_node_attributes(G, node_attrs, "node_color")


#Choose a title!
title = 'Rnd Network'

#Establish which categories will appear when hovering over each node
HOVER_TOOLTIPS = [("Character", "@index")]

#Create a plot — set dimensions, toolbar, and title
plot = figure(tooltips = HOVER_TOOLTIPS,
              tools="pan,wheel_zoom,save,reset", active_scroll='wheel_zoom',
              x_range=Range1d(-10.1, 10.1), y_range=Range1d(-10.1, 10.1), title=title)

#Create a network graph object with spring layout
# https://networkx.github.io/documentation/networkx-1.9/reference/generated/networkx.drawing.layout.spring_layout.html
network_graph = from_networkx(G, networkx.spring_layout, scale=10, center=(0, 0))

#Set node size and color
network_graph.node_renderer.glyph = Circle(size=15, fill_color='skyblue')

#Set edge opacity and width
network_graph.edge_renderer.glyph = MultiLine(line_alpha=0.5, line_width=1)

#Add network graph to the plot
plot.renderers.append(network_graph)

menu = [("color_edge", "color_edges")]
dropdown = Dropdown(label="edge menu",
                    button_type="warning",
                    menu=menu)


callback = CustomJS(args=dict(source=network_graph), code="""
        var f = this.item
        if (f  == "color_edges")
            source.node_renderer.glyph.fill_color = {'field': 'node_color'}
        source.change.emit();
    """)

dropdown.js_on_event('menu_item_click', callback)
curdoc().add_root(column(dropdown, plot))
1 Like

If you isolate a a difference or question with the real code please let us know and we will try to figure it out.

Thank you, Bryan. I was not able to isolate the exact cause, but after rewriting the javascript code the real code is now executing as intended.

Cheers