API ガイド > Qt の使用 >

# プラグインでの Qt の使用方法

開発者の準備作業

Maya は Nokia のカスタマイズ バージョンの Qt フレームワークを使用し、プラグインでの Qt の使用に必要なすべての独自バージョンのライブラリとヘッダー ファイルが付属しています。Linux と OS X では、ライブラリは Maya の lib フォルダにあります。Windows では、リンク ライブラリが Maya の lib フォルダ、DLL が Maya の bin フォルダにあります。ヘッダー ファイルは、すべてのプラットフォームにおいて Maya の include/Qt フォルダに圧縮アーカイブ(e.g. qt-4.7.1-include.tar.gz)として存在します。Qt プラグインの作業を開始する前に、圧縮アーカイブを書き出し権限があるフォルダに解凍し、そのフォルダが他のフォルダの前にくるようにインクルード パスを指定します。

Maya のインストールでは、qmake または designer などの Qt 開発ツールは提供されません。これらの開発ツールが必要な場合は、標準バージョンの Nokia の Qt をインストールするか、Autodesk のオープン ソース Web サイト(http://www.autodesk.com/lgplsource)からカスタマイズされた Qt ソースをダウンロードしてビルドします。後者の方法を選択した場合、Maya のサポート対象のプラットフォームで Qt を設定、ビルド、インストールする方法が記載されたテキスト ファイルを参照できます。

プラグインの作成

独自の Qt アプリケーションを一から作成するには、独自の QCoreApplication または QApplication のインスタンスを作成してそのアプリケーションのイベント ループを処理できるようにする必要があります。代わって、Maya プラグインの作成には、Qt の qApp マクロで読み出せる Maya 独自のアプリケーション オブジェクトを使用します。以下のサンプル コードでは、qApp マクロを使用して Maya のアプリケーション名を読み出しています。

    QCoreApplication*  app = qApp;
    if (app) {
        cout << "Application name is '" << app->applicationName().toStdString() << "'" << endl;
    }

Qt を使用した新規ウィンドウやダイアログの作成は、Qt の標準的なアプリケーションの作成と同じ方法で行います。devkit の helixQtCmd プラグインにごく単純なサンプルが用意されています。このプラグインには、Qt ベースのボタンを作成する helixQt コマンドがあります。このボタンを押すと、常に API コールを使用してらせん状のカーブが作成されます。

このボタンは、カーブの作成を処理するために createHelix() スロットを追加した通常の Qt pushButton です。

    class HelixButton : public QPushButton
    {
       Q_OBJECT
    public:
       HelixButton(const QString& text, QWidget* parent = 0);
       virtual	~HelixButton();
    public slots:
       void	createHelix(bool checked);
    };

helixQt コマンドを実行すると、常に、まずそのボタンの有無が確認されます。ボタンがない場合は作成し、クリックした信号をその createHelix スロットに接続して、ボタンをクリックしたときにらせんを作成するコードが必ず実行されるようにします。

    QPointer	HelixQtCmd::button;
    MStatus HelixQtCmd::doIt(const MArgList& /* args */)
    {
       if (button.isNull()) {
           button = new HelixButton("Create Helix");
           button->connect(button, SIGNAL(clicked(bool)), button, SLOT(createHelix(bool)));
           button->show();
           }

ボタンがある場合、このコマンドは、そのボタンを確実に表示して他のウィンドウの後ろに隠れないようにします。

       else {
           button->showNormal();
           button->raise();
           }
       return MS::kSuccess;
    }

Qt を使用して直接作成したコントロールは、通常、Maya の UI コマンドで認識されます。このようなコントロールは、lsUI コマンドでは一覧表示されず、button など特定タイプのコントロールのコマンドでは認識もされません。ただし、汎用のコントロール コマンドは例外です。一意の名前を指定してさえいれば、control コマンドを使用してその有無をテストし、表示設定などコントロールに関する基本操作を実行できます。

たとえば、helixQtCmd プラグインの createHelix メソッドでボタンに「myButton」という一意の名前が指定されているとします。

       button = new HelixButton("Create Helix");
       button->setObjectName("myButton");

その場合、ボタンを非表示にするために以下の MEL スクリプトを使用できます。

    if (`control -q -exists myButton`) {
       control -e -visible false myButton;
    }

ただし、同じコードで button コマンドを使用しても、動作しません。これは、button コマンドが自身が作成したボタンしか認識しないためです。

    if (`button -q -exists myButton`) {      // Will always be false.
       button -e -visible false myButton;   // Will never be executed.
    }

Qt を使用して Maya コマンドで作成した UI 要素に直接アクセスできるようにした方が望ましい場合もあります。MQtUtil API クラスには、Maya のコントロール、レイアウト、ウィンドウ、メニュー項目の基本となる Qt オブジェクトを読み出すためのメソッドが用意されています。たとえば、以下の MEL スクリプトで作成した「myCheckBox」という名前の Maya checkBox コントロールがあるとします。

    window;
    columnLayout;
    checkBox myCheckBox;
    showWindow;

MQtUtil クラスを使用して checkBox の QWidget ポインタを読み出し、それにより checkBox のカレントの状態を定義することができます。

    QWidget* control = MQtUtil::findControl("myCheckBox");
    if (control) {
       QCheckBox* cb = qobject_cast(control);
       if (cb) {
          if (cb->isChecked()) {
              MGlobal::displayInfo("myCheckBox is checked");
          } else {
              MGlobal::displayInfo("myCheckBox is not checked");
          }
       }
    }

前述の例では、MQtUtil を使用する場合は常に十分な注意が必要であることもわかります。このコードは、Maya の checkBox コマンドで QCheckBox ウィジェットまたは QCheckBox 由来の要素を作成する場合のみ動作します。これは Maya の現行バージョンの場合で、将来的には変更される可能性があります。Qt を使用して Maya のコマンドで作成した UI 要素に直接アクセスできるようにコードを記述しておくと、Maya の新しいリリースでも確実に意図した通りに動作します。

レイアウトは、注意して作成しなければ、さまざまな問題のもとになりかねません。

- Maya ではレイアウトはウィジェット階層の一部と捉えられていますが、Qt はそうではありません。つまり、Maya は、すべてとは言わないまでも、ほとんどのレイアウトにプロキシ ウィジェットを作成します。MQtUtil には、レイアウトを容易にナビゲートするためのメソッドが用意されていますが、プラグイン作成者にとっては実際のレイアウトに対してプロキシを記録する作業はまだまだ大変であり、バグの原因としてよく知られています。
- Maya のコントロールやレイアウトの中には、相互に作用する特殊なコードを持つものがあります。Qt で作成した独自のコントロールを Maya のレイアウトに追加する場合、または Maya のコントロールを Qt で作成した独自のレイアウトに追加する場合、予期せぬ結果が生じることがあります。Qt を使用して Maya のコントロールを Maya のレイアウトに追加する場合でさえ問題が生じることがあります。
- Maya のレイアウトの中には、単一の Qt レイアウトではなく、レイアウトやコントロールの小さな階層として実装されているものがあります。MQtUtil にはこのような複雑なレイアウトをナビゲートするためのメソッドが用意されていますが、使用には注意が必要です。

以上の問題やレイアウトに関する問題の詳細については、MQtUtil を参照してください。

プラグインのビルド

このセクションの最初で説明したとおり、Maya にはカスタマイズ バージョンの Qt ライブラリとヘッダー ファイルが付属しています。Maya のバージョンのヘッダーを含むフォルダが、システムの他の場所の別の Qt インストールにあるフォルダの前にくるようにインクルード パスを指定し、Maya のバージョンのライブラリを含むフォルダが、他のフォルダの前にくるようにライブラリ パスを指定することが重要です。これには、Maya devkit にある Makefile.qt を使用してコマンド ラインからプラグインをビルドするのが最も簡単です。たとえば、次のようになります。

- Linux: make -f Makefile.qt myPlugin.so
- Mac OS X: make -f Makefile.qt myPlugin.bundle
- Windows: nmake /f Makefile.qt release\myPlugin.mll

Makefile.qt は Qt の qmake コマンドを使用してプラグインのビルドを制御します。パスのいずれかの場所に qmake コマンドを用意する必要があります。ヘッダーやインクルード ファイルとは異なり、qmake などの標準的な Qt インストールのツールは安全に使用できますが、Windows では Maya の bin フォルダが別の Qt インストールの前にくるようにパスを指定してください。そうしないと、誤った DLL を選択する可能性があります。

Makefile.qt を使用するには、qmake プロジェクト ファイルの拡張子が .pro で終わらなければならないというプラグインの要件があります。以下に示す helixQtCmd.pro は、devkit で helixQtCmd プラグインに用意されているプロジェクト ファイルです。これで、大部分のプロジェクト ファイルは単純な構成であることが良くわかります。

    include(qtconfig)
    TARGET = helixQtCmd
    HEADERS += helixQtCmd.h
    SOURCES += helixQtCmd.cpp
    LIBS += -lOpenMayaUI

TARGET 設定には、プラットフォーム固有の拡張子を除いたプラグインの名前を記述します。

HEADERS 設定には、プラグインの一部であるすべてのヘッダー ファイルの一覧をスペースで区切って記述します。

SOURCES 設定には、プラグインの一部であるすべてのソース ファイルの一覧をスペースで区切って記述します。

デフォルトでは、プラグインは Maya の Foundation ライブラリと OpenMaya ライブラリに自動的にリンクされます。他のライブラリが必要な場合は、LIBS 設定に追加します。 ライブラリの名前の前には「-l」を付け、追加ライブラリのディレクトリは「-L」で指定する必要があります。たとえば、次のようになります。

    LIBS += -L/usr/local/lib -lxml

さらに複雑な事例の場合は、Qt qmake のマニュアルを参照してください。

デバッグ

Qt のサンプル プラグインは、すべてリリース（非デバッグ）モードでビルドされています。デバッグ用にビルドするには、.pro ファイルで以下の手順に従って qmake のデバッグ構成パラメータをオンにします。

    CONFIG += debug

Linux と OS X でのデバッグ用にプラグインをビルドする場合はこれで十分です。ただし、残念ながら、Windows の場合は、デバッグ構成の設定はデバッグ情報付きのプラグインをコンパイルするだけではなく、デバッグ バージョンの Qt ライブラリに強制的にリンクします。このため、Qt ライブラリのリリース バージョンでリンクされた Maya との互換性が失われてしまいます。プラグインに使用する Qt のクラスによっては、Maya にロードできたりできなかったりしますが、ロードされた場合でも、正しく動作しないと思われます。これは、Maya の QCoreApplication またはその他の Qt のグローバル値にアクセスできないためです。この問題を回避するには、debug 構成パラメータを前述のように設定し、以下のコマンドを使用して myPlugin のプラグイン名を置き換えて中間的な makefile をビルドします。

    nmake /f Makefile.qt myPlugin.mak

これは特に、プラグイン用の .mak.Debug ファイル（myPlugin.mak.Debug など）を生成します。生成されたファイルを編集してLIBS 行を探し、ファイル名の末尾の d を削除してすべてのデバッグ用の Qt ライブラリを非デバッグ バージョンに置き換えます。たとえば、helixQtCmd.mak.Debug の LIBS 業の冒頭は以下のようになります。

    LIBS = /LIBPATH:..\..\lib ..\..\lib\OpenMaya.lib ..\..\lib\Foundation.lib ..\..\lib\OpenMayaUI.lib c:\qt-adsk-4.7.1\lib\QtGuid4.lib c:\qt-adsk-4.7.1\lib\QtCored4.lib`

QtGuid4.lib を QtGui4.lib、QtCored4.lib を QtCore4.lib に置き換えると、以下のようになります。

    LIBS = /LIBPATH:..\..\lib ..\..\lib\OpenMaya.lib ..\..\lib\Foundation.lib ..\..\lib\OpenMayaUI.lib c:\qt-adsk-4.7.1\lib\QtGui4.lib c:\qt-adsk-4.7.1\lib\QtCore4.lib`

ここで、変更した .mak.Debug ファイルを使用し、以下のようにプラグインを明示的にビルドします。

    nmake /f myPlugin.mak.Debug debug\myPlugin.mll

これでも、Windows の場合はまだ問題が残ります。Qt の QList テンプレート クラスには、Qt オブジェクトを作成、破棄できるインライン メソッドがあります。 このメソッドはインライン化されているため、デバッグ用にビルドしたプラグイン コードによってコールされると、作成または削除したすべてのオブジェクトが Microsoft の C Runtime Library のデバッグ バージョンからメモリ アロケータを使用します。メソッドを Maya 内でコールした場合、C Runtime Library のリリース バージョンが使用されます。したがって、Maya 内でリリース バージョンのランタイム ライブラリを使用してオブジェクト割り当てて、デバッグ バージョンのランタイム ライブラリで削除する、またはその逆の状況が考えられます。ところが、Microsoft の C Runtime Library のリリース バージョンとデバッグ バージョンでは互換性がないため、このような操作を行うとクラッシュしてしまいます。

現在のところ、このような問題の可能性が確認されている Qt クラスはQList だけですが、他の Qt テンプレート クラスでも同様の問題が発生する可能性はあります。これに対する現段階での回避策は、コードに Qt テンプレート クラスを使用しないこと、または Windows ではプラグインのビルドをリリース モードでのみ行うことのいずれかです。