Skip to content
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

DOC: add a blitting tutorial #10187

Merged
merged 3 commits into from Jun 16, 2020
Merged

Conversation

tacaswell
Copy link
Member

PR Summary

First pass at adding a more through blitting tutorial than is currently in the animation docs.

Had some thoughts about hardening BlitManager a bit more and putting into the library, but was not sure where to put it and starting in a tutorial seems like a lower-stakes place to start.

ax.draw_artist(ln)
# copy the result to the screen
fig.canvas.blit(fig.bbox)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, I ran this using Qt5Agg and an axis pops up with the default axis limits, but nothing is drawn 😢 If I run on MacOSX nothing is popped up. The loop runs, as tested via a print statement...

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you add plt.ion to the top does it work?

Adding plt.pause(.1) in here may also help...

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did:

x = np.linspace(0, 2*np.pi, 100)

fig, ax = plt.subplots()

plt.ion()
# animated=True makes the artist be excluded from normal draw tree
ln, = ax.plot(x, np.sin(x), animated=True)
# stop to admire our empty window axes and ensure it is drawn
plt.pause(.1)

it still doesn't work.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bump the iteration from 100 to 1000. It runs to fast that you almost do not see anything.
A pause in the loop might help. I found that plt.show() at the end do not destroy the figure when the loop is finished.

Is there another way to do this without using the plt.pause() function? The function doc says it is an experimental function.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No window update at all happens, and the original artist is never drawn; just an empty window: Qt5Agg, MacOSx, TkAgg, on master, MacOSX 10.13.2. The loop certainly runs, and terminates normally, but the plot is never drawn or animated...

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe there could be a problem with copying the background. For example the window get resized after the first copying.
Though you should still be able to see the plot.
Have you tried the method in the example with dynamic update of background?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, hmmm. I'm on a hidpi monitor (retina). Is that what you mean?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This works for me if I add plt.ion() at the top, using Qt5Agg.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I do python blitting.py as-is I see animation with both tkagg and qt5agg on master and 3.2.1.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just chiming in that this works on my computer as-is.

@lkjell
Copy link
Contributor

lkjell commented Jan 10, 2018

The window manager to macOS might resize the figure canvas after matplotlib copy the background the first time. That is what I meant. If the other method in the example works for you then the problem with background could be the issue.

@jklymak
Copy link
Member

jklymak commented Jan 10, 2018

The other example (BlitManager) at least shows the first plot. But does not update that plot. i.e. sine wave does not progress and the count stays at 0.

@lkjell
Copy link
Contributor

lkjell commented Jan 10, 2018

Try this.

fig = plt.figure(figsize=(8,6))
ax = fig.add_subplot(111)

#Force a draw
fig.canvas.draw()
plt.show(block=False)


x = np.arange(100)

# animated keyword can be omitted
ln, = ax.plot(x, np.sin(x), animated=True)
# add a frame number
fr_number = ax.annotate('0', (0, 1),
                        xycoords='axes fraction',
                        xytext=(10, -10),
                        textcoords='offset points',
                        ha='left', va='top',
                        animated=True)
bm = BlitManager(fig.canvas, [ln, fr_number])

for j in range(100):
    # update the artists
    ln.set_ydata(np.sin(x + (j / 100) * np.pi))
    fr_number.set_text('frame: {j}'.format(j=j))
    # tell the blitting manager to do it's thing
    bm.update()

plt.show()

@lkjell
Copy link
Contributor

lkjell commented Jan 10, 2018

If that still a problem then there is something wrong with the cv.flush_events()

@jklymak
Copy link
Member

jklymak commented Jan 10, 2018

Oops, my apologies. The second BlitManager example works fine with Qt5Agg. Neither it, nor your modification, work on macOSX.

However the first example above does not work on any backends (for me).

@lkjell
Copy link
Contributor

lkjell commented Jan 10, 2018

The first example lack dynamic background update thus my previous comment on that.

