Back to Home

Draw a color cube in Mayavi

mayavi · enthought · python

Draw a color cube in Mayavi

    Greetings, habravchane!

    Today I want to tell you about what Mayavi is and what it is washed with .

    Mayavi is a cross-platform application for visualization of scientific data (and not only). Distributed under the BSD license, which allows its use in commercial applications.
    image

    What can?
    • Build 2D and 3D models based on scalar / vector data
    • Open VTK, PLOT3D files
    • Save render results in various graphic formats
    • May even render MRI (Magnetic Resonance Imaging) results


    There are three ways to work with it:
    1) Work directly in the Mayavi interface.
    2) Upload / transfer ready-made data to Mayavi.
    3) Write a python script in which you can set for Mayavi all the desired features.


    After launching the stand-alone version, we get such a nice window: The
    image

    screen is divided into 4 parts:
    • The upper left part stores objects
    • In the lower left part the properties of objects are changed
    • In the upper right there is a render
    • In the lower right corner is the terminal


    Let me explain what the terminal is for:

    Create some object and select it in the list of objects. Select and, without releasing the left mouse button, drag it into the terminal window. We will get something like

    Now let's write:
    explore(_)
    and then a window with all the parameters of the object will appear. This is a very useful thing that can help in learning and creating your own scripts. I learned some properties of objects in this way, not finding in the documentation. In this window, the Drag'N'Drop rule works similarly.

    image

    There are many ( examples ) in the Mayavi documentation that generally explain what and how to do.

    I will try to describe the rake that I stepped on. For the most part, they will relate to embedding the Mayavi frame in the PyQt widget (well, or PySide, which is already aboutwrote .

    • Do not read the documentation from pdf files. True, do not read. A waste of time.
    • Do not replace class constructors out of habit. Create some methods to which variables / objects will be passed, which are then used for rendering in the update_plot () functions.
    • Try to pinpoint what you want to see. TVTK classes may not support the functions that mlab classes have (for example, Volume).
    • When working with numpy arrays, cast them to float32 type, as by default, a 64-bit system creates a float64 array that Mayavi will not work with.
    • Let me give you one example of building a regular cube with embedding it in the PyQT widget.

      1. # To embed in PyQt4 widget, you need to set the ETS_TOOLKIT variable to qt4.
      2. import os
      3. os.environ ['ETS_TOOLKIT'] = 'qt4'
      4. from PySide import QtGui, QtCore
      5.  
      6. from enthought.mayavi import mlab
      7. from enthought.tvtk.api import tvtk
      8. from enthought.tvtk.pyface.api import Scene
      9. from numpy import arange, nonzero, float32, min, max, median, copy, random, shape
      10. from numpy.core.numeric import ravel
      11.  
      12. from enthought.traits.api import HasTraits, Instance, on_trait_change, \
      13.     Int, dict
      14. from enthought.traits.ui.api import View, Item
      15. from enthought.mayavi.core.ui.api import MayaviScene, MlabSceneModel, \
      16.         Sceneeditor
      17.  
      18. #The actual visualization
      19. class Visualization (HasTraits):
      20.     scene = Instance (MlabSceneModel, ())
      21.     view = View (Item ('scene', 
      22.                      editor = SceneEditor (scene_class = MayaviScene),
      23.                      # Instead of MayaviScene, you can write just Scene
      24.                      # and then we get a clean window without a toolbar.
      25.                      height = 250, 
      26.                      width = 300, 
      27.                      show_label = False),
      28.                 resizable = True
      29.                 )
      30.  
      31.     needUpdate = None
      32.  
      33.     def takePlotParametres (self, grid):
      34.         self.grid = grid
      35.  
      36.         # The clf () method clears the current scene. It CANNOT be called in the update_plot () method
      37.         self.scene.mlab.clf ()
      38.         self.needUpdate = True
      39.  
      40.         self.update_plot ()
      41.  
      42.     @on_trait_change ('scene.activated')
      43.     def update_plot (self):
      44.         # This method is called for rendering.
      45.         # Everything that needs to be drawn is written here and nowhere else
      46.  
      47.         # While there is no data, we will draw the standard model
      48.         if not self.needUpdate:
      49.             self.scene.mlab.test_points3d ()
      50.         else:
      51.             # Here we write all our rice cookers
      52.  
      53.             # Everything that is rendered via mlab needs to be called
      54.             # like self.scene.mlab. *, otherwise the Mayavi window gets out of control
      55.             # and starts to draw everything in a separate window.
      56.             # opacity greatly affects rendering performance!
      57.             surf = self.scene.mlab.pipeline.surface (self.grid, opacity = 1)
      58.  
      59.             # You can also display the internal grid by uncommenting the lines
      60.             #edges = mlab.pipeline.extract_edges (surf)
      61.             #edgesSurf = mlab.pipeline.surface (edges)
      62.             # edgesSurf.actor.property.interpolation = 'flat'
      63.  
      64.             # We remove the interpolation of the color of the surface of the cube. Let each cell be seen clearly.
      65.             surf.actor.property.interpolation = 'flat'
      66.  
      67.         self.scene.mlab.orientation_axes ()
      68.         self.scene.background = (0, 0, 0)
      69.         self.scene.mlab.colorbar (orientation = 'vertical')
      70.  
      71.  
      72. ####################################################### #################################
      73. # Widget in which to embed the scene. We use it as a regular PyQt widget.
      74.  
      75. class MayaviQWidget (QtGui.QWidget):
      76.     def __init __ (self, parent = None):
      77.         QtGui.QWidget .__ init __ (self, parent)
      78.         layout = QtGui.QVBoxLayout (self)
      79.         layout.setMargin (0)
      80.         layout.setSpacing (0)
      81.         self.visualization = Visualization ()
      82.  
      83.         # Ideally, this is where you can call the takePlotParametres method with the initial data, but this is not necessary
      84.         # self.visualization.takePlotParametres ()
      85.  
      86.         # The edit_traits method generates the window in which the scene is built.
      87.         # This window needs to be made Qt'sh, for which we call the .control method
      88.         self.ui = self.visualization.edit_traits (parent = self, 
      89.                                                  kind = 'subpanel'). control
      90.         layout.addWidget (self.ui)
      91.         self.ui.setParent (self)
      92.  
      93.     def create_cube (self):
      94.         grid = self.createGrid ()
      95.  
      96.         self.visualization.takePlotParametres (grid)
      97.  
      98.     def createGrid (self):
      99.         x, y, z = (10, 10, 10)
      100.  
      101.         # Create a grid for our cube
      102.         grid = tvtk.RectilinearGrid ()
      103.  
      104.         # It is important to cast data to float32, as on a 64-bit OS, numpy creates float64 by default,
      105.         # and Mayavi does not know how to work with it.
      106.         # Create random data
      107.         scalars = float32 (random.random ((x * y * z)))
      108.  
      109.         # scalars is the value that will be stored in each cell of our cube. 
      110.         # It is on this value that the surface color of the cells is determined
      111.         grid.point_data.scalars = scalars
      112.         grid.point_data.scalars.name = 'scalars'
      113.  
      114.         grid.dimensions = (x, y, z)
      115.         grid.x_coordinates = float32 (arange (x))
      116.         grid.y_coordinates = float32 (arange (y))
      117.         grid.z_coordinates = float32 (arange (z))
      118.  
      119.         return grid
      120.  
      121. if __name__ == "__main__":
      122.     # It’s important not to create a new application, but to use something 
      123.     # which Mayavi creates automatically, otherwise you will hit all Traits signals / slots,
      124.     # and we just don’t need it.
      125.     # To get the link, call the instance () method.
      126.     # Do not write out of habit QtGui.QApplication (sys.argv) .instance (),
      127.     # I already stepped on this rake.
      128.     app = QtGui.QApplication.instance ()
      129.  
      130.     container = QtGui.QWidget ()
      131.     container.setWindowTitle ("Hello Habrahabr!")
      132.     layout = QtGui.QVBoxLayout (container)
      133.  
      134.     mayavi_widget = MayaviQWidget ()
      135.  
      136.     button = QtGui.QPushButton ('Create cube')
      137.     button.clicked.connect (mayavi_widget.create_cube)
      138.  
      139.     layout.addWidget (mayavi_widget)
      140.     layout.addWidget (button)
      141.  
      142.     container.show ()
      143.  
      144.     app.exec_ ()




    We start and get this window:
    image

    Click the button and get a colored cube:
    image

    If you want to see something more scientific, you can take a look at this:
    image

    Well, you can also create something futuristic (inside one box with internal faces turned on):
    image

    On this I complete my topic. If you want to see something else, then I am waiting for comments.

    PS A little later I will write a topic about Chaco. I hope you are just as interested in reading about him?

    Read Next