Maya AdbentCalender 2018 7日目の記事です。体調不良とスマブラが重なって投稿が遅くなりましたが、なんとか書ききれたので、必要最低限にコードの視認性を意識したGUIの組み方をまとめました。
@amanatsu-knit さん、遅れてすみませんでした。
表題に至った経緯
VFXの現場ではMaya以外にHoudiniやNukeを併用するので、カスタムツールを作るために独自uiを覚える意味をあまり見出せません。それらのアプリケーションがパイプラインに組み込まれないゲーム業界だと、Qtで作る意味があんまりなかったりします。
今年に入っていくつか maya.cmds で組まれたツールを引き継いで感じた事は、QtでMayaらしいui開発をするためには「雑用」が多くなるという現実です。ボタンが少ししかないツールのためにレイアウトを頑張って親子付したり、ウィンドウの重複を避ける、実行した時のパラメータでもう一回開くなど… shibokenも面倒です。
堅実で使い慣れたUIが求められることが多い現場で、スタイルシートやスタイルまで操れるQtを駆使するのは骨が折れます。1 そこで適当に書いてもそれっぽくなる maya のビルトイン ui メソッドを使ってツールを構築してみます。
カッコ良いツールの書き方を求めている方はこのカレンダーに素晴らしい記事が上がっている(日本のTAすごい)なので、そちらを参照してください。
実装
すべてのGUIツールの foundation ui を作ります。先に目的について述べましたが、Mayaのデザインルールに則って作ることで、個々人の美的感覚に左右されなくなるのは大きなメリットです。2
基本となる要素は以下の5つです。経験上、以下項目を実装すれば文句を言われることはありません。3
- Helpメニュー
- FrameLayout
- 底面にボタン3つ
- 重複しないウインドウ
- optionVar によるパラメータ復元
ちなみにMELでは組みません。というか、MELで組んだGUIで保守しやすいコードを見たことがありません。エンドユーザー側では綺麗なguiに見えても、実装コードを他の担当者が引き継ぐのはほぼ無理です。へいしゃは独自ラッパーがあるので通常それを使って ui を組みますが、流石にソース出したら怒られるので、頑張ってpymel
で書きます。
コード
# -*- coding: utf-8 -*-
from pymel import core as pm
from pymel.core import uitypes as ui
def pressed(*args):
if args:
print(args)
else:
print("Other func")
class Widget(object):
"""
0.Hide 重複しない
1.Menu Help
2.FrameLayout
3.Buttons 底面に3つ
4.OptionVar による復元
"""
def __init__(self):
self.name = "Foundation"
self.size = [400, 320]
self.options = pm.optionVar
self.text = None
def show(self):
# 0. 重複しない
if pm.window(self.name, ex=True):
pm.deleteUI(self.name)
with pm.window(self.name, t=self.name + "Widget", wh=self.size, menuBar=True):
# 1. メニュー
with ui.Menu(label="Help"):
ui.MenuItem(label='Help on %s' % self.name, c=pm.Callback(pressed, "Help"))
# 2. FrameLayout + メイン
with ui.AutoLayout(ratios=[3, 1]):
with ui.FrameLayout(l="Params"):
with ui.AutoLayout():
self.text = ui.TextFieldGrp(label="Value")
# 3. 底面ボタン
with ui.AutoLayout(orientation="horizontal"):
ui.Button(label='Other', c=pm.Callback(pressed))
ui.Button(label='Apply', c=pm.Callback(self.apply))
ui.Button(label='Close', c=pm.Callback(pm.deleteUI, self.name))
# 4. optionVar による値の復元
self.text.setText(self.options["text"])
def apply(self, *args):
"""ここは引数を渡すだけ"""
text = self.text.getText()
pressed(text)
self.options["text"] = text
if __name__ == "__main__":
window = Widget()
window.show()
with statement を使うことで、parent を気にせず視認性が上がるのがポイントです。
ui の command フラグに実行する関数を渡す方法はいくつかあるのですが、Callback を使った書き方がロジックとguiを分離しやすいため、これを採用しています。
AutoLayout は formLayout をいい感じにしてくれる便利なメソッドなのでよく使います。
まとめ
所属セクションに求められる役割に従って、最も適した道具を自由に使えると良いですよね。
派手さより学習コストが低い、ボタン押すだけみたいなツールが好まれるのは技術者としてつまらなく思うこともあります。そんなツール Maya が提供しろ、と他のツールを触ってみたらすぐわかりそうなものなんですが何かと手がかかるからこそ専任のTAを欲している現実も否めません。
そういうツールばかり作っているとエンジニア力が伸びないので、Qtなどで学習しつつ納期や要求に柔軟に対応出来るTAが求められる時代だと思います。