The second example I believe the flush_event is the culprit. Could try to embed the figure in a macOS backend canvas and see if it works.

@tacaswell tacaswell modified the milestones: needs sorting, v2.2-doc May 4, 2018
@dstansby dstansby modified the milestones: v2.2-doc, v3.1-doc May 19, 2019
@tacaswell tacaswell modified the milestones: v3.1-doc, v3.3.0 Jan 13, 2020
tutorials/intermediate/blitting.py Outdated Show resolved Hide resolved
tutorials/intermediate/blitting.py Outdated Show resolved Hide resolved
tutorials/intermediate/blitting.py Outdated Show resolved Hide resolved
tutorials/intermediate/blitting.py Outdated Show resolved Hide resolved
tutorials/intermediate/blitting.py Outdated Show resolved Hide resolved
tutorials/intermediate/blitting.py Outdated Show resolved Hide resolved
tutorials/intermediate/blitting.py Outdated Show resolved Hide resolved
tutorials/intermediate/blitting.py Outdated Show resolved Hide resolved
ax.draw_artist(ln)
# copy the result to the screen
fig.canvas.blit(fig.bbox)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This works for me if I add plt.ion() at the top, using Qt5Agg.

@tacaswell
Copy link
Member Author

I rebased and force-pushed to get the updated CI setup.

Copy link
Contributor

@brunobeltran brunobeltran left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@tacaswell somehow guessed that I've never used the event loop/animation stuff before. And I didn't even know what "blitting" was before reading this, so here's a take from a complete neophyte.

Overall, a very useful tutorial, I would definitely advocate for it being merged. The main things that would make these docs cleaner would be to use this as an excuse to remove the whole blitting section from the animation API docs (just link to this tutorial), and to make sure that this tutorial's purpose is clearly stated at the outset, for people who are landing on this docs page directly from google (and not being linked from the animation api docs).

tutorials/intermediate/blitting.py Outdated Show resolved Hide resolved
Simple Example
--------------

We can implement this via methods on `.FigureCanvasAgg` and setting
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this work with the Cairo backend? Should there be a sentence just mentioning that we're doing bit-wise blitting here, so this only makes sense for rasterizing renderers?

Up to this point, I had no incentive to click the wiki link, so it seemed like this procedure was general enough that I could use it also for saving large batches of rasterized images, because nothing in "draw non-animated-objects, save canvas state, draw animated objects, restore state..." seems to require pixels. I can see someone going through this then trying to use it to save a bunch of PDFs faster, only to realize that copy_from_bbox works with pixels directly.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, alternatively, we could keep some of the wording in the Animation api docs (which should probably go), insert something above like "The general gist is to take an existing bit map (in our case a mostly rasterized figure) and then 'blit' one more artist on top", to make clear that we're doing this with bit maps.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 to adding something about "some backends support it and some do not, makes the most sense with raster based backends".

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added a note in the previous section about this (and how to check if your canvas supports it).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

see #17478 :)

@@ -55,7 +55,7 @@ performance), to be non-blocking, not repeatedly start/stop the GUI
event loop, handle repeats, multiple animated axes, and easily save
the animation to a movie file.

'Blitting' is a `old technique
'Blitting' is a `standard technique
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this example code cannot really be actually used (it would fail if the screen is resized, for example), and now we explain blitting much more in-depth in a separate tutorial, what is the purpose of having a more complicated version of what amounts to pseudocode here?

Either way, we should make "handle 'blitting'" above a link to the blitting tutorial. But once that link is there, does the average Animation user gain anything except for an unsolicited lesson on rendering by being shown an outline of how blitting works?

I think if the point is to convince people to set blit=True, then we should just mention that we always suggest doing so, and to see the tutorial for details on the implementation if you're interested.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the goal of the mini-blitting intro here is to justify why the signature of the callback in FuncAnimation is what it is.

