Skip to content

Commit a4c007e

Browse files
committed
TST: Test number of draw events from a blitted animation
This adds a test that only one draw_event is called when using blitting with animations. There will be more than one draw_event if blitting is not implemented properly and instead calls a full redraw of the canvas each time.
1 parent e29b6f2 commit a4c007e

File tree

1 file changed

+69
-0
lines changed

1 file changed

+69
-0
lines changed

lib/matplotlib/tests/test_backends_interactive.py

+69
Original file line numberDiff line numberDiff line change
@@ -413,3 +413,72 @@ def _lazy_headless():
413413
@pytest.mark.backend('QtAgg', skip_on_importerror=True)
414414
def test_lazy_linux_headless():
415415
proc = _run_helper(_lazy_headless, timeout=_test_timeout, MPLBACKEND="")
416+
417+
418+
def _test_number_of_draws_script():
419+
import matplotlib.pyplot as plt
420+
421+
fig, ax = plt.subplots()
422+
423+
# animated=True tells matplotlib to only draw the artist when we
424+
# explicitly request it
425+
ln, = ax.plot([0, 1], [1, 2], animated=True)
426+
427+
# make sure the window is raised, but the script keeps going
428+
plt.show(block=False)
429+
plt.pause(0.1)
430+
# Connect to draw_event to count the occurrences
431+
fig.canvas.mpl_connect('draw_event', print)
432+
433+
# get copy of entire figure (everything inside fig.bbox)
434+
# sans animated artist
435+
bg = fig.canvas.copy_from_bbox(fig.bbox)
436+
# draw the animated artist, this uses a cached renderer
437+
ax.draw_artist(ln)
438+
# show the result to the screen
439+
fig.canvas.blit(fig.bbox)
440+
441+
for j in range(10):
442+
# reset the background back in the canvas state, screen unchanged
443+
fig.canvas.restore_region(bg)
444+
# Create a **new** artist here, this is poor usage of blitting
445+
# but good for testing to make sure that this doesn't create
446+
# excessive draws
447+
ln, = ax.plot([0, 1], [1, 2])
448+
# render the artist, updating the canvas state, but not the screen
449+
ax.draw_artist(ln)
450+
# copy the image to the GUI state, but screen might not changed yet
451+
fig.canvas.blit(fig.bbox)
452+
# flush any pending GUI events, re-painting the screen if needed
453+
fig.canvas.flush_events()
454+
455+
# Let the event loop process everything before leaving
456+
plt.pause(0.1)
457+
458+
459+
_blit_backends = _get_testable_interactive_backends()
460+
for param in _blit_backends:
461+
backend = param.values[0]["MPLBACKEND"]
462+
if backend == "gtk3cairo":
463+
# copy_from_bbox only works when rendering to an ImageSurface
464+
param.marks.append(
465+
pytest.mark.skip("gtk3cairo does not support blitting"))
466+
elif backend == "wx":
467+
param.marks.append(
468+
pytest.mark.skip("wx does not support blitting"))
469+
470+
471+
@pytest.mark.parametrize("env", _blit_backends)
472+
# subprocesses can struggle to get the display, so rerun a few times
473+
@pytest.mark.flaky(reruns=4)
474+
def test_blitting_events(env):
475+
proc = _run_helper(_test_number_of_draws_script,
476+
timeout=_test_timeout,
477+
**env)
478+
479+
# Count the number of draw_events we got. We could count some initial
480+
# canvas draws (which vary in number by backend), but the critical
481+
# check here is that it isn't 10 draws, which would be called if
482+
# blitting is not properly implemented
483+
ndraws = proc.stdout.count("DrawEvent")
484+
assert 0 < ndraws < 5

0 commit comments

Comments
 (0)