New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Artist's draw method prevents rasterization by default #24484
Artist's draw method prevents rasterization by default #24484
Conversation
lib/matplotlib/artist.py
Outdated
# (draw._supports_rasterization is True), it won't be decorated by | ||
# `_prevent_rasterization`. | ||
|
||
if hasattr(draw, "_supports_rasterization"): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we also want to set draw._supports_rasterization = False
in this wrapper?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have no preference, as far as we use the current condition of getattr(self.draw, "_supports_rasterization", False)
.
And not sure which is better.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On one hand, not setting it is the safer minimal change, but setting it to False (and finding + fixing our internal references to use getattr(obj, '_supports_rasterization', False)
instead) makes everything more consistent and understandable for the next set of people.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I will not block merging this over sorting that out.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The only occurrence of _supports_rasterization
is in the Artist.set_rasterized
method (which already does getattr(obj, '_supports_rasterization', False)
. And I will go ahead and add draw._supports_rasterization = False
here.
Co-authored-by: Thomas A Caswell <tcaswell@gmail.com>
Co-authored-by: Thomas A Caswell <tcaswell@gmail.com>
👍🏻 I think this is on the right path. |
lib/matplotlib/artist.py
Outdated
@@ -106,6 +131,8 @@ def __init_subclass__(cls): | |||
# Inject custom set() methods into the subclass with signature and | |||
# docstring based on the subclasses' properties. | |||
|
|||
cls.draw = _prevent_rasterization(cls.draw) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Either the above comment needs updating, or this should not go between it and the code below.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks. I will move this line above the comment.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should be more careful here about not not over-writing the draw
method unless we have to, I will push a commit to try and fix this.
Co-authored-by: Elliott Sales de Andrade <quantum.analyst@gmail.com>
Co-authored-by: Elliott Sales de Andrade <quantum.analyst@gmail.com>
Co-authored-by: Elliott Sales de Andrade <quantum.analyst@gmail.com>
Co-authored-by: Elliott Sales de Andrade <quantum.analyst@gmail.com>
Somehow, |
Doc failure is because of these warnings: The links in some of the old deprecation/removal files are underspecified, as there are multiple options that it could choose. I think that before this change, the With this change, the subclasses still get a (Note that current docs for axislines.Axes do not render a This can be resolved to make sphinx happy by being more explicit about wanting |
@leejjoon I took the liberty of pushing an extra commit to fix a subtle issue with (deep) class hierarchies. I hope you do not mind. |
No, not at all. However, the same check is also done inside the decorator, matplotlib/lib/matplotlib/artist.py Line 33 in 9de19d0
which I think is now redundant. Do you want it to be removed? -JJ |
Putting the check at this level has some subtle distinctions for subclasses and behavior of |
Thanks. I did not know if that will make a difference. I have removed the if statement inside the decorator. |
Thank you @leejjoon ! |
1 similar comment
Thank you @leejjoon ! |
👋 unfortunately this has broken some plotting in from matplotlib.text import Text
import matplotlib.pyplot as plt
from matplotlib.artist import Artist
class CustomArtist(Artist):
def draw(self, renderer):
t = CustomText(0, 0, 'a')
t.draw(renderer, custom_param='custom_param_value')
class CustomText(Text):
def draw(self, renderer, custom_param=None):
super().draw(renderer)
fig, ax = plt.subplots()
ax.add_artist(CustomArtist())
plt.show() The key here is the dummy Would be very greatful if there's an easy way to fix this for Traceback (most recent call last):
File "/Users/dstansby/github/matplotlib/lib/matplotlib/backend_bases.py", line 1212, in _on_timer
ret = func(*args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^
File "/Users/dstansby/github/matplotlib/lib/matplotlib/backends/backend_macosx.py", line 68, in callback_func
callback()
File "/Users/dstansby/github/matplotlib/lib/matplotlib/backends/backend_macosx.py", line 88, in _draw_idle
self.draw()
File "/Users/dstansby/github/matplotlib/lib/matplotlib/backends/backend_macosx.py", line 50, in draw
super().draw()
File "/Users/dstansby/github/matplotlib/lib/matplotlib/backends/backend_agg.py", line 400, in draw
self.figure.draw(self.renderer)
File "/Users/dstansby/github/matplotlib/lib/matplotlib/artist.py", line 95, in draw_wrapper
result = draw(artist, renderer, *args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/dstansby/github/matplotlib/lib/matplotlib/artist.py", line 72, in draw_wrapper
return draw(artist, renderer)
^^^^^^^^^^^^^^^^^^^^^^
File "/Users/dstansby/github/matplotlib/lib/matplotlib/figure.py", line 3077, in draw
mimage._draw_list_compositing_images(
File "/Users/dstansby/github/matplotlib/lib/matplotlib/image.py", line 131, in _draw_list_compositing_images
a.draw(renderer)
File "/Users/dstansby/github/matplotlib/lib/matplotlib/artist.py", line 72, in draw_wrapper
return draw(artist, renderer)
^^^^^^^^^^^^^^^^^^^^^^
File "/Users/dstansby/github/matplotlib/lib/matplotlib/axes/_base.py", line 3148, in draw
mimage._draw_list_compositing_images(
File "/Users/dstansby/github/matplotlib/lib/matplotlib/image.py", line 131, in _draw_list_compositing_images
a.draw(renderer)
File "/Users/dstansby/github/matplotlib/lib/matplotlib/artist.py", line 39, in draw_wrapper
return draw(artist, renderer)
^^^^^^^^^^^^^^^^^^^^^^
File "/Users/dstansby/github/matplotlib/test.py", line 8, in draw
t.draw(renderer, custom_param='custom_param_value')
TypeError: CustomText.draw() got an unexpected keyword argument 'custom_param' |
The wrapper should blindly forward |
diff --git a/lib/matplotlib/artist.py b/lib/matplotlib/artist.py
index a4693f78cf..7967f597d4 100644
--- a/lib/matplotlib/artist.py
+++ b/lib/matplotlib/artist.py
@@ -29,14 +29,14 @@ def _prevent_rasterization(draw):
# (e.g., change in dpi).
@wraps(draw)
- def draw_wrapper(artist, renderer):
+ def draw_wrapper(artist, renderer, *args, **kwargs):
if renderer._raster_depth == 0 and renderer._rasterizing:
# Only stop when we are not in a rasterized parent
# and something has been rasterized since last stop.
renderer.stop_rasterizing()
renderer._rasterizing = False
- return draw(artist, renderer)
+ return draw(artist, renderer, *args, **kwargs)
draw_wrapper._supports_rasterization = False
return draw_wrapper
should be enough, working on a PR. |
from matplotlib.text import Text
import matplotlib.pyplot as plt
from matplotlib.artist import Artist
class CustomArtist(Artist):
def draw(self, renderer):
t = CustomText(0, 0, 'a', figure=self.figure)
t.draw(renderer, custom_param='custom_param_value')
class CustomText(Text):
def draw(self, renderer, custom_param=None):
super().draw(renderer)
fig, ax = plt.subplots()
ax.add_artist(CustomArtist())
plt.show() Fixes the other issue. |
PR Summary
This is another PR to address #23999
(An alternative PR was proposed #24473 but was not favored as it prevents merging rasterized artists together)
It utilizes
Artist.__init_subclass__
to decorate itsdraw
method by defaullt with_prevent_rasterization
, so that the rasterization can be properly stopped when an artist that does not support rasterization is encountered.Documentation and Tests
pytest
passes)Release Notes
.. versionadded::
directive in the docstring and documented indoc/users/next_whats_new/
.. versionchanged::
directive in the docstring and documented indoc/api/next_api_changes/
next_whats_new/README.rst
ornext_api_changes/README.rst