Figures¶
Figures, typically abbreviated to figs, 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 PyQt5.
Overview¶
Some of this is handled automatically. There is a global “current working figure”.
This can be get and set using vpl.gcf()
and vpl.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
vpl.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')¶ 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, optional) – Enter interactive mode, otherwise just open the window, defaults to True.
- fig (
vtkplotlib.figure
,vtkplotlib.QtFigure
, optional) – The figure to show, defaults tovtkplotlib.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.
gcf¶
-
vtkplotlib.
gcf
(create_new=True)¶ Gets the current working figure.
Parameters: create_new (bool, optional) – Allow a new one to be created if none exist, defaults to True. Returns: The current figure or None. Return type: vtkplotlib.figure
orvtkplotlib.QtFigure
If none exists then a new one gets created by
vpl.figure()
unlesscreate_new=False
in which case None is returned. Will always return None ifauto_figure(False)
has been called.
screenshot_fig¶
-
vtkplotlib.
screenshot_fig
(magnification=1, pixels=None, trim_pad_width=None, off_screen=False, fig='gcf')¶ Take a screenshot of a figure. The image is returned as an array. To save a screenshot directly to a file, see vpl.save_fig.
Parameters: - path (str or Pathlike) – The path, including extension, to save to.
- magnification (int or a (width, height) tuple of ints, optional) – Image dimensions relative to the size of the render (window), defaults to 1.
- pixels (int or a (width, height) tuple of ints, optional) – Image dimensions in pixels, defaults to None.
- trim_pad_width (positive int for padding in pixels, float from 0.0 - 1.0 for pad width relative to original size.) – Optionally auto crop to contents, this specifies how much space to give it, defaults to None for no cropping.
- off_screen (bool, optional) – If true, attempt to take the screenshot without opening the figure’s window, defaults to False.
- fig (
vtkplotlib.figure
,vtkplotlib.QtFigure
, optional) – The figure to screenshot, defaults tovtkplotlib.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.
Warning
There has been a case where the contents of other windows (e.g Firefox) have somehow ended up in the output (see <https://github.com/bwoodsend/vtkplotlib/issues/1>). Which can be resolved by setting
off_screen=True
. But this option can lead to a bad X server connection error on (I think) Linux machines without built in graphics hardware. This is currently unresolved.
save_fig¶
-
vtkplotlib.
save_fig
(path, magnification=1, pixels=None, trim_pad_width=None, off_screen=False, fig='gcf', **imsave_plotargs)¶ Take a screenshot and saves it to a file.
Parameters: - path (str or Pathlike) – The path, including extension, to save to.
- magnification (int or a (width, height) tuple of ints, optional) – Image dimensions relative to the size of the render (window), defaults to 1.
- pixels (int or a (width, height) tuple of ints, optional) – Image dimensions in pixels, defaults to None.
- off_screen (bool, optional) – If true, attempt to take the screenshot without opening the figure’s window, defaults to False.
- fig (
vtkplotlib.figure
,vtkplotlib.QtFigure
, optional) – The figure to save, defaults tovtkplotlib.gcf()
.
This just calls
vpl.screenshot_fig
then passes it to matplotlib’spylab.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.
view¶
-
vtkplotlib.
view
(focal_point=None, camera_position=None, camera_direction=None, up_view=None, fig='gcf')¶ Set/get the camera position/orientation.
Parameters: - focal_point (np.array([x, y, z]), optional) – A point the camera should point directly to, defaults to None.
- camera_position (np.array([x, y, z]), optional) – The point the camera is situated, defaults to None.
- camera_direction (np.array([eX, eY, eZ]), optional) – The direction the camera is pointing, defaults to None.
- up_view (np.array([eX, eY, eZ]), optional) – Roll the camera so that the up_view vector is pointing towards the top of the screen, defaults to None.
- fig (
vtkplotlib.figure
,vtkplotlib.QtFigure
, optional) – The figure to modify, defaults tovtkplotlib.gcf()
.
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 callingprint(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.
- If forwards is used then focal_point and camera_position are ignored.
- 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')¶ Reset the position and zoom of the camera so that all plots are visible.
Parameters: fig ( vtkplotlib.figure
,vtkplotlib.QtFigure
, optional) – The figure to modify, defaults tovtkplotlib.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')¶ 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 (iterable of plots, optional) – Plots that are unimportant and can be cropped out, defaults to
()
. - padding (int, float, optional) – Amount of space to leave around the contents, in pixels if integer or relative to
min(fig.render_size)
if float defaults to0.05
. - fig (
vtkplotlib.figure
,vtkplotlib.QtFigure
, optional) – The figure zoom, defaults to:meth:`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 (usingplot.visible = False
) then restored. 2D plots such as alegend()
orscalar_bar()
which have a fixed position on the render are always excluded.Note
New in v1.3.0.
- plots_to_exclude (iterable of plots, optional) – Plots that are unimportant and can be cropped out, defaults to
close¶
-
vtkplotlib.
close
(fig='gcf')¶ Close a figure.
Parameters: fig ( vtkplotlib.figure
,vtkplotlib.QtFigure
, optional) – The figure to close, defaults tovtkplotlib.gcf()
.If the figure is the current figure then the current figure is reset.
auto_figure¶
-
vtkplotlib.
auto_figure
(auto=None)¶ Enables/disables automatic figure management. If no parameters are provided then it returns the current state.
Parameters: auto (bool, optional) – Defaults to None. On by default. Disabling causes
vpl.gcf()
to always return None. Meaning that all plot commands will not add to a figure unless told to explicitly using thefig=a_figure
argument. This can be useful for complex scenarios involving multiple figures.
QtFigure¶
-
class
vtkplotlib.
QtFigure
(name='qt vtk figure', parent=None)¶ The VTK render window embedded into a PyQt5 QWidget. This can be embedded into a GUI the same way all other QWidgets are used.
Parameters: - name (str, optional) – The window title of the figure, only applicable is parent is None, defaults to ‘qt vtk figure’.
- parent (PyQt5.QtWidgets.QWidget, optional) – 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
PyQt5.QtWidgets.QWidget
and a vtkplotlib BaseFigure class. Therefore it can be used exactly the same as you would normally use either a QWidget or avtkplotlib.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
self.show()
,self.qapp.exec_()
is called automatically ifself.parent() is None
(unless specified otherwise). If the QFigure is part of a larger window thenlarger_window.show()
must also explicitly show the figure. It won’t begin interactive mode untilqapp.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 vtkplotlib as vpl from PyQt5 import QtWidgets import numpy as np import sys # python 2 compatibility from builtins import super 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 reshowable if the figure has a parent.
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)¶ 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:
- A screenshot button.
- A panel for preset camera views.
- An actor table to show / hide / color plots interactively (although it needs some way to group them).
I hope/intend to add:
- Suggestions welcome here…
Use this class the same way you would use
vpl.QtFigure
(see there first.) Each feature is added with afig.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()