Mayaviを使用したアプリケーションの構築

Mayaviは,GUIアプリケーションに完全に統合されたインタラクティブな3Dプロットツールとして使用できます.TraitsとTVTKの背後にあるイベントモデルを使うと,可視化されたデータ自体を含め,可視化のさまざまなプロパティーをすべて動的に変更することができます.

このセクションでは,まず Traits を使用して,Mayaviシーンを埋め込んだ対話型ダイアログを構築する方法を説明します.次に,このダイアログをWxPythonまたはPyQt(またはPySide)アプリケーションに統合する方法を示します.

カスタム対話型ダイアログ

MayaviとTVTKはすべてTraitsライブラリを使用して構築されており,オブジェクトアトリビュートのコールバックと視覚化を簡単に行うことができます.パイプラインオブジェクトとパイプラインオブジェクトのさまざまなプロパティはすべて,Traitとして表されます.Traitとは,ダイアログに表示できる特別な属性で,変更されるとコールバックを起動するものです.特に,ビジュアライゼーションオブジェクトが変更されると,シーンは自動的に更新されます.

詳細については, Traits documentation を参照することを強くお勧めします.TraitsとTraits UIを学ぶための入門の materials <https://support.enthought.com/hc/en-us/articles/204469620-Introductory-materials-for-Traits-and-Traits-UI> のリストもあります.

特性ダイアログへのMayaviシーンの埋め込み

Mayaviシーンを使用してカスタムダイアログボックスを構築するには,ベースとなる Traits クラスから派生するクラスを作成することをお勧めします.オブジェクトを受け入れることができるMayaviシーンを表すアトリビュートとして,SceneModelという特別なアトリビュートを使用できます.これは model ,すなわちアプリケーションロジックが含まれるメインの HasTraits オブジェクトを定義します.

このオブジェクトのビューをダイアログとして作成するには,このオブジェクトの .configure_traits メソッドです.ビューが明示的に指定されている場合,埋め込まれたMayaviシーンは SceneEditor を指定することにより,通常のシーンウィジェットで表すことができます.

from traits.api import HasTraits, Instance
from traitsui.api import View, Item
from tvtk.pyface.scene_model import SceneModel
from 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()

SceneEditor に別のシーンビューを指定することにより,ツールバーにパイプラインダイアログをポップアップする Mayavi ボタンを追加できます.

from mayavi.core.ui.mayavi_scene import MayaviScene

#...
editor=SceneEditor(scene_class=MayaviScene)
#...

逆に,ツールバーのないビューが必要な場合は, MayaviView をraw tvtkビューに置き換えることができます.

from tvtk.pyface.api import Scene

#...
editor=SceneEditor(scene_class=Scene)
#...

Mayavi traits uiの例 は,Mayavi UIのほぼ全体をtraitベースのUIに埋め込む方法を示す,かなり包括的な例です.

mlab が埋め込まれたシーン

シーンを表すオブジェクトは,シーンでデータを視覚化できる場合にのみ有効です.このために, Engine をインスタンス化してシーンに割り当てることができます. Engine を1つのシーンだけに設定すると,アクションオブジェクトとビジュアライゼーションオブジェクトをこのシーンだけに制限できます.

また, mayavi.tools.mlab_scene_model から読み込まれた SceneModel の代わりに MlabSceneModel のインスタンスを使用することもできます.このシーンモデルでは, mlab (mlab: 3Dプロット用のPythonスクリプト) にフィギュアが登録されます.mlab属性が埋め込まれており,mlabコマンドを公開しています( numpy arrayの3Dプロット関数 参照).たとえば,3D点のプロットには self.scene.mlab.points3d(x, y, z, s) を使用できます.

警告

アプリケーションへの複数のシーンの埋め込み

アプリケーションで複数の 'MlabSceneModel' を使用する場合,どのシーンmlabを使用してプロットするかが不明確になります.このため,mlabのように現在の値に依存するのは危険です.

これを解決するには,Mayaviフィギュアをmlabのフィギュアキーワード引数に明示的に渡します.

mlab.points3d(x, y, z, s, figure=self.scene.mayavi_scene)

ただし,この機能はMayavi 3.2 .1で新しく追加されました.

2つのシーンが埋め込まれた完全な例については, 複数のmlabシーンモデルの例 を参照してください.

