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