|
# -*- coding: utf-8 -*- |
|
from __future__ import unicode_literals |
|
import os |
|
import sys |
|
|
|
import ast |
|
import _ast |
|
|
|
from textwrap import dedent |
|
from collections import OrderedDict |
|
import jinja2 |
|
|
|
|
|
ROOT_MENU_NAME = "sample_menu" |
|
ROOT_MENU_LABEL = "sample" |
|
|
|
|
|
def menu_setup(): |
|
import maya.cmds as cmds |
|
|
|
if cmds.menu(ROOT_MENU_NAME, exists=True): |
|
cmds.deleteUI(ROOT_MENU_NAME) |
|
|
|
# the top menu |
|
cmds.menu( |
|
ROOT_MENU_NAME, |
|
label=ROOT_MENU_LABEL, |
|
parent='MayaWindow', |
|
tearOff=True, |
|
allowOptionBoxes=True |
|
) |
|
|
|
# ------------------------------------------------------------------------ |
|
# animation |
|
cmds.menuItem( |
|
'sample_animation', |
|
label='Animation', |
|
subMenu=True, |
|
tearOff=True, |
|
parent=ROOT_MENU_NAME |
|
) |
|
|
|
cmds.menuItem( |
|
'sample_animation_mirror_animation', |
|
label='Mirror Selected (animation)', |
|
annotation="", |
|
parent='sample_animation', |
|
echoCommand=True, |
|
command=dedent( |
|
''' |
|
print("hello world") |
|
''' |
|
) |
|
) |
|
|
|
cmds.menuItem( |
|
'sample_animation_transfer_via_atom', |
|
label=jpn('Load animation via ATOM'), |
|
annotation=jpn(dedent( |
|
"""ATOMを介しモーションの流し込みを行う |
|
""" |
|
)), |
|
parent='sample_animation', |
|
echoCommand=True, |
|
command=dedent( |
|
''' |
|
print("hello world") |
|
''' |
|
) |
|
) |
|
|
|
# ------------------------------------------------------------------------ |
|
# export |
|
cmds.menuItem( |
|
'sample_export', |
|
label='Export', |
|
subMenu=True, |
|
tearOff=True, |
|
parent=ROOT_MENU_NAME |
|
) |
|
|
|
cmds.menuItem( |
|
'sample_export_current_scene_current_time_range', |
|
label=jpn('FBX 吐出し'), |
|
annotation=jpn(dedent( |
|
"""現在のシーンをエクスポートする。 |
|
""" |
|
)), |
|
parent='sample_export', |
|
echoCommand=True, |
|
command=dedent( |
|
''' |
|
print("hello world") |
|
''' |
|
) |
|
) |
|
|
|
cmds.menuItem( |
|
'sample_export_folder', |
|
label=jpn('Folder一括 FBX 吐出し'), |
|
annotation=jpn(dedent( |
|
"""フォルダ一括してfbxエクスポートする |
|
エクスポート対象 シーンファイルは命名規則に準ずる |
|
""" |
|
)), |
|
parent='sample_export', |
|
echoCommand=True, |
|
command=dedent( |
|
''' |
|
print("hello world") |
|
''' |
|
) |
|
) |
|
|
|
cmds.menuItem( |
|
parent='sample_export', |
|
divider=True |
|
) |
|
|
|
cmds.menuItem( |
|
'sample_edit_export_setting_for_current_scene', |
|
label=jpn('吐出し設定の編集'), |
|
annotation=jpn(dedent( |
|
"""FBXエクスポート設定を編集する |
|
""" |
|
)), |
|
parent='sample_export', |
|
echoCommand=True, |
|
command=dedent( |
|
''' |
|
print("hello world") |
|
''' |
|
) |
|
) |
|
|
|
# ------------------------------------------------------------------------ |
|
# rigging |
|
cmds.menuItem( |
|
'sample_rigging', |
|
label='Rigging', |
|
subMenu=True, |
|
tearOff=True, |
|
parent=ROOT_MENU_NAME |
|
) |
|
|
|
cmds.menuItem( |
|
'sample_rigging_guide', |
|
label='load/export guide', |
|
subMenu=True, |
|
tearOff=True, |
|
parent='sample_rigging' |
|
) |
|
|
|
cmds.menuItem( |
|
'sample_rigging_guide_load', |
|
label='load guides', |
|
annotation="", |
|
parent='sample_rigging_guide', |
|
echoCommand=True, |
|
command=dedent( |
|
''' |
|
print("hello world") |
|
''' |
|
) |
|
) |
|
|
|
cmds.menuItem( |
|
'sample_rigging_match_guide', |
|
label='match guide', |
|
annotation=jpn(dedent( |
|
"""ガイドの位置合わせを行う |
|
""" |
|
)), |
|
parent='sample_rigging', |
|
echoCommand=True, |
|
command=dedent( |
|
''' |
|
print("hellow world") |
|
''' |
|
) |
|
) |
|
|
|
cmds.menuItem( |
|
parent='sample_rigging', |
|
divider=True |
|
) |
|
|
|
cmds.menuItem( |
|
'sample_rigging_publish_as_all', |
|
label='publish rig', |
|
annotation=jpn(dedent( |
|
"""カレントプロジェクトのリグをパブリッシュする |
|
|
|
""" |
|
)), |
|
parent='sample_rigging', |
|
echoCommand=True, |
|
command=dedent( |
|
''' |
|
print("hellow world") |
|
''' |
|
) |
|
) |
|
|
|
cmds.menuItem( |
|
'sample_rigging_publish_as_majorupdate', |
|
label='publish part-rig with major upgrade', |
|
annotation=jpn(dedent( |
|
"""開いているシーンを `リグパーツ` として保存する |
|
""" |
|
)), |
|
parent='sample_rigging', |
|
echoCommand=True, |
|
command=dedent( |
|
''' |
|
print("hellow world") |
|
''' |
|
) |
|
) |
|
|
|
|
|
def jpn(string): |
|
# type: (str) -> str |
|
"""encode utf8 into cp932""" |
|
|
|
try: |
|
string = unicode(string, "utf-8") |
|
string = string.encode("cp932") |
|
return string |
|
|
|
except Exception: |
|
return string |
|
|
|
|
|
############################################################################## |
|
|
|
|
|
def find_func(tree, func_name): |
|
# type: (_ast.Module, str) -> _ast.FunctionDef |
|
|
|
for k in tree.body: |
|
if isinstance(k, _ast.FunctionDef) and func_name in k.name: |
|
return k |
|
|
|
raise Exception("FuncDef named {} not found".format(func_name)) |
|
|
|
|
|
def match_func_name(func, search_func_name): |
|
# type: (_ast.Call, str) -> bool |
|
|
|
return "{}.{}".format(func.value.id, func.attr) == search_func_name |
|
|
|
|
|
def dig_str_recursive(node): |
|
# |
|
if isinstance(node, str): |
|
return node |
|
|
|
if isinstance(node, _ast.Call): |
|
return dig_str_recursive(node.args[0]) |
|
|
|
elif isinstance(node, _ast.Str): |
|
return node.s |
|
|
|
elif isinstance(node, _ast.Name): |
|
return node.id |
|
|
|
else: |
|
return node |
|
|
|
|
|
def dump_args(func): |
|
# type: (_ast.Call) -> Dict |
|
|
|
res = {} |
|
menu_label = func.args[0].s |
|
for keyword in func.keywords: |
|
k = keyword.arg |
|
v = dig_str_recursive(keyword.value) |
|
|
|
res[k] = v |
|
|
|
return {menu_label: res} |
|
|
|
|
|
def generate_rst(entries): |
|
# type: (dict) -> None |
|
|
|
reload(sys) |
|
sys.setdefaultencoding("utf-8") |
|
|
|
template = jinja2.Template(dedent(""" |
|
MAYA sampleメニュー 解説 |
|
=========================== |
|
|
|
|
|
{%- for label, entry in entries.iteritems() %} |
|
{%- if entry["parent"] == "ROOT_MENU_NAME" %} |
|
|
|
{{ entry["label"] }} |
|
~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
|
|
{%- else %} |
|
|
|
{{ entry["label"] }}: |
|
{%- if "annotation" not in entry.keys() or entry["annotation"] == "" %} |
|
no description |
|
|
|
{%- else %} |
|
{{ entry['annotation'] }} |
|
|
|
{%- endif %} |
|
|
|
{%- endif %} |
|
{%- endfor %} |
|
""")) |
|
|
|
return template.render(entries=entries) |
|
|
|
|
|
def generate_documentation(dst_rst_path): |
|
|
|
inspect_func_name = "menu_setup" |
|
search_func_name = "cmds.menuItem" |
|
file_path = os.path.abspath(__file__) |
|
|
|
with open(file_path, 'r') as f: |
|
|
|
tree = ast.parse(f.read()) |
|
menu_setup = find_func(tree, inspect_func_name) |
|
|
|
entries = OrderedDict() |
|
for k in menu_setup.body: |
|
if isinstance(k, _ast.Expr): |
|
|
|
try: |
|
if not k.value.args: |
|
# divider |
|
continue |
|
|
|
except Exception: |
|
continue |
|
|
|
if not match_func_name(k.value.func, search_func_name): |
|
continue |
|
|
|
entries.update(dump_args(k.value)) |
|
|
|
rst = generate_rst(entries) |
|
with open(dst_rst_path, 'w') as f: |
|
f.write(rst) |
|
|
|
|
|
if __name__ == '__main__': |
|
dst_rst_path = "D:/except_result.rst" |
|
generate_documentation(dst_rst_path) |