46
46
import sys
47
47
import time
48
48
import warnings
49
+ from weakref import WeakKeyDictionary
49
50
50
51
import numpy as np
51
52
import matplotlib .cbook as cbook
@@ -2781,9 +2782,7 @@ class NavigationToolbar2(object):
2781
2782
def __init__ (self , canvas ):
2782
2783
self .canvas = canvas
2783
2784
canvas .toolbar = self
2784
- # a dict from axes index to a list of view limits
2785
- self ._views = cbook .Stack ()
2786
- self ._positions = cbook .Stack () # stack of subplot positions
2785
+ self ._nav_stack = cbook .Stack ()
2787
2786
self ._xypress = None # the location and axis info at the time
2788
2787
# of the press
2789
2788
self ._idPress = None
@@ -2805,19 +2804,20 @@ def __init__(self, canvas):
2805
2804
self .set_history_buttons ()
2806
2805
2807
2806
@partial (canvas .mpl_connect , 'draw_event' )
2808
- def define_home (event ):
2809
- self .push_current ()
2810
- # The decorator sets `define_home` to the callback cid, so we can
2811
- # disconnect it after the first use.
2812
- canvas .mpl_disconnect (define_home )
2807
+ def update_stack (event ):
2808
+ nav_info = self ._nav_stack ()
2809
+ if (nav_info is None # True initial navigation info.
2810
+ # An axes has been added or removed, so update the
2811
+ # navigation info too.
2812
+ or set (nav_info ) != set (self .canvas .figure .axes )):
2813
+ self .push_current ()
2813
2814
2814
2815
def set_message (self , s ):
2815
2816
"""Display a message on toolbar or in status bar."""
2816
2817
2817
2818
def back (self , * args ):
2818
2819
"""move back up the view lim stack"""
2819
- self ._views .back ()
2820
- self ._positions .back ()
2820
+ self ._nav_stack .back ()
2821
2821
self .set_history_buttons ()
2822
2822
self ._update_view ()
2823
2823
@@ -2836,15 +2836,13 @@ def remove_rubberband(self):
2836
2836
2837
2837
def forward (self , * args ):
2838
2838
"""Move forward in the view lim stack."""
2839
- self ._views .forward ()
2840
- self ._positions .forward ()
2839
+ self ._nav_stack .forward ()
2841
2840
self .set_history_buttons ()
2842
2841
self ._update_view ()
2843
2842
2844
2843
def home (self , * args ):
2845
2844
"""Restore the original view."""
2846
- self ._views .home ()
2847
- self ._positions .home ()
2845
+ self ._nav_stack .home ()
2848
2846
self .set_history_buttons ()
2849
2847
self ._update_view ()
2850
2848
@@ -3021,16 +3019,13 @@ def _switch_off_zoom_mode(self, event):
3021
3019
3022
3020
def push_current (self ):
3023
3021
"""Push the current view limits and position onto the stack."""
3024
- views = []
3025
- pos = []
3026
- for a in self .canvas .figure .get_axes ():
3027
- views .append (a ._get_view ())
3028
- # Store both the original and modified positions
3029
- pos .append ((
3030
- a .get_position (True ).frozen (),
3031
- a .get_position ().frozen ()))
3032
- self ._views .push (views )
3033
- self ._positions .push (pos )
3022
+ self ._nav_stack .push (
3023
+ WeakKeyDictionary (
3024
+ {ax : (ax ._get_view (),
3025
+ # Store both the original and modified positions.
3026
+ (ax .get_position (True ).frozen (),
3027
+ ax .get_position ().frozen ()))
3028
+ for ax in self .canvas .figure .axes }))
3034
3029
self .set_history_buttons ()
3035
3030
3036
3031
def release (self , event ):
@@ -3151,19 +3146,17 @@ def _update_view(self):
3151
3146
"""Update the viewlim and position from the view and
3152
3147
position stack for each axes.
3153
3148
"""
3154
-
3155
- views = self ._views ()
3156
- if views is None :
3157
- return
3158
- pos = self ._positions ()
3159
- if pos is None :
3149
+ nav_info = self ._nav_stack ()
3150
+ if nav_info is None :
3160
3151
return
3161
- for i , a in enumerate (self .canvas .figure .get_axes ()):
3162
- a ._set_view (views [i ])
3152
+ # Retrieve all items at once to avoid any risk of GC deleting an Axes
3153
+ # while in the middle of the loop below.
3154
+ items = list (nav_info .items ())
3155
+ for ax , (view , (pos_orig , pos_active )) in items :
3156
+ ax ._set_view (view )
3163
3157
# Restore both the original and modified positions
3164
- a .set_position (pos [i ][0 ], 'original' )
3165
- a .set_position (pos [i ][1 ], 'active' )
3166
-
3158
+ ax .set_position (pos_orig , 'original' )
3159
+ ax .set_position (pos_active , 'active' )
3167
3160
self .canvas .draw_idle ()
3168
3161
3169
3162
def save_figure (self , * args ):
@@ -3181,8 +3174,7 @@ def set_cursor(self, cursor):
3181
3174
3182
3175
def update (self ):
3183
3176
"""Reset the axes stack."""
3184
- self ._views .clear ()
3185
- self ._positions .clear ()
3177
+ self ._nav_stack .clear ()
3186
3178
self .set_history_buttons ()
3187
3179
3188
3180
def zoom (self , * args ):
0 commit comments