@@ -319,3 +319,76 @@ def test_lazy_linux_headless():
319
319
if proc .returncode :
320
320
pytest .fail ("The subprocess returned with non-zero exit status "
321
321
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