-
-
Notifications
You must be signed in to change notification settings - Fork 594
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
ui.plotly plotly_selected
event not triggered if many points are selected
#3762
Comments
I did a bit more research on the problem and wanted to find out where the event is no longer triggered Could the reason be that there is a message size limitation for the websocket? So that if too many points are selected, the event is too big and gets lost? ui.run_javascript("""
setTimeout(function() {
var plotElement = document.querySelector('div.js-plotly-plot');
if (plotElement) {
plotElement.on('plotly_selected', function(eventData) {
console.log('plotly_selected event detected:', eventData);
});
} else {
console.log('Plotly plot element not found.');
}
}, 1000);
""") |
You could try to change the |
That's interesting: The "plotly_selected" event arguments have the following structure (here for selecting 2 out of 50 points): {
'points': [
{'data': {'marker': {'opacity': 0.6, 'size': 10}, 'mode': 'markers', 'name': 'Original Data', 'x': [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], 'y': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 'type': 'scattergl', 'selectedpoints': [0, 1]}, 'curveNumber': 0, 'pointNumber': 0, 'pointIndex': 0, 'x': 1, 'y': 0},
{'data': {'marker': {'opacity': 0.6, 'size': 10}, 'mode': 'markers', 'name': 'Original Data', 'x': [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], 'y': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 'type': 'scattergl', 'selectedpoints': [0, 1]}, 'curveNumber': 0, 'pointNumber': 1, 'pointIndex': 1, 'x': 2, 'y': 0}
],
'lassoPoints': {'x': [0.40599744789451314, 0.48936622713738853, 1.114632071458954, 1.7398979157805194, 2.1150574223734586, 2.4068481497235226, 2.323479370480647, 2.3651637601020847], 'y': [-0.17037037037037037, -0.05925925925925926, 0.16296296296296298, 0.28888888888888886, 0.2814814814814815, 0.13333333333333333, -0.05925925925925926, -0.07407407407407407]},
'selections': [{'xref': 'x', 'yref': 'y', 'line': {'width': 1, 'dash': 'dot'}, 'type': 'path', 'path': 'M0.40599744789451314,-0.17037037037037037L0.48936622713738853,-0.05925925925925926L1.114632071458954,0.16296296296296298L1.7398979157805194,0.28888888888888886L2.1150574223734586,0.2814814814814815L2.4068481497235226,0.13333333333333333L2.323479370480647,-0.05925925925925926L2.3651637601020847,-0.07407407407407407Z'}]
}
So the question is, how to get the information about selected points without lots of unnecessary copies of point clouds. Do we need to introduce some kind of For reference, a condensed reproduction: ui.plotly({
'data': [{
'x': np.concatenate([np.arange(1, 51), np.random.uniform(-10, 0, 1000)]),
'y': np.concatenate([np.zeros(50), np.random.uniform(-10, 0, 1000)]),
'mode': 'markers',
}],
'layout': {'dragmode': 'lasso'},
}).on('plotly_selected', lambda e: print(e.args)) |
Thanks, that was also my first guess. Setting
But it would make sense if the buffer size is the bottleneck, because the problem only occurs for larger amounts of data |
If you add
|
I have actually found a reliable solution for the issue:
Note: I'm not very familiar with js, so I'm sure there are simpler/nicer solutions. But it works for me :) Fully working solution for #3762import numpy as np
from nicegui import ui, app
from fastapi import Request
# needed for explicit ui context
container = ui.element()
ui.plotly(
{
"data": [{
"x": np.concatenate([np.arange(1, 51), np.random.uniform(-10, 0, 1000)]),
"y": np.concatenate([np.zeros(50), np.random.uniform(-10, 0, 1000)]),
"mode": "markers",
}],
"layout": {"dragmode": "lasso"},
}
)
def setup_js():
ui.run_javascript(
"""
const plotElement = document.querySelector('div.js-plotly-plot');
if (plotElement) {
plotElement.on('plotly_selected', eventData => {
if (eventData?.points) {
const selectedIndices = eventData.points.map(p => p.pointIndex);
fetch('/select', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ selectedIndices })
});
}
});
} else {
fetch('/log_error', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ message: 'Plotly plot element not found.' })
});
}
"""
)
# API route that handles the POST request
@app.post("/select")
async def select(request: Request):
data = await request.json()
selected_indices = data["selectedIndices"]
print(f"Selected point indices: {selected_indices}")
notify_ui(selected_indices)
@app.post("/log_error")
async def log_error(request: Request):
data = await request.json()
print(f"Client Error: {data['message']}")
def notify_ui(selected_indices):
if selected_indices:
with container:
ui.notify(f"Selected point indices: {selected_indices}")
app.on_connect(setup_js)
ui.run(reload=True) |
A small addition, as I noticed that it is much easier to trigger custom events: ui.add_head_html(
"""
<script>
document.addEventListener('DOMContentLoaded', function () {
var plot = document.getElementsByClassName('js-plotly-plot')[0];
plot.on('plotly_selected', function(eventData) {
var pointIndices = eventData.points.map(function(point) {
return point.pointIndex;
});
emitEvent('custom_point_selection', pointIndices);
});
});
</script>
"""
)
ui.on("custom_point_selection", handle_selected_points) |
@flooxo That's a great idea to filter the event data on the client before emitting a custom event to the server. You can even further simplify the code by defining a ui.plotly({
'data': [{
'x': np.concatenate([np.arange(1, 51), np.random.uniform(-10, 0, 1000)]),
'y': np.concatenate([np.zeros(50), np.random.uniform(-10, 0, 1000)]),
'mode': 'markers',
}],
'layout': {'dragmode': 'lasso'},
}).on('plotly_selected', js_handler='''
(event) => emitEvent('custom_points_selected', event.points.map(point => point.pointIndex));
''')
ui.on('custom_points_selected', lambda e: ui.notify(f'Selected {len(e.args)} points')) |
@falkoschindler Thanks! I've also come across |
What are you trying to do?
I want to select points in a Plotly plot in NiceGUI and, based on the selection, convert the selection into a shape and display it in the plot. This is all working so far.
However, I happened to run into the problem that if there are more than 1000 data points plotted in the plot and I select more than 20 points with the lasso select, the
plotly_selected
event is no longer triggeredMinimal code
The points are specially created so that it is easier to count how many points have been selected in the positive area
What do you expect to happen?
event is triggered
What happens instead?
no event is triggered
The text was updated successfully, but these errors were encountered: