Dynamically display images in tooltip upon clicking on data table

Hello:
I am working slowly towards my end goal of showing images in a tooltip as response to clicking on a data table cell.
As shown in ’ Using URLs to Load Images and Text in Tooltips’ for a graph object Scatter element it is possible to do this.

Is it possible to do the same for a dash_table.DataTable element?
Thank you very much in advance for any comment you might have?
Best regards,
Markus

Hi @metma

Could you say more about what you are trying to do? I thought you found the solution in this post: Images in Tooltips for tables do not display - #3 by metma

Thank you for your comment AnnMarie:

For Images in Tooltips for tables do not display - #3 by metma
I need to define manullay all the info beforehand in the tooltip_data.
And from the manual:
DataTable Tooltips | Dash for Python Documentation | Plotly
" * tooltip_data: Statically defined tooltip for each row/column combination"

So, in a nutshell, I would like to avoid defining the images as in tooltip_data.
What I am looking for is to show an image by clicking on a row, and the image is created as in the

under 'Using URLs to Load Images and Text in Tooltips ’

Please let me if you or anybody else requires additional info.
Best regards,
Markus

Hi @metma,

I’m not sure what you’re attempting to do, but maybe this can guide you:

Generate a link based on the value of the selected cell:

from dash import Dash, dash_table, callback, Input, Output, State
from dash.exceptions import PreventUpdate

app = Dash(__name__)

app.layout = dash_table.DataTable(
    data=[
        {'shop': 'Bakersfield', 'sales': 4, 'goal': 10},
        {'shop': 'Berkeley', 'sales': 10, 'goal': 1},
        {'shop': 'Big Bear Lake', 'sales': 5, 'goal': 4}
    ],
    columns=[
        {'id': 'shop', 'name': 'Store Location'},
        {'id': 'sales', 'name': 'Sales Revenue'},
        {'id': 'goal', 'name': 'Revenue Goal'},
    ],

    # Style headers with a dotted underline to indicate a tooltip
    style_data_conditional=[{
        'if': {'column_id': 'shop'},
        'textDecoration': 'underline',
        'textDecorationStyle': 'dotted',
    }],

    tooltip_delay=0,
    tooltip_duration=None,
    id='data-table'
)


@callback(
    [Output('data-table', 'tooltip_data')],
    [Input('data-table', 'active_cell')],
    [State('data-table', 'data')],
    prevent_initial_call=True
)
def update_url(active_cell, data):
    # use the value of the active cell to generate the URL output
    selected_row = active_cell['row']
    selected_col = active_cell['column_id']
    data_val = data[selected_row][selected_col]
    if data_val == 'Bakersfield':
        return [[
            {
                'shop': {
                    'value': f'Row {selected_row} in "{selected_col}" column\n\n![Bakersfield]({app.get_relative_path("/assets/image.jpg")})',
                    'type': 'markdown'
                }
            }
        ]]
    raise PreventUpdate


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

Define links using a list comprehension along with some data structure

from dash import Dash, dash_table, callback, Input, Output, State
from dash.exceptions import PreventUpdate

app = Dash(__name__)

# this could be in a DataFrame, Series, or any other data structure
img_paths_data = {
    1: {'link': app.get_relative_path("/assets/image1.jpg"), 'alt_text': 'IMAGE 1', 'description': 'DESCRIPTION 1'},
    2: {'link': app.get_relative_path("/assets/image2.jpg"), 'alt_text': 'IMAGE 2', 'description': 'DESCRIPTION 2'},
    3: {'link': app.get_relative_path("/assets/image3.jpg"), 'alt_text': 'IMAGE 3', 'description': 'DESCRIPTION 3'},
}

app.layout = dash_table.DataTable(
    data=[
        {'shop': 'Bakersfield', 'sales': 4, 'goal': 10},
        {'shop': 'Berkeley', 'sales': 10, 'goal': 1},
        {'shop': 'Big Bear Lake', 'sales': 5, 'goal': 4}
    ],
    columns=[
        {'id': 'shop', 'name': 'Store Location'},
        {'id': 'sales', 'name': 'Sales Revenue'},
        {'id': 'goal', 'name': 'Revenue Goal'},
    ],

    # Style headers with a dotted underline to indicate a tooltip
    style_data_conditional=[{
        'if': {'column_id': 'shop'},
        'textDecoration': 'underline',
        'textDecorationStyle': 'dotted',
    }],

    tooltip_data=[
        {
            'shop': {
                'value': f"{img_data['description']}\n![{img_data['alt_text']}]({img_data['link']})",
                'type': 'markdown'
            }
        }
        for img_data in img_paths_data.values()
    ],

    tooltip_delay=0,
    tooltip_duration=None,
    id='data-table'
)

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

You could adapt this to whatever your own use. If these don’t meet what you need, feel free to provide more information.

Thank you 3d65 for your suggestions.
If I read your examples correctly then in both cases I am adding the specific image information to the script before I run the webpage.
I guess I need a graphobject to display images on the fly. At least in the example I have cited before it is a go.Scatter object.
Thank you again for your suggestions!
Markus

@metma ,

