How to drag and drop signals or legends in plotly

I use Plotly to display my vcd as waveform I was successful in showing the waveform in the viewer but now I want the signals to be reorders as per user preference

My update_graph callback that renders the waveform viewer is as below

def update_graph(json_data, selected_signals, relayout_data, annotations, preview_marker, 
annotations_by_signal, marker_store,index):
signalvalue = 0
if not json_data:
    return go.Figure(), annotations

df = pd.read_json(StringIO(json_data))
if df.empty:
    return go.Figure(), annotations

if not selected_signals:
    signalvalue = 1
    selected_signals = df.columns[df.columns != "Time"]

if relayout_data and 'xaxis.range' in relayout_data:
    min_time, max_time = relayout_data['xaxis.range']
else:
    min_time, max_time = df["Time"].min(), df["Time"].max()

time_range = max_time - min_time
base_steps = [1000, 500, 200, 100, 50, 20, 10, 5, 2, 1]
step_size = max([s for s in base_steps if time_range / s > 5], default=10)

fig = go.Figure()
y_offset = {signal: idx * 2 for idx, signal in enumerate(selected_signals)}

for signal in selected_signals:
    fig.add_trace(go.Scatter(
        x=df["Time"],
        y=df[signal],
        mode="lines+markers",
        name=signal,
        line_shape="hv",
        hoverinfo="x+y",
        customdata=[signal]*len(df)
    ))

    
""" for x_value in df["Time"]:
    fig.add_vline(x=x_value, line_dash="dot", line_color="gray", line_width=1)
 """


# Add preview marker
if preview_marker:
    fig.add_trace(go.Scatter(
        x=[preview_marker["x"]],
        y=[preview_marker["y"]],
        mode="markers",
        marker=dict(color="yellow", size=12, symbol="circle"),
        name="Preview Marker",
        showlegend=False,
        hoverinfo="skip"
    ))
# Add final markers
marker_annotation=[]
if marker_store:
    for marker in marker_store:
        if marker["signal"] in selected_signals:
            try:
                #y_val = df[marker["signal"]][df["Time"] == marker["x"]].values[0] + y_offset[marker["signal"]]
                fig.add_trace(go.Scatter(
                    x=[marker["x"]],
                    y=[marker["y"]],
                    mode="markers",
                    marker=dict(color="red", size=10, symbol="circle"),
                    name="Marker",
                    showlegend=False,
                    hoverinfo="x+y",
                ))
                marker_annotation.append(dict(
                x=marker["x"],
                y=marker["y"],
                text=marker["custom"],  
                showarrow=True,
                arrowhead=2,
                ax= 0,
                ay= -30,
                arrowcolor='#FFFFFF',
                font=dict(color='#FFFFFF')
            ))
            except IndexError:
                continue
# Add annotations from annotation store
updated_annotations = []
if signalvalue == 0:
    for ann_list in annotations_by_signal.values():
        for ann in ann_list:
            for signal in selected_signals:
                if signal in ann['text']:
                    try:
                        ann['y'] = df[signal][df["Time"] == ann["x"]].values[0] + y_offset[signal]
                        if not any(existing_ann['x'] == ann['x'] and existing_ann['y'] == ann['y'] for existing_ann in updated_annotations):
                            updated_annotations.append(ann)
                    except IndexError:
                        continue
                    break

delta_annotation = None
if len(marker_store) >= 2:
    m1 = marker_store[-2]
    m2 = marker_store[-1]

    x1 = m1["x"]
    x2 = m2["x"]
    delta = abs(x2 - x1)
    x_middle = (x1 + x2) / 2

    fig.add_vline(x=x1, line_dash="dash", line_color="red")
    fig.add_vline(x=x2, line_dash="dash", line_color="red")

    delta_annotation = {
    "x": x_middle,
    "y": 0,
    "text": f"Ξ”t = {delta} ns",
    "showarrow": False,
    "font": dict(color="white", size=12),
    "bgcolor": "rgba(0,0,0,0.6)",
    "bordercolor": "white",
    "borderwidth": 1
}

if delta_annotation:  
    updated_annotations.append(delta_annotation)

if marker_annotation:
    updated_annotations.extend(marker_annotation)

zoom_range = relayout_data.get('xaxis.range', None)
if zoom_range:
    min_time, max_time = zoom_range
else:
    min_time, max_time = df["Time"].min(), df["Time"].max()

fig.update_layout(
    title="Waveform",
    xaxis=dict(range=[min_time, max_time], showgrid=True, zeroline=False),
    yaxis = dict(
    tickmode = 'array',
    tickvals=[df[signal].min() for signal in selected_signals],
    ticktext=selected_signals,
    ),
    template="plotly_dark",
    hovermode="x unified",
    annotations=updated_annotations,
    dragmode="pan",
    uirevision="same",
    autosize=True
)

and it gives waveform like below


in this how to achieve if the user wants to move signal ctr to top and place below p4

Hey @vidhya_vishwa welcome to the forums.

Are you referring to the order in the legend or on the y- axis?

Is this part of a dash app? Or how would you know about the user preference?

Hi ,
i expect to reorder the signal on y axis as this is not directly possible I thought of having the legend and make it draggable and then make the graph update the order that is there in the legend . User preference is like I want this to be dynamic the user can drag any signal up and down based on his requirement

I don’t think its possible to do that. In general, plotly creates the graph based on your data. Since each curve of your graph has x-y pairs, you would need to change the y values.

1 Like