12
12
from .config import rc
13
13
from .utils import units
14
14
from .internals import ic # noqa: F401
15
- from .internals import warnings , _not_none , _set_state
15
+ from .internals import warnings , _not_none , _dummy_context , _set_state
16
16
17
17
__all__ = ['Figure' ]
18
18
@@ -77,11 +77,13 @@ def _preprocess(self, *args, **kwargs):
77
77
self .draw ()
78
78
79
79
# Bail out if we are already pre-processing
80
- # The return value for macosx _draw is the renderer, for qt draw is
80
+ # NOTE: The _is_autoresizing check necessary when inserting new gridspec
81
+ # rows or columns with the qt backend.
82
+ # NOTE: Return value for macosx _draw is the renderer, for qt draw is
81
83
# nothing, and for print_figure is some figure object, but this block
82
84
# has never been invoked when calling print_figure.
83
85
renderer = fig ._get_renderer () # any renderer will do for now
84
- if fig ._is_preprocessing :
86
+ if fig ._is_autoresizing or fig . _is_preprocessing :
85
87
if method == '_draw' : # macosx backend
86
88
return renderer
87
89
else :
@@ -230,21 +232,20 @@ def __init__(
230
232
**kwargs
231
233
Passed to `matplotlib.figure.Figure`.
232
234
""" # noqa
235
+ # Initialize first, because need to provide fully initialized figure
236
+ # as argument to gridspec, because matplotlib tight_layout does that
233
237
tight_layout = kwargs .pop ('tight_layout' , None )
234
238
constrained_layout = kwargs .pop ('constrained_layout' , None )
235
239
if tight_layout or constrained_layout :
236
240
warnings ._warn_proplot (
237
241
f'Ignoring tight_layout={ tight_layout } and '
238
242
f'contrained_layout={ constrained_layout } . ProPlot uses its '
239
243
'own tight layout algorithm, activated by default or with '
240
- 'tight=True.'
244
+ 'plot.subplots( tight=True) .'
241
245
)
242
-
243
- # Initialize first, because need to provide fully initialized figure
244
- # as argument to gridspec, because matplotlib tight_layout does that
245
246
self ._authorized_add_subplot = False
246
247
self ._is_preprocessing = False
247
- self ._is_resizing = False
248
+ self ._is_autoresizing = False
248
249
super ().__init__ (** kwargs )
249
250
250
251
# Axes sharing and spanning settings
@@ -330,7 +331,7 @@ def _add_axes_panel(self, ax, side, filled=False, **kwargs):
330
331
idx2 += 1
331
332
332
333
# Draw and setup panel
333
- with self ._authorize_add_subplot ():
334
+ with self ._context_authorize_add_subplot ():
334
335
pax = self .add_subplot (gridspec [idx1 , idx2 ], projection = 'cartesian' ) # noqa: E501
335
336
pgrid .append (pax )
336
337
pax ._panel_side = side
@@ -439,7 +440,7 @@ def _add_figure_panel(
439
440
)
440
441
441
442
# Draw and setup panel
442
- with self ._authorize_add_subplot ():
443
+ with self ._context_authorize_add_subplot ():
443
444
pax = self .add_subplot (gridspec [idx1 , idx2 ], projection = 'cartesian' ) # noqa: E501
444
445
pgrid = getattr (self , '_' + side + '_panels' )
445
446
pgrid .append (pax )
@@ -611,19 +612,19 @@ def _align_labels_figure(self, renderer):
611
612
}
612
613
suptitle .update (kw )
613
614
614
- def _authorize_add_subplot (self ):
615
+ def _context_authorize_add_subplot (self ):
615
616
"""
616
617
Prevent warning message when adding subplots one-by-one. Used
617
618
internally.
618
619
"""
619
620
return _set_state (self , _authorized_add_subplot = True )
620
621
621
- def _context_resizing (self ):
622
+ def _context_autoresizing (self ):
622
623
"""
623
624
Ensure backend calls to `~matplotlib.figure.Figure.set_size_inches`
624
625
during pre-processing are not interpreted as *manual* resizing.
625
626
"""
626
- return _set_state (self , _is_resizing = True )
627
+ return _set_state (self , _is_autoresizing = True )
627
628
628
629
def _context_preprocessing (self ):
629
630
"""
@@ -753,30 +754,24 @@ def _insert_row_column(
753
754
spaces = subplots_kw [w + 'space' ]
754
755
spaces_orig = subplots_orig_kw [w + 'space' ]
755
756
756
- # Slot already exists
757
+ # Adjust space, ratio, and panel indicator arrays
757
758
slot_type = 'f' if figure else side [0 ]
758
759
slot_exists = idx not in (- 1 , len (panels )) and panels [idx ] == slot_type
759
- if slot_exists : # already exists!
760
+ if slot_exists :
761
+ # Slot already exists
760
762
if spaces_orig [idx_space ] is None :
761
763
spaces_orig [idx_space ] = units (space_orig )
762
764
spaces [idx_space ] = _not_none (spaces_orig [idx_space ], space )
763
765
764
- # Make room for new panel slot
765
766
else :
766
- # Modify basic geometry
767
+ # Modify basic geometry and insert new slot
767
768
idx += idx_offset
768
769
idx_space += idx_offset
769
770
subplots_kw [ncols ] += 1
770
- # Original space, ratio array, space array, panel toggles
771
771
spaces_orig .insert (idx_space , space_orig )
772
772
spaces .insert (idx_space , space )
773
773
ratios .insert (idx , ratio )
774
774
panels .insert (idx , slot_type )
775
- # Reference ax location array
776
- # TODO: For now do not need to increment, but need to double
777
- # check algorithm for fixing axes aspect!
778
- # ref = subplots_kw[x + 'ref']
779
- # ref[:] = [val + 1 if val >= idx else val for val in ref]
780
775
781
776
# Update figure
782
777
figsize , gridspec_kw , _ = pgridspec ._calc_geometry (** subplots_kw )
@@ -788,7 +783,9 @@ def _insert_row_column(
788
783
else :
789
784
# Make new gridspec
790
785
gridspec = pgridspec .GridSpec (self , ** gridspec_kw )
786
+ self ._gridspec_main .figure = None
791
787
self ._gridspec_main = gridspec
788
+
792
789
# Reassign subplotspecs to all axes and update positions
793
790
for ax in self ._iter_axes (hidden = True , children = True ):
794
791
# Get old index
@@ -800,26 +797,24 @@ def _insert_row_column(
800
797
else :
801
798
inserts = (idx , idx , None , None )
802
799
subplotspec = ax .get_subplotspec ()
803
- igridspec = subplotspec .get_gridspec ()
804
- topmost = subplotspec .get_topmost_subplotspec ()
800
+ gridspec_ss = subplotspec .get_gridspec ()
801
+ subplotspec_top = subplotspec .get_topmost_subplotspec ()
805
802
806
803
# Apply new subplotspec
807
- _ , _ , * coords = topmost .get_active_rows_columns ()
804
+ _ , _ , * coords = subplotspec_top .get_active_rows_columns ()
808
805
for i in range (4 ):
809
806
if inserts [i ] is not None and coords [i ] >= inserts [i ]:
810
807
coords [i ] += 1
811
808
row1 , row2 , col1 , col2 = coords
812
809
subplotspec_new = gridspec [row1 :row2 + 1 , col1 :col2 + 1 ]
813
- if topmost is subplotspec :
810
+ if subplotspec_top is subplotspec :
814
811
ax .set_subplotspec (subplotspec_new )
815
- elif topmost is igridspec ._subplot_spec :
816
- igridspec ._subplot_spec = subplotspec_new
812
+ elif subplotspec_top is gridspec_ss ._subplot_spec :
813
+ gridspec_ss ._subplot_spec = subplotspec_new
817
814
else :
818
815
raise ValueError (
819
816
f'Unexpected GridSpecFromSubplotSpec nesting.'
820
817
)
821
-
822
- # Update parent or child position
823
818
ax .update_params ()
824
819
ax .set_position (ax .figbox )
825
820
@@ -1205,9 +1200,8 @@ def set_canvas(self, canvas):
1205
1200
# `~matplotlib.backend_bases.FigureCanvasBase.draw_idle` and
1206
1201
# `~matplotlib.backend_bases.FigureCanvasBase.print_figure`
1207
1202
# methods. The latter is called by save() and by the inline backend.
1208
- # See `_canvas_preprocessor` for details."""
1203
+ # See `_canvas_preprocessor` for details.
1209
1204
# TODO: Concatenate docstrings.
1210
- # TODO: Figure out matplotlib>=3.3 bug with macos backend.
1211
1205
# NOTE: Cannot use draw_idle() because it causes complications for qt5
1212
1206
# backend (wrong figure size).
1213
1207
if callable (getattr (canvas , '_draw' , None )): # for macos backend
@@ -1230,6 +1224,9 @@ def set_size_inches(self, w, h=None, forward=True, auto=False):
1230
1224
# renderer calls set_size_inches, size may be effectively the same, but
1231
1225
# slightly changed due to roundoff error! Therefore, always compare to
1232
1226
# *both* get_size_inches() and the truncated bbox dimensions times dpi.
1227
+ # NOTE: If we fail to detect 'manual' resize as manual, not only will
1228
+ # result be incorrect, but qt backend will crash because it detects a
1229
+ # recursive size change, since preprocessor size will differ.
1233
1230
if h is None :
1234
1231
width , height = w
1235
1232
else :
@@ -1241,20 +1238,18 @@ def set_size_inches(self, w, h=None, forward=True, auto=False):
1241
1238
width_true , height_true = self .get_size_inches ()
1242
1239
width_trunc = int (self .bbox .width ) / self .dpi
1243
1240
height_trunc = int (self .bbox .height ) / self .dpi
1244
- if auto : # internal resizing not associated with any draws
1245
- with self ._context_resizing ():
1246
- super ().set_size_inches (width , height , forward = forward )
1247
- else : # manual resizing on behalf of user
1248
- if (
1249
- (
1250
- width not in (width_true , width_trunc )
1251
- or height not in (height_true , height_trunc )
1252
- )
1253
- and not self ._is_resizing
1254
- and not self .canvas ._is_idle_drawing # standard
1255
- and not getattr (self .canvas , '_draw_pending' , None ) # pyqt5
1256
- ):
1257
- self ._subplots_kw .update (width = width , height = height )
1241
+ if (
1242
+ (
1243
+ width not in (width_true , width_trunc )
1244
+ or height not in (height_true , height_trunc )
1245
+ )
1246
+ and not auto
1247
+ and not self ._is_autoresizing
1248
+ and not getattr (self .canvas , '_is_idle_drawing' , None ) # standard
1249
+ ):
1250
+ self ._subplots_kw .update (width = width , height = height )
1251
+ context = self ._context_autoresizing if auto else _dummy_context
1252
+ with context ():
1258
1253
super ().set_size_inches (width , height , forward = forward )
1259
1254
1260
1255
def get_alignx (self ):
0 commit comments