I created a dash core components for plotting vis.js network,
it has few functions currently, I will keep updating if other function need to be used
I created a dash core components for plotting vis.js network,
it has few functions currently, I will keep updating if other function need to be used
Update an new function: Animate or move the camera:
Thanks for your great work. Your visdcc works like a charm when it serves only one individual dash app. I have multiple dash app and I put them together with file structure as below.
File structure:
- app.py
- index.py
- apps
|-- __init__.py
|-- app1.py
|-- app2.py
import dash
app = dash.Dash()
server = app.server
app.config.supress_callback_exceptions = True
app.css.config.serve_locally = True
external_css = ["static/base.css",
"static/custom.css"]
for css in external_css:
app.css.append_css({"external_url": css})
from dash.dependencies import Input, Output
import dash_core_components as dcc
import dash_html_components as html
from app import app
from apps import app1, app2
app.layout = html.Div([
dcc.Location(id='url', refresh=False),
html.Div(id='page-content')
])
@app.callback(Output('page-content', 'children'),
[Input('url', 'pathname')])
def display_page(pathname):
if pathname == '/':
return app1.layout
elif pathname == '/apps/app2':
return app2.layout
else:
return '404'
if __name__ == '__main__':
app.run_server(debug=True)
app1
from dash.dependencies import Input, Output
import dash_html_components as html
import dash_core_components as dcc
from app import app
layout = html.Div([
html.H3('App 1'),
dcc.Dropdown(
id='app-1-dropdown',
options=[
{'label': 'App 1 - {}'.format(i), 'value': i} for i in [
'NYC', 'MTL', 'LA'
]
]
),
html.Div(id='app-1-display-value'),
dcc.Link('Go to App 2', href='/apps/app2')
],className='container')
@app.callback(
Output('app-1-display-value', 'children'),
[Input('app-1-dropdown', 'value')])
def display_value(value):
return 'You have selected "{}"'.format(value)
app2
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output, Event, State
import visdcc
from app import app
layout = html.Div([
visdcc.Network(id = 'net',
options = dict(height= '600px', width= '100%')),
dcc.Input(id = 'label',
placeholder = 'Enter a label ...',
type = 'text',
value = '' ),
html.Br(),html.Br(),
dcc.RadioItems(id = 'color',
options=[{'label': 'Red' , 'value': '#ff0000'},
{'label': 'Green', 'value': '#00ff00'},
{'label': 'Blue' , 'value': '#0000ff'} ],
value='Red' )
])
@app.callback(
Output('net', 'data'),
[Input('label', 'value')])
def myfun(x):
data ={'nodes':[{'id': 1, 'label': x , 'color':'#00ffff'},
{'id': 2, 'label': 'Node 2'},
{'id': 4, 'label': 'Node 4'},
{'id': 5, 'label': 'Node 5'},
{'id': 6, 'label': 'Node 6'} ],
'edges':[{'id':'1-3', 'from': 1, 'to': 3},
{'id':'1-2', 'from': 1, 'to': 2} ]
}
return data
@app.callback(
Output('net', 'options'),
[Input('color', 'value')])
def myfun(x):
return {'nodes':{'color': x}}
So the problem I encountered is the app2 with visdcc works great as individual dash app but wont work as file structure above. It shows blank on the screen and shows no error. When I remove visdcc.Network(id = 'net', options = dict(height= '600px', width= '100%')),
, the content is showing up again. That is why I think there may be a problem related to visdcc when constructing multiple dash apps.
I have posted the same thing to your github. Does anyone encounter the same problem?
i don’t know what the difference between multiple app and single app on visdcc, the problem may be on your css , or file structure … and so on.
Thanks tho.
This issue happened to someone who used custom component with Multi-Page Apps before.
Custom component with Multi-Page Apps
The reason for this type of error is (from @chriddyp) :
A little context: When Dash serves the page, it crawls the app.layout to see which component libraries are being used (e.g. dash_core_components). Then, with this list of unique component libraries, it serves the necessary JS and CSS bundles that are distributed with those component libraries.
In this case, we’re serving dash_table_experiments on a separate page, as the response of a callback. Dash only sees dash_html_components and dash_core_components in the app.layout and so it doesn’t serve the necessary JS and CSS bundles that are required for the dash-table-components component that is rendered in the future.
So the solution for fixing this kind of problem when constructing multiple pages with visdcc (in this case) is adds the following code to the Index.py :
app.layout = html.Div([
dcc.Location(id='url', refresh=False),
html.Div(id='page-content'),
html.Div(visdcc.Network(id='net',
options = dict(height= '600px',
width= '100%',
physics={'barnesHut': {'avoidOverlap': 0}},
)),style={'display': 'none'})
])
Great !
@jimmybow wow! really nice component for network viz! one short Q, How can I add a hyperlink to a given node? Is there a way to do this in the component w/o messing up with JS?
I only found this on the visjs side:
And I couldn’t find any example on the visdcc side.
Thanks!
it’s not support click event now, but you can use ‘selection’ prop to get selected nodes, and design it just like the click event, the code will like…
@app.callback(
Output(...),
[Input('net', 'selection')])
def myfun(x):
if len(x['nodes']) > 0 :
...
return ...
@jimmybow I am still a bit behind my project because I got stuck in a much more trivial task:
When I do this code I do not see the graph, no idea why:
""" ETL Flow diagram for Reference Match against supplier names """
import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output, Event, State
import visdcc
app = dash.Dash()
app.layout = html.Div([
visdcc.Network(
id = 'net',
options = dict(
height= '1100px',
width= '100%',
layout={
'hierarchical': {
'hierarchical.enabled':'true'
}
}
)
),
dcc.RadioItems(id='color',
options=[{'label': 'Red' , 'value': '#ff0000'},
{'label': 'Green', 'value': '#00ff00'},
{'label': 'Blue' , 'value': '#0000ff'}],
value='Red')
])
@app.callback(
Output('net', 'data'),
[Input('color', 'value')]
)
def myfun(x):
data ={
'nodes':
[
{'id': 1, 'label': 'chemical', 'color': 'rgb(0, 102, 204)', 'shape': 'box', 'level':'0'},
{'id': 2, 'label': 'security_master', 'color': 'rgb(204, 0, 0)', 'shape': 'box', 'level':'0'},
{'id': 3, 'label': 'supplier', 'color': 'rgb(211,211,211)', 'shape': 'ellipse', 'level':'1'},
{'id': 4, 'label': 'I/O', 'color': 'rgb(255, 204, 102)', 'shape': 'circle', 'level':'1'},
{'id': 5, 'label': 'common_words', 'color': 'rgb(204, 0, 0)', 'shape': 'box', 'level': '1'},
{'id': 6, 'label': 'I/O', 'color': 'rgb(255, 204, 102)', 'shape': 'circle', 'level': '2'}
],
'edges':
[
{'id':'1-3', 'from': 1, 'to': 3, 'arrows':'to'},
{'id': '2-4', 'from': 2, 'to': 4, 'arrows': 'to'},
{'id': '5-4', 'from': 5, 'to': 4, 'arrows': 'to'}
]
}
return data
if __name__ == '__main__':
app.run_server(host='0.0.0.0', port=8687, debug=True)
But if I remove the ‘id’:‘6’ I suddenly can see the graph:
""" ETL Flow diagram for Reference Match against supplier names """
import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output, Event, State
import visdcc
app = dash.Dash()
app.layout = html.Div([
visdcc.Network(
id = 'net',
options = dict(
height= '1100px',
width= '100%',
layout={
'hierarchical': {
'hierarchical.enabled':'true'
}
}
)
),
dcc.RadioItems(id='color',
options=[{'label': 'Red' , 'value': '#ff0000'},
{'label': 'Green', 'value': '#00ff00'},
{'label': 'Blue' , 'value': '#0000ff'}],
value='Red')
])
@app.callback(
Output('net', 'data'),
[Input('color', 'value')]
)
def myfun(x):
data ={
'nodes':
[
{'id': 1, 'label': 'chemical', 'color': 'rgb(0, 102, 204)', 'shape': 'box', 'level':'0'},
{'id': 2, 'label': 'security_master', 'color': 'rgb(204, 0, 0)', 'shape': 'box', 'level':'0'},
{'id': 3, 'label': 'supplier', 'color': 'rgb(211,211,211)', 'shape': 'ellipse', 'level':'1'},
{'id': 4, 'label': 'I/O', 'color': 'rgb(255, 204, 102)', 'shape': 'circle', 'level':'1'},
{'id': 5, 'label': 'common_words', 'color': 'rgb(204, 0, 0)', 'shape': 'box', 'level': '1'}
],
'edges':
[
{'id':'1-3', 'from': 1, 'to': 3, 'arrows':'to'},
{'id': '2-4', 'from': 2, 'to': 4, 'arrows': 'to'},
{'id': '5-4', 'from': 5, 'to': 4, 'arrows': 'to'}
]
}
return data
if __name__ == '__main__':
app.run_server(host='0.0.0.0', port=8687, debug=True)
Then I can see the plot:
Is there a restriction in number of nodes/levels? I have no idea what’s different between one code and the other besides adding a new node.
This is such an odd error that to clarify with diff, the code on the left works but the code on the right doesn’t work and I have no idea why. I also submit it as a github issue in case it’s useful to the repo too.
I guess for me the mystery continues. Trying to fix it I eliminated all the app.callback code and put the data in the visdcc.Network.
This code works and puts the nodes in the level’s of hierarchy specified:
""" ETL Flow diagram for Reference Match against supplier names """
import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output, Event, State
import visdcc
app = dash.Dash()
app.layout = html.Div([
visdcc.Network(
id = 'net',
options = dict(
height= '1100px',
width= '100%',
layout={
'hierarchical': {
'dsfdsfsadfa': 'true'
}
}
),
data={
'nodes':
[
{'id': 1, 'label': 'chemical', 'color': 'rgb(0, 102, 204)', 'shape': 'box', 'level': '0'},
{'id': 2, 'label': 'security_master', 'color': 'rgb(204, 0, 0)', 'shape': 'box', 'level': '0'},
{'id': 3, 'label': 'supplier', 'color': 'rgb(211,211,211)', 'shape': 'ellipse', 'level': '1'},
{'id': 4, 'label': 'I/O', 'color': 'rgb(255, 204, 102)', 'shape': 'circle', 'level': '1'},
{'id': 5, 'label': 'common_words', 'color': 'rgb(204, 0, 0)', 'shape': 'box', 'level': '1'},
{'id': 6, 'label': 'I/O', 'color': 'rgb(255, 204, 102)', 'shape': 'circle', 'level': '2'},
{'id': 7, 'label': 'oil document', 'color': 'rgb(204, 0, 0)', 'shape': 'box', 'level': '2'},
{'id': 8, 'label': 'security_master_enriched', 'color': 'rgb(204, 0, 0)', 'shape': 'box', 'level': '2'}
],
'edges':
[
{'id': '1-3', 'from': 1, 'to': 3, 'arrows': 'to'},
{'id': '2-4', 'from': 2, 'to': 4, 'arrows': 'to'},
{'id': '5-4', 'from': 5, 'to': 4, 'arrows': 'to'},
{'id': '3-6', 'from': 3, 'to': 6, 'arrows': 'to'},
{'id': '7-4', 'from': 7, 'to': 4, 'arrows': 'to'},
{'id': '4-8', 'from': 4, 'to': 8, 'arrows': 'to'},
{'id': '7-6', 'from': 7, 'to': 6, 'arrows': 'to'}
]
}
)
])
if __name__ == '__main__':
app.run_server(host='0.0.0.0', port=8687, debug=True)
However, it puts two nodes overlapping so one cannot read full text from the nodes (oil document and master_security_enriched).
To fix this I have been trying the suggestions here:
https://stackoverflow.com/questions/35431990/connected-nodes-overlapping-other-edges-or-nodes
This one works but does not put the nodes in the right levels specified:
""" ETL Flow diagram for Reference Match against supplier names """
import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output, Event, State
import visdcc
app = dash.Dash()
app.layout = html.Div([
visdcc.Network(
id = 'net',
options = dict(
height= '1100px',
width= '100%',
layout={
'hierarchical': {
'enable': 'true'
}
}
),
data={
'nodes':
[
{'id': 1, 'label': 'chemical', 'color': 'rgb(0, 102, 204)', 'shape': 'box', 'level': '0'},
{'id': 2, 'label': 'security_master', 'color': 'rgb(204, 0, 0)', 'shape': 'box', 'level': '0'},
{'id': 3, 'label': 'supplier', 'color': 'rgb(211,211,211)', 'shape': 'ellipse', 'level': '1'},
{'id': 4, 'label': 'I/O', 'color': 'rgb(255, 204, 102)', 'shape': 'circle', 'level': '1'},
{'id': 5, 'label': 'common_words', 'color': 'rgb(204, 0, 0)', 'shape': 'box', 'level': '1'},
{'id': 6, 'label': 'I/O', 'color': 'rgb(255, 204, 102)', 'shape': 'circle', 'level': '2'},
{'id': 7, 'label': 'oil document', 'color': 'rgb(204, 0, 0)', 'shape': 'box', 'level': '2'},
{'id': 8, 'label': 'security_master_enriched', 'color': 'rgb(204, 0, 0)', 'shape': 'box', 'level': '2'}
],
'edges':
[
{'id': '1-3', 'from': 1, 'to': 3, 'arrows': 'to'},
{'id': '2-4', 'from': 2, 'to': 4, 'arrows': 'to'},
{'id': '5-4', 'from': 5, 'to': 4, 'arrows': 'to'},
{'id': '3-6', 'from': 3, 'to': 6, 'arrows': 'to'},
{'id': '7-4', 'from': 7, 'to': 4, 'arrows': 'to'},
{'id': '4-8', 'from': 4, 'to': 8, 'arrows': 'to'},
{'id': '7-6', 'from': 7, 'to': 6, 'arrows': 'to'}
]
}
)
])
if __name__ == '__main__':
app.run_server(host='0.0.0.0', port=8687, debug=True)
If I do ‘enabled’: ‘false’ it also doesn’t put the nodes in the right levels.
Now, if I add ‘nodeSpacing’:425 as suggested in here:
https://stackoverflow.com/questions/35431990/connected-nodes-overlapping-other-edges-or-nodes
it doesn’t work, i.e., no error whatsoever, just as before I cannot see any graph in the site.
At this point I am clueless as of how this componnent work and how to effectively debug since it seems to behave/missbehave in sort of a random manner… if you can shed some light on the logic of why this errrors may be happening and how to test/debug I’d be supper happy.
Thanks!
it seem layout in options need to use this format:
layout: {
improvedLayout:True,
hierarchical: {
enabled:False,
levelSeparation: 150,
nodeSpacing: 100,
treeSpacing: 200,
blockShifting: True,
edgeMinimization: True,
parentCentralization: True,
direction: 'UD', # UD, DU, LR, RL
sortMethod: 'hubsize' # hubsize, directed
}
}
the format from the document of vis.js http://visjs.org/docs/network/layout.html#,
boolean is corresponding to boolean
Woooaw! This is great.
Does it support clustering? I trying to figure out how implement it. Any clues??
Thank you!
yes, but it don’t have any axis and tick.
i think using scatter plot to perform clustering results is better …
anyway, you can draw complex network or tree by using visdcc.Network
You may need to use this with NetworkX, which provide clustering algorithm, and a lot more features.
I have made a hierarchical network with huge number of nodes and want to cluster them to improve performance. Is there a way to cluster them? I’m sorry I can’t show the code but it would be very helpful if I anyone could show how to cluster the nodes (cluster by connection or zoom) using visdcc.
I’m truly grateful for your sharing of the component. However, I have trouble in changing the edge’s length. It would be highly appreciated if you could give me an example. Sincerely thanks again.
what’s the edge’s length? you can fix the node initial location so the edge’ length are fixed too
you can view the doc of vis.js http://visjs.org/docs/network/
the visdcc use the same format of json/dictionary as vis.js
you can set the options, the visdcc use the same format of json/dictionary as vis.js
you can see the vis.js example here http://visjs.org/network_examples.html
Thanks for your kindly reply. The edge’s length means I want to set the length of edges in different size. Though I set the property of length in edges,it didn’t work.Here is my code.
import dash import dash_core_components as dcc import dash_html_components as html from dash.dependencies import Input, Output, Event, State import visdcc app = dash.Dash() mydata ={ 'nodes': [ {'id': 1, 'label': 'node1'}, {'id': 2, 'label': 'node2'}, {'id': 3, 'label': 'node3'} ], 'edges': [ {'id':'1-2', 'from': 1, 'to': 2, 'arrows':'to','length':10}, {'id': '2-3', 'from': 2, 'to': 3, 'arrows': 'to','length':5}, {'id': '3-1', 'from': 3, 'to': 1, 'arrows': 'to','length':1} ] } app.layout = html.Div([ visdcc.Network( id = 'net', data=mydata, options = dict( height= '600px', width= '100%', node=dict(shape='circle'), physics=False ) ) ]) if __name__ == '__main__': app.run_server(debug=False)
The following picture is my result. It seems that the length of edges is not the value I set.
Now I am confused about whether the length of edge could be set. It would be highly appreciated for your reply. Sincerely thanks.
there is no option ‘length’ for edges, you need to set the node initial location so the edge’ length will be different