この記事は、MayaPython アドベントカレンダー 9日目の記事です。
Mayaのツール開発でよく使っているフレーズをまとめて見ました。
デザインパターンのことは書いていませんので、間違えて入ってこられた方はご了承下さい。
Maya 編
sys.pathを通す / pyc 作らない
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 に書きたいので、スターインポートしています。
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 をプリインストールしてくれれば、わざわざ布教する必要もないのですが...
技術ブログ界隈で使っておられる方が多いようなので、これからの標準でしょうか。
シーンを元に戻す
import maya.cmds as cmds
cmds.undoInfo(ock=1)
cmds.refresh(su=1)
# 書きたい処理
cmds.refresh(su=0)
cmds.undoInfo(cck=1)
cmds.undo()
スクリプト処理前に戻ります。
シーン状態を整理するので、エクスポートの時に使うことが多いかも。
スタンドアロン or スクリプトを区別する
# 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
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 []:
...
listRelatives
や listConnections
など要素が None になる可能性がある関数でエラーが起きません。ls
コマンドは空のlistを返すという違いがあるので、戻り値の型を気にします。
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)
logging
や print
とか。進んでいる職場ではチケットシステムに統合されているのでしょうか?
絵で説得する
ロジックは完璧なのに求める絵が違うことが、稀に発生します。
それはアーティストにとってエラーと同義なので、早い段階で絵を共有しておくと問題になりにくいです。
自前のコードを載せようと思いましたが、既に開発済みのものがあるので、有難く使わせて頂きます。
https://github.com/guncys-inc/saveScreenShot
プラグインのチェック
# 2016 later
for p in cmds.unknownPlugin(q=1, l=1) or []:
cmds.unknownPlugin(p, r=1)
個々の環境の違いで、消耗したくないですね。
まとめ
過去に解決済みの課題を同じようなロジックで書くことが、割とあります。
特にテクニカルは共同でコードを書く経験が少ないので、車輪の再発明をしがちです。
既知のものが多いかと思いますが、1つでも参考になることがあれば幸いです。
皆様が持っている知識があれば、ぜひ教えてください。