Py.Cafe

maartenbreddels/

solara-plotly-lasso-bug

Sometimes on_selection does not trigger

DocsPricing
  • app.py
  • requirements.txt
app.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
import numpy as np
import pandas as pd
import plotly.graph_objects as go
import solara


def find_row_ids(fig, select, n_sel_max=99999):
    """A very annoying function to get row IDs because Plotly is unhelpful"""
    # goes from trace index and point index to row index in a dataframe
    # requires passing df.index as custom_data
    trace_index = select["points"]["trace_indexes"]
    point_index = select["points"]["point_indexes"]
    point_ids = []

    for t, p in zip(trace_index, point_index, strict=True):
        point_trace = fig.data[t]
        if point_trace.customdata is not None:
            point_ids.append(point_trace.customdata[0][p]) # For go.Scatter
    return point_ids[:n_sel_max]

# Create sample data
np.random.seed(42)
n_points = 10000
df = pd.DataFrame({
    'x': np.random.normal(0, 1, n_points),
    'y': np.random.normal(0, 1, n_points),
    'group': np.random.choice(['A', 'B', 'C'], n_points),
    'cell_idx': np.arange(n_points)
})

selected_points = solara.reactive([])
    
@solara.component
def MinimalScatterExample():
    
    # Create figure
    fig = go.Figure()
    select_point_ids = lambda data: selected_points.set(find_row_ids(fig, data))
    
    # Add scatter traces for each group
    for n, g in df.groupby(['group']):

        fig.add_trace(
            go.Scattergl(
                x=g.x,
                y=g.y,     
                mode='markers',
                selected=go.scattergl.Selected(marker={"color":"red", "size":5}),
                customdata=[df.cell_idx],
            )
        )
    
    # Update layout
    fig.update_layout(
        height=600,
        width=800,
        dragmode='lasso',  # Set default mode to lasso
        selectdirection='any',  # Allow selection in any direction
        hovermode='closest'
    )
    

    def configure():
        fig_widget = solara.get_widget(fig_element)
        fig_widget._config = fig_widget._config | {
            'modeBarButtonsToAdd': ['select2d', 'lasso2d'],
            'modeBarButtonsToRemove': ['autoScale2d'],
        }
    
    # Create figure element
    fig_element = solara.FigurePlotly(fig, on_selection=select_point_ids)
    solara.use_effect(configure)
    
    # Display number of selected points

    solara.Info(f"Number of selected points: {len(selected_points.value)}")

@solara.component
def Page():
    MinimalScatterExample()

# # Run the app
# if __name__ == "__main__":
#     Page()