Hello all,
I’m having trouble syncing cytoscape with other components (such as dropdown menus) and would appreciate any advice/suggestions.
I’m attempting to sync cytoscape selected nodes (selectedNodesData
) bidirectionally with a dropdown menu selection. That is, selecting nodes from the dropdown menu highlights the network on the fly, and vice versa. While I have managed the first step: updating the dropdown menu value using a cytoscape node. It works as follows:
code:
from gc import callbacks
import dash
import dash_cytoscape as cyto
import pandas as pd
from dash import callback_context, dcc, html, no_update
from dash.dependencies import Input, Output
graphs = ["A", "B"]
app = dash.Dash(__name__)
my_layout = {"name": "grid"}
my_style = {
"display": "inline-block",
"width": "1000px",
"height": "800px",
"border": "2px black solid",
}
app.layout = html.Div(
children=[
html.H1("My Network", style={"color": "black", "fontSize": 32}),
html.Br(),
dcc.Dropdown(
id="dropdown",
options=["A", "B"],
disabled=False,
multi=True,
),
html.Br(),
cyto.Cytoscape(
id="network",
elements=[
{
"data": {"id": "A", "label": "A"},
"position": {"x": 75, "y": 75},
"classes": "A",
},
{
"data": {"id": "B", "label": "B"},
"position": {"x": 200, "y": 200},
"classes": "B",
},
{"data": {"source": "A", "target": "B"}},
], # How do I have an option for different elements here?
layout=my_layout,
style=my_style,
stylesheet=[
{
"selector": "node",
"style": {"width": "50px", "height": "50px"},
}
],
),
html.Br(),
]
)
@app.callback(
Output(component_id="dropdown", component_property="value"),
Input(component_id="network", component_property="selectedNodeData"),
)
def sync(selected_nodes):
if selected_nodes is None:
raise dash.exceptions.PreventUpdate
else:
return [i['id'] for i in selected_nodes]
# @app.callback(
# [
# Output(component_id="dropdown", component_property="value"),
# Output(component_id="network", component_property="selectedNodeData"),
# ],
# [
# Input(component_id="dropdown", component_property="value"),
# Input(component_id="network", component_property="selectedNodeData"),
# ],
# )
# def sync(value_dropdown, value_network):
# match callback_context.triggered[0]["prop_id"].split(".")[0]:
# case "dropdown":
# return no_update, [{"id": i, "label": i} for i in value_dropdown]
# case "network":
# return [i["id"] for i in value_network], no_update
# case _:
# return no_update, no_update
if __name__ == "__main__":
app.run_server(debug=True, host="0.0.0.0", port=8051, dev_tools_hot_reload=True)
However, when I attempted to take this a step further by using a bidirectional callback like this:
@app.callback(
[
Output(component_id="dropdown", component_property="value"),
Output(component_id="network", component_property="selectedNodeData"),
],
[
Input(component_id="dropdown", component_property="value"),
Input(component_id="network", component_property="selectedNodeData"),
],
)
def sync(value_dropdown, value_network):
match callback_context.triggered[0]["prop_id"].split(".")[0]:
case "dropdown":
return no_update, [{"id": i, "label": i} for i in value_dropdown]
case "network":
return [i["id"] for i in value_network], no_update
case _:
return no_update, no_update
The dropdown menu updates as expected when nodes are selected, but it does not update network selection:
I reasoned that this could be because cytoscape does not support callbacks on the selectedNodesData
attribute. So, my question is, is there a hack/workaround for this?
Appreciate any feedbacks,
Don