devlog

http://twitter.com/yusukei

pyd4maya2013

某氏より、どうにか Maya2013 で numpy と scipy 使えるようにならないかって相談を受けたので manifest を埋め込むツールを作ってみた。

前回、Manifest をpydに埋め込むことで Maya2013 でも numpy が読み込めるようになるという事が分かったのでツール化してみました。

ここよりダウンロードしてください。

また、Python2.6かPython2.7とVisualStudio2005~2010のいずれかをインストールしておいてください。

使い方は NumPy のモジュールをそのまま pyd4maya2013.bat にドロップしてください。
ディレクトリ以下の pyd を検索して manifest を埋め込みます。
埋め込む manifest は template.manifest に書かれているので必要に応じて変更してください。

ちなみに、msvcr90.dll と msvcr100.dll が両方読み込まれる状態になるので、前回書いたように正攻法はリビルドです。 とりあえず読み込めればいい、手軽に試したい、という方のみこれを試してみるといいかもしれません。

(補足)
numpyにmanifestを埋め込み、

import numpy
numpy.test()

mayapy にてと試したところ、テストの途中で落ちてしまいました。
CPython や Maya 2012 の mayapy では落ちないので、やはりダメらしいです。
おそらくメモリアロケーション周りが原因ではないかと思っています。

いまさらだけどMaya 2013のお話

いまさらですが、Maya 2013を使おうとしたときにハマった小ネタです。

Maya 2012でnumpyなどを使っていたのですが、Maya 2013で使おうとしたら読み込めない!

相変わらず不親切なメッセージが。
ImportError: DLL load failed: 指定されたモジュールが見つかりません。
と出るだけ。

まぁ、この時点で、msvcr90.dllであろうことは容易に予想はつくのですが。
ちょとっと検索してみると、

なんて記事が出てきます。
ここに書いてあるように、根本的にはリビルドするのが正攻法です。

とはいえ、msvcr90.dllとmsvcr100.dllが混在しても動くもの、動かないものがあるのは
納得がいかないので、なぜ動かないのか調べてみました。というのが今回のお話。

さて、ロードできないのがmsvcr90.dllであることから原因としてはmanifest周りしか考えられません。
というわけで、どんなmanifestが埋め込んであるか調べてみました。

manifestを取り出すにはVisualStudioに付属のコマンドを使うのが簡単です。
DLLのmanifestはID:2であるので以下のようなコマンドで取り出せます。

mt -inputresource:QtCore.pyd;2 -out:QtCore.pyd.manifest

これはPySideのQtCore.pydのmanifestを取り出した例です。
ちなみに、この2012用にビルドしたPySide.QtCoreはmaya 2013でも読み込むことができています。(自前ビルドですが)
これをテキストエディタで開くと

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
  <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
    <security>
      <requestedPrivileges>
        <requestedExecutionLevel level="asInvoker" uiAccess="false"></requestedExecutionLevel>
      </requestedPrivileges>
    </security>
  </trustInfo>
  <dependency>
    <dependentAssembly>
      <assemblyIdentity type="win32" name="Microsoft.VC90.CRT" version="9.0.21022.8" processorArchitecture="amd64" publicKeyToken="1fc8b3b9a1e18e3b"></assemblyIdentity>
    </dependentAssembly>
  </dependency>
</assembly>

こんな感じのXMLになっています。
このXMLにどのバージョンのmsvcr90を読み込めばいいのかという情報が書かてれいます。

で、お次にnumpyのmultiarray.pydを取り出してみると、

mt.exe : general error c101008c: Failed to read the manifest from the resource of file "multiarray.pyd". 

と言われてしまいます。要は埋め込みmanifestが存在しないということです。

ということで、numpyのpydにはmanifestが存在しないためロードできないという感じです。
実際、先ほど取り出したmanifestをmultiarray.pydに埋め込んでみます。

mt -manifest QtCore.pyd.manifest -outputresource:multiarray.dll;2

再度、Maya 2013でimportすると読み込めないpydの名前が変わっているかと思います。
numpyのすべてのpydに対してこれを行えば読み込めるようになるかと思います。(未確認)

git覚書

新たにリモートブランチへpushする

# git push origin test

pushした際には.git/configに

[branch "local_branch_name"]
remotes = origin
merge = refs/heads/test

を書かないとブランチ名が必要になる。

リモートのブランチからブランチする

