LoginSignup
45
32

More than 5 years have passed since last update.

Mayaのツール開発で書くパターン集

Last updated at Posted at 2017-12-08

この記事は、MayaPython アドベントカレンダー 9日目の記事です。
Mayaのツール開発でよく使っているフレーズをまとめて見ました。

デザインパターンのことは書いていませんので、間違えて入ってこられた方はご了承下さい。

Maya 編

sys.pathを通す / pyc 作らない

sys
import sys
sys.dont_write_bytecode = True

libpath = "//server/dev/app/maya/tools/python"
if not libpath in sys.path:
    sys.path.append(libpath)

個人環境であれば気にすることはないですが、
スタジオでツールを共有するシチュエーションでは、よく使います。

Qt.pyを使う

PySide2, PySide, PyQt5, PyQt4 をラッピングしてくれるサードパーティモジュールです。
例外処理で長いファイルヘッダーを自前で書くべきではありません。
シングルファイルなので、ツールに組み込んで配布しやすいのも良いですね。

私は、C++ like に書きたいので、スターインポートしています。

Qt
from Qt.QtGui import *
from Qt.QtCore import *
from Qt.QtWidgets import *

shiboken or shiboken2 (sip) の wrapInstance も以下のコードで共用出来ます。

from Qt import QtCompat
QtCompat.wrapInstance()

Houdini と同じく、Qt.py をプリインストールしてくれれば、わざわざ布教する必要もないのですが...
技術ブログ界隈で使っておられる方が多いようなので、これからの標準でしょうか。

シーンを元に戻す

undo
import maya.cmds as cmds

cmds.undoInfo(ock=1)
cmds.refresh(su=1)

# 書きたい処理

cmds.refresh(su=0)
cmds.undoInfo(cck=1)
cmds.undo()

スクリプト処理前に戻ります。
シーン状態を整理するので、エクスポートの時に使うことが多いかも。

スタンドアロン or スクリプトを区別する

QApplication
# Qt.py import 済み状態で

if not QApplication.instance():
    app = QApplication(sys.argv)

    # view を呼ぶ処理

    sys.exit(app.exec_())

else:
    # view を呼ぶ処理

PySide を、Mayaスクリプトエディタでもスタンドアロンでも起動できるようにします。
view と controller を適切に分離すれば、スタンドアロン経由の mayapy 起動なんてこともできますね。

ロジック編

私がよく書くパターンを紹介します。

疑わしきは try except

traceback
import traceback

try:
    # …
except Exception:
    print (traceback.format_exc())

文字列を定義するよりわかりやすいエラーメッセージが見えるので使います。

for と if を減らす

forとifを減らすことは、可読性をあげつつメンテナンスしやすいコードになるので、
意識して減らします。いくつか方法があります。

1. itertools.prodect 使う

import itertools

for m, n in itertools.product(members, numbers):
    ...

要素数が同じ配列同士なら zip 関数ですね。itertools は色々なパターンの
アルゴリズムが入っているので、for文の中で条件分岐するようなコードがあれば、
itertools の出番かもしれません。車輪の再発明をする必要はありません。

2. all or any

if all([f for f in exp_files if os.path.exists(f)]):
    ...

リスト内包表記と組み合わせて、要素の中身を全て洗います。
for文とif文だけで書くと、面倒くさくて読みにくいコードになりそう。

3. None 回避の or

import maya.cmds as cmds
from Qt.QtGui import *

cmds.file(q=1, sn=1) or QFileDialog.getOpenFileName()

if 文で None チェックするより書き方がスマートです。

for item in cmds.listRelatives() or []:
    ...

listRelativeslistConnections など要素が None になる可能性がある関数でエラーが起きません。lsコマンドは空のlistを返すという違いがあるので、戻り値の型を気にします。

Python3

python3
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

Mayaのツール開発でデバッガーが付いたエディタで開発する方が少数である以上、
print文が多く含まれることが予測されるツールは、未来の Python3 でエラーになります。
問題は未然に防いでおきたいので、print 関数で書いておきます。

参考: https://docs.python.jp/3.6/howto/pyporting.html#prevent-compatibility-regressions

書き方編

もはや Python の書き方じゃないかと突っ込まれそうですが、
MayaでPythonを書いているとありがちな内容なのでまとめます。

引数を2個以上記入する際は改行する

import maya.cmds as cmds

cmds.setAttr(
    "{}.dso".format(shape),
    "{}/{}/{}.ass".format(
        os.path.dirname(os.path.dirname(dir)), "element/ASS", file
    ),
    typ="string"
)

MELプロシージャは、Bad Python と言えるほどに引数が多いので、
視認性を考えて改行します。リスト内包表記も同様です。

シーケンスを引数へ一度に展開する

xform や getattr で得たlistに都度インデックスアクセスして書くより、アスタリスクで要素展開するとシンプルです。
MVector や MColor 等は __getitem__ を実装しているので同じように展開できます。(スライシングは無理)

import maya.cmds as cmds
import maya.api.OpenMaya as om

v0 = om.MVector(1, 4, 1) #cmds.xform("pSphere1", t=1, q=1)
cmds.setAttr("pSphere1.translate", *v0)

引数の長さはきっちり合わせる必要があります。

listのアンパックに _ を使う

使わないトランスフォームなど、変数名を割り当てずに明示的に使用しないことを表現するために、
Swift like なアンダースコアを使います。

a, _ = cmds.listRelatives()

インデックスによるリストへのアクセスと使い分けます。

パス置換

import os
string.replace(os.sep, "/")

Windows環境はよく使いますね。

運用編

ツールを試して貰う際に起きやすい問題に対処します。

察しない。ログを出す

ローカル環境でうまくいっていたことが他環境でうまくいかないことが多いので、
Mayaのバージョン、チェックしているファイル、作成日時など、参考にできる情報は全てログに流します。

cmds.about(q=1, v=1), cmds.about(q=1, cd=1), cmds.about(q=1, ct=1), cmds.file(q=1, sn=1)

loggingprint とか。進んでいる職場ではチケットシステムに統合されているのでしょうか?

絵で説得する

ロジックは完璧なのに求める絵が違うことが、稀に発生します。
それはアーティストにとってエラーと同義なので、早い段階で絵を共有しておくと問題になりにくいです。

自前のコードを載せようと思いましたが、既に開発済みのものがあるので、有難く使わせて頂きます。
https://github.com/guncys-inc/saveScreenShot

プラグインのチェック

plugins
# 2016 later
for p in cmds.unknownPlugin(q=1, l=1) or []:
    cmds.unknownPlugin(p, r=1)

個々の環境の違いで、消耗したくないですね。

まとめ

過去に解決済みの課題を同じようなロジックで書くことが、割とあります。
特にテクニカルは共同でコードを書く経験が少ないので、車輪の再発明をしがちです。

既知のものが多いかと思いますが、1つでも参考になることがあれば幸いです。
皆様が持っている知識があれば、ぜひ教えてください。

45
32
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
45
32