Black Lives Matter. Please consider donating to Black Girls Code today.

Animated annotation and image

Hi

I’m building my first dashboard in Plot.ly and I need to have a big number in the corner of my dashboard with the text changing as the animation progresses. Also, we wanted to have an image that also changes as the animation progresses ( for instance 11am a image of the sun, 11pm a image of the moon). Is this possible at all? Does anyone know of a sample doing this?

Thank you

Rui

Bump

I would also be interested in having annotations that change with each frame of the animation

Is this currently possible?

I poked around a bit and in fact this easily achieved via the Frames constructor by simply passing a Layout argument that has an annotation that is different for each frame

I don’t have too much time right now to post a complete code snippet, but if anybody is interested I can provide a complete working example

i_cnt = 0

layout_i = go.Layout(

    annotations=[
        go.layout.Annotation(
            x=50,
            y=2,
            xref="x",
            yref="y",
            text="TEMP_" + str(i_cnt).zfill(3),
            showarrow=True,
            arrowhead=7,
            ax=0,
            ay=-40
            )
        ],

    )


# #################################################################
frame_i = go.Frame(data=data_i, name=str(i_cnt),
    layout=layout_i,
    )
frames.append(frame_i)

# #################################################################

fig = go.Figure(
    data=data,
    layout=layout_anim,
    frames=frames)

Hi @flores12,

I would be interested in seeing a complete working example, if you don’t mind.
I’m especially interested in also adding shapes to individual frames.

Thanks!

Here is a working example @lisnux, it’s a bit of a mess but should run. It was originally a Jupyter Notebook example which I converted to .py with jupytext, if you’re familiar with jupytext you can convert this back into a jupyter notebook.

It’s not perfect, the annotation doesn’t appear unti you hit play, maybe you need to add an annotation to the main layout object as well.

Edit: Let me know if this doesn’t work there is probably some trivial error that I can fix.

# ---
# jupyter:
#   jupytext:
#     formats: ipynb,py:light
#     text_representation:
#       extension: .py
#       format_name: light
#       format_version: '1.5'
#       jupytext_version: 1.4.2
#   kernelspec:
#     display_name: Python [conda env:PROJ_irox_oer] *
#     language: python
#     name: conda-env-PROJ_irox_oer-py
# ---

# # Import Modules

# +
import os
import sys

import pandas as pd

import plotly.graph_objs as go

# #############################################################################
# from local_methods import get_sliders_init_dict, get_slider_step_i, get_layout
# -

# # Methods

# +
import plotly.graph_objs as go


def get_layout(
    duration_long=1000 * 2,
    duration_short=800 * 2,
    ):
    """
    """
    #| - get_layout

    #| - updatemenus
    updatemenus = [
        {
            'buttons': [
                {
                    'args': [
                        None,
                        {
                            'frame': {
                                'duration': duration_long,  # TEMP_DURATION
                                'redraw': False,
                                },
                            'fromcurrent': True,
                            'transition': {
                                'duration': duration_short,  # TEMP_DURATION
                                'easing': 'quadratic-in-out',
                                }
                            }
                        ],
                    'label': 'Play',
                    'method': 'animate'
                    },
                {
                    'args': [
                        [None],
                        {
                            'frame': {
                                'duration': 0,
                                'redraw': False,
                                },
                            'mode': 'immediate',
                            'transition': {'duration': 0},
                            }
                        ],
                    'label': 'Pause',
                    'method': 'animate'
                    }
                ],
            'direction': 'left',
            'pad': {'r': 10, 't': 87},
            'showactive': False,
            'type': 'buttons',
            'x': 0.1,
            'xanchor': 'right',
            'y': 0,
            'yanchor': 'top'
            }
        ]

    #__|

    layout = go.Layout(
        # title='Material Discovery Training',
        showlegend=False,
        font=dict(
            # family='Courier New, monospace',

            family='Arial',
            size=20,
            color='black'
            ),

        xaxis={
            'title': 'X-axis Title',

            # 'range': [0 - 5, len(models_list[0]) + 5],
            # 'autorange': False,
            'autorange': True,

            'showgrid': False,
            'zeroline': False,
            'showline': True,
            'ticks': '',
            'showticklabels': True,
            'mirror': True,
            'linecolor': 'black',

            },

        yaxis={
            'title': 'Y-axis Title',

            # 'range': [global_y_min, global_y_max],
            # 'range': [-1.5, 2.4],
            # 'autorange': False,
            'autorange': True,
            'fixedrange': False,

            'showgrid': False,
            'zeroline': True,
            'showline': True,
            'ticks': '',
            'showticklabels': True,
            'mirror': True,
            'linecolor': 'black',

            },

        updatemenus=updatemenus,
        )

    return(layout)
    #__|

    
