enthought / mayavi

3D visualization of scientific data in Python
http://docs.enthought.com/mayavi/mayavi/
Other
1.3k stars 284 forks source link

Can we combine a mayavi scene and chaco plot together in a single traitsui frame #1109

Closed neurosignal closed 2 years ago

neurosignal commented 2 years ago

Hi, I am trying to integrate a mayavi scene and a Chaco plot in the same Traits-based user interface, but I am getting errors. I tried to explore the tutorials and examples but did not find any. Is it possible at all or not, is there any link explaining such a case? Thanks in advance. Below is the code snippet where I am defining my View

from mayavi.core.ui.api import MayaviScene
from chaco.api import Plot
scene = Instance(MlabSceneModel, ())

plot  = Instance(Plot)   
view = View(HGroup(
                VGroup(
                    VGroup(
                        Item('X_scale', label='X'),
                        Item('Y_scale', label='Y'),
                        Item('Z_scale', label='Z'),
                        show_left=False, label=' Scale', show_border=True, show_labels=True),
                    show_border=True),             
                Item('scene', editor=SceneEditor(scene_class=MayaviScene),
                     height=800, width=1000, show_label=False),                
                 Item('plot', editor=ComponentEditor(), show_label=True),                 
            show_border=False),
        resizable=True, title='My Application')

The complete code:

from traits.api import HasTraits, Instance, on_trait_change, File, Button, Float, Directory, Range
from traitsui.api import View, Item, VGroup, HGroup, Label, FileEditor, VGrid, DirectoryEditor, Spring
from mayavi.core.ui.api import MayaviScene, SceneEditor, MlabSceneModel
from chaco.api import Plot
from enable.api import ComponentEditor
import numpy as np

def zoom_it(points, X_scale, Y_scale, Z_scale):
    x = points[:,0] * (X_scale/1)
    y = points[:,1] * (Y_scale/1)
    z = points[:,2] * (Z_scale/1)
    return x, y, z

class MyModel(HasTraits):
    X_scale    = Range(0.1, 10.0, 1.0)
    Y_scale    = Range(0.1, 10.0, 1.0)
    Z_scale    = Range(0.1, 10.0, 1.0)
    points = np.random.rand(200,3)
    scene = Instance(MlabSceneModel, ())
    # plot  = Instance(Plot)     # <------------------

    def __init__(self):
        HasTraits.__init__(self)
        x, y, z = zoom_it(self.points, self.X_scale, self.Y_scale, self.Z_scale)
        self.plot = self.scene.mlab.points3d(x, y, z, color=(1,0.5,0.5))    

    @on_trait_change('X_scale, Y_scale, Z_scale, points')
    def update_plot(self):
        x, y, z = zoom_it(self.points, self.X_scale, self.Y_scale, self.Z_scale)
        self.plot.mlab_source.trait_set(x=x, y=y, z=z)

    view = View(
        HGroup(
                VGroup(
                    VGroup(
                        Item('X_scale', label='X'),
                        Item('Y_scale', label='Y'),
                        Item('Z_scale', label='Z'),
                        show_left=False, label=' Scale', show_border=True, show_labels=True),
                    show_border=True), 
                Item('scene', editor=SceneEditor(scene_class=MayaviScene),
                     height=800, width=1000, show_label=False),

                # Item('plot', editor=ComponentEditor(), show_label=True), # <------------------

            show_border=False),
        resizable=True, title='My Application')

my_model = MyModel()
my_model.configure_traits()
rahulporuri commented 2 years ago

Hey @neurosignal . I edited your description to fix the code formatting. I hope that's okay.

corranwebster commented 2 years ago

There used to be a VTK backend for Enable (and hence Chaco), but it was not well-supported and so was removed for Enable 5. It was not particularly sophisticated: it just rendered the Chaco plot into a bitmap and then displayed it in the VTK scene, but it might be a starting point for what you need.

The code can be found here: https://github.com/enthought/enable/tree/4.8.1/enable/vtk_backend

ETA: this may not match your need - this was overlaying the chaco plot on top of the Mayavi scene.

corranwebster commented 2 years ago

Actually, looking a bit closer the problem you have is more fundamental: you are not creating your Chaco Plot correctly. Chaco and Mayavi have different internal models, and so your Chaco plot is completely independent of the Mayavi plot. In particular, this code is returning a Mayavi object, not a Chaco Plot, so it will fail:

        self.plot = self.scene.mlab.points3d(x, y, z, color=(1,0.5,0.5))    

You need to do something like:

    def __init__(self):
        ...
        plot_data = ArrayPlotData(x=self.points[:, 0], y=self.points[:, 1], z=self.points[:, 2])
        plot = Plot(plot_data)
        plot.plot(['x', 'y'], 'scatter')
        self.chaco_plot = plot

and

    @observe('points')
    def update_points(self, event):
        self.chaco_plot.data.update(x=self.points[:, 0], y=self.points[:, 1], z=self.points[:, 2])

and

    Item('chaco_plot', editor=ComponentEditor(), show_label=True)

or something similar, following standard Chaco conventions.

You will still need something like your existing code, but the plot there is a Mayavi plot, and it probably makes sense to call it that.

neurosignal commented 2 years ago

Thank you very much @rahulporuri for fixing it.

neurosignal commented 2 years ago

Thanks a lot, @corranwebster. It helped very much and now it's working after integrating your suggestions.