{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "\n.. redirect-from:: /tutorials/advance/patheffects_guide\n\n\n# Path effects guide\n\nDefining paths that objects follow on a canvas.\n\n.. py:currentmodule:: matplotlib.patheffects\n\nMatplotlib's :mod:`.patheffects` module provides functionality to apply a\nmultiple draw stage to any Artist which can be rendered via a `.path.Path`.\n\nArtists which can have a path effect applied to them include `.patches.Patch`,\n`.lines.Line2D`, `.collections.Collection` and even `.text.Text`. Each artist's\npath effects can be controlled via the `.Artist.set_path_effects` method,\nwhich takes an iterable of `AbstractPathEffect` instances.\n\nThe simplest path effect is the `Normal` effect, which simply draws the artist\nwithout any effect:\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "import matplotlib.pyplot as plt\n\nimport matplotlib.patheffects as path_effects\n\nfig = plt.figure(figsize=(5, 1.5))\ntext = fig.text(0.5, 0.5, 'Hello path effects world!\\nThis is the normal '\n 'path effect.\\nPretty dull, huh?',\n ha='center', va='center', size=20)\ntext.set_path_effects([path_effects.Normal()])\nplt.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Whilst the plot doesn't look any different to what you would expect without\nany path effects, the drawing of the text has now been changed to use the\npath effects framework, opening up the possibilities for more interesting\nexamples.\n\n## Adding a shadow\n\nA far more interesting path effect than `Normal` is the drop-shadow, which we\ncan apply to any of our path based artists. The classes `SimplePatchShadow`\nand `SimpleLineShadow` do precisely this by drawing either a filled patch or\na line patch below the original artist:\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "import matplotlib.patheffects as path_effects\n\ntext = plt.text(0.5, 0.5, 'Hello path effects world!',\n path_effects=[path_effects.withSimplePatchShadow()])\n\nplt.plot([0, 3, 2, 5], linewidth=5, color='blue',\n path_effects=[path_effects.SimpleLineShadow(),\n path_effects.Normal()])\nplt.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Notice the two approaches to setting the path effects in this example. The\nfirst uses the ``with*`` classes to include the desired functionality\nautomatically followed with the \"normal\" effect, whereas the latter\nexplicitly defines the two path effects to draw.\n\n## Making an Artist stand out\n\nOne nice way of making artists visually stand out is to draw an outline in\na bold color below the actual artist. The :class:`Stroke` path effect makes\nthis a relatively simple task:\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "fig = plt.figure(figsize=(7, 1))\ntext = fig.text(0.5, 0.5, 'This text stands out because of\\n'\n 'its black border.', color='white',\n ha='center', va='center', size=30)\ntext.set_path_effects([path_effects.Stroke(linewidth=3, foreground='black'),\n path_effects.Normal()])\nplt.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "It is important to note that this effect only works because we have drawn\nthe text path twice; once with a thick black line, and then once with the\noriginal text path on top.\n\nYou may have noticed that the keywords to `Stroke` and `SimplePatchShadow`\nand `SimpleLineShadow` are not the usual Artist keywords (*facecolor*\n*edgecolor*, etc.). This is because with these path effects we are operating\nat lower level of Matplotlib. In fact, the keywords which are accepted are\nthose for a `matplotlib.backend_bases.GraphicsContextBase` instance, which\nhave been designed for making it easy to create new backends - and not for\nits user interface.\n\n\n## Greater control of the path effect Artist\n\nAs already mentioned, some of the path effects operate at a lower level\nthan most users will be used to, meaning that setting keywords such as\n*facecolor* and *edgecolor* raise an AttributeError. Luckily there is a\ngeneric `PathPatchEffect` path effect which creates a `.patches.PathPatch`\nclass with the original path. The keywords to this effect are identical to\nthose of `.patches.PathPatch`:\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "fig = plt.figure(figsize=(8.5, 1))\nt = fig.text(0.02, 0.5, 'Hatch shadow', fontsize=75, weight=1000, va='center')\nt.set_path_effects([\n path_effects.PathPatchEffect(\n offset=(4, -4), hatch='xxxx', facecolor='gray'),\n path_effects.PathPatchEffect(\n edgecolor='white', linewidth=1.1, facecolor='black')])\nplt.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "..\n Headings for future consideration:\n\n Implementing a custom path effect\n ---------------------------------\n\n What is going on under the hood\n --------------------------------\n\n" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.13.2" } }, "nbformat": 4, "nbformat_minor": 0 }