def get_sliders_init_dict(duration_short):
    """
    """
    # | - get_sliders_init_dict
    sliders_dict = {
        # 'active': 0,
        'active': 0,
        'yanchor': 'top',
        'xanchor': 'left',
        'currentvalue': {
            'font': {'size': 20},
            'prefix': 'Loop #:',
            'visible': True,
            'xanchor': 'right'
            },
        'transition': {
            'duration': duration_short,  # TEMP_DURATION
            'easing': 'cubic-in-out'},
        'pad': {'b': 10, 't': 50},
        'len': 0.8,
        'x': 0.1,
        'y': 0,
        'steps': [],
        }

    return(sliders_dict)
    #__|

def get_slider_step_i(i_cnt, duration_short):
    """
    """
    #| - get_slider_step_i
    slider_step_i = {
        'args': [
            [str(i_cnt)],
            {
                'frame': {
                    'duration': duration_short,  # TEMP_DURATION
                    'redraw': False},
                'mode': 'immediate',
                'transition': {
                    'duration': duration_short,  # TEMP_DURATION
                    },
                },
            ],
        'label': str(i_cnt),
        'method': 'animate',
        }

    return(slider_step_i)
    #__|



# -

# # Script Inputs

duration_long = 1000 * 3
duration_short = 800 * 3

# # Read Data

# +
dict_data = {
    ('0', 'name'): {0: 'a', 1: 'b', 2: 'c', 3: 'd'},
    ('0', 'x'): {0: 1, 1: 2, 2: 3, 3: 4},
    ('0', 'y'): {0: 1, 1: 2, 2: 3, 3: 4},
    ('0', 'color'): {0: 'red', 1: 'blue', 2: 'green', 3: 'black'},
    ('1', 'name'): {0: 'a', 1: 'b', 2: 'c', 3: 'd'},
    ('1', 'x'): {0: 2, 1: 1, 2: 3, 3: 4},
    ('1', 'y'): {0: 2, 1: 1, 2: 3, 3: 4},
    ('1', 'color'): {0: 'red', 1: 'blue', 2: 'green', 3: 'black'},
    ('2', 'name'): {0: 'a', 1: 'b', 2: 'c', 3: 'd'},
    ('2', 'x'): {0: 4, 1: 3, 2: 1, 3: 2},
    ('2', 'y'): {0: 4.0, 1: 3.0, 2: 1.0, 3: 0.8},
    ('2', 'color'): {0: 'red', 1: 'blue', 2: 'green', 3: 'black'},
    ('3', 'name'): {0: 'a', 1: 'b', 2: 'd', 3: 'c'},
    ('3', 'x'): {0: 4, 1: 3, 2: 1, 3: -1},
    ('3', 'y'): {0: 4.0, 1: 3.0, 2: 0.8, 3: 1.0},
    ('3', 'color'): {0: 'red', 1: 'blue', 2: 'green', 3: 'black'},
    }


df_data = pd.DataFrame(dict_data)

# +
# df_data = pd.read_csv("data.csv", header=[0, 1])

time_series_indices = list(df_data.columns.levels[0])

df_list = []
for i in time_series_indices:
    df_i = df_data[i]
    df_i = df_i.set_index("name")
    # df_i = df_i.sort_index()
    df_list.append(df_i)


# -

# # methods | get_traces

def get_traces(df):
    trace = go.Scatter(
        x=df["x"],
        y=df["y"],
        mode="markers",
        marker=dict(
            symbol="circle",
            color=df["color"],
            size=18),
        )
    data = [trace]

    return(data)


# +
layout_anim = get_layout(
    duration_long=duration_long,
    duration_short=duration_short)
sliders_dict = get_sliders_init_dict(duration_short)

frames = []; data = []
for i_cnt, df_i in enumerate(df_list):
    traces_i = get_traces(df_i)


    # #####################################################
    # i_cnt = 0
    layout_i = go.Layout(

        annotations=[
            go.layout.Annotation(
                x=1,
                y=1,
                xref="x",
                yref="y",
                text="TEMP_" + str(i_cnt).zfill(3),
                showarrow=True,
                arrowhead=7,
                ax=0,
                ay=-40
                )
            ],

        )
    # #####################################################



    # #####################################################
    if i_cnt == 0: data.extend(traces_i)
    data_i = []; data_i.extend(traces_i)
    # frame_i = go.Frame(data=data_i, name=str(i_cnt))
    frame_i = go.Frame(data=data_i, name=str(i_cnt), layout=layout_i)
    frames.append(frame_i)
    slider_step_i = get_slider_step_i(i_cnt, duration_short)
    sliders_dict['steps'].append(slider_step_i)

# +
layout_anim["showlegend"] = True

fig = go.Figure(
    data=data,
    layout=layout_anim,
    frames=frames)
fig['layout']['sliders'] = [sliders_dict]

fig.show()