Skip to content

Fix drawing animated artist updated in selector callback #20977

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

Conversation

ericpre
Copy link
Member

@ericpre ericpre commented Sep 2, 2021

PR Summary

Fix drawing animated artists (external to selector artists itself), which are updated in selector callback - see changes in doc example: the animated line will not drawn if the selector doesn't draw it when updating itself.
I am not sure if it deserves a new feature entry.

PR Checklist

  • Has pytest style unit tests (and pytest passes).
  • Is Flake 8 compliant (run flake8 on changed files to check).
  • New features are documented, with examples if plot related.
  • Documentation is sphinx and numpydoc compliant (the docs should build without error).
  • Conforms to Matplotlib style conventions (install flake8-docstrings and run flake8 --docstring-convention=all).
  • [?] New features have an entry in doc/users/next_whats_new/ (follow instructions in README.rst there).
  • [?] API changes documented in doc/api/next_api_changes/ (follow instructions in README.rst there).

tacaswell and others added 30 commits August 28, 2021 13:35
Switch documented deprecations in mathtext by `__getattr__` deprecations
Co-authored-by: Tim Hoffmann <2836374+timhoffm@users.noreply.github.com>
…_output

API: rename draw_no_output to draw_without_rendering
Make warning for no-handles legend more explicit.
Remove unused HostAxes._get_legend_handles.
- parse() already normalizes prop=None to prop=FontProperties().
- We don't need to explicitly attach a canvas to a plain Figure() before
  calling savefig() anymore; FigureCanvasBase is always attached and
  handles the dispatching to the correct concrete canvas class.
According to the docs, this function was added in GTK 2.2, so probably
was only needed when we supported GTK2.

It also no longer exists in GTK4.
Since these examples don't need to support the very oldest GTK3, I took
the opportunity to rewrite them using the recommended Application-styled
model.
The `Gtk.Window.destroy` doesn't work, but `.close` correctly triggers
our cleanup.
ENH: Adding callbacks to Norms for update signals
BUG: Fix f_back is None handling
Small cleanups to math_to_image.
Mostly D401 (imperative mood) fixes, and a few other things.
…he artists to the `depending_artists` list attribute, we can update them in the `updating_background` and `update` methods.
@timhoffm
Copy link
Member

timhoffm commented Sep 2, 2021

I'm sorry I can't follow. Can you please give a bit more context:

  • Has this been a bug before or has this issue been introduced with the recent selector changes?

  • Do I understand correctly, that the user has to manually add artists to the selector?
    If so, we need a user comprehendable description which artists must be added. I'm afraid the note

    "With useblit=True or when the callback update an animated artist, the artist needs to be added"

    is not clear enough.

    From a usability perspective: Having to add artists so that the selector can reasonably interact with them is quite awkward. Have you thought about different solutions? (It might be that there are none and we actually need this workaround).

  • This needs some sort of user-facing documentation in the API. Usually, that would go into an Attributes section on the implementing class. Unfortunately, _SelectorWidget is private and does not show up in the docs. I don't have a good solution for that. May just copy&paste that to all Selectors?

@ericpre
Copy link
Member Author

ericpre commented Sep 3, 2021

I'm sorry I can't follow. Can you please give a bit more context:

Thanks @timhoffm, it seems that you are following as well as I do, and yes, it does need more context! :)

  • Has this been a bug before or has this issue been introduced with the recent selector changes?

The issue was already there:

  • in matplotlib 3.4.3, a similar workaround works: add the animated artists to the artists list.
  • in matplotlib 3.5.0b1, no workaround that I am aware of.

With #20693 the previous workaround doesn't work anymore because the artists property now returns a tuple.

  • Do I understand correctly, that the user has to manually add artists to the selector?
    If so, we need a user comprehendable description which artists must be added. I'm afraid the note

    "With useblit=True or when the callback update an animated artist, the artist needs to be added"

    is not clear enough.

Agreed, I will explain more once we figure out what should be done.

From a usability perspective: Having to add artists so that the selector can reasonably interact with them is quite awkward. Have you thought about different solutions? (It might be that there are none and we actually need this workaround).

