tight_layout may not adjust correctly without a renderer in matplotlib figure layout
0 reputation · 24 Oct 2021, 03:03 UTC
0 reputation · 24 Oct 2021, 03:03 UTC
When a matplotlib Figure is created and tight_layout() is called before any rendering step, the layout adjustments may not be applied correctly, especially on non-interactive backends such as Agg where no renderer exists prior to drawing.
If constrained_layout is then enabled—either via subplots(constrained_layout=True) or Figure.set_constrained_layout(True)—the figure may retain stale tight_layout settings or produce overlapping and clipped labels because constrained_layout evaluates at draw time while tight_layout runs only once.
Does constrained_layout ignore tight_layout adjustments when no renderer has been created? Does this conflict cause label overlap or clipping in saved figures versus on-screen display? What is the difference in behavior across Matplotlib 3.x releases?
35775 reputation · 24 Oct 2021, 12:21 UTC
Does constrained_layout ignore tight_layout adjustments when no renderer exists? Yes. constrained_layout recomputes the entire layout at draw time and does not preserve the one-time subplot parameters set by tight_layout(). The two engines are documented as alternatives; using both is unsupported.
Does this conflict cause label overlap or clipping in saved figures versus on-screen display? It can. When tight_layout() runs without a real renderer (common on the Agg backend), it uses approximate font metrics. If constrained_layout is then enabled, the draw-time recomputation may produce different spacing. Artists positioned during the interim tight_layout pass can fall outside the final constrained layout bounds, leading to clipping in the saved output. On-screen interactive backends often create a renderer earlier, so the discrepancy is less visible there.
Behavior differences across Matplotlib 3.x releases: Newer 3.x minor versions increasingly emit UserWarning when both layout engines are active and may change which engine takes precedence at draw time. Exact symptom (silent override, warning, or clipping) varies by version and backend.
| Confirmed (documented / reproducible) | Likely explanation (inferred from behavior) |
|---|---|
tight_layout() requires a renderer for accurate text extents; without one it falls back to a temporary renderer or default metrics. | The temporary renderer on Agg uses a default DPI and font size, so spacing is approximate. |
constrained_layout is evaluated at draw time (draw() / savefig()), not when enabled. | Enabling it after tight_layout() causes a full recompute that discards the earlier subplotpars. |
| Matplotlib docs state the two engines are mutually exclusive. | No internal coordination exists; whichever runs last at draw time wins. |
| Minor-version changes in 3.x alter warning behavior and draw-time precedence. | Release notes mention layout-engine fixes; pin the version to reproduce reliably. |
import matplotlib; matplotlib.use('Agg').fig.tight_layout() before any draw, then enable fig.set_constrained_layout(True).fig.subplotpars immediately after tight_layout() and again after savefig().Which exact Matplotlib version (e.g., 3.7.2, 3.8.0) and backend are you targeting? The warning behavior and draw-time precedence changed between 3.6, 3.7, and 3.8, so the recommendation to avoid the combination or to migrate entirely to constrained_layout depends on that version.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.