Shelfにあると便利かもしれないスクリプト集

INDEX

はじめに

こんにちは。モノリスソフト テクニカルアーティストの菅原です。

今回はMayaのShelfに登録しておくと便利かもしれないスクリプトをまとめてみます。記事内のスクリプトはMayaのScriptEditorへのコピーアンドペーストで動作しますので気軽に使用してみてください。

内容としてはいくつかは簡単なものになっていると思いますので、自分が使用しやすいように書き換える・学習に利用する等に活用していただければと思います。

現在開いているシーンを強制的に開き直す

import os
import maya.cmds as cmds
 
def reopen_scene():
    fp = cmds.file(q=True, sn=True)
    if os.path.exists(fp):
        cmds.file(fp, o=True, f=True)
 
reopen_scene()

ツール開発時には頻繁に検証用のシーンを開き直すため、こういったよく使うものは簡単なものでもShelfに登録しておくと作業が楽になります。

tech_09_02.gif

現在開いているシーンファイルのパスをクリップボードにコピーする

import maya.cmds as cmds
from PySide2 import QtGui
 
def copy_scene_path():
    fp = cmds.file(q=True, sn=True)
    if os.path.exists(fp):
        cb = QtGui.QClipboard()
        cb.setText(fp.replace('\\', '/'))
 
copy_scene_path()

Windowsのショートカットとして、Shiftキーを押しながらエクスプローラでファイルを右クリックすると「パスのコピー」というメニューは表示されるのですが、それを使用すると区切りがバックスラッシュになってしまいます。
場合によってはそちらの方が都合が良かったりもするのですが、このように処理を自分で用意することで好きな形式でパスを取得することが可能になります。

tech_09_03.gif

現在開いているシーンファイルのフォルダを開く

import os
import subprocess
import maya.cmds as cmds
 
def open_scene_dir():
    p = os.path.dirname(cmds.file(q=True, sn=True))
    if not os.path.exists(p):
        return
 
    subprocess.Popen(["explorer", p.replace('/', '\\')])
 
open_scene_dir()

シーンファイルと同じフォルダに出力したツールのlogファイルの確認や、シーンファイルの近くにあるTexture等にアクセスする際に使用するスクリプトです。

tech_09_04.gif

最近開いたファイルのフォルダからシーンファイルを選択して開く

import os
import maya.cmds as cmds
import maya.mel as mel
from maya.app.general import mayaMixin
from PySide2 import QtWidgets, QtGui, QtCore
 
def open_recent_dir():
    recent_dlg = RecentDialog()
 
    def on_double_clicked():
        recent_dlg.close()
        file_path, _ = QtWidgets.QFileDialog.getOpenFileName(None,
                                                             'Open Maya Scene',
                                                             recent_dlg.list.currentItem().text(),
                                                             'Maya Ascii (*.ma);;Maya Binary (*.mb);;All (*.*)')
        if not os.path.exists(file_path):
            return
 
        save_result = 1
        if cmds.file(q=True, modified=True):
            save_result = mel.eval('saveChanges ""')
        if save_result > 0:
            print('open : ' + file_path)
            cmds.file(file_path, open=True, force=True)
 
            file_type = (cmds.file(file_path, q=True, type=True) or ["type_not_found"])[0]
            mel.eval('addRecentFile("{0}", "{1}")'.format(file_path, file_type))
 
    recent_dlg.list.doubleClicked.connect(on_double_clicked)
    recent_dlg.exec_()
 
 
class RecentDialog(mayaMixin.MayaQWidgetBaseMixin, QtWidgets.QDialog):
    def __init__(self, parent=None):
        super(RecentDialog, self).__init__(parent)
 
        self.setWindowTitle('Recent Directory')
        self.setLayout(QtWidgets.QVBoxLayout())
        self.setMinimumSize(QtCore.QSize(600, 300))
 
        high_col = QtGui.QColor(68, 68, 68)
        def_col = QtGui.QColor(43, 43, 43)
 
        self.__list_widget = QtWidgets.QListWidget()
        self.__list_widget.addItems(sorted(list(set([os.path.dirname(fp) for fp in cmds.optionVar(q='RecentFilesList')]))))
        [self.__list_widget.item(i).setBackgroundColor(high_col if i % 2 == 1 else def_col) for i in range(self.__list_widget.count())]
 
        style = 'QListWidget{font-size: 10pt;}'
        style += 'QListWidget{background-color: rgb(68, 68, 68);}' if self.__list_widget.count() % 2 == 1 else ''
        self.__list_widget.setStyleSheet(style)
 
        self.layout().addWidget(self.__list_widget)
 
    @property
    def list(self):
        return self.__list_widget
 