I don't disagree that this should be re-written, but would rather not do it in this (already pretty old) PR.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The other ways of doing the callback function should also be written out...

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I guess my top-level comment is that I don't feel like that sections adds anything that you wouldn't also get if you just changed

If blit == True, func must return an iterable of all artists that were modified or created.

to

If blit == True, then only modified artists are re-drawn, and func must return an iterable of all artists that were modified or created.

And, frankly, the docs feel like they be equally useful with that existing section totally removed now that you have a blitting tutorial, even without the above change.

Now that you've added a blitting tutorial, it feels like it detracts from the docs by serving as a lot of unnecessary "noise" to describe something that the user doesn't even need to understand to correctly use the function.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm going to stick to doing this removal in a second pass.

tutorials/intermediate/blitting.py Outdated Show resolved Hide resolved
# animated=True makes the artist be excluded from normal draw tree
(ln,) = ax.plot(x, np.sin(x), animated=True)

# stop to admire our empty window axes and ensure it is drawn
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I very much enjoy this kind of lighthearted tone a lot in tutorials, but people arriving here from the Animation api docs may not know about plt.pause(). plt.pause is one of the methods in mpl whose name differs most from its purpose (here, just flushing the draw tree), and I don't think "ensure it is drawn" will be enough to cue people in who haven't seen this before into what's happening here.

Suggested change
# stop to admire our empty window axes and ensure it is drawn
# plt.pause is the easiest way to trigger the figure to draw all non-animated
# artists, and let's us stop to admire our empty window axes

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As documented in #4779 (which I'm also trying to get across the finish line!). If you think of plt.pause as primarily suspending the execution of your script (by running the event loop) pause does make sense.

