-
-
Notifications
You must be signed in to change notification settings - Fork 1.9k
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
First attempt at a click anywhere #5138
Conversation
Example which adds a vertical line on clicking anywhere in the chart. Code of example
|
Thank you very much for this pull request, it's a very exciting feature :) We should probably chat about the external API for this before moving too much farther with it... Some concerns/questions I have:
I'd love to hear your thoughts on the above questions! |
Re If we created a new event (say Note that |
|
Re 1, that's a nice solution, I hadn't seen that, thanks :) @alexcjohnson can you provide a hint re 3? |
For point 3 I believe our discussion landed on something like "for the click point, generate a list of the x/y coordinates for the intersection of every x/y pair on the plot, and then filter than by some internal data structure that tracks the mapping of traces-to-x/y-pairs"? |
Regarding 1) there may still be a validation issue when creating a figure from a JSON or something. I stumbled into that once and have to dig it up again. |
re subplots: the code The I'm all in favor of starting by ignoring non-cartesian subplots for now, and coming back to these later. @sleighsoft you commented in #2696:
You should use the bounding box of plotly.js/src/components/fx/hover.js Lines 334 to 343 in 94b31a8
That will deal with positioning on the page as well as automargins. Also I notice you're using |
Yes, once a new flaglist attribute is added to the schema, Plotly.py's |
Offset has already been fixed as can be seen in the gif |
@alexcjohnson so I have to switch between p2c and p2d depending on axes type? |
src/components/fx/click.js
Outdated
if(data) { | ||
if(annotationsDone && annotationsDone.then) { | ||
// TODO(j) add gd._hoverdata to emitClick here | ||
annotationsDone.then(emitClick); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How do I pass data here. Not too familiar with JavaScript syntax
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
annotationsDone.then(emitClick); | |
annotationsDone.then(function() { emitClick(data); }); |
Not sure why the two tests fail. Help welcome:) |
I think what you have will still have problems if (a) something increases the margins beyond what's in
|
I like the idea of always reporting the exact position (c value) and if available/makes sense, then also report d values. This would be in line with the idea of click anywhere. Though maybe reporting c and d should be entirely separated and configurable by the user. I always have my use case in mind, where I want to put a vertical line in a time series, so I may be biased :) I'll try to update my example code to account for more of the cases discussed above. |
For |
Any suggestions on how/where to add tests for this as well as how to run test. I haven't researched that yet. |
Check out https://github.com/plotly/plotly.js/blob/master/test/jasmine/tests/click_test.js - it gives a lot of relevant example tests, and you can add new tests for this behavior there too. As to running the tests, see https://github.com/plotly/plotly.js/blob/master/CONTRIBUTING.md#jasmine-tests |
I am not quite sure I can follow the description in the link you provided.
Do I also have to clone |
Updated demo: import json
import time
from textwrap import dedent as d
import dash
from dash.dependencies import Input, Output, State
import dash_core_components as dcc
import dash_html_components as html
import plotly.graph_objects as go
import plotly.express as px
app = dash.Dash(__name__, assets_folder="/home/dev/plotly.js/build/")
styles = {"pre": {"border": "thin lightgrey solid", "overflowX": "scroll"}}
def scatter_with_int_xaxis():
return go.Figure(
{
"data": [
{
"x": [1, 2, 3, 4],
"y": [4, 1, 3, 5],
"mode": "lines+markers",
"marker": {"size": 12},
},
],
"layout": {"clickmode": "event+anywhere"},
}
)
def scatter_with_date_xaxis():
return go.Figure(
{
"data": [
{
"x": [time.time() + 60 * i for i in range(4)],
"y": [4, 1, 3, 5],
"mode": "lines+markers",
"marker": {"size": 12},
},
],
"layout": {
"clickmode": "event+anywhere",
"xaxis": dict(type="date", tickmode="auto"),
},
}
)
def bar_chart_with_date_xaxis():
data_canada = px.data.gapminder().query("country == 'Canada'")
fig = px.bar(data_canada, x="year", y="pop")
fig.update_layout(clickmode="event+anywhere")
return fig
def add_vertical_line_shape(clickData, figure):
x = clickData["points"][0]["x"]
shapes = figure["layout"].setdefault("shapes", [])
shapes.append(
dict(
type="line",
x0=x,
y0=0,
x1=x,
y1=1,
yref="paper",
line=dict(color="RoyalBlue", width=3),
)
)
app.layout = html.Div(
className="row",
children=[
dcc.Graph(
id="scatter_with_int_xaxis",
className="six columns",
figure=scatter_with_int_xaxis(),
),
html.Div(
className="row",
children=[
html.Div(
[
dcc.Markdown("**Click Data**"),
html.Pre(
id="click-scatter_with_int_xaxis", style=styles["pre"]
),
],
className="three columns",
),
],
),
dcc.Graph(
id="scatter_with_date_xaxis",
className="six columns",
figure=scatter_with_date_xaxis(),
),
html.Div(
className="row",
children=[
html.Div(
[
dcc.Markdown("**Click Data**"),
html.Pre(
id="click-scatter_with_date_xaxis", style=styles["pre"]
),
],
className="three columns",
),
],
),
dcc.Graph(
id="bar_chart_with_date_xaxis",
className="six columns",
figure=bar_chart_with_date_xaxis(),
),
html.Div(
className="row",
children=[
html.Div(
[
dcc.Markdown("**Click Data**"),
html.Pre(
id="click-bar_chart_with_date_xaxis", style=styles["pre"]
),
],
className="three columns",
),
],
),
],
)
@app.callback(
Output("click-scatter_with_int_xaxis", "children"),
[Input("scatter_with_int_xaxis", "clickData")],
)
def display_click_scatter_with_int_xaxis(clickData):
return json.dumps(clickData, indent=2)
@app.callback(
Output("scatter_with_int_xaxis", "figure"),
[Input("scatter_with_int_xaxis", "clickData")],
[State("scatter_with_int_xaxis", "figure")],
)
def add_shape_to_scatter_with_int_xaxis(clickData, figure):
print(figure)
print(clickData)
if clickData:
add_vertical_line_shape(clickData, figure)
return figure
@app.callback(
Output("click-scatter_with_date_xaxis", "children"),
[Input("scatter_with_date_xaxis", "clickData")],
)
def display_click_scatter_with_date_xaxis(clickData):
return json.dumps(clickData, indent=2)
@app.callback(
Output("scatter_with_date_xaxis", "figure"),
[Input("scatter_with_date_xaxis", "clickData")],
[State("scatter_with_date_xaxis", "figure")],
)
def add_shape_to_scatter_with_date_xaxis(clickData, figure):
print(figure)
print(clickData)
if clickData:
add_vertical_line_shape(clickData, figure)
return figure
@app.callback(
Output("click-bar_chart_with_date_xaxis", "children"),
[Input("bar_chart_with_date_xaxis", "clickData")],
)
def display_click_bar_chart_with_date_xaxis(clickData):
return json.dumps(clickData, indent=2)
@app.callback(
Output("bar_chart_with_date_xaxis", "figure"),
[Input("bar_chart_with_date_xaxis", "clickData")],
[State("bar_chart_with_date_xaxis", "figure")],
)
def add_shape_to_bar_chart_with_date_xaxis(clickData, figure):
print(figure)
print(clickData)
if clickData:
add_vertical_line_shape(clickData, figure)
return figure
if __name__ == "__main__":
app.run_server(debug=True) |
I know this is slightly off-topic here, but how would I achieve something like this: https://community.plotly.com/t/moving-shapes-with-mouse-in-plotly-js-reactjs/11457? Again, also willing to add this functionality to plotly if not too difficult. |
Hi @sleighsoft, I'm sorry I'm so behind on answering your questions!
Doing development across all three layers here is pretty tricky, indeed :( You'll have to push your branch up to Github and wait for the Plotly.js CI to run, then you'll have to clone plotly.py and run the commands in the readme and install that locally, then you'll have to build a Dash app with the |
@nicolaskruchten Thank you for getting back to me :) Puh... that sounds like a terrible number of steps to follow. I hope I'll find the time to continue this, though it probably won't be within the next 2-3 weeks. |
Sorry I am pretty new to Dash. I am currently doing a project whereby I would like to annotate the figure with a line or circle when the user clicks anywhere on the graph. I tried to use the above code, but however I am only able to annotate where there are points in the graph (refer to above picture). May I know if I am missing anything out ? Thank you for the help in advance ! |
This adds a click anywhere feature as proposed in #2696
This PR is meant as a point of discussion. Feel free to leave feedback.