My examples are simple cases, but you can adapt them such that you don’t need the links initially. In the example you’re referencing, the links are also predefined in this line img_src = df_row['IMG_URL'] (this is exactly the same as my first example), it’s just hard to see clearly.

Could you provide more information on what you’re actually attempting? Are you trying to add an image url based on user input? Are you trying to add an image the user uploads?

OK, below is an example heavily borrowed from Tooltip | Dash for Python Documentation | Plotly with a boxplot.
As you can see, I do not neet to specify any image name but I retrieve this info from the web when I click on the web page.
I was hoping to do the same with a table.
Does this make sense?
Thank you again for your comment.
Markus


from dash import Dash, dcc, html, Input, Output, no_update
import plotly.graph_objects as go
import plotly.express as px

import pandas as pd

import io
import base64

from PIL import Image

import urllib.request

# Small molcule drugbank dataset
# Source: https://raw.githubusercontent.com/plotly/dash-sample-apps/main/apps/dash-drug-discovery/data/small_molecule_drugbank.csv'
data_path = 'small_molecule_drugbank.csv'

df = pd.read_csv(data_path)
df.head()

# Small molecule drugbank dataset
# Source: https://raw.githubusercontent.com/plotly/dash-sample-apps/main/apps/dash-drug-discovery/data/small_molecule_drugbank.csv'

fig = go.Figure(data=[
    go.Box(
        x=df["LOGP"],boxpoints='all'
    )
])

# turn off native plotly.js hover effects - make sure to use
# hoverinfo="none" rather than "skip" which also halts events.
fig.update_traces(hoverinfo="none", hovertemplate=None)


app = Dash(__name__)

app.layout = html.Div([
    dcc.Graph(id="graph-basic-2", figure=fig, clear_on_unhover=True),
    dcc.Tooltip(id="graph-tooltip"),
])


@app.callback(
    Output("graph-tooltip", "show"),
    Output("graph-tooltip", "bbox"),
    Output("graph-tooltip", "children"),
    Input("graph-basic-2", "hoverData"),
)
def display_hover(hoverData):
    if hoverData is None:
        return False, no_update, no_update

    # demo only shows the first point, but other points may also be available
    pt = hoverData["points"][0]
    bbox = pt["bbox"]
    num = pt["pointNumber"]

    df_row = df.iloc[num]
    img_url = df_row['IMG_URL']
    name = df_row['NAME']
    
    headers={'User-Agent': 'Mozilla/5.0'}
    
    request=urllib.request.Request(img_url,headers=headers) #The assembled request
    response = urllib.request.urlopen(request)
    im = response.read() # The data u need
    
    #im = Image.open('chemstr.png')
    
    # dump it to base64
    #buffer = io.BytesIO()
    #im.save(buffer, format="jpeg")
    encoded_image = base64.b64encode(im).decode()
    im_url = "data:image/jpeg;base64, " + encoded_image
    
    children = [
        html.Div([
            html.Img(src=img_url, style={"width": "100%"}),
            html.H2(f"{name}", style={"color": "darkblue", "overflow-wrap": "break-word"}),
        ], style={'width': '200px', 'white-space': 'normal'})
    ]

    return True, bbox, children


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

@metma,

Here’s an example of using a URL image with the DataTable by creating a temporary image and updating it.

from dash import Dash, dash_table, callback, Input, Output, State
from dash.exceptions import PreventUpdate
import io
import urllib.request
from PIL import Image

app = Dash(__name__)

app.layout = dash_table.DataTable(
    data=[
        {'shop': 'Bakersfield', 'sales': 4, 'goal': 10},
        {'shop': 'Berkeley', 'sales': 10, 'goal': 1},
        {'shop': 'Big Bear Lake', 'sales': 5, 'goal': 4}
    ],
    columns=[
        {'id': 'shop', 'name': 'Store Location'},
        {'id': 'sales', 'name': 'Sales Revenue'},
        {'id': 'goal', 'name': 'Revenue Goal'},
    ],

    # Style headers with a dotted underline to indicate a tooltip
    style_data_conditional=[{
        'if': {'column_id': 'shop'},
        'textDecoration': 'underline',
        'textDecorationStyle': 'dotted',
    }],

    tooltip_delay=0,
    tooltip_duration=None,
    id='data-table'
)


@callback(
    [Output('data-table', 'tooltip_data')],
    [Input('data-table', 'active_cell')],
    [State('data-table', 'data')],
    prevent_initial_call=True
)
def update_url(active_cell, data):
    # use the value of the active cell to generate the URL output
    selected_row = active_cell['row']
    selected_col = active_cell['column_id']

    if selected_col == 'shop':
        img_url = 'https://www.google.com/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png'
        name = 'Google'

        headers = {'User-Agent': 'Mozilla/5.0'}
        request = urllib.request.Request(img_url, headers=headers)  # The assembled request
        response = urllib.request.urlopen(request)
        buffer = io.BytesIO(response.read())
        im = Image.open(buffer)
        im.save('assets/tmp.png')

        return [[
            {},
            {},
            {
                'shop': {
                    'value': f'{name}\n\n![{name}]({app.get_relative_path("/assets/tmp.png")})',
                    'type': 'markdown'
                }
            },

        ]]
    raise PreventUpdate


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