Skip to content

Commit 7f32eb8

Browse files
more advanced showcase example
1 parent 72e6038 commit 7f32eb8

2 files changed

Lines changed: 527 additions & 0 deletions

File tree

examples/advanced_showcase.py

Lines changed: 336 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,336 @@
1+
# examples/advanced_showcase.py
2+
3+
import os
4+
import random
5+
from typing import Any
6+
7+
import networkx as nx
8+
9+
from nx_vis_visualizer import nx_to_vis
10+
11+
12+
def create_showcase_graph() -> nx.Graph[Any]:
13+
"""Creates a more complex NetworkX graph for demonstration."""
14+
G: nx.Graph[Any] = nx.Graph(name="Advanced Showcase Graph")
15+
16+
# --- Add Nodes with various properties ---
17+
num_random_nodes = 20
18+
for i in range(num_random_nodes):
19+
G.add_node(
20+
f"R{i}", # Random node
21+
label=f"R{i}",
22+
group=i % 4, # Assign to one of 4 groups
23+
value=(i % 5) + 1, # For size scaling (1 to 5)
24+
title=f"Random Node R{i}\nGroup: {i % 4}\nValue: {(i % 5) + 1}",
25+
font={"color": "black", "size": 10} if i % 4 == 0 else {},
26+
)
27+
28+
# Special, styled nodes
29+
G.add_node(
30+
"Source",
31+
label="START",
32+
shape="diamond",
33+
color={
34+
"background": "lightgreen",
35+
"border": "darkgreen",
36+
"highlight": {"background": "green", "border": "darkgreen"},
37+
},
38+
size=30,
39+
group=4, # Special group
40+
title="This is the main Source node",
41+
font={
42+
"size": 16,
43+
"color": "darkgreen",
44+
"face": "Arial",
45+
"strokeWidth": 1,
46+
"strokeColor": "white",
47+
},
48+
fixed={"x": True, "y": True}, # Fix position
49+
x=-400,
50+
y=-200, # Specify coordinates
51+
)
52+
G.add_node(
53+
"Sink",
54+
label="END",
55+
shape="star",
56+
color="gold",
57+
size=30,
58+
group=4,
59+
title="This is the main Sink node",
60+
font={"size": 16, "color": "black", "face": "Georgia"},
61+
fixed={"x": True, "y": True},
62+
x=400,
63+
y=200,
64+
)
65+
G.add_node(
66+
"Hub",
67+
label="Central Hub",
68+
shape="ellipse",
69+
color="lightblue",
70+
size=25,
71+
group=5,
72+
title="A central connecting hub",
73+
font={
74+
"multi": "html",
75+
"bold": "18px arial darkblue",
76+
}, # Using multi-font for bold
77+
)
78+
G.add_node(
79+
"Isolated",
80+
label="I'm Alone",
81+
shape="text",
82+
title="An isolated node example",
83+
color="#FFCCFF", # Light pink
84+
font={"size": 12, "color": "purple"},
85+
)
86+
87+
# --- Add Edges with various properties ---
88+
# Connect Source to some random nodes and Hub
89+
for i in range(0, num_random_nodes, 5):
90+
G.add_edge(
91+
"Source",
92+
f"R{i}",
93+
weight=(i % 3) + 1,
94+
color="darkgreen",
95+
label=f"S->R{i}",
96+
)
97+
98+
G.add_edge(
99+
"Source",
100+
"Hub",
101+
weight=5,
102+
color="darkgreen",
103+
width=4,
104+
title="Main link to Hub",
105+
)
106+
107+
# Connect some random nodes to Hub and Sink
108+
for i in range(1, num_random_nodes, 4):
109+
G.add_edge(
110+
f"R{i}",
111+
"Hub",
112+
weight=(i % 2) + 1,
113+
dashes=[5, 5],
114+
color="gray",
115+
title=f"R{i} to Hub",
116+
)
117+
if i + 2 < num_random_nodes:
118+
G.add_edge(
119+
f"R{i + 2}",
120+
"Sink",
121+
weight=3,
122+
color="orange",
123+
label=f"R{i + 2}->E",
124+
arrows="to",
125+
)
126+
127+
G.add_edge(
128+
"Hub",
129+
"Sink",
130+
weight=5,
131+
color="blue",
132+
width=4,
133+
title="Main link from Hub to Sink",
134+
arrows="to",
135+
smooth={"type": "curvedCW", "roundness": 0.2},
136+
)
137+
138+
# Inter-random node connections
139+
for i in range(num_random_nodes - 1):
140+
if random.random() < 0.2: # Sparsely connect random nodes
141+
G.add_edge(
142+
f"R{i}",
143+
f"R{i + 1}",
144+
weight=1,
145+
color="#cccccc",
146+
title=f"R{i}-R{i + 1}",
147+
)
148+
149+
# Add an edge with a complex color object
150+
if num_random_nodes > 5:
151+
G.add_edge(
152+
"R0",
153+
"R5",
154+
color={
155+
"color": "red",
156+
"highlight": "darkred",
157+
"hover": "pink",
158+
"opacity": 0.7,
159+
},
160+
label="Opacity Edge",
161+
weight=2,
162+
)
163+
164+
return G
165+
166+
167+
if __name__ == "__main__":
168+
showcase_graph = create_showcase_graph()
169+
170+
# --- Define Custom Vis.js Options ---
171+
# Start with a deep copy of defaults if you want to modify them,
172+
# or define options from scratch for full control.
173+
# For this showcase, let's define many options explicitly.
174+
custom_vis_options: dict[str, Any] = {
175+
"nodes": {
176+
"borderWidth": 2,
177+
"borderWidthSelected": 4,
178+
"font": {
179+
"size": 12,
180+
"face": "Tahoma",
181+
"color": "#333333", # Default node font color
182+
},
183+
"scaling": { # Scale node size based on 'value' attribute
184+
"min": 10,
185+
"max": 30,
186+
"label": {"enabled": True, "min": 8, "max": 20},
187+
},
188+
"shadow": {"enabled": True, "size": 5, "x": 3, "y": 3},
189+
},
190+
"edges": {
191+
"width": 1, # Default edge width
192+
"color": {
193+
"color": "#848484",
194+
"highlight": "#000000",
195+
"hover": "#555555",
196+
"inherit": "from", # Inherit color from 'from' node, or 'both', 'to', False
197+
"opacity": 1.0,
198+
},
199+
"arrows": { # Default arrow settings (can be overridden by individual edges)
200+
"to": {"enabled": False, "scaleFactor": 0.8, "type": "arrow"},
201+
# "middle": {"enabled": True, "scaleFactor":0.5, "type":"circle"},
202+
# "from": {"enabled": False}
203+
},
204+
"smooth": {
205+
"enabled": True,
206+
"type": "dynamic", # "dynamic", "continuous", "discrete", "diagonalCross", "straightCross", "horizontal", "vertical", "curvedCW", "curvedCCW", "cubicBezier"
207+
"roundness": 0.5,
208+
},
209+
"font": {
210+
"size": 10,
211+
"color": "black",
212+
"strokeWidth": 0, # No stroke for edge labels by default
213+
"align": "horizontal", # "top", "middle", "bottom"
214+
},
215+
"scaling": { # Scale edge width based on 'weight' attribute
216+
"min": 1,
217+
"max": 8,
218+
"label": {"enabled": False},
219+
},
220+
"hoverWidth": 1.5,
221+
"selectionWidth": 2,
222+
},
223+
"groups": {
224+
0: {
225+
"color": {"background": "cyan", "border": "blue"},
226+
"shape": "dot",
227+
},
228+
1: {
229+
"color": {"background": "pink", "border": "purple"},
230+
"shape": "square",
231+
"font": {"color": "purple"},
232+
},
233+
2: {
234+
"color": {"background": "yellow", "border": "orange"},
235+
"shape": "triangle",
236+
},
237+
3: {
238+
"color": {"background": "lime", "border": "green"},
239+
"shape": "triangleDown",
240+
},
241+
4: {
242+
"borderWidth": 3,
243+
"shadow": {"enabled": False},
244+
}, # Special group for Source/Sink
245+
5: {"shape": "hexagon", "color": "lightgray"}, # Hub group
246+
},
247+
"layout": {
248+
"randomSeed": 42, # For reproducible layout if physics is re-enabled
249+
"improvedLayout": True,
250+
# Example for hierarchical layout (disable physics if using this)
251+
# "hierarchical": {
252+
# "enabled": False,
253+
# "direction": "UD", # UD, DU, LR, RL
254+
# "sortMethod": "directed", # hubsize, directed
255+
# "shakeTowards": "roots"
256+
# }
257+
},
258+
"interaction": {
259+
"hover": True,
260+
"dragNodes": True, # Can nodes be dragged?
261+
"dragView": True, # Can the view be dragged?
262+
"zoomView": True,
263+
"navigationButtons": True, # Show zoom and fit buttons
264+
"tooltipDelay": 200,
265+
"keyboard": {
266+
"enabled": True,
267+
"speed": {"x": 10, "y": 10, "zoom": 0.05},
268+
"bindToWindow": True,
269+
},
270+
"multiselect": True,
271+
"selectable": True,
272+
"selectConnectedEdges": True,
273+
},
274+
"physics": {
275+
"enabled": True, # Nodes with fixed:true will ignore physics
276+
"solver": "barnesHut", # barnesHut, forceAtlas2Based, repulsion, hierarchicalRepulsion
277+
"barnesHut": {
278+
"gravitationalConstant": -15000,
279+
"centralGravity": 0.1,
280+
"springLength": 120,
281+
"springConstant": 0.05,
282+
"damping": 0.15,
283+
"avoidOverlap": 0.2,
284+
},
285+
# "forceAtlas2Based": {
286+
# "gravitationalConstant": -50,
287+
# "centralGravity": 0.01,
288+
# "springLength": 100,
289+
# "springConstant": 0.08,
290+
# "damping": 0.4,
291+
# "avoidOverlap": 0
292+
# },
293+
"stabilization": { # Run stabilization before displaying
294+
"enabled": True,
295+
"iterations": 1000, # More iterations for better layout
296+
"updateInterval": 50,
297+
"onlyDynamicEdges": False,
298+
"fit": True, # Fit the network to the screen after stabilization
299+
},
300+
"adaptiveTimestep": True,
301+
"minVelocity": 0.75,
302+
},
303+
"configure": { # Show the configuration GUI
304+
"enabled": True,
305+
# "filter": "nodes,edges,layout,interaction,physics", # String or array or function
306+
"showButton": False, # Show a button to toggle the configurator
307+
},
308+
}
309+
310+
# Define output path relative to this script for cleanliness
311+
current_dir = os.path.dirname(os.path.abspath(__file__))
312+
output_file = os.path.join(current_dir, "advanced_showcase_graph.html")
313+
314+
print(
315+
f"Generating advanced showcase graph with {showcase_graph.number_of_nodes()} nodes and {showcase_graph.number_of_edges()} edges..."
316+
)
317+
file_path = nx_to_vis(
318+
showcase_graph,
319+
output_filename=output_file,
320+
html_title="NX-VIS Visualizer - Advanced Showcase",
321+
vis_options=custom_vis_options,
322+
show_browser=True,
323+
graph_height="90vh", # Make it take most of the viewport height
324+
graph_width="100%",
325+
)
326+
327+
if file_path:
328+
print(f"Advanced showcase graph saved to: {file_path}")
329+
print(
330+
"Open this HTML file in your browser to see the interactive graph."
331+
)
332+
print(
333+
"Try using the configuration panel (usually a button at the bottom) to tweak settings live!"
334+
)
335+
else:
336+
print("Failed to generate the advanced showcase graph.")

0 commit comments

Comments
 (0)