# git branch -a
  remotes/origin/test

となっている場合、そのまま

# git checkout remotes/origin/test

とはできない。 一度、ローカルにブランチを切る必要がある。

# git checkout test remotes/origin/test
# git checkout test
# git pull
# git push

リモートのブランチ名とローカルのブランチ名が一致しない場合は

# git push origin test
# git pull origin test

とすること

upstream mode

git config --global push.default upstream

AfterEffectsにPythonを組み込む

初めに

たまには外に情報を発信しないとね!ということで、AfterEffectsにPythonを組み込むという企画を行ってみようかと思います。

用意するもの

今回はAfterEffectsのスクリプトからPython 2.6を呼び出しその結果を返すという実装を行ってみます。 はじめにAfterEffectsのスクリプトからC++のコードを呼び出す部分です。 これに関してはJavaScript Tools GuideのIntegrating External Librariesの章に書いてあります。

Pythonの初期化

Pythonを初期化する際にPythonのある位置をPythonに教えます。 初期化後、そのままではbuiltinモジュールが使えないため、mainモジュールをインポートし、それをglobalsとします。

        // Pythonの初期化
        Py_SetPythonHome("C:/Python26");
        Py_Initialize();
        printf("Py_Initialize\n");

        // globals空間の生成
        PyObject* main = PyImport_AddModule("__main__");
        gGlobals = PyModule_GetDict(main);

ExtendScriptにクラスを登録する

ExtendScript側からC++を呼び出せるようにクラスを登録します。

// ExtendScript側のPyAEクラス
SoObjectInterface objectInterface =
{
    PySample::constructor, // initialize
    NULL,                  // put
    NULL,                  // get
    PySample::call,        // call
    NULL,                  // valueOf
    NULL,                  // toString
    PySample::destructor   // finalize
};

これは登録するクラスのインターフェイスです。 コンストラクタ、アトリビュートの取得設定、メソッドの呼び出し、デストラクタなどの際に呼ばれる関数を定義します。

        // ExtendScriptにPyAEクラスを登録する
        ret = gpServer->addClass(hServer, "PyAE", &objectInterface) ;
        if (ret!=kESErrOK)
            printf("Failed register pyAE Class.\n");

これをExtendScript側に渡すことで、ExtendScript側から

var pyae = New PyAE();

とすることでコンストラクタが呼ばれるようになります。

次にコンストラクタですが、

// PyAEクラスのコンストラクタ
ESerror_t PySample::constructor(SoHObject hObject,int argc, TaggedData* argv)
{
    gpServer->addMethod(hObject, "eval", 0, NULL);
    return kESErrOK;
}

と先ほどインターフェイスで定義した関数が呼び出され、その中でメソッドを登録します。

こうすることで、

pyae.eval("print 'aaa'");

のようにメソッドが呼び出しできるようになります。

Pythonでevalする

PyAEクラスのevalで呼び出されると、PyAE::callが呼び出されます。 eval以外のメソッドが呼び出された場合も一度すべてここが呼び出されます。 そのため、必要であれば引数nameに入っている値で処理を分けます。

// PyAEクラスのメソッドをcallすると呼ばれる
ESerror_t PyAE::call(SoHObject hObject, SoCClientName* name,
                     int argc, TaggedData* argv, TaggedData* pResult)
{
    // name->name_sigにcallされたメソッド名が入っている
    if (strcmp(name->name_sig, "eval")==0)
        return PyAE::pyEval(hObject, name, argc, argv, pResult);
    
    return kESErrNotImplemented;
}

ExtendScriptで呼び出された際の引数とその数はargv, argcに入っているので、そこから実行するPythonスクリプトの文字列を取得します。 retがNULLの際は何かしらエラーが発生しているので、トレースバック文字列を表示して終了します。

// Pythonのevalを行う
ESerror_t PyAE::pyEval(SoHObject hObject, SoCClientName* name,
                       int argc, TaggedData* argv, TaggedData* pResult)
{
    if (argc!=1)
        return kESErrBadArgumentList;
    
    int start_symbol = Py_eval_input;
    
    // 引数に渡された文字列をPythonでevalする
    PyObject* ret = PyRun_String(argv[0].data.string, start_symbol, gGlobals, gGlobals);
    if (ret==NULL) {
        PyErr_Print();
        PyErr_Clear();
        return kESErrException;
    }
    
    py2as(ret, pResult);

    return kESErrOK;
}

