Writing Examples
================

One of the easiest ways to learn VisPy is to use example scripts as a starting
point. VisPy includes a lot of its example scripts in the
:doc:`../gallery/index` which is generated by the
`sphinx-gallery <https://sphinx-gallery.github.io/stable/>`_ library.
While any example that shows a usage of VisPy is usage care must be taken to
make it runnable by sphinx-gallery and allow
:class:`VisPy's sphinx-gallery scraper <vispy.util.gallery_scraper.VisPyGalleryScraper>`
to take screenshots of it.

The below sections describe some important points of writing an example and
gotchas to avoid so the examples are properly included in the gallery.

Using the VisPy Scraper in other projects
-----------------------------------------

It is possible for projects outside of VisPy to use the sphinx-gallery
scraper. After following the sphinx-gallery configuration steps, include
"vispy" as one of your ``image_scrapers`` in your ``conf.py`` configuration:

.. code-block:: python

    sphinx_gallery_conf = {
        'image_scrapers': ('vispy',),
        ...
    }

It can be used alongside other scrapers like the sphinx-gallery default
``matplotlib`` scraper. The scraper follows all the same rules described
below unless stated otherwise.

Script Location
---------------

Example scripts that are meant to be included in the gallery should be placed
in one of ``examples/gloo/``, ``examples/scene/``, or ``examples/plotting/``
directories. Sphinx-gallery will only search in these directories and only at
the top 2 levels (only the first sub-directories).

Note this is specific to the VisPy project. Third-party libraries using the
VisPy scraper are free to put examples wherever they configure sphinx-gallery
to look.

Canvas
------

The scraper expects examples to have one of the following defined in the
global namespace:

* ``canvas``: An instance of a ``Canvas``, ``SceneCanvas``, or
  subclass of one of these. It must be named "canvas".
* ``Canvas``: A class that can be instantiated by calling ``Canvas()``.
* ``fig``: A ``Figure`` instance. It must be named "fig".

In the case of an example that is a larger Qt5 Application see the below
section.

Applications
------------

The scraper can also collect screenshots of Qt5 Applications. It will find the
top-level ``QMainWindow`` or ``QWidget`` and ignore any VisPy canvases.

At the end of the example should be included an ``app.run()`` call where
``app`` **must** be a VisPy ``Application`` instance. Using a QApplication
instance will work for basic running of the example, but currently can cause
segmentation faults when run by sphinx-gallery. VisPy's
:meth:`vispy.app.application.Application.run` has special handling to not block
execution when run by sphinx-gallery.

Examples using other GUI frameworks are not currently supported in gallery
execution.

Specify frames to grab
----------------------

A special comment can be added to the top of the example script to tell the
scraper what frame(s) to grab of an animation. By default (if none is
specified) the scraper will draw the Canvas-like object multiple times (5)
and then grab the current visualized output. This ensures that the
visualization has been completely drawn. To customize a different number of
draws before a screenshot is grabbed put the following comment in the first
ten lines of the example script (usually the second line after the hashbang):

.. code-block:: python

    # vispy: gallery 30

This tells the scraper to draw the Canvas-like object 30 times. To build an
animation (currently stored as a gif image), you can specify a range of frames
by doing:

.. code-block:: python

    # vispy: gallery 10:50:10

This is passed to ``range`` as individual arguments (start, end, step). So the
above will produce a list of ``[10, 15, 20, 25,30, 35, 40, 45]`` meaning draw
the Canvas-like object 10 times and grab a screenshot, then 5 more times
(15 total) and grab a screenshot, then 5 more times (20 total) and grab a
screenshot, and so on.

By default the scraper will not grab any frames. If the gallery comment is
specified, but has no number or range specified then the first frame will be
grabbed.

Specify images created by example
---------------------------------

Some examples may involve creating screenshots or animations. To tell the
scraper to grab these files instead of trying to generate another screenshot
specify the following frame specifier comment:

.. code-block:: python

    # vispy: gallery-exports animation.gif

Where ``animation.gif`` is a filename relative to the example script that is
produced by running the example. It can be any image format. Multiple
space-separated filenames can be provided also.
