Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 7499015

Browse files
authored
Merge pull request matplotlib#26302 from tacaswell/fix/tightlayout_threading
FIX: move the font lock higher up the call and class tree
2 parents 477f781 + 339dcb1 commit 7499015

File tree

4 files changed

+51
-38
lines changed

4 files changed

+51
-38
lines changed

lib/matplotlib/backend_bases.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,6 @@ class RendererBase:
170170
* `draw_path_collection`
171171
* `draw_quad_mesh`
172172
"""
173-
174173
def __init__(self):
175174
super().__init__()
176175
self._texmanager = None
@@ -2147,9 +2146,10 @@ def print_figure(
21472146
functools.partial(
21482147
print_method, orientation=orientation)
21492148
)
2149+
# we do this instead of `self.figure.draw_without_rendering`
2150+
# so that we can inject the orientation
21502151
with getattr(renderer, "_draw_disabled", nullcontext)():
21512152
self.figure.draw(renderer)
2152-
21532153
if bbox_inches:
21542154
if bbox_inches == "tight":
21552155
bbox_inches = self.figure.get_tightbbox(

lib/matplotlib/backends/backend_agg.py

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323

2424
from contextlib import nullcontext
2525
from math import radians, cos, sin
26-
import threading
2726

2827
import numpy as np
2928

@@ -62,19 +61,6 @@ class RendererAgg(RendererBase):
6261
context instance that controls the colors/styles
6362
"""
6463

65-
# we want to cache the fonts at the class level so that when
66-
# multiple figures are created we can reuse them. This helps with
67-
# a bug on windows where the creation of too many figures leads to
68-
# too many open file handles. However, storing them at the class
69-
# level is not thread safe. The solution here is to let the
70-
# FigureCanvas acquire a lock on the fontd at the start of the
71-
# draw, and release it when it is done. This allows multiple
72-
# renderers to share the cached fonts, but only one figure can
73-
# draw at time and so the font cache is used by only one
74-
# renderer at a time.
75-
76-
lock = threading.RLock()
77-
7864
def __init__(self, width, height, dpi):
7965
super().__init__()
8066

@@ -395,8 +381,7 @@ def draw(self):
395381
self.renderer = self.get_renderer()
396382
self.renderer.clear()
397383
# Acquire a lock on the shared font cache.
398-
with RendererAgg.lock, \
399-
(self.toolbar._wait_cursor_for_draw_cm() if self.toolbar
384+
with (self.toolbar._wait_cursor_for_draw_cm() if self.toolbar
400385
else nullcontext()):
401386
self.figure.draw(self.renderer)
402387
# A GUI class may be need to update a window using this draw, so

lib/matplotlib/figure.py

Lines changed: 33 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
import itertools
3636
import logging
3737
from numbers import Integral
38+
import threading
3839

3940
import numpy as np
4041

@@ -2365,6 +2366,18 @@ class Figure(FigureBase):
23652366
*suppressComposite* is a boolean, this will override the renderer.
23662367
"""
23672368

2369+
# we want to cache the fonts and mathtext at a global level so that when
2370+
# multiple figures are created we can reuse them. This helps with a bug on
2371+
# windows where the creation of too many figures leads to too many open
2372+
# file handles and improves the performance of parsing mathtext. However,
2373+
# these global caches are not thread safe. The solution here is to let the
2374+
# Figure acquire a shared lock at the start of the draw, and release it when it
2375+
# is done. This allows multiple renderers to share the cached fonts and
2376+
# parsed text, but only one figure can draw at a time and so the font cache
2377+
# and mathtext cache are used by only one renderer at a time.
2378+
2379+
_render_lock = threading.RLock()
2380+
23682381
def __str__(self):
23692382
return "Figure(%gx%g)" % tuple(self.bbox.size)
23702383

@@ -3124,33 +3137,33 @@ def clear(self, keep_observers=False):
31243137
@allow_rasterization
31253138
def draw(self, renderer):
31263139
# docstring inherited
3127-
3128-
# draw the figure bounding box, perhaps none for white figure
31293140
if not self.get_visible():
31303141
return
31313142

3132-
artists = self._get_draw_artists(renderer)
3133-
try:
3134-
renderer.open_group('figure', gid=self.get_gid())
3135-
if self.axes and self.get_layout_engine() is not None:
3136-
try:
3137-
self.get_layout_engine().execute(self)
3138-
except ValueError:
3139-
pass
3140-
# ValueError can occur when resizing a window.
3143+
with self._render_lock:
31413144

3142-
self.patch.draw(renderer)
3143-
mimage._draw_list_compositing_images(
3144-
renderer, self, artists, self.suppressComposite)
3145+
artists = self._get_draw_artists(renderer)
3146+
try:
3147+
renderer.open_group('figure', gid=self.get_gid())
3148+
if self.axes and self.get_layout_engine() is not None:
3149+
try:
3150+
self.get_layout_engine().execute(self)
3151+
except ValueError:
3152+
pass
3153+
# ValueError can occur when resizing a window.
31453154

3146-
for sfig in self.subfigs:
3147-
sfig.draw(renderer)
3155+
self.patch.draw(renderer)
3156+
mimage._draw_list_compositing_images(
3157+
renderer, self, artists, self.suppressComposite)
31483158

3149-
renderer.close_group('figure')
3150-
finally:
3151-
self.stale = False
3159+
for sfig in self.subfigs:
3160+
sfig.draw(renderer)
3161+
3162+
renderer.close_group('figure')
3163+
finally:
3164+
self.stale = False
31523165

3153-
DrawEvent("draw_event", self.canvas, renderer)._process()
3166+
DrawEvent("draw_event", self.canvas, renderer)._process()
31543167

31553168
def draw_without_rendering(self):
31563169
"""

lib/matplotlib/tests/test_figure.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1644,3 +1644,18 @@ def test_get_constrained_layout_pads():
16441644
fig = plt.figure(layout=mpl.layout_engine.ConstrainedLayoutEngine(**params))
16451645
with pytest.warns(PendingDeprecationWarning, match="will be deprecated"):
16461646
assert fig.get_constrained_layout_pads() == expected
1647+
1648+
1649+
def test_not_visible_figure():
1650+
fig = Figure()
1651+
1652+
buf = io.StringIO()
1653+
fig.savefig(buf, format='svg')
1654+
buf.seek(0)
1655+
assert '<g ' in buf.read()
1656+
1657+
fig.set_visible(False)
1658+
buf = io.StringIO()
1659+
fig.savefig(buf, format='svg')
1660+
buf.seek(0)
1661+
assert '<g ' not in buf.read()

0 commit comments

Comments
 (0)