Section summary
This section describes how Mayavi can be used as a scientific data visualization and 3D plotting tool in interactive application.
Mayavi can be used as a fully integrated and interactive 3D plotting tool in a GUI application. Using the event model behind Traits and TVTK, all the different properties of a visualization can be changed dynamically, including the data visualized itself.
In this section, we first show how an interactive dialog embedding a Mayavi scene can be built, using Traits. Then we show how to integrate this dialog in a WxPython or a PyQt application.
Mayavi and TVTK are entirely built using the Traits library which provide easy callbacks and visualization for objects attribute. All the different properties of the pipeline and pipeline objects are expressed as Traits, ie special attributes that can be visualized in dialogs, and that fire callbacks when they are modified. In particuler this means that when a visualization object is modified, the scene can update automatically.
We strongly suggest that you refer to the Traits documentation for more details, and to the tutorial for a quick introduction.
To build a custom dialog with a Mayavi scene, the best option is to create a class deriving from the base Traits class. A special attribute, called SceneModel can be used as an attribute to represent a Mayavi scene that can accept objects. This defines the model, ie the main HasTraits object in which the application logics is contained.
A view of this object, as a dialog, can be created using the .configure_traits method of this object. If a view is explicitely specified the embedded Mayavi scene can be represented with the usual widget for scene by specifying for it the SceneEditor:
from enthought.traits.api import HasTraits, Instance
from enthought.traits.ui.api import View, Item
from enthought.tvtk.pyface.scene_model import SceneModel
from enthought.tvtk.pyface.scene_editor import SceneEditor
class MyModel(HasTraits):
scene = Instance(SceneModel, ())
view = View(Item('scene', height=400, show_label=False,
editor=SceneEditor()))
MyModel().configure_traits()
A Mayavi button to pop up the pipeline dialog can be added on the toolbar by specifying a different scene view to the SceneEditor:
from enthought.mayavi.core.ui.mayavi_scene import MayaviScene
#...
editor=SceneEditor(scene_class=MayaviScene)
#...
The mayavi_traits_ui.py example is a fairly comprehensive example that demonstrates how you can embed almost the entire mayavi UI into your traits based UI. mlab based visualization.
An object representing a scene is interesting only if you can visualize data with the scene. For this we can instanciate an Engine and assign it to the scene. Having an Engine only for one scene allows us to confine action and visualization objects only to this scene.
We can also use an MlabSceneModel instance, rather than a SceneModel, imported from enthought.mayavi.tools.mlab_scene_model. This scene model has an embedded mlab attribute, that exposes all the mlab commands (see 3D Plotting functions for numpy arrays) as attributes, applying on the scene. For instance plotting 3D points can be achieved with self.scene.mlab.points3d(x, y, z, s).
Having an interactive application is interesting only if you can do custom, domain-specific, interaction with the visualization.
An important use case is modifying the data visualized as a parameter is changed interactively. For this we can use the inplace modification of the data of an mlab object, as for animation of an mlab plot (see Animating the data). Suppose we are plotting a line curve defined by a function of two parameters:
from numpy import linspace, pi, cos, sin
def curve(n_mer, n_long):
phi = linspace(0, 2*pi, 2000)
return [ cos(phi*n_mer) * (1 + 0.5*cos(n_long*phi)),
sin(phi*n_mer) * (1 + 0.5*cos(n_long*phi)),
0.5*sin(n_long*phi),
sin(phi*n_mer)]
Using mlab, we could plot the curve with plot3d:
x, y, z, s = curve(4, 6)
from enthought.mayavi import mlab
plot = mlab.plot3d(x, y, z, s)
Modifying the plot for new parameters could be written:
x, y, z, t = curve(4, 8)
plot.mlab_source.set(x=x, y=y, z=z, scalars=t)
In a dialog, this would be:
from enthought.traits.api import HasTraits, Range, Instance, \
on_trait_change
from enthought.traits.ui.api import View, Item, HGroup
from enthought.tvtk.pyface.scene_editor import SceneEditor
from enthought.mayavi.tools.mlab_scene_model import \
MlabSceneModel
from enthought.mayavi.core.ui.mayavi_scene import MayaviScene
class Visualization(HasTraits):
meridional = Range(1, 30, 6)
transverse = Range(0, 30, 11)
scene = Instance(MlabSceneModel, ())
def __init__(self):
# Do not forget to call the parent's __init__
HasTraits.__init__(self)
x, y, z, t = curve(self.meridional, self.transverse)
self.plot = self.scene.mlab.plot3d(x, y, z, t,
colormap='Spectral')
@on_trait_change('meridional,transverse')
def update_plot(self):
x, y, z, t = curve(self.meridional, self.transverse)
self.plot.mlab_source.set(x=x, y=y, z=z, scalars=t)
view = View(Item('scene', height=400, show_label=False,
editor=SceneEditor(scene_class=MayaviScene)),
HGroup('meridional', 'transverse'))
visualization = Visualization()
visualization.configure_traits()
A complete, runnable, code based on the above comments is given by the mlab_traits_ui.py example.
Using the Visualization class defined above:
import wx
class MainWindow(wx.Frame):
def __init__(self, parent, id):
wx.Frame.__init__(self, parent, id, 'Mayavi in Wx')
self.visualization = Visualization()
self.control = self.visualization.edit_traits(parent=self,
kind='subpanel').control
self.Show()
app = wx.PySimpleApp()
frame = MainWindow(None, wx.ID_ANY)
app.MainLoop()
Before defining the Visualization class:
import os
os.environ['ETS_TOOLKIT'] = 'qt4'
And using this class:
from PyQt4 import QtGui
class MainWindow(QtGui.QMainWindow):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
self.visualization = Visualization()
self.ui = self.visualization.edit_traits().control
self.setCentralWidget(self.ui)
window = MainWindow()
window.show()
QtGui.qApp.exec_()