Pythonで評価した値とAfterEffectのScriptの値を変換する

Pythonで評価した結果の値をAfterEffectのScriptへ返すため、変換を行います。 今回は文字列、整数、小数の3種類のみに対応しています。

// PyObject -> TaggedDataへ変換する
void py2as(PyObject* pyObj, TaggedData* aeObj)
{
    if (PyInt_Check(pyObj)||PyLong_Check(pyObj))
    {
        aeObj->data.intval = PyInt_AsLong(pyObj);
        aeObj->type = kTypeInteger;
    }
    else if (PyFloat_Check(pyObj))
    {
        aeObj->data.fltval = PyFloat_AsDouble(pyObj);
        aeObj->type = kTypeDouble;
    }
    else if (PyString_Check(pyObj))
    {
        aeObj->data.string = strdup(PyString_AsString(pyObj));
        aeObj->type = kTypeString;
    }    
}

おわりに

さくっとAfterEffectsにPythonを組み込んでみました。 実際に使うには色々と問題が残っているのですが、基本的な組み込み方は分かっていただけたのではないかと思います。

次回があるとすれば、このままでは標準出力がどこにも出ないので、そのあたりの話を書こうかと思います。

pyae.cpp

gist

Maya 2012向けにPySide 1.1.0をビルドする

http://www.autodesk.com/lgplsource
から QT4.7.1 modified for Maya をダウンロードします。


howToBuildQtOnWindows_m2012.txt に書いてある通り、
Visual Studio 2008 x64 Win64 コマンド プロンプトを立ち上げて、
> configure -platform win32-msvc2008 -release -no-qt3support
とし、configureが終わったら、
> nmake
とすることで、ビルドが始まります。

ftp://ftp.qt.nokia.com/qt/source/
から Qt 4.7.3 をダウンロードします。
(qt-everywhere-opensource-src-4.7.3.tar.gzなど)

解凍した後、Visual Studio 2008 x64 Win64 コマンド プロンプトを立ち上げて、
> configure -platform win32-msvc2008 -release -no-qt3support
とし、configureが終わったら、
> nmake
とすることで、ビルドが始まります。


あとは以前に書いたPySide for Windows 64bitと同じになります。
pyside-buildscriptsをダウンロードした後に、build-config.batを書き換えます。

その後は
> build-all.bat
とすることでビルドが完了します。


一点、自分の試した環境ではビルド中にエラーが出たので、コードを修正して対応しました。
pyside-qt4.7+1.1.0\PySide\QtGui\PySide\qmenu_wrapper.cppの3708行目。
変更前 ::QPoint* cppArg0 = QPoint::NULL;
変更後 ::QPoint* cppArg0 = NULL;


(追記)
この方法だと一部のモジュールがコンパイルされていません。
QT4.7.1 modified for Maya ではなく Qt 4.7.3をコンパイルすれば問題ないようです。
それに伴い、本文を大幅修正。

Maya 2012でPySideを使う方法 (Windows編)

Maya 2012でPySideを使う方法をまとめました。


1.CPythonをインストールします。
http://www.python.org/download/releases/2.6.6/
よりインストーラを落としてきます。
このとき、Mayaの64bit版を使っていれば、X86-64を。
32bitを使っていれば、X86を落としてきます。
ダウンロードが完了したらexeを立ち上げてCPythonをインストールします。


2.PySideをCPythonにインストールします。
http://www.pyside.org/files/
よりPySideを落としてきます。
64bit版 http://www.pyside.org/files/PySide-1.0.6qt473.win-amd64-py2.6.exe
32bit版 http://www.pyside.org/files/PySide-1.0.6qt473.win32-py2.6.exe
ダウンロードが完了したらexeを立ち上げてPySideをインストールします。


3.PySideをMayaのPythonにコピーします。
C:\Python26\Lib\site-packages
の下にPySideというディレクトリがあるはずなので、これを
C:\Program Files\Autodesk\Maya2012\Python\lib\site-packages
にコピーします。



これでPySideが使えるようになります。



スクリプトエディタで以下のコードが動けば正しくインストールできています。

import sys
from PySide.QtCore import *
from PySide.QtGui import *

# Create a Label and show it
label = QLabel(u"Hello World")
label.show()


どうでしょうか?
Windows版のバイナリが公開されてからは、簡単にPySideが使えるようになりました!