I also find this approach odd and this issue has been puzzling me for a while without finding a clearly convincing solution. The other alternative I could think of is to figure out what are the animated of the figure and draw them all explicitly in the selector update method and also update_background. To a certain extent, it defies the purpose of blitting by redrawing artists which should be considered as being part of the background of the selector.

  • This needs some sort of user-facing documentation in the API. Usually, that would go into an Attributes section on the implementing class. Unfortunately, _SelectorWidget is private and does not show up in the docs. I don't have a good solution for that. May just copy&paste that to all Selectors?

Yes, makes sense.

@ericpre
Copy link
Member Author

ericpre commented Sep 23, 2021

Would it be possible to get this PR in for 3.5?
I think it should not be take too long to finish it but I would like to know if this approach is OK - as mentioned above, I am not very happy with this approach but I couldn't find a better alternative.

To reproduce the issue (from the example adapted in this PR):

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.widgets import SpanSelector
# Fixing random state for reproducibility
np.random.seed(19680801)
fig, (ax1, ax2) = plt.subplots(2, figsize=(8, 6))
x = np.arange(0.0, 5.0, 0.01)
y = np.sin(2 * np.pi * x) + 0.5 * np.random.randn(len(x))
ax1.plot(x, y)
ax1.set_ylim(-2, 2)
ax1.set_title('Press left mouse button and drag '
              'to select a region in the top graph')

line2, = ax2.plot([], [])
# if the line is animated, it will not be drawn
line2, = ax2.plot([], [], animated=True)


def onselect(xmin, xmax):
    indmin, indmax = np.searchsorted(x, (xmin, xmax))
    indmax = min(len(x) - 1, indmax)
    region_x = x[indmin:indmax]
    region_y = y[indmin:indmax]
    if len(region_x) >= 2:
        line2.set_data(region_x, region_y)
        ax2.set_xlim(region_x[0], region_x[-1])
        ax2.set_ylim(region_y.min(), region_y.max())
        fig.canvas.draw_idle()

span = SpanSelector(
    ax1,
    onselect,
    "horizontal",
    # useblit=True, # it doesn't matter if blitting is used or not
    props=dict(alpha=0.5, facecolor="tab:blue"),
    interactive=True,
    drag_from_anywhere=True
)

plt.show()

@ericpre
Copy link
Member Author

ericpre commented Oct 4, 2021

Anyone to review this PR? Once we are happy with the fix, I will finish the user-facing documentation.

@anntzer
Copy link
Contributor

anntzer commented Oct 12, 2021

I think I see the point of this PR, but I don't really like the solution: it is too manual and requires fiddling with rather internal parts of the library. I wonder if an alternative solution (approximately) like the following would be possible: when, in update_background(), self.canvas.draw() walks the artist tree, we additionally record any animated=True artist in a separate list; all such artists are automatically redrawn together with self.artists.

@ericpre
Copy link
Member Author

ericpre commented Oct 12, 2021

Yes, I have thought about something along these lines but I was concerned that it would redraw animated artists which wouldn't need to be redrawn and therefore defy the purpose of using animated artists and blitting. I consider it again but with using the stale attribute to filter artists which needs to be redrawn - see alternative fix in #21342.

@jklymak jklymak marked this pull request as draft October 13, 2021 06:12
@jklymak
Copy link
Member

jklymak commented Oct 13, 2021

Moved to draft since this seems superseded.

@tacaswell
Copy link
Member

This PR is affected by a re-writing of our history to remove a large number of accidentally committed files see discourse for details.

To recover this PR it will need be rebased onto the new default branch (main). There are several ways to accomplish this, but we recommend (assuming that you call the matplotlib/matplotlib remote "upstream"

git remote update
git checkout main
git merge --ff-only upstream/main
git checkout YOUR_BRANCH
git rebase --onto=main upstream/old_master
# git rebase -i main # if you prefer
git push --force-with-lease   # assuming you are tracking your branch

If you do not feel comfortable doing this or need any help please reach out to any of the Matplotlib developers. We can either help you with the process or do it for you.

Thank you for your contributions to Matplotlib and sorry for the inconvenience.

@ericpre
Copy link
Member Author

ericpre commented Oct 20, 2021

Closing in favour of #21342.

@ericpre ericpre closed this Oct 20, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

9 participants