open_recent_dir()

プロジェクト設定をしていなかったときや、開いているシーンファイルと同フォルダにある検証用に少しだけ変更したシーンファイルを開きたい場合等に使用するツールです。
前述の現在開いているシーンファイルのフォルダを開くからフォルダパスをコピーして同じようなことは可能ですが、それすら面倒な人向けです。

tech_09_05.gif

選択したノードのアトリビュートのタイプと値を一覧表示

import pymel.core as pm
from maya.app.general import mayaMixin
from PySide2 import QtWidgets, QtCore
 
def list_sorted_attr():
    text_items = []
 
    for n in pm.ls(sl=True):
        text_items.append('NODE: {0}'.format(str(n)))
 
        names = []
        values = []
        types = []
 
        for attr in sorted(n.listAttr()):
            if not attr.exists() or attr.isHidden():
                continue
            if attr.type() == 'TdataCompound' or attr.isCompound():
                continue
 
            print(attr)
            names.append('{0} ({1})'.format(attr.plugAttr(longName=True, fullPath=True),
                                            attr.plugAttr(longName=False, fullPath=False)))
            types.append(str(attr.type()))
            values.append(str(attr.get()))
 
        max_len = [0] * 3
        max_len[0] = len(sorted(names, reverse=True, key=len)[0])
        max_len[1] = len(sorted(types, reverse=True, key=len)[0])
        max_len[2] = len(sorted(values, reverse=True, key=len)[0])
 
        for i, item in enumerate(zip(names, values, types)):
            n, v, t = item
            text_items.append('{0}: type:{1}, value:{2}'.format(n.ljust(max_len[0]), t.ljust(max_len[1]), v))
        text_items.append('---------')
 
    dlg = TextDialog()
    dlg.setMinimumSize(QtCore.QSize(700, 400))
    dlg.log.setText('\n'.join(text_items or ['None']))
    dlg.log.setFont('courier new')
    dlg.show()
 
 
class TextDialog(mayaMixin.MayaQWidgetBaseMixin, QtWidgets.QDialog):
    def __init__(self, parent=None):
        super(TextDialog, self).__init__(parent)
 
        self.setWindowTitle(u'Output')
        self.setAttribute(QtCore.Qt.WA_DeleteOnClose)
 
        self.__log = QtWidgets.QTextEdit(self)
        self.__log.setLineWrapMode(QtWidgets.QTextEdit.NoWrap)
        self.__log.setReadOnly(True)
 
        layout = QtWidgets.QVBoxLayout(self)
        layout.addWidget(self.__log)
 
        self.setLayout(layout)
 
    @property
    def log(self):
        return self.__log
 
list_sorted_attr()

選択したノードのアトリビュートをABC順でソートして名前と値を一覧表示するツールです。

tech_09_06.gif

鮫モデルの作成

import maya.cmds as cmds
 
def create_shark():
    nodes = cmds.file(r'C:/Program Files/Autodesk/Maya2019/Examples/Modeling/Sculpting_Base_Meshes/Animals/Shark.ma',
                      i=True, type='mayaAscii', options='v=0;p=17;f=0', rnn=True)
    nodes = cmds.ls(nodes, l=True)
    shark_dags = cmds.ls(nodes, type=['transform', 'mesh'], l=True)
    if len(shark_dags) is not 2:
        cmds.delete(nodes)
        return
 
    cmds.sets(shark_dags, e=True, forceElement='initialShadingGroup')
    cmds.delete(list(set(nodes) - set(shark_dags)))
 
create_shark()

検証用メッシュとしてInitialShadingGroupを適用した鮫を作成するスクリプトです。MayaのExamplesにある鮫のデータを読み込んでいます。
他のデータを使用したい場合は、ファイルパス部分をExamplesにある他のデータに変更して使用してください。

tech_09_07.gif

まとめ

今回はShelfに登録しておくと便利かもしれないスクリプトをまとめてみました。
簡単なものがいくつかありますので、全くスクリプトを触らないという人にとって興味を持つきっかけになれば幸いです。

執筆者:菅原

インタラクティブコンテンツ制作会社を経てモノリスソフトへ入社。 以来、テクニカルアーティストとして主にMayaツール開発の業務を担当。 好きな食べものはごはん。

ABOUT

モノリスソフト開発スタッフが日々取り組んでいる技術研究やノウハウをご紹介

RECRUIT採用情報