Hi!
Is there a way to do the following inside a JS code_text:
graph_renderer.layout_provider = StaticLayoutProvider(graph_layout=graph_layout)
I would like to change the layout_provider (the position of nodes) from client’s side. Not from server. In other words, I want to replace the graph_layout with a new one. I tried several ways, but the plot is not changing. Even if I used plot.change.emit(); graph_rendererplot.change.emit(); inside the JS code-text.
Any ideas?
Thank you in advance
@ThemisKoutsellis it’s much easier to offer help if you share some of the things you tried, and really a complete Minimal Reproducible Example.
@Bryan you are right! Sorry about that. Here is a sample of code. Hope this helps. Thank you in advance!
# Bokeh_question.py
# -*- coding: utf-8 -*-
#-----------------------------------------------------------------------------
#>> Imports:
#-----------------------------------------------------------------------------
# ΝetworkΧ
import networkx as nx
# Bokeh library
from bokeh.io import output_file
from bokeh.plotting import figure, show
from bokeh.layouts import layout
from bokeh.models.graphs import from_networkx, NodesAndLinkedEdges, EdgesAndLinkedNodes
from bokeh.models import (
ColumnDataSource,
CustomJS,
Circle,
HoverTool,
MultiLine,
Range1d,
ResetTool,
TapTool,
BoxSelectTool,
Select,
PanTool,
WheelZoomTool,
SaveTool,
LabelSet,
)
#-----------------------------------------------------------------------------
#<< Imports.
#-----------------------------------------------------------------------------
#-----------------------------------------------------------------------------
#>> NetworX graph creation:
#-----------------------------------------------------------------------------
#define the graph's elements:
source = ['C1', 'C1', 'C1','C3','C2','C2','C2','C4','C5']
target = ['C3', 'C2','C4','C4','C3','C4','C1','C3', 'C3']
weight = [1,0.3,1,1,1,0.8, 0.4, 1, 1]
nodes_disc = {
'C1': 'C1 description',
'C2': 'C2 description',
'C3': 'C3 description',
'C4': 'C4 description',
'C5': 'C5 description',
}
# create list of tuples with all necessary elements for graph
edges_list = list(zip(source, target, weight))
# create netwokx graph:
nx_graph = nx.DiGraph()
nx_graph.add_weighted_edges_from(edges_list)
#-----------------------------------------------------------------------------
#<< NetworX graph creation.
#-----------------------------------------------------------------------------
#-----------------------------------------------------------------------------
#>> Output HTML file:
#-----------------------------------------------------------------------------
# The figure will be rendered in a static HTML file called foo.html
output_file('FooF.html', title='Foo')
#-----------------------------------------------------------------------------
#<< Output HTML file.
#-----------------------------------------------------------------------------
#-----------------------------------------------------------------------------
#>> Figure:
#-----------------------------------------------------------------------------
plot = figure(
plot_width=400,
plot_height=350,
x_range=Range1d(-3,5),
y_range=Range1d(-3,5),
tools=[],
)
plot.grid.grid_line_color = None
# plot title
plot.title.text = "Foo"
plot.title.text_font = "times"
# Plot toolbar tools:
node_hover_tool = HoverTool()
plot_wheel_zoom = WheelZoomTool()
plot_pan_tool = PanTool()
plot.add_tools(
plot_pan_tool,
node_hover_tool,
plot_wheel_zoom,
BoxSelectTool(),
ResetTool(),
TapTool(),
SaveTool(),
)
# active tools:
plot.toolbar.active_inspect = node_hover_tool
plot.toolbar.active_scroll = plot_wheel_zoom
plot.toolbar.active_drag = plot_pan_tool
plot.toolbar.active_tap = None
plot.toolbar.logo = None
# define some stitic nodes. e.g. Input or Output nodes:
node_initial_pos = {'C1':(0, 0), 'C2':(0, 3), 'C5':(3,0)}
##### graph renderer
# Different layout positions
bipartite_pos= nx.bipartite_layout(
nx_graph,
['C1', 'C2'],
align='vertical',
scale=2,
center=(0, 0),
aspect_ratio=1.33,
)
circular_pos = nx.circular_layout(
nx_graph,
scale=2,
center=(0, 0),
dim=2,
)
kamada_kawai_pos = nx.kamada_kawai_layout(
nx_graph,
weight='weight',
scale=2,
center=(0, 0),
dim=2,
)
planar_pos = nx.planar_layout(
nx_graph,
scale=2,
center=(0, 0),
dim=2,
)
random_pos = nx.random_layout(
nx_graph,
center=(0, 0),
dim=2,
)
spring_pos = nx.spring_layout(
nx_graph,
scale=2,
center=(0, 0),
pos=node_initial_pos,
fixed=['C1', 'C2', 'C5'],
iterations=2,
k=0.1,
)
pos_dict = planar_pos
graph_renderer = from_networkx(
nx_graph,
pos_dict,
)
## node sub-renderer
graph_renderer.node_renderer.glyph = Circle(size=20, fill_color="#2b83ba", line_width=1.5, fill_alpha=0.7)
graph_renderer.node_renderer.nonselection_glyph = Circle(size=10, fill_color='#DAF7A8')
graph_renderer.node_renderer.selection_glyph = Circle(size=22, fill_color='#E74C3C')
graph_renderer.node_renderer.hover_glyph = Circle(size=15, fill_color='#DAF7A6')
## edge sub-renderer
graph_renderer.edge_renderer.glyph = MultiLine(line_color="grey", line_alpha=0.8, line_width=1.25)
graph_renderer.edge_renderer.selection_glyph = MultiLine(line_color='#E74C3C', line_width=2)
graph_renderer.edge_renderer.nonselection_glyph = MultiLine(line_color='#DAF7A8', line_width=1)
graph_renderer.edge_renderer.hover_glyph = MultiLine(line_color='#DAF7A6', line_width=2) # #abdda4
# Hover policy
graph_renderer.selection_policy = NodesAndLinkedEdges()
graph_renderer.inspection_policy = EdgesAndLinkedNodes()
# append graph renderer to plot figure
plot.renderers.append(graph_renderer)
# Adding concept names to Figure:
node_names = list(graph_renderer.layout_provider.graph_layout.keys())
_xs, _ys = map(list, zip(*graph_renderer.layout_provider.graph_layout.values()))
node_labels_source = ColumnDataSource(
data=dict(
xs=_xs,
ys=_ys,
names=node_names,
)
)
# Node labels layout:
node_labels = LabelSet(
x='xs',
y='ys',
text='names',
x_offset=0,
y_offset=-5,
source=node_labels_source,
render_mode='canvas',
text_font = 'times',
text_font_size='9pt',
text_font_style='normal',
text_color='black',
text_align='center',
level='glyph',
angle=0,
)
plot.add_layout(node_labels)
#>> Widgets related to FCM display layout:
##########################################
_layout_options = [
'spring_layout',
'bipartite_layout',
'circular_layout',
'kamada_kawai_layout',
'planar_layout',
'random_layout',
]
nodes_display_layout_select = Select(
title="Display layout:",
value="planar_layout",
options=_layout_options,
width=300,
height=50
)
nodes_display_layout_select_args = dict(
graph_renderer=graph_renderer,
plot=plot,
bipartite_pos=bipartite_pos,
circular_pos=circular_pos,
kamada_kawai_pos=kamada_kawai_pos,
planar_pos=planar_pos,
random_pos=random_pos,
spring_pos=spring_pos,
)
nodes_display_layout_select_str = """
graph_renderer.layout_provider.graph_layout = spring_pos;
graph_renderer.change.emit();
plot.reset.change();
"""
nodes_display_layout_select_callback = CustomJS(
args=nodes_display_layout_select_args,
code=nodes_display_layout_select_str,
)
nodes_display_layout_select.js_on_change("value", nodes_display_layout_select_callback)
#<< Widgets related to FCM display layout.
##########################################
## Final Layout:
html_layout = layout(
[
[plot],
[nodes_display_layout_select ],
]
)
#Show HTML Layout:
show(html_layout)
Does anyone has a solution for this problem?
Is it a bug or something I miss?
Is it possible to change the JScript object from client perspective or not?
I have attached the code above.
Thanks in advance!
@ThemisKoutsellis your reply escaped my notice. There is no plot.reset.change()
function and you will see an error about that if yo look in your browser JavaScript console. I would expect the entire callback to be
nodes_display_layout_select_str = """
graph_renderer.layout_provider.graph_layout = spring_pos;
"""
If I add a debugger
statement I can see the values getting changed but the plot is not updating. I will have to investigate more deeply when I can spare more time (hopefully this weekend).
@ThemisKoutsellis At this point I think there must just be a bug, e.g. some missing event plumbing that is not hooked up where it should be. Please file a GitHub Issue with these details.