Figures

Figures, typically abbreviated to fig, are the window that you plot into. This section outlines:

  • Their creation.

  • General figure management.

  • Functions for controlling the camera position.

  • Screenshotting the figure contents to a frozen image (file).

  • Embedding a figure into PyQt6.

Overview

Some of this is handled automatically. There is a global current working figure. This can be get and set using gcf() and scf(fig). If it doesn’t exist then it is automatically created. Each plot command will add itself to the current working figure unless explicitly told not by setting fig=None or fig=alternative_fig in the plot command. The figure is shown using vtkplotlib.show or fig.show(). After the shown figure is closed it ceases to be the current working figure but you can use it by referencing it explicitly. Figures can be reshown indefinitely and should be exactly as you left them on close.


show

vtkplotlib.show(block=True, fig='gcf')[source]

Shows a figure. This is analogous to matplotlib’s show function. After your plot commands call this to open the interactive 3D viewer.

Parameters
  • block (bool) – Enter interactive mode, otherwise just open the window, defaults to True.

  • fig (figure or QtFigure) – The figure to show, defaults to vtkplotlib.gcf().

If block is True then it enters interactive mode and the program is held until window exit. Otherwise the window is opened but not monitored. i.e an image will appear on the screen but it wont respond to being clicked on. By editing the plot and calling fig.update() you can create an animation but it will be non-interactive. True interactive animation hasn’t been implemented yet - it’s on the TODO list.

Note

You might feel tempted to run show in a background thread. It doesn’t work. If anyone does manage it then please let me know.

Warning

A window can not be closed by the close button until it is in interactive mode. Otherwise it’ll just crash Python. Use vtkplotlib.close() to close a non interactive window.

The current figure is reset on exit from interactive mode.


figure

vtkplotlib.figure(name='')

Create a new figure. This will automatically be set as the current working figure (returned by vtkplotlib.gcf()).

Parameters

name (str) – The window title, defaults to 'vtk figure'.


gcf

vtkplotlib.gcf(create_new=True)[source]

Gets the current working figure.

Parameters

create_new (bool) – Allow a new one to be created if none exist, defaults to True.

Returns

The current figure or None.

Return type

vtkplotlib.figure or vtkplotlib.QtFigure

If none exists then a new one gets created by vtkplotlib.figure() unless create_new=False in which case None is returned. This function will always return None if auto_figure(False) has been called.


scf

vtkplotlib.scf(figure)[source]

Sets the current working figure.

Parameters

figure (figure or QtFigure) – A figure or None.


screenshot_fig

vtkplotlib.screenshot_fig(magnification=1, pixels=None, trim_pad_width=None, off_screen=False, fig='gcf')[source]

Take a screenshot of a figure. The image is returned as an array. To save a screenshot directly to a file, use save_fig().

Parameters
  • magnification (int or tuple) – Image dimensions relative to the size of the render window.

  • pixels (int or tuple) – Image (width, height) or just height in pixels.

  • trim_pad_width (int or float) – Optionally auto crop to contents, this specifies how much space to give it, defaults to no cropping. A positive int for padding in pixels, float from 0.0 - 1.0 for pad width relative to original size

  • off_screen (bool) – If true, attempt to take the screenshot without opening the figure’s window.

  • fig (figure or QtFigure) – The figure to screenshot, defaults to vtkplotlib.gcf().

Setting pixels overrides magnification. If only one dimension is given to pixels then it is the height and an aspect ration of 16:9 is used. This is to match with the 1080p/720p/480p/… naming convention.

Note

VTK can only work with integer multiples of the render size (given by figure.render_size). pixels will be therefore be rounded to conform to this.

Note

I have no idea why it spins. But vtk’s example in the docs does it as well so I think it’s safe to say there’s not much we can do about it.

Note

For QtFigures off_screen is ignored.

Changed in version v2.0.0: The shape of the returned array went from (m, n, 3) to (m, n, 4) so as to respect opacity.


save_fig

vtkplotlib.save_fig(path, magnification=1, pixels=None, trim_pad_width=None, off_screen=False, fig='gcf', **imsave_plotargs)[source]

Take a screenshot and saves it to a file.

Parameters
  • path (str or os.PathLike) – The path, including extension, to save to.

  • magnification (int or tuple) – Image dimensions relative to the size of the render (window), defaults to 1.

  • pixels (int or tuple) – Image (width, height) or just height in pixels.

  • trim_pad_width (int or float) – Padding to leave when cropping to contents, see screenshot_fig().

  • off_screen (bool) – If true, attempt to take the screenshot without opening the figure’s window.

  • fig (figure or QtFigure) – The figure to save, defaults to vtkplotlib.gcf().