I'll re-word this a bit, bu I think that this suggestion is a bit misleading in other ways (as plt.pause will only trigger a draw on the current figure if it is stale)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assume it makes perfectly good sense given the correct mental model of the event loop (since you guys wrote it ;)). Unfortunately, it does not make intuitive sense IME to people (like me) who don't know how the event loop works in mpl, and just want to have a way to do what "plt.show" sounds like it does (which is what it "feels" like you're doing here).

Please feel free to change the wording to more appropriately describe how one should be thinking about what "plt.pause" is doing here. My top-level suggestion is that, if possible, someone who is new to mpl and reading this (and likely has no mental model for the event loop) should be able to understand what plt.pause is doing in this context.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a plt.show(block=False) and tweak the comments here a bit.

tutorials/intermediate/blitting.py Outdated Show resolved Hide resolved
ax.draw_artist(ln)
# copy the result to the screen
fig.canvas.blit(fig.bbox)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just chiming in that this works on my computer as-is.

animated=True,
)
bm = BlitManager(fig.canvas, [ln, fr_number])
plt.pause(0.1)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe add a comment like "plt.pause can go anywhere before the first call to bm.update"? 99% of people are going to skip the huge class definition and just straight to the example so they can copy/paste the class and use it themselves (assuming they made it this far, they're probably either just curious or need to do something the Animation classes are giving them trouble with?)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably should replace this with lower-level things to get the figure on the screen.

# update the screen
cv.blit(fig.bbox)
# let the GUI event loop process anything it has to do
cv.flush_events()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've never worked with the event loop. Is there some incantation like

fig.draw()
cv.flush_events()

that could be used instead of plt.pause in the original simple example? It would make a lot more semantic sense (and avoid confusion of introducing new concepts in the class version that weren't used in the simpler example above).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, I don't think so. What plt.pause is primarily doing above is managing the event loop (because in the first example we know that the script is in charge so it is ok to start / stop the event loop). In this case it is meant to be a more general-purpose bit of code that can easily be embedded in other contexts so we should do the least invasive thing (which is to spin the event loop "by hand").

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Below you write "Probably should replace this with lower-level things to get the figure on the screen."

That's probably all I was looking for, actually.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ended up going "higher level", but I think it is clearer now.

tutorials/intermediate/blitting.py Outdated Show resolved Hide resolved
tutorials/intermediate/blitting.py Outdated Show resolved Hide resolved
Copy link
Contributor

@brunobeltran brunobeltran left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM.

w.r.t. my long-winded comment, I think this tutorial should just go out. If you don't agree with the suggestion (or, as is more likely, my suggestion still belies a fundamental misunderstanding of the event loop), I can open a separate PR against this tutorial to discuss how to clarify it :)

Comment on lines 58 to 70
# make sure the window is raised, but the script keeps going
plt.show(block=False)
# stop to admire our empty window axes and ensure it is rendered at
# least once
plt.pause(0.1)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This sequence now makes a lot more sense for a beginner.

I think this is good to go in as-is.

However, there's still some easily confusing things here (like, you can remove the pause if you run this code line-by-line and it will work, but if you paste all the lines together into ipython at once it fails, presumably due to not being in interactive mode). Again, I don't think this should block this tutorial from going in (something about perfect and enemies and good), as it's a valuable addition. But it took me an hour just now to figure out how to make any changes to this example without breaking it (and I had to read the source and inspect private runtime attributes of pyplot to do so). So maybe consider the following version (if I'm not still totally off-base about how to use the interactive interface)?

# "draw" all non-interactive artists onto the canvas
fig.canvas.draw()
# make sure our backend knows to display our canvas onto the screen
plt.show(block=False)
# let the event loop spin quickly, to process e.g. window resizing events
# (this is mandatory for tiling window managers)
fig.canvas.start_event_loop(0.1)  # runs GUI for 0.1s

Saying "fig.canvas.start_event_loop" instead of "plt.pause" adds even more semantic clarity on top of your explicit call to "plt.show" (which had already made this example much more clear, IMO). And it seems like the only other thing in pyplot.pause is a call to fig.canvas.draw_idle, which is necessary to cache the renderer so that later calls to draw_artist will work.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Which backend were you using that didn't work when pasted in to IPython and what OS? I get the expected behavior with Qt5Agg.

What were you trying to change that went badly?

I would rather not drop to the lower-level things at this point if we can avoid it.

But this does suggest I should add start_event_loop to #4779 🤦

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that mention in the new docs is the only reason I was ever able to get this to work. The sequence of events of "things that went wrong" is complicated enough that I pasted it below so it doesn't get buried in comments here.

Copy link
Contributor

@brunobeltran brunobeltran left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Weird, my copy-edits didn't get included in the other review, probably because they were made in a separate window? Anywho, just copy-edits.

# restoring the background, drawing the artists, and then blitting the
# result to the screen. Additionally, we can use the ``'draw_event'``
# callback to capture a new background whenever a full re-draw
# happens.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe end this with "this solves the issues with screen resizing mentioned above"? I think that's not clear (I initially even left a comment reminding you to mention that this still does not fix that very problem before I tested it and realized it does).

tutorials/intermediate/blitting.py Outdated Show resolved Hide resolved
@tacaswell
Copy link
Member Author

A re-size will always trigger a full re-draw. After the draw is finished, but before control is returned to the user the draw_event callbacks fire which gives us a chance to both stash the new background and to draw the animated artists 'by hand'.

@brunobeltran
Copy link
Contributor

brunobeltran commented May 21, 2020

So given that I'm the target audience (having never heard of blitting or really used interactive matplotlib before), here is my interaction with the toy example presented here. I'm on Debian Jessie with Qt5Agg, using iPython from pip.

  1. removed plt.pause to see what would happen (since the docs make it sound kinda like a sanity check), and the code still worked.
  2. removed plt.show, confirmed that the code runs but never displays anything, cool.
  3. tried to reposition bg = ... to before the "show" and crashes iPython. Okay, fine, something needs to draw first or something, I accept that.
  4. now I'm iterating faster, and pasting larger chunks of code. I %paste in everything in the example up to the for loop, except the plt.pause, since I've confirmed that removing this should still allow the code to work, but I get
----------------------------------------------------------
AttributeError           Traceback (most recent call last)
<ipython-input-11-971e34c12db6> in <module>
     17 bg = fig.canvas.copy_from_bbox(fig.bbox)
     18 # draw the animated artist
---> 19 ax.draw_artist(ln)
     20 # show the result to the screen
     21 fig.canvas.blit(fig.bbox)

~/developer/matplotlib/lib/matplotlib/axes/_base.py in draw_artist(self, a)
   2758         """
   2759         if self.figure._cachedRenderer is None:
-> 2760             raise AttributeError("draw_artist can only be used after an "
   2761                                  "initial draw which caches the renderer")
   2762         a.draw(self.figure._cachedRenderer)

AttributeError: draw_artist can only be used after an initial draw which caches the renderer
  1. Okay, I happen to know enough conceptually about the draw tree to interpret what's happening here, but most won't. So I try to figure out what the correct incantation is...fig.draw...needs renderer....can't find the right function. Now I'm salty that I don't understand why I don't understand why plt.pause is only necessary when I paste into iPython and not when I run things line-by-line.
  2. Give up trying to infer what the incantation is, open pyplot.py and check the code for pause. Of course, draw is a method of the canvas, that actually makes perfect sense and I feel pretty silly for a minute (but tbh most users don't know what the canvas is).
  3. Replace plt.pause with fig.canvas.draw, which seems to work, except the animation is being drawn on the wrong part of my window.
  4. Try a bunch of flush_events, and other things, feel kinda lost again.
  5. Thomas links me to DOC: Start to document interactive figures #4779 from Gitter (not knowing that I'm currently struggling with this adventure). After reading it, I realize that the issue is probably because I have a tiling window manager that "immediately" resizes new windows, and that "immediately" is apparently not before the bg = ... line.
  6. Realize canvas.start_event_loop in plt.pause is allowing the code afterwards to interact with the post-window-resizing figure. and the section on window resizing tips me off.
  7. some more discussion here and on Gitter
  8. Finally realize the plt.pause thing was because my tiling manager's window-resizing was triggering a redraw (maybe that spins the event loop?) or something like that.

Anywho, as it currently stands, the tutorial makes it seem (a little bit) like the pause is just there as a sanity check, but it's doing two important things (fig.canvas.draw_idle and spinning the event loop). I don't think I'm the only person using matplotlib who learns by example, so if the existence of these concepts is not even mentioned here, I don't think I'd ever have any opportunity to learn about them.

Had I not been part of the community here (and already deep into learning the ins-and-outs of the library), I probably would have taken the issue with plt.pause to just be weird black magic and gotten spooked away from doing interactive stuff again.

@jklymak
Copy link
Member

jklymak commented May 21, 2020

I think maybe some of @brunobeltran comments can be handled by saying that this code is not really meant to be run interactively?

@brunobeltran
Copy link
Contributor

Thanks @jklymak! Didn't even think of that. Yes, if you just said 'the following is meant to be run as a script, before trying run it line-by-line ("interactively"), please see [interaction tutorial]', then I think that would have basically solved all my difficulties!

Although I'm not super clear on why @tacaswell is not keen on demonstrating the more low-level routines, I imagine there's a good reason (to avoid having to explicitly access the canvas in a user-facing tutorial?)

@tacaswell
Copy link
Member Author

Are the updated comments any clearer?


I probably would have taken the issue with plt.pause to just be weird black magic

The problem is that we are trying to hide a lot of complexity (there is 2 event loops, 1 or 2 double-buffer situations, 1-2 layers of deffering doing the work of rendering the canvas). For the 95% use case it "just works" and can be thought of as magic, but once you start looking inside you get to see the nested rube-goldberg machines made mostly of razor blades ;)

event loops:

  • python
  • GUI framwork

double buffers:

  • our canvas is one double buffer (you update the internal RGBA array and eventually we move it to the GUI)
  • some GUIs have their own double buffer where updating the array on the Python side does not immediately get reflected in a repaint of the screen

defference of work levels:

  • stale state on the figure
  • draw_idle
  • request paint vs paint

tutorials/intermediate/blitting.py Outdated Show resolved Hide resolved
tutorials/intermediate/blitting.py Outdated Show resolved Hide resolved
@QuLogic
Copy link
Member

QuLogic commented Jun 11, 2020

Needs rebase to fix docs CI.

@tacaswell
Copy link
Member Author

rebased and green!

@efiring
Copy link
Member

efiring commented Jun 15, 2020

This is nice and I don't want to hold it up, but after reading it without running it (yet), I wonder:

@efiring
Copy link
Member

efiring commented Jun 15, 2020

Echoing earlier comments from @jklymak, this doesn't work in general with macosx the way it does with qt, even though macosx supposedly "supports_blit". I think this should be noted explicitly in the tutorial. Otherwise, osx users who are not overriding the default macosx backend will wonder what is going on.

@efiring
Copy link
Member

efiring commented Jun 15, 2020

If you put a plt.pause(0.1) at the end of each loop in the tutorial, then both figures display properly when the tutorial is run as a script with the macosx backend. It seems that for that backend, the cv.flush_events() call is inadequate.

@tacaswell
Copy link
Member Author

Ah, that at least makes sense.

matplotlib/src/_macosx.m

Lines 391 to 402 in 77f23da

static PyObject*
FigureCanvas_flush_events(FigureCanvas* self)
{
View* view = self->view;
if(!view)
{
PyErr_SetString(PyExc_RuntimeError, "NSView* is NULL");
return NULL;
}
[view displayIfNeeded];
Py_RETURN_NONE;
}
this is the relevant code for the OSX backend, which looks like it just makes sure the figure is shown not that the UI queue is cleared.

tacaswell and others added 3 commits June 15, 2020 22:59
Co-authored-by: Elliott Sales de Andrade <quantum.analyst@gmail.com>
Co-authored-by: Bruno Beltran <brunobeltran0@gmail.com>
Co-authored-by: Tim Hoffmann<2836374+timhoffm@users.noreply.github.com>
@jklymak
Copy link
Member

jklymak commented Jun 16, 2020

I'm going to merge as an improvement, without going through carefully and checking the whole thing. This seems like the kind of guide that is super helpful, even if there are still rough edges, and it should not sit around for another couple of years.

@jklymak jklymak merged commit 4249e18 into matplotlib:master Jun 16, 2020
@tacaswell tacaswell deleted the doc_blitting_tutorial branch June 16, 2020 18:01
@joukewitteveen
Copy link

@tacaswell wrote:

Had some thoughts about hardening BlitManager a bit more and putting into the library, but was not sure where to put it and starting in a tutorial seems like a lower-stakes place to start.

That BlitManager class looks pretty useful for 'live' plots that are not timed (e.g. we want to plot at the rate that data becomes available). Maybe the class could be put in matplotlib.animation?

@tacaswell
Copy link
Member Author

@joukewitteveen Have you been using BlitManager for actual work?

@joukewitteveen
Copy link

I was reviewing a class with a similar purpose at work. There were some subtle issues with the code I reviewed which made me wonder whether this functionality would be available directly from the library. I would rather use an upstream solution that is kept working with updates to matplotlib than a home-grown reinterpretation that might fail for a slew of reasons at any moment.

The use case is a variant of the following. Data comes in at an irregular rate and we want to update the artist that shows the data at each new chunk of data. It's more of a live view than a timed animation.

@tacaswell
Copy link
Member Author

I would rather use an upstream solution that ....

Fair! What I would like feedback on from you is if this API is actually the right API and if it solves your actual problems. I wrote BlitManager mostly for the purposes of this documentation and have a worry that it is not general enough / does not provide the right interface.

@tacaswell
Copy link
Member Author

@joukewitteveen Lets move this discussion to #22183

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.

None yet