Callbacks cannot remove and add nodes
See original GitHub issueI am trying to show a graph with parent and child nodes (representing clusters) and have a menu or button callback to remove/hide the parent nodes and leave the children (and appropriate edges.) I’ve tried various approaches, but essentially, they seem to all return the same error. The callback(s) return a whole new “elements” to Cytoscape and they inevitably result in a JS error in the browser like this example:
Can not create edge 7cf808c5-c6a2-4937-8ca8-8b43d1bd8893
with nonexistant source nyc
I have put together a small test/demo to show the problem, based directly on some of the examples from the documentation. As far as I can see from the documentation examples, this should do what I want, but I still get the errors. I don’t think I’m missing anything (this is pretty straightforwardly copying and pasting from the examples…) so I suspect there’s a but here.
In my debugging efforts, I noticed that if one changes the IDs of the nodes in the callback instead of leaving them the same as the originals, then it seems to work. But that would be somewhat kludgey and cumbersome to do for a graph with lots of actual data. (Note: I did try looking at the JS in the browser console tool. if I followed it correctly, it looks like it’s removing all the nodes and edges, then trying to add back edges before creating the nodes… maybe…)
Thanks in advance for your help! -Al
My Example CODE:
import dash import dash_cytoscape as cyto import dash_html_components as html from dash.dependencies import Input, Output, State
app = dash.Dash(name) app.config.suppress_callback_exceptions = True
app.layout = html.Div([ cyto.Cytoscape( id=‘cytoscape-compound’, layout={‘name’: ‘preset’}, style={‘width’: ‘100%’, ‘height’: ‘450px’}, stylesheet=[ { ‘selector’: ‘node’, ‘style’: {‘content’: ‘data(label)’} }, { ‘selector’: ‘.countries’, ‘style’: {‘width’: 5, ‘visible’: ‘false’ } }, { ‘selector’: ‘.cities’, ‘style’: {‘line-style’: ‘dashed’} } ], elements=[ # Parent Nodes { ‘data’: {‘id’: ‘us’, ‘label’: ‘United States’} }, { ‘data’: {‘id’: ‘can’, ‘label’: ‘Canada’} }, # Children Nodes { ‘data’: {‘id’: ‘nyc’, ‘label’: ‘New York’, ‘parent’: ‘us’}, ‘position’: {‘x’: 100, ‘y’: 100} }, { ‘data’: {‘id’: ‘sf’, ‘label’: ‘San Francisco’, ‘parent’: ‘us’}, ‘position’: {‘x’: 100, ‘y’: 200} }, { ‘data’: {‘id’: ‘mtl’, ‘label’: ‘Montreal’, ‘parent’: ‘can’}, ‘position’: {‘x’: 400, ‘y’: 100} }, # Edges { ‘data’: {‘source’: ‘can’, ‘target’: ‘us’}, ‘classes’: ‘countries’ }, { ‘data’: {‘source’: ‘nyc’, ‘target’: ‘sf’}, ‘classes’: ‘cities’ }, { ‘data’: {‘source’: ‘sf’, ‘target’: ‘mtl’}, ‘classes’: ‘cities’ } ] ), html.Div([ html.Button(‘Add’, id=‘btn-add-node-example’, n_clicks_timestamp=0), html.Button(‘Remove’, id=‘btn-remove-node-example’, n_clicks_timestamp=0) ]) ])
@app.callback(Output(‘cytoscape-compound’, ‘elements’), [Input(‘btn-add-node-example’, ‘n_clicks_timestamp’), Input(‘btn-remove-node-example’, ‘n_clicks_timestamp’)], [State(‘cytoscape-compound’, ‘elements’)]) def update_elements(btn_add, btn_remove, elements): if int(btn_add) > int(btn_remove): elements = [ # Children Nodes { ‘data’: {‘id’: ‘nyc’, ‘label’: ‘New York’, ‘parent’: ‘us’}, ‘position’: {‘x’: 100, ‘y’: 100} }, { ‘data’: {‘id’: ‘sf’, ‘label’: ‘San Francisco’, ‘parent’: ‘us’}, ‘position’: {‘x’: 100, ‘y’: 200} }, { ‘data’: {‘id’: ‘mtl’, ‘label’: ‘Montreal’, ‘parent’: ‘can’}, ‘position’: {‘x’: 400, ‘y’: 100} }, # Parent Nodes { ‘data’: {‘id’: ‘us’, ‘label’: ‘United States’} }, { ‘data’: {‘id’: ‘can’, ‘label’: ‘Canada’} },
# Edges
{
'data': {'source': 'can', 'target': 'us'},
'classes': 'countries'
},
{
'data': {'source': 'nyc', 'target': 'sf'},
'classes': 'cities'
},
{
'data': {'source': 'sf', 'target': 'mtl'},
'classes': 'cities'
}
]
return elements
elif int(btn_remove) > int(btn_add):
elements = [
{
'data': {'id': 'nyc', 'label': 'New York'},
'position': {'x': 100, 'y': 100}
},
{
'data': {'id': 'sf', 'label': 'San Francisco'},
'position': {'x': 100, 'y': 200}
},
{
'data': {'id': 'mtl', 'label': 'Montreal'},
'position': {'x': 400, 'y': 100}
},
# Edges
{
'data': {'source': 'nyc', 'target': 'sf'},
'classes': 'cities'
},
{
'data': {'source': 'sf', 'target': 'mtl'},
'classes': 'cities'
}
]
return elements
return elements
if name == ‘main’: app.run_server(debug=True)
Issue Analytics
- State:
- Created 3 years ago
- Comments:13
Top GitHub Comments
@agersch2 @riverchen99 This definitely seems like an issue in v0.2.0, since the add/remove node example worked correctly with older versions of dash-cytoscape (v0.1.1 and v0.0.4). I’ll spend some time investigating this issue and hopefully find a fix for v0.2.1.
@riverchen99 Thanks for the commiseration and helpful suggestion. I had already done a similar - very kludgey - workaround. But I’d prefer a real solution to the issue to a workaround. Also, as mentioned above, it’s not only parent-child node modifications that don’t work. Removing and adding nodes exactly per the documentation example doesn’t either! I think that requires a real fix.