This just calls screenshot_fig() then passes it to matplotlib.image.imsave function. See those for more information.

The available file formats are determined by matplotlib’s choice of backend. For JPEG, you will likely need to install PILLOW. JPEG has considerably better file size than PNG.

Changed in version v2.0.0: If saving to a format which supports opacity and fig.background_opacity has been set to a value less than one, then the saved image will respect the opacity of the background and any translucent plots.


view

vtkplotlib.view(focal_point=None, camera_position=None, camera_direction=None, up_view=None, fig='gcf')[source]

Set/get the camera position/orientation.

Parameters
Returns

A dictionary containing the new configuration.

Return type

dict

Note

This function’s not brilliant. You may be better off manipulating the vtk camera directly (stored in fig.camera). If you do choose this route, start experimenting by calling print(fig.camera). If anyone makes a better version of this function then please share.

There is an unfortunate amount of implicit chaos going on here. Here are some hidden implications. I’m not even sure these are all true.

  1. If forwards is used then focal_point and camera_position are ignored.

  2. If camera_position is given but focal_point is not also given then camera_position is relative to where VTK determines is the middle of your plots. This is equivalent to setting camera_direction=-camera_position.

The following is well behaved:

vpl.view(camera_direction=...,
         up_view=...,)        # set orientations first
vpl.reset_camera()            # auto reset the zoom

reset_camera

vtkplotlib.reset_camera(fig='gcf')[source]

Reset the position and zoom of the camera so that all plots are visible.

Parameters

fig (figure or QtFigure) – The figure to modify, defaults to vtkplotlib.gcf().

This does not touch the orientation. It pushes the camera without rotating it so that, whichever direction it is pointing, it is pointing into the middle of where all the plots are. Then it adjusts the zoom so that everything fits on the screen.


zoom_to_contents

vtkplotlib.zoom_to_contents(plots_to_exclude=(), padding=0.05, fig='gcf')[source]

VTK, by default, leaves the camera zoomed out so that the renders contain a large amount of empty background. zoom_to_contents() zooms in so that the contents fill the render.

Parameters
  • plots_to_exclude – Plots that are unimportant and can be cropped out, defaults to ().

  • padding (int or float) – Amount of space to leave around the contents, in pixels if integer or relative to min(fig.render_size) if float defaults to 0.05.

  • fig (figure or QtFigure) – The figure zoom, defaults to vtkplotlib.gcf().

This method only zooms in. If you need to zoom out to fit all your plots in call vtkplotlib.reset_camera() first then this method. Plots in plots_to_exclude are temporarily hidden (using plot.visible = False) then restored. 2D plots such as a legend() or scalar_bar() which have a fixed position on the render are always excluded.

Note

New in v1.3.0.


close

vtkplotlib.close(fig='gcf')[source]

Close a figure.

Parameters

fig (figure or QtFigure) – The figure to close, defaults to vtkplotlib.gcf().

If the figure is the current figure then the current figure is reset.


figure_history

vtkplotlib.figure_history = <FigureHistory deque([], maxlen=2)>

auto_figure

vtkplotlib.auto_figure(auto=None)[source]

Enables/disables automatic figure management. If no parameters are provided then it returns the current state.

Parameters

auto (bool) – Defaults to None.

On by default. Disabling causes vtkplotlib.gcf() to always return None. Hence all plot commands will not add to a figure unless told to explicitly using the fig=a_figure argument. This can be useful for complex scenarios involving multiple figures.


QtFigure

class vtkplotlib.QtFigure(name='qt vtk figure', parent=None)[source]

The VTK render window in a QWidget. This can be embedded into a GUI the same way all other QWidgets are used.

Parameters
  • name (str) – The window title of the figure, only applicable is parent is None, defaults to ‘qt vtk figure’.

  • parent (QWidget) – Parent window, defaults to None.

Note

If you are new to Qt then this is a rather poor place to start. Whilst many libraries in Python are intuitive enough to be able to just dive straight in, Qt is not one of them. Preferably familiarise yourself with some basic Qt before coming here.

This class inherits both from QWidget and a vtkplotlib base figure class. Therefore it can be used exactly the same as you would normally use either a QWidget or a vtkplotlib.figure.

Care must be taken when using Qt to ensure you have exactly one QApplication. To make this class quicker to use the qapp is created automatically but is wrapped in a

if QApplication.instance() is None:
    self.qapp = QApplication(sys.argv)
else:
    self.qapp = QApplication.instance()

This prevents multiple QApplication instances from being created (which causes an instant crash) whilst also preventing a QWidget from being created without a qapp (which also causes a crash).

