Reading Mayavi's documentation "http://docs.enthought.com/mayavi/mayavi/building_applications.html" gets me real confused.
My question is, if I just want a 3d plot that allows user interaction, for example, dragging a slider and change the content of the plot, I don't have to use Traits, right? By the documentation, the standard procedure is something like:
class visual(HasTraits):
scene = ...
view = View(...)
@on_trait_change(...)
def do_something()
...
I don't know what traits are. I don't even understand what's an attribute in a class that's defined outside of the constructor (it's a 'trait'?).
Back to the original question, if I just wanted a 3d plot that's changeable, why don't I just directly do it? A working example is as follows:
import numpy as np
from mayavi import mlab
from matplotlib import pyplot as plt
from matplotlib.widgets import Slider
def slider_changed(val):
s.mlab_source.scalars = np.asarray(x * (val + 1), 'd')
# mayavi 3d plot
x, y = np.mgrid[0:3:1,0:3:1]
s = mlab.surf(x, y, np.asarray(x*0.1, 'd'))
# a matplotlib slider
plt.figure()
ax = plt.subplot(1, 1, 1)
slider = Slider(ax, valmin=0., valmax=1., label='test')
slider.on_changed(slider_changed)
plt.show()
mlab.show()
Seems to me if I don't care about embedding the 3d plot in applications, this is a much simpler way to do it? A lot of the attributes of the 3d plot is manipulable through the attributes of mlab_source
. I am using a matplotlib slider here as an example, but it seems it can be a pyqt UI or whatever.
However, the documentation says
All the different properties of the pipeline and pipeline objects are expressed as Traits, i.e. special attributes that can be visualized in dialogs and that fire callbacks when they are modified. In particular this means that when a visualization object is modified, the scene can update automatically.
Does it mean if I wanted something that's automatically updated, I have to use Traits in the way it was described above?
Possibly a good start understanding traits would be this article. Those traits, we are talking about here, have been developped by Enthough. It is worth noting that there are also other objects/concepts called traits, which are completely unrelated to what is relevant for using mayavi.
I don't have to use Traits, right?
You are using Traits when calling s.mlab_source.scalars = np.asarray(x * (val + 1), 'd')
. The fact that Mayavi updates the plot simply because you change the underlying data is a result of Traits being used.
Mayavi simply uses those traits, instead of usual objects. So once you use mayavi, you inevitably use traits.
Does it mean if I wanted something that's automatically updated, I have to use Traits in the way it was described above?
No, you gave the counter example yourself. You do not have to subclass HasTraits
to update a plot. You may use whatever other solution you can think of. However, once you want to embed a mayavi scene into a GUI, it is probably a good idea to do it the way the documentation states it.
Also, subclassing HasTraits
is actually a very neat way to easily obtain an interactive figure. So the example from the question could look like
import numpy as np
from mayavi import mlab
from traits.api import HasTraits, Range, Instance,on_trait_change
from traitsui.api import View, Item, Group
from mayavi.core.ui.api import MayaviScene, SceneEditor, MlabSceneModel
x, y = np.mgrid[0:3:1,0:3:1]
class MyModel(HasTraits):
slider = Range(-5., 5., 0.5, )
scene = Instance(MlabSceneModel, ())
def __init__(self):
HasTraits.__init__(self)
self.s = mlab.surf(x, y, np.asarray(x*1.5, 'd'), figure=self.scene.mayavi_scene)
@on_trait_change('slider')
def slider_changed(self):
self.s.mlab_source.scalars = np.asarray(x * (self.slider + 1), 'd')
view = View(Item('scene', editor=SceneEditor(scene_class=MayaviScene)),
Group("slider"))
my_model = MyModel()
my_model.configure_traits()
The nice thing to notice here is that you do not actually define an explicit callback (i.e. there is no on_slider_change
or similar).