Skip to content

add_vline/hline annotation text location fixed to first shape if using a pre-instantiated dict for the "annotation" input parameter #3547

Open

Description

@Jamie-B22

Hi

When I instantiate a dict once with some constant formatting that I want to pass to the "annotation" argument for several calls of .add_vline() (or .add_hline()), all annotations for shapes created with this dict are placed with the shape created by the initial .add_vline() call.

e.g.:

import plotly.graph_objects as go

fig = go.Figure()

y_1 = [3000, 3500, 4000, 3500, 3000, 3500]
x_1 = list(range(len(y_1)))
fig.add_trace(go.Scatter(x=x_1, y=y_1))


annotation = dict(font_color="#000000")

fig.add_vline(x=2, annotation_text="vline 1", annotation=annotation)

print(f"annotation after first .add_vline():\n{annotation}")

fig.add_vline(x=3, annotation_text="vline 2", annotation=annotation)

print(f"annotation after second .add_vline():\n{annotation}")


fig.write_html("graph.html")

produces graph and output (similar result if .add_hline() used instead) - expected graph can be seen at the end of this post:
image

annotation after first .add_vline():
{'font_color': '#000000', 'text': 'vline 1', 'xanchor': 'left', 'yanchor': 'top', 'x': 2, 'y': 1, 'showarrow': False}
annotation after second .add_vline():
{'font_color': '#000000', 'text': 'vline 2', 'xanchor': 'left', 'yanchor': 'top', 'x': 2, 'y': 1, 'showarrow': False}

Is this expected behaviour? As the x coordinate is explicitly given in the method I would expect that the annotation defined would be placed at that x, as happens with the different annotation_text values.

As we can see from the output, it is because the original annotation dictionary instance is being mutated in the process of adding the annotation. Then if an x value is present in the annotation dictionary, it is not overwritten, but the text value is if a new annotation_text value is passed.

Looking at the package code, I can achieve the behaviour I expected by implementing an else here:

if annotation is None:
annotation = dict()

Suggestion:

    if annotation is None:
        annotation = dict()
    else:
        annotation = annotation.copy()

I have made this change locally and run fine and as I expect.

If the current behaviour is not the expected behaviour, and if the suggested change above is acceptable, I would be glad to implement and test it appropriately then submit a pull request.

Cheers!


Graph I expect:
image

Activity

  1. Jamie-B22 commented on Jan 14, 2022

    @Jamie-B22
    Author

    It just occurred to me that the fix I suggested above would still preserve the discrepancy in behaviour between not updating an x key value if it exists, but updating the text key value. It simply allows the annotation dict instance to be reused in a case like this.

    It seems the discrepancy between how the x and text values are set in the method are from the lines below:
    text is set here:

    for k in annotation_keys:
    if k == "annotation_position":
    # don't set so that Annotation constructor doesn't complain
    continue
    subk = k[len_prefix:]
    annotation[subk] = kwargs[k]

    and x is set here:

    for k in shape_dict.keys():
    # only set property derived from annotation_position if it hasn't already been set
    # see above: this would be better as a go.layout.Annotation then the key
    # would be checked for validity here (otherwise it is checked later,
    # which I guess is ok too)
    if (k not in annotation) or (annotation[k] is None):
    annotation[k] = shape_dict[k]

    I'm not sure exactly what the comment is trying to say, but it seems to indicate that there is some doubt as to whether this is the best way to set the annotation dict items. Is there any reason that the if statement cannot be removed or changed to allow parameters explicitly passed to the .add_vline()/hline() methods to be reflected in the annotation dict here? Or should the go.layout.Annotation option suggested be implemented?

  2. self-assigned this
    on Jun 26, 2024
  3. removed their assignment
    on Aug 2, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    P3backlogbugsomething broken

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions