Slowat_Kela
Slowat_Kela

Reputation: 1521

Add in URL hyperlink to text returned upon click in plotly dash

This code generates a network as expected using plotly dash:

import dash
from dash import dcc
from dash import html
import dash_cytoscape as cyto
from dash.dependencies import Input, Output
import plotly.express as px

dash_elements = [{'data': {'id': '6840', 'label': '6840'}, 'classes': 'black'}, {'data': {'id': '7431', 'label': '7431'}, 'classes': 'red'}, {'data': {'source': '6840', 'target': '7431'}}, {'data': {'id': '6640', 'label': '6640'}, 'classes': 'black'}, {'data': {'id': '5217', 'label': '5217'}, 'classes': 'black'}, {'data': {'source': '6640', 'target': '5217'}}, {'data': {'id': '823', 'label': '823'}, 'classes': 'black'}, {'data': {'id': '7431', 'label': '7431'}, 'classes': 'red'}, {'data': {'source': '823', 'target': '7431'}}]

app = dash.Dash(__name__)

styles = {
    'pre': {
        'border': 'thin lightgrey solid',
        'overflowX': 'scroll'
    }
}


default_stylesheet=[
    # Class Selectors
    {
        'selector':'.black',
        #'selector': 'node',
        'style': {
            'background-color': '#000000',
            'label': 'data(label)'
        },
    },

    {
        'selector':'.red',
        #'selector':'node',
        'style': {
            'background-color': '#FF0000',
            'label': 'data(label)'
        },
    },
]


app.layout = html.Div([
    #html.P("Dash Cytoscape:"),
    cyto.Cytoscape(
        id='cytoscape-event-callbacks-2',
        elements = dash_elements,
        layout={'name': 'breadthfirst'},
        #layout={'name': 'preset'},
        style={'width': '1000px', 'height': '1000px'},
        stylesheet = default_stylesheet
        
        
        
    ),
    html.P(id='cytoscape-tapNodeData-output'),
    html.P(id='cytoscape-tapEdgeData-output'),
    html.P(id='cytoscape-mouseoverNodeData-output'),
    html.P(id='cytoscape-mouseoverEdgeData-output')
])

@app.callback(Output('page-content', 'children'),
              Input('cytoscape-event-callbacks-2', 'tapNodeData'))

#app.callback(Output('url', 'children'),
# Input('cytoscape-event-callbacks-2', 'tapNodeData'))


def displayTapNodeData(data):
    if data:
        return "https://www.ncbi.nlm.nih.gov/gene/" + str(data['label'])

    
    

@app.callback(Output('cytoscape-tapEdgeData-output', 'children'),
              Input('cytoscape-event-callbacks-2', 'tapEdgeData'))

def displayTapEdgeData(data):
    if data:
        return "There is a physical interaction between " + \
               str(data['source']) + " and " + str(data['target'])

    
    

@app.callback(Output('cytoscape-mouseoverNodeData-output', 'children'),
              Input('cytoscape-event-callbacks-2', 'mouseoverNodeData'))

def displayTapNodeData(data):
    if data:
        return "https://www.ncbi.nlm.nih.gov/gene/" + str(data['label'])


@app.callback(Output('cytoscape-mouseoverEdgeData-output', 'children'),
              Input('cytoscape-event-callbacks-2', 'mouseoverEdgeData'))



def displayTapEdgeData(data):
    if data:
        return "There is a physical interaction between " + \
               str(data['source']) + " and " + str(data['target'])

if __name__ == '__main__':
   app.run_server()

The output:

enter image description here

When I click on a node, as expected, text in the bottom left appears with the URL to find out more information about the node.

I just want to turn that text into a hyperlink.

I can see the example documentation to do that here and I can get this sample code to work.

I am unclear specifically which changes I make to my own code to incorporate this, I've left the attempt I've tried commented out, where I thought I would input a tapNodeData and the output would be a url.