シーン間の分離を作成するもう1つの方法は,別のエンジンにシーンを明示的に挿入することです.これは 複数エンジンの例 に示されています.

ビジュアル化をライブにする

対話型アプリケーションが興味深いのは,カスタムでドメイン固有の対話をビジュアル化で行える場合だけです.

重要な使用例は,パラメータが対話的に変更されるときに表示されるデータを変更することです.このためには,mlabプロットのアニメーションと同様に,mlabオブジェクトのデータのinplace変更を使用できます( データのアニメート 参照).2つのパラメータの関数によって定義されたラインカーブをプロットするとします.

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)]

mlab を使うと,曲線を plot3d

x, y, z, s = curve(4, 6)
from mayavi import mlab
plot = mlab.plot3d(x, y, z, s)

新しいパラメータのプロットを変更すると,次のように記述できます.

x, y, z, t = curve(4, 8)
plot.mlab_source.trait_set(x=x, y=y, z=z, scalars=t)

ダイアログでは,次のようになります.

from traits.api import HasTraits, Range, Instance, \
                    on_trait_change
from traitsui.api import View, Item, HGroup
from tvtk.pyface.scene_editor import SceneEditor
from mayavi.tools.mlab_scene_model import \
                    MlabSceneModel
from 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.trait_set(x=x, y=y, z=z, scalars=t)


    # the layout of the dialog created
    view = View(Item('scene', editor=SceneEditor(scene_class=MayaviScene),
                    height=250, width=300, show_label=False),
                HGroup(
                        '_', 'meridional', 'transverse',
                    ),
                )

visualization = Visualization()
visualization.configure_traits()

このコードは,次のダイアログを作成します.

_images/example_mlab_interactive_dialog.jpg

上記のコメントに基づいた完全で実行可能なコードは Mlab対話型ダイアログの例 に記載されています.

警告

シーンが使用可能になる前に作成されたビジュアル化オブジェクトおよびプロパティ

シーンが埋め込まれたオブジェクトを作成する場合,シーンのビューを実際に開く前にシーンを作成して取り込むことができます.ただし,VTKのオブジェクトやプロパティの中には,カメラとインタラクションを持つシーンを開いていないと正常に機能しないものがあります.これは,主にカメラの方向に向いていたり,キーボードやマウスとのインタラクションに対応していたりするためです(例えばインターアクタや暗黙の平面).その結果,VTKオブジェクトのプロパティを変更すると,ダイアログボックスを開かずに適用した場合に警告が表示されたり,まったく機能しなくなったりします.シーンをTraitsオブジェクトに埋め込む場合は,シーンがアクティブになったときにのみ, 'scene.activated' 特性の変更を取得して,これらのオブジェクトを作成および変更することをお勧めします.

@on_trait_change('scene.activated')
def create_plot(self):
    # Do the plotting here
    # ...

Lorenz uiの例 はこの状況の良い例を示しています.

WxPythonアプリケーションへの統合

上記で定義した Visualization クラスを使用します.

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.App(False)
frame = MainWindow(None, wx.ID_ANY)
app.MainLoop()

MayaviビジュアライゼーションをWxアプリケーションと統合する2つの例を示します.

Qtアプリケーションへの統合

Mayaviダイアログは,PyQtまたはPySideアプリケーションに統合することもできます.

Visualization クラスを定義する前に,Traitsで使用するツールキットをQtバックエンドに設定する必要があります.

import os
os.environ['ETS_TOOLKIT'] = 'qt4'

また,TraitsはPyQtとPySideで動作するため,PyQtを使用する場合は, PyQtコードを読み込む前 に,PySideと互換性のあるモード(内部文字列表現モード)でバインドを切り替える必要があります.

import sip
sip.setapi('QString', 2)

Enthought Tool Suiteの最新バージョンでは,デフォルトでPySideが使用されます.PyQtを強制的に使用するには,QT_API環境変数を設定する必要があります.

os.environ['QT_API'] = 'pyqt'

次に,上記で定義したビジュアル化クラスを使用します.

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_()

Qtでの埋め込みの完全な例については, Qt埋め込みの例 を参照してください.

警告

モデルの定義(したがって,イベントループの開始の前)に関して,TraitsはメインのQApplicationにhookをいくつか設定します.そのため,たとえば次のようにして新しいインスタンスを作成します.

app = QtGui.QApplication()

Traitsアプリケーションは動作しません.