88 matplotlib.use("'module://pygame_matplotlib.backend_pygame'")
99"""
1010from __future__ import annotations
11+ from logging import setLoggerClass
12+ from typing import List
1113import matplotlib
14+ from matplotlib .artist import Artist
1215from matplotlib .pyplot import figure
16+ from matplotlib .axes import Axes
1317import numpy as np
1418from matplotlib .transforms import Affine2D
1519import pygame
1620from pygame import surface
21+ from pygame import time
22+ from pygame .draw import arc
23+ from pygame .event import wait
1724import pygame .image
1825from pygame import gfxdraw
1926from matplotlib ._pylab_helpers import Gcf
@@ -36,6 +43,7 @@ class FigureSurface(pygame.Surface, Figure):
3643 """
3744
3845 canvas : FigureCanvasPygame
46+ animated_artists : List [Artist ]
3947
4048 def __init__ (self , * args , ** kwargs ):
4149 """Create a FigureSurface object.
@@ -44,7 +52,7 @@ def __init__(self, *args, **kwargs):
4452 """
4553 Figure .__init__ (self , * args , ** kwargs )
4654 pygame .Surface .__init__ (self , self .bbox .size )
47- self .fill ("white" )
55+ # self.fill("white")
4856
4957 def set_bounding_rect (self , rect : pygame .Rect ):
5058 """Set a bounding rectangle around the figure."""
@@ -56,9 +64,52 @@ def set_bounding_rect(self, rect: pygame.Rect):
5664 # Redraw the figure
5765 self .canvas .draw ()
5866
59- #def draw(self, *args, **kwargs):
60- # print(*args, **kwargs)
61- # return super().draw(self, *args, **kwargs)
67+ def draw (self , renderer ):
68+ return super ().draw (renderer )
69+
70+ def get_interactive_artists (self , renderer ):
71+ """Get the interactive artists.
72+
73+ Custom method used for the blitting.
74+ Modification of _get_draw_artists
75+ Also runs apply_aspect.
76+ """
77+ artists = self .get_children ()
78+
79+ for sfig in self .subfigs :
80+ artists .remove (sfig )
81+ childa = sfig .get_children ()
82+ for child in childa :
83+ if child in artists :
84+ artists .remove (child )
85+
86+ artists .remove (self .patch )
87+ artists = sorted (
88+ (artist for artist in artists if artist .get_animated ()),
89+ key = lambda artist : artist .get_zorder (),
90+ )
91+
92+ for ax in self ._localaxes .as_list ():
93+ locator = ax .get_axes_locator ()
94+
95+ if locator :
96+ pos = locator (ax , renderer )
97+ ax .apply_aspect (pos )
98+ else :
99+ ax .apply_aspect ()
100+
101+ for child in ax .get_children ():
102+ if child .get_animated ():
103+ artists .append (child )
104+ if hasattr (child , "apply_aspect" ):
105+ locator = child .get_axes_locator ()
106+ if locator :
107+ pos = locator (child , renderer )
108+ child .apply_aspect (pos )
109+ else :
110+ child .apply_aspect ()
111+
112+ return artists
62113
63114
64115class RendererPygame (RendererBase ):
@@ -78,7 +129,7 @@ def rect_from_bbox(self, bbox: Bbox) -> pygame.Rect:
78129 raise NotImplementedError ()
79130
80131 def draw_path (self , gc , path , transform , rgbFace = None ):
81-
132+ """Draw a path using pygame functions."""
82133 if rgbFace is not None :
83134 color = tuple (
84135 [int (val * 255 ) for i , val in enumerate (rgbFace ) if i < 3 ]
@@ -103,11 +154,7 @@ def draw_path(self, gc, path, transform, rgbFace=None):
103154
104155 previous_point = (0 , 0 )
105156 poly_points = []
106- # print(path)
107157 for point , code in path .iter_segments (transform ):
108- # previous_point = point
109- # print(point, code)
110-
111158 if code == Path .LINETO :
112159 draw_func (
113160 self .surface , color , previous_point , point , linewidth
@@ -253,13 +300,7 @@ def copy_from_bbox(self, bbox):
253300 copy .bbox = bbox
254301 return copy
255302
256- def restore_region (self , region : pygame .Surface , bbox : Bbox , xy ):
257- rect = (
258- pygame .Rect (0 , 0 , * self .surface .get_size ())
259- if bbox is None
260- else self .rect_from_bbox (bbox )
261- )
262- region .blit (self .surface , rect )
303+
263304
264305
265306class GraphicsContextPygame (GraphicsContextBase ):
@@ -347,6 +388,7 @@ class methods button_press_event, button_release_event,
347388 figure : `matplotlib.figure.Figure`
348389 A high-level Figure instance
349390 """
391+ blitting : bool = False
350392
351393 # File types allowed for saving
352394 filetypes = {
@@ -358,6 +400,7 @@ class methods button_press_event, button_release_event,
358400 }
359401 figure : FigureSurface
360402 renderer : RendererPygame
403+ main_display : pygame .Surface
361404
362405 def __init__ (self , figure = None ):
363406 super ().__init__ (figure )
@@ -372,8 +415,14 @@ def copy_from_bbox(self, bbox):
372415 return renderer .copy_from_bbox (bbox )
373416
374417 def restore_region (self , region , bbox = None , xy = None ):
375- renderer = self .get_renderer ()
376- return renderer .restore_region (region , bbox , xy )
418+ rect = (
419+ pygame .Rect (* (
420+ 0 , 0 if xy is None else xy
421+ ), * self .get_renderer ().surface .get_size ())
422+ if bbox is None
423+ else self .rect_from_bbox (bbox )
424+ )
425+ self .figure .blit (region , rect )
377426
378427 def draw (self ):
379428 """
@@ -386,13 +435,21 @@ def draw(self):
386435 """
387436 self .renderer = self .get_renderer (cleared = True )
388437 self .renderer .surface = self .figure
389- self .figure .draw (self .renderer )
438+ if self .blitting :
439+ # Draw only interactive artists
440+ artists = self .figure .get_interactive_artists (self .renderer )
441+ for a in artists :
442+ a .draw (self .renderer )
443+ else :
444+ # Full redraw of the figure
445+ self .figure .draw (self .renderer )
446+
390447
391448 def blit (self , bbox = None ):
392- # self._png_is_old = True
393449 self .renderer = self .get_renderer (cleared = False )
394450 self .renderer .surface = self .figure
395- self .figure .draw (self .renderer )
451+ self .blitting = True
452+
396453
397454
398455 def get_renderer (self , cleared = False ) -> RendererPygame :
@@ -419,11 +476,13 @@ def get_default_filetype(self):
419476 def flush_events (self ):
420477 self .main_display .blit (self .figure , (0 , 0 ))
421478 pygame .display .update ()
479+ self .pygame_quit_event_check ()
480+
481+ def pygame_quit_event_check (self ):
422482 events = pygame .event .get ()
423483 for event in events :
424484 if event .type == pygame .QUIT :
425485 pygame .quit ()
426- show_fig = False
427486
428487 def start_event_loop (self , interval : float ):
429488 FPS = 60
@@ -443,20 +502,30 @@ class FigureManagerPygame(FigureManagerBase):
443502
444503 For non-interactive backends, the base class is sufficient.
445504 """
446-
447- def show (self , block ):
448- # do something to display the GUI
449- pygame .init ()
505+ canvas : FigureCanvasPygame
506+ def get_main_display (self ):
507+ if hasattr (self .canvas , 'main_display' ):
508+ if (self .canvas .figure .get_size () == self .canvas .main_display .get_size ()):
509+ # If the main display exist and its size has not changed
510+ return self .canvas .main_display
450511 main_display = pygame .display .set_mode (
451512 self .canvas .figure .get_size (), # Size matches figure size
452513 )
453-
514+ main_display . fill ( 'white' )
454515 self .canvas .main_display = main_display
516+ return main_display
517+
518+ def show (self , block ):
519+ # do something to display the GUI
520+ pygame .init ()
521+ main_display = self .get_main_display ()
455522
456523 FPS = 60
457524 FramePerSec = pygame .time .Clock ()
458525
459- self .canvas .figure .canvas .draw ()
526+ if not self .canvas .blitting :
527+ # Draw if we are not blitting
528+ self .canvas .draw ()
460529 main_display .blit (self .canvas .figure , (0 , 0 ))
461530 pygame .display .update ()
462531
0 commit comments