If someone could demonstrate how to convert what is returned upon click to a URL I'd appreciate it.

Upvotes: 1

Views: 1980

Answers (1)

John Collins
John Collins

Reputation: 2971

Returning active URL links upon node selection

You can just use the Dash html.A component, no?:

import dash
from dash import dcc
from dash import html
import dash_cytoscape as cyto
from dash.dependencies import Input, Output, State
import plotly.express as px

dash_elements = [
    {"data": {"id": "6840", "label": "6840"}, "classes": "black"},
    {"data": {"id": "7431", "label": "7431"}, "classes": "red"},
    {"data": {"source": "6840", "target": "7431"}},
    {"data": {"id": "6640", "label": "6640"}, "classes": "black"},
    {"data": {"id": "5217", "label": "5217"}, "classes": "black"},
    {"data": {"source": "6640", "target": "5217"}},
    {"data": {"id": "823", "label": "823"}, "classes": "black"},
    {"data": {"id": "7431", "label": "7431"}, "classes": "red"},
    {"data": {"source": "823", "target": "7431"}},
]

app = dash.Dash(__name__)

styles = {"pre": {"border": "thin lightgrey solid", "overflowX": "scroll"}}


default_stylesheet = [
    # Class Selectors
    {
        "selector": ".black",
        #'selector': 'node',
        "style": {"background-color": "#000000", "label": "data(label)"},
    },
    {
        "selector": ".red",
        #'selector':'node',
        "style": {"background-color": "#FF0000", "label": "data(label)"},
    },
]


app.layout = html.Div(
    [
        # html.P("Dash Cytoscape:"),
        cyto.Cytoscape(
            id="cytoscape-event-callbacks-2",
            elements=dash_elements,
            layout={"name": "breadthfirst"},
            # layout={'name': 'preset'},
            style={"width": "1000px", "height": "1000px"},
            stylesheet=default_stylesheet,
        ),
        html.P(id="cytoscape-tapNodeData-output"),
        html.P(id="cytoscape-tapEdgeData-output"),
        html.P(id="cytoscape-mouseoverNodeData-output"),
        html.P(id="cytoscape-mouseoverEdgeData-output"),
        html.Div([], id="page-content"),
    ]
)


@app.callback(
    Output("page-content", "children"),
    Input("cytoscape-event-callbacks-2", "tapNodeData"),
)
def displayTapNodeDataLink(data):
    if data:
        url = f"https://www.ncbi.nlm.nih.gov/gene/{data['label']}"
        return html.A(url, href=url, target="_blank")


@app.callback(
    Output("cytoscape-tapEdgeData-output", "children"),
    Input("cytoscape-event-callbacks-2", "tapEdgeData"),
)
def displayTapEdgeData(data):
    if data:
        return (
            "There is a physical interaction between "
            + str(data["source"])
            + " and "
            + str(data["target"])
        )


@app.callback(
    Output("cytoscape-mouseoverNodeData-output", "children"),
    Input("cytoscape-event-callbacks-2", "mouseoverNodeData"),
)
def displayTapNodeData(data):
    if data:
        return "https://www.ncbi.nlm.nih.gov/gene/" + str(data["label"])


@app.callback(
    Output("cytoscape-mouseoverEdgeData-output", "children"),
    Input("cytoscape-event-callbacks-2", "mouseoverEdgeData"),
)
def displayTapEdgeData(data):
    if data:
        return (
            "There is a physical interaction between "
            + str(data["source"])
            + " and "
            + str(data["target"])
        )


if __name__ == "__main__":
    app.run_server(debug=True, dev_tools_hot_reload=True)

example of url when mouse click event on node occurs

I've also added the parameter target="_blank", which makes clicking the URL open in a new tab. I personally find it really annoying most of the time, but especially in a Dash app, for a link to open in the same tab and the app to be then left. But, that's of course subjective and up to you!

Returning stylesheet's along with html.A urls as multiple outputs in one callback upon node selection

