@@ -319,3 +319,76 @@ def test_lazy_linux_headless():
319319 if proc .returncode :
320320 pytest .fail ("The subprocess returned with non-zero exit status "
321321 f"{ proc .returncode } ." )
322+
323+
324+ def _test_number_of_draws_script ():
325+ import matplotlib .pyplot as plt
326+
327+ fig , ax = plt .subplots ()
328+
329+ # animated=True tells matplotlib to only draw the artist when we
330+ # explicitly request it
331+ ln , = ax .plot ([0 , 1 ], [1 , 2 ], animated = True )
332+
333+ # make sure the window is raised, but the script keeps going
334+ plt .show (block = False )
335+ plt .pause (0.1 )
336+ # Connect to draw_event to count the occurrences
337+ fig .canvas .mpl_connect ('draw_event' , print )
338+
339+ # get copy of entire figure (everything inside fig.bbox)
340+ # sans animated artist
341+ bg = fig .canvas .copy_from_bbox (fig .bbox )
342+ # draw the animated artist, this uses a cached renderer
343+ ax .draw_artist (ln )
344+ # show the result to the screen
345+ fig .canvas .blit (fig .bbox )
346+
347+ for j in range (10 ):
348+ # reset the background back in the canvas state, screen unchanged
349+ fig .canvas .restore_region (bg )
350+ # Create a **new** artist here, this is poor usage of blitting
351+ # but good for testing to make sure that this doesn't create
352+ # excessive draws
353+ ln , = ax .plot ([0 , 1 ], [1 , 2 ])
354+ # render the artist, updating the canvas state, but not the screen
355+ ax .draw_artist (ln )
356+ # copy the image to the GUI state, but screen might not changed yet
357+ fig .canvas .blit (fig .bbox )
358+ # flush any pending GUI events, re-painting the screen if needed
359+ fig .canvas .flush_events ()
360+
361+ # Let the event loop process everything before leaving
362+ plt .pause (0.1 )
363+
364+
365+ _blit_backends = _get_testable_interactive_backends ()
366+ for param in _blit_backends :
367+ backend = param .values [0 ]["MPLBACKEND" ]
368+ if backend == "gtk3cairo" :
369+ # copy_from_bbox only works when rendering to an ImageSurface
370+ param .marks .append (
371+ pytest .mark .skip ("gtk3cairo does not support blitting" ))
372+ elif backend == "wx" :
373+ param .marks .append (
374+ pytest .mark .skip ("wx does not support blitting" ))
375+
376+
377+ @pytest .mark .parametrize ("env" , _blit_backends )
378+ # subprocesses can struggle to get the display, so rerun a few times
379+ @pytest .mark .flaky (reruns = 4 )
380+ def test_blitting_events (env ):
381+ proc = subprocess .run (
382+ [sys .executable , "-c" ,
383+ inspect .getsource (_test_number_of_draws_script )
384+ + "\n _test_number_of_draws_script()" ],
385+ env = {** os .environ , "SOURCE_DATE_EPOCH" : "0" , ** env },
386+ timeout = _test_timeout ,
387+ stdout = subprocess .PIPE , universal_newlines = True )
388+
389+ # Count the number of draw_events we got. We could count some initial
390+ # canvas draws (which vary in number by backend), but the critical
391+ # check here is that it isn't 10 draws, which would be called if
392+ # blitting is not properly implemented
393+ ndraws = proc .stdout .count ("DrawEvent" )
394+ assert 0 < ndraws < 5
0 commit comments