|
| 1 | +.. _patheffects-guide: |
| 2 | + |
| 3 | +****************** |
| 4 | +Path effects guide |
| 5 | +****************** |
| 6 | + |
| 7 | +.. py:module:: matplotlib.patheffects |
| 8 | +
|
| 9 | +
|
| 10 | +Matplotlib's :mod:`~matplotlib.patheffects` module provides functionality to |
| 11 | +apply a multiple draw stage to any Artist which can be rendered via a |
| 12 | +:class:`~matplotlib.path.Path`. |
| 13 | + |
| 14 | +Artists which can have a path effect applied to them include :class:`~matplotlib.patches.Patch`, |
| 15 | +:class:`~matplotlib.lines.Line2D`, :class:`~matplotlib.collections.Collection` and even |
| 16 | +:class:`~matplotlib.text.Text`. Each artist's path effects can be controlled via the |
| 17 | +``set_path_effects`` method (:class:`~matplotlib.artist.Artist.set_path_effects`), which takes |
| 18 | +an iterable of :class:`AbstractPathEffect` instances. |
| 19 | + |
| 20 | +The simplest path effect is the :class:`Normal` effect, which simply |
| 21 | +draws the artist without any effect: |
| 22 | + |
| 23 | +.. plot:: |
| 24 | + :include-source: |
| 25 | + |
| 26 | + import matplotlib.pyplot as plt |
| 27 | + import matplotlib.patheffects as path_effects |
| 28 | + |
| 29 | + fig = plt.figure(figsize=(5, 1.5)) |
| 30 | + text = fig.text(0.5, 0.5, 'Hello path effects world!\nThis is the normal ' |
| 31 | + 'path effect.\nPretty dull, huh?', |
| 32 | + ha='center', va='center', size=20) |
| 33 | + text.set_path_effects([path_effects.Normal()]) |
| 34 | + plt.show() |
| 35 | + |
| 36 | +Whilst the plot doesn't look any different to what you would expect without any path |
| 37 | +effects, the drawing of the text now been changed to use the the path effects |
| 38 | +framework, opening up the possibilities for more interesting examples. |
| 39 | + |
| 40 | +Adding a shadow |
| 41 | +--------------- |
| 42 | + |
| 43 | +A far more interesting path effect than :class:`Normal` is the |
| 44 | +drop-shadow, which we can apply to any of our path based artists. The classes |
| 45 | +:class:`SimplePatchShadow` and |
| 46 | +:class:`SimpleLineShadow` do precisely this by drawing either a filled |
| 47 | +patch or a line patch below the original artist: |
| 48 | + |
| 49 | +.. plot:: |
| 50 | + :include-source: |
| 51 | + |
| 52 | + import matplotlib.pyplot as plt |
| 53 | + import matplotlib.patheffects as path_effects |
| 54 | + |
| 55 | + text = plt.text(0.5, 0.5, 'Hello path effects world!', |
| 56 | + path_effects=[path_effects.withSimplePatchShadow()]) |
| 57 | + |
| 58 | + plt.plot([0, 3, 2, 5], linewidth=5, color='blue', |
| 59 | + path_effects=[path_effects.SimpleLineShadow(), |
| 60 | + path_effects.Normal()]) |
| 61 | + plt.show() |
| 62 | + |
| 63 | + |
| 64 | +Notice the two approaches to setting the path effects in this example. The |
| 65 | +first uses the ``with*`` classes to include the desired functionality automatically |
| 66 | +followed with the "normal" effect, whereas the latter explicitly defines the two path |
| 67 | +effects to draw. |
| 68 | + |
| 69 | +Making an artist stand out |
| 70 | +-------------------------- |
| 71 | + |
| 72 | +One nice way of making artists visually stand out is to draw an outline in a bold |
| 73 | +color below the actual artist. The :class:`Stroke` path effect |
| 74 | +makes this a relatively simple task: |
| 75 | + |
| 76 | +.. plot:: |
| 77 | + :include-source: |
| 78 | + |
| 79 | + import matplotlib.pyplot as plt |
| 80 | + import matplotlib.patheffects as path_effects |
| 81 | + |
| 82 | + fig = plt.figure(figsize=(7, 1)) |
| 83 | + text = fig.text(0.5, 0.5, 'This text stands out because of\n' |
| 84 | + 'its black border.', color='white', |
| 85 | + ha='center', va='center', size=30) |
| 86 | + text.set_path_effects([path_effects.Stroke(linewidth=3, foreground='black'), |
| 87 | + path_effects.Normal()]) |
| 88 | + plt.show() |
| 89 | + |
| 90 | +It is important to note that this effect only works because we have drawn the text |
| 91 | +path twice; once with a thick black line, and then once with the original text |
| 92 | +path on top. |
| 93 | + |
| 94 | +You may have noticed that the keywords to :class:`Stroke` and |
| 95 | +:class:`SimplePatchShadow` and :class:`SimpleLineShadow` are not the usual Artist |
| 96 | +keywords (such as ``facecolor`` and ``edgecolor`` etc.). This is because with these |
| 97 | +path effects we are operating at lower level of matplotlib. In fact, the keywords |
| 98 | +which are accepted are those for a :class:`matplotlib.backend_bases.GraphicsContextBase` |
| 99 | +instance, which have been designed for making it easy to create new backends - and not |
| 100 | +for its user interface. |
| 101 | + |
| 102 | + |
| 103 | +Greater control of the path effect artist |
| 104 | +----------------------------------------- |
| 105 | + |
| 106 | +As already mentioned, some of the path effects operate at a lower level than most users |
| 107 | +will be used to, meaning that setting keywords such as ``facecolor`` and ``edgecolor`` |
| 108 | +raise an AttributeError. Luckily there is a generic :class:`PathPatchEffect` path effect |
| 109 | +which creates a :class:`~matplotlib.patches.PathPatch` class with the original path. |
| 110 | +The keywords to this effect are identical to those of :class:`~matplotlib.patches.PathPatch`: |
| 111 | + |
| 112 | +.. plot:: |
| 113 | + :include-source: |
| 114 | + |
| 115 | + import matplotlib.pyplot as plt |
| 116 | + import matplotlib.patheffects as path_effects |
| 117 | + |
| 118 | + fig = plt.figure(figsize=(8, 1)) |
| 119 | + t = fig.text(0.02, 0.5, 'Hatch shadow', fontsize=75, weight=1000, va='center') |
| 120 | + t.set_path_effects([path_effects.PathPatchEffect(offset=(4, -4), hatch='xxxx', |
| 121 | + facecolor='gray'), |
| 122 | + path_effects.PathPatchEffect(edgecolor='white', linewidth=1.1, |
| 123 | + facecolor='black')]) |
| 124 | + plt.show() |
| 125 | + |
| 126 | + |
| 127 | +.. |
| 128 | + Headings for future consideration: |
| 129 | +
|
| 130 | + Implementing a custom path effect |
| 131 | + --------------------------------- |
| 132 | + |
| 133 | + What is going on under the hood |
| 134 | + -------------------------------- |
0 commit comments