(optional change which interactively indicates which node was most recently clicked on)

Also, here I've updated it such that the node which has most recently been clicked changes color, if you happen to want that behavior:

import dash
import dash_cytoscape as cyto
import plotly.express as px

from dash import dcc
from dash import html
from dash import no_update
from dash.dependencies import Input
from dash.dependencies import Output
from dash.dependencies import State


dash_elements = [
    {"data": {"id": "6840", "label": "6840"}, "classes": "node-6840"},
    {"data": {"id": "7431", "label": "7431"}, "classes": "node-7431"},
    {"data": {"source": "6840", "target": "7431"}},
    {"data": {"id": "6640", "label": "6640"}, "classes": "node-6640"},
    {"data": {"id": "5217", "label": "5217"}, "classes": "node-5217"},
    {"data": {"source": "6640", "target": "5217"}},
    {"data": {"id": "823", "label": "823"}, "classes": "node-823"},
    {"data": {"id": "7431", "label": "7431"}, "classes": "node-7431"},
    {"data": {"source": "823", "target": "7431"}},
]

app = dash.Dash(__name__)

styles = {"pre": {"border": "thin lightgrey solid", "overflowX": "scroll"}}


default_stylesheet = [
    # Class Selectors
    {
        "selector": "node",
        "style": {"background-color": "#000000", "label": "data(label)"},
    },
]


app.layout = html.Div(
    [
        # html.P("Dash Cytoscape:"),
        cyto.Cytoscape(
            id="cytoscape-event-callbacks-2",
            elements=dash_elements,
            layout={"name": "breadthfirst"},
            # layout={'name': 'preset'},
            style={"width": "750px", "height": "750px"},
            stylesheet=default_stylesheet,
        ),
        html.P(id="cytoscape-tapNodeData-output"),
        html.P(id="cytoscape-tapEdgeData-output"),
        html.P(id="cytoscape-mouseoverNodeData-output"),
        html.P(id="cytoscape-mouseoverEdgeData-output"),
        html.Div([], id="page-content"),
        html.Br(),
    ],
    style={"margin": "5%"},
)


@app.callback(
    [
        Output("page-content", "children"),
        Output("cytoscape-event-callbacks-2", "stylesheet"),
    ],
    Input("cytoscape-event-callbacks-2", "tapNodeData"),
)
def displayTapNodeDataLink(data):
    if data:
        stylesheet = [
            {"selector": "node", "style": {"label": "data(label)"},},
            {
                "selector": f".node-{data['id']}",
                "style": {"background-color": "red", "label": "data(label)",},
            },
        ]
        url = f"https://www.ncbi.nlm.nih.gov/gene/{data['label']}"
        return [html.A(url, href=url, target="_blank")], stylesheet
    else:
        return no_update, default_stylesheet


@app.callback(
    Output("cytoscape-tapEdgeData-output", "children"),
    Input("cytoscape-event-callbacks-2", "tapEdgeData"),
)
def displayTapEdgeData(data):
    if data:
        return (
            "There is a physical interaction between "
            + str(data["source"])
            + " and "
            + str(data["target"])
        )


@app.callback(
    Output("cytoscape-mouseoverNodeData-output", "children"),
    Input("cytoscape-event-callbacks-2", "mouseoverNodeData"),
)
def displayTapNodeData(data):
    if data:
        return "https://www.ncbi.nlm.nih.gov/gene/" + str(data["label"])


@app.callback(
    Output("cytoscape-mouseoverEdgeData-output", "children"),
    Input("cytoscape-event-callbacks-2", "mouseoverEdgeData"),
)
def displayTapEdgeData(data):
    if data:
        return (
            "There is a physical interaction between "
            + str(data["source"])
            + " and "
            + str(data["target"])
        )


if __name__ == "__main__":
    app.run_server(debug=True, dev_tools_hot_reload=True)

app with interactive stylesheet dynamics

Upvotes: 1

Related Questions