On show(), qapp.exec() is called automatically if figure.parent() is None (unless specified otherwise).

If the figure is part of a larger window then larger_window.show() must also explicitly show the figure. It won’t begin interactive mode until exec is called.

If the figure is not to be part of a larger window then it behaves exactly like a regular figure. You just need to explicitly create it first.

import vtkplotlib as vpl

# Create the figure. This automatically sets itself as the current
# working figure. The qapp is created automatically if one doesn't
# already exist.
vpl.QtFigure("Exciting Window Title")

# Everything from here on should be exactly the same as normal.

vpl.quick_test_plot()

# Automatically calls ``qapp.exec()``. If you don't want it to then
# use ``vpl.show(False)``.
vpl.show()

However this isn’t particularly helpful. A more realistic example would require the figure be part of a larger window. In this case, treat the figure as you would any other QWidget. You must explicitly call figure.show() however. (Not sure why.)

import sys

import numpy as np
from PyQt6 import QtWidgets
import vtkplotlib as vpl


class FigureAndButton(QtWidgets.QWidget):
    def __init__(self):
        super().__init__()

        # Go for a vertical stack layout.
        vbox = QtWidgets.QVBoxLayout()
        self.setLayout(vbox)

        # Create the figure
        self.figure = vpl.QtFigure()

        # Create a button and attach a callback.
        self.button = QtWidgets.QPushButton("Make a Ball")
        self.button.released.connect(self.button_pressed_cb)

        # QtFigures are QWidgets and are added to layouts with `addWidget`
        vbox.addWidget(self.figure)
        vbox.addWidget(self.button)

    def button_pressed_cb(self):
        """Plot commands can be called in callbacks. The current working
        figure is still self.figure and will remain so until a new
        figure is created explicitly. So the ``fig=self.figure``
        arguments below aren't necessary but are recommended for
        larger, more complex scenarios.
        """

        # Randomly place a ball.
        vpl.scatter(np.random.uniform(-30, 30, 3),
                    color=np.random.rand(3),
                    fig=self.figure)

        # Reposition the camera to better fit to the balls.
        vpl.reset_camera(self.figure)

        # Without this the figure will not redraw unless you click on it.
        self.figure.update()

    def show(self):
        # The order of these two are interchangeable.
        super().show()
        self.figure.show()

    def closeEvent(self, event):
        """This isn't essential. VTK, OpenGL, Qt and Python's garbage
        collect all get in the way of each other so that VTK can't
        clean up properly which causes an annoying VTK error window to
        pop up. Explicitly calling QtFigure's `closeEvent()` ensures
        everything gets deleted in the right order.
        """
        self.figure.closeEvent(event)


qapp = QtWidgets.QApplication.instance() or QtWidgets.QApplication(sys.argv)

window = FigureAndButton()
window.show()
qapp.exec()

Note

QtFigures are not reshow-able if the figure has a parent.

Note

VTK automatically selects the Qt variant (PyQt6, PyQt5, PySide6 or PySide2) based on what has already been imported. To use a different Qt variant, import it before importing vtkplotlib.

See also

vtkplotlib.QtFigure2 is an extension of this to provide some standard GUI elements, ready-made.


QtFigure2

class vtkplotlib.QtFigure2(name='qt vtk figure', parent=None)[source]

This is intended to be used as/for a more sophisticated GUI when one is needed. By providing some common features here, hopefully this can speed up the tedious process of building a GUI. Any contributions here would be very welcome. I want to write this so that each extra feature is optional allowing custom GUIs can be built quickly.

This is still under development. Currently it has:

  1. A screenshot button.

  2. A panel for preset camera views.

  3. An actor table to show / hide / color plots interactively (although it needs some way to group them).

  4. A cursor tracker displaying 3D coordinates of the mouse.

I hope/intend to add:

  1. Suggestions welcome here…

Use this class the same way you would use QtFigure (see there first.) Each feature is added with a fig.add_***() method.

import vtkplotlib as vpl
import numpy as np

# Create the figure. This as-is looks like just a QtFigure.
fig = vpl.QtFigure2()

# Add each feature you want. Pass arguments to customise each one.
fig.add_screenshot_button(pixels=1080)
fig.add_preset_views()
fig.add_show_plot_table_button()
# Use ``fig.add_all()`` to add all them all.

# You will likely want to dump the above into a function. Or a class
# inheriting from ``vpl.QtFigure2``.

# The usual, plot something super exciting.
vpl.polygon(np.array([[1, 0, 0],
                      [1, 1, 0],
                      [0, 1, 1],
                      [0, 0, 1]]), color="grey")

# Then either ``vpl.show()`` or
fig.show()