【Maya】キーフレームのスタート / エンドの取得 | リグログ!
ノード単体からの取得する関数です。
元の関数と同じようにTranslate / Rotate / Scale から対象選べるようにしてみました。
(もうちょいスマートな方法ある気がしますが。。。)
import maya.cmds as cmds def get_time_range_from_node(node=None, t=True, r=True, s=True): ''' ノードからアニメカーブの範囲を取得する t : Translate r : Rotate s : Scale ''' anim_curves = [] if t is True: anim_curves += cmds.listConnections(node, t='animCurveTL', et=True) if r is True: anim_curves += cmds.listConnections(node, t='animCurveTA', et=True) if s is True: anim_curves += cmds.listConnections(node, t='animCurveTU', et=True) min_time = None max_time = None for anim_curve in anim_curves: indices = cmds.getAttr('%s.ktv' %anim_curve, mi=True) for i in indices: t,v = cmds.getAttr('%s.ktv[%i]' %(anim_curve,i))[0] if min_time is None or t < min_time: min_time = t elif max_time is None or max_time < t: max_time = t return min_time,max_time]]>
multi_prog_win = None class MultiProgressWin(object): WIDTH = 300 def __init__(self, title="MultiProgressWin", max=100, display_text=""): self.showwin = False if multi_prog_win is None: global multi_prog_win multi_prog_win = cmds.window(t=title, w=self.WIDTH, s=False) cmds.showWindow(multi_prog_win) self.showwin = True self.__add_bar(display_text, max) cmds.refresh() def __add_bar(self, display_text, max): self.layout = cmds.columnLayout(p=multi_prog_win) self.text = cmds.text(display_text, p=self.layout) self.bar = cmds.progressBar(maxValue=max, w=self.WIDTH, p=self.layout, progress=0) # デフォルトの高さが39ずつ増えていたのでキリが良いように40増やしてみる preHeight = cmds.window(multi_prog_win, q=True, height=True) global multi_prog_win cmds.window(multi_prog_win, edit=True, h=preHeight + 40) def update(self, display_text=None, step=1): ''' プログレスバーを動かす。 通常は1ずつ増加 ''' cmds.progressBar(self.bar, e=True, step=step) if display_text is not None: cmds.text(self.text, e=True, l=display_text) cmds.refresh() progress = cmds.progressBar(self.bar, q=True, progress=True) max = cmds.progressBar(self.bar, q=True, maxValue=True) global multi_prog_win preHeight = cmds.window(multi_prog_win, q=True, height=True) if progress == max and preHeight - 40 > 0: # 0以下になったときのエラー回避 cmds.window(multi_prog_win, edit=True, h=preHeight - 40) def reset(self, display_text=None): ''' プログレスバーのリセット ''' cmds.progressBar(self.bar, e=True, progress=0) if display_text is not None: cmds.text(self.text, e=True, l=display_text) cmds.refresh() def kill(self): ''' 作ったものを削除。 Windowを生成していたらWindowが消えるが、 追加プログレスバーの場合だったらプログレスバーだけ消える ''' if self.showwin is False: cmds.deleteUI(self.layout, control=True) return global multi_prog_win cmds.deleteUI(multi_prog_win) del multi_prog_win
def test(text=u"一つ追加"): pw = MultiProgressWin(display_text=text, max=100) for i in range(100): pw.update(text) time.sleep(0.02) pw.kill() def test2(): text=u"追加一つ目" pw = MultiProgressWin(display_text=text, max=100) for i in range(100): pw.update(text) time.sleep(0.02) if i == 50: test(u"二つ目追加!!") text=u"2つ目が終わったので再開" pw.kill() pw = MultiProgressWin(display_text="ベースのプログレスバー" ,max=5) time.sleep(1) pw.update() time.sleep(1) pw.update("プログレスバー1つ追加するよ") time.sleep(1) test() pw.update("ベースのプログレスバー") time.sleep(1) pw.update("プログレスバー2つ追加するよ") time.sleep(1) test2() pw.update() time.sleep(1) pw.reset("リセットもできる") time.sleep(2) pw.kill()
コノあたりのサンプルコードはちょっと変更するだけでMayaでも動作させられます。
from PyQt4 import QtGui, QtCore,Qt
となっている部分を以下の様に書き換えます(元の内容によって変わりますが。)
from PySide import QtCore, QtGui
次にクラスの定義部分。
class Example(QtGui.QWidget):
Mayaで表示させるために以下の様にします。
from maya.app.general.mayaMixin import MayaQWidgetBaseMixin class Example(MayaQWidgetBaseMixin, QtGui.QWidget):
GUIの表示部分
app = QtGui.QApplication(sys.argv) ex = Example() sys.exit(app.exec_())
こんな感じに。
app = QtGui.QApplication.instance() Example() sys.exit() app.exec_()
後ろ二つについては以前の記事とおなじですね。
MOCHI-MOCHI 【MAYA】Maya2015からはshiboken使わなくてもいいらしい
ただし、PyQtとPySideの差異もあるので考慮しなければならない部分もあるようです。
PySideとPyQtの差異 - None is None is None
しかし、これで色々と情報を入手できそうです!
]]># -*- coding: utf-8 -*- from maya import cmds from PIL import Image import pymel.core as pm import os import stat def get_shading_engines(root_node=None): ''' 指定ノード以下のshading_engineの重複しないリストを取得 ''' en_list = [] if root_node is None: shapes = pm.ls(type="mesh") else: if isinstance(root_node, (str, unicode)): root_node = pm.PyNode(root_node) shapes = root_node.listRelatives(ad=True, type="mesh") file_nodes = [] for i in shapes: shading_engines = i.shadingGroups() en_list+=shading_engines #重複をなくしてから戻す return list(set(en_list)) def get_all_texture_node(root_node=None): ''' 指定ノード以下に接続されている全テクスチャノードを取得 ''' file_nodes = [] en_list = get_shading_engines(root_node) for en in en_list: file_nodes.extend(cmds.ls(cmds.listHistory(en.name()),type='file')) return list(set(file_nodes)) def get_truth_file_path(filepath, dir): ''' パスの存在をしらべる。なければセットプロジェクトされているフォルダ以下にないか探しにいく。 ''' if os.path.exists(filepath): return filepath workpath = pm.workspace( q=True, rootDirectory=True ) if dir is not None: workpath = workpath + dir filename = os.path.basename(filepath) for root, dirs, files in os.walk(workpath): for file in files: if filename == file: return root + r"/" +file return filepath def get_use_texture_path_list(root_node=None): ''' 指定ノード以下に接続されているテクスチャパスのリスト ''' file_nodes = get_all_texture_node(root_node) files = [] for tex in file_nodes: path = cmds.getAttr(tex + '.fileTextureName') truth_path= get_truth_file_path(path, "sourceimages") if truth_path is None:continue; files.append(truth_path) files = list(set(files)) return files def set_texture_to_specified_dir(root_node=None, dir=""): ''' 指定ノード以下に接続されているテクスチャを指定のディレクトリに接続し直す ''' file_nodes = get_all_texture_node(root_node) for tex in file_nodes: def_path = cmds.getAttr(tex + '.fileTextureName') base_name = os.path.basename(def_path) new_path = os.path.join(dir, base_name) if os.path.isfile(new_path): cmds.setAttr(tex + '.fileTextureName', new_path, type='string') def resize_texture(root_node=None, dir="", magnification=1, width=None, height=None): ''' 指定ノード以下のテクスチャをリサイズ(別フォルダに出力) 画像幅と高さが両方していされてればそっち優先 root_node : テクスチャ書き出すルートノード export_dir : 書き出すフォルダ magnification : テクスチャの倍率 width : 画像幅 height: 画像高さ ''' files = get_use_texture_path_list(root_node) if os.path.exists(dir) == False: os.makedirs(dir) # ペースト先のファイルが読み取り専用だった場合エラーになるので対処しとく for f in files: dir_path, file_name = os.path.split(f) save_file_path = dir+'\\'+file_name if os.path.exists(save_file_path): os.chmod(save_file_path, stat.S_IWRITE) im = Image.open(f) w = int(im.size[0]) * magnification h = int(im.size[1]) * magnification if width is not None and height is not None: w = width h = height im.resize((int(w),int(h))).save(save_file_path) set_texture_to_specified_dir(root_node=root_node, dir=dir)
resize_texture(root_node='root', dir=r'C:\temp\test', magnification=0.1)
## -*- coding: utf-8 -*- import os import re class Node(object): def __init__(self, path, nodetype): self.path = path self.nodetype = nodetype def _get_value(self, param, stg): ''' -n "hogehoge" のhogehoge部分を取得 ''' pattern = param + ' "(.+?)"' matchedList = re.findall(pattern, stg) if len(matchedList) is 0: return '' pattern = '^' + param + ' "|"$' return re.sub(pattern, '', matchedList[0]) def _get_node_name(self, stg): return self._get_value('-n', stg) def _get_parent(self, stg): return self._get_value('-p', stg) def search(self): list = [] fil = "createNode " + self.nodetype f = open(self.path) line = self._read(f) while line: if fil not in line: line = self._read(f) continue #目的のタイプのノードを発見 nd = NodeData() nd.name = self._get_node_name(line) nd.parent = self._get_parent(line) #attr line = self._read(f) while re.match('setAttr' , line) is not None: at = self._disassembly_setattr_data(line) nd.attr[at.name] = at line = self._read(f) list.append(nd) f.close() return list def _disassembly_setattr_data(self, stg): ''' setAttrの文字列を分解する ''' attr = AttrData() #アトリビュート名 attr.name = re.findall('"\..+?"', stg) if len(attr.name) > 0: attr.name = re.sub('"\.|"', '', attr.name[0]) #タイプ attr.type = re.findall('-type ".+?"', stg) if len(attr.type) > 0: attr.type = re.sub('-type "|"', '', attr.type[0]) else: attr.type = '' #値(多分末尾に書かれてる) attr.value = re.sub('^.+ |"|;', '', stg) attr.value = attr.value.strip() return attr def _read(self, file): ''' ;までを1行として読み込む strip()で先頭と末尾の空白文字(改行・タブなどなど)が簡単に取り除けるらしいよ。 ''' line = file.readline().strip() while line[-1:] != ';': l = file.readline().strip() #EOF if l == '': break line = line + l return line class NodeData(object): ''' ノードデータ ''' def __init__(self): self.name = '' self.parent = '' self.attr = {} class AttrData(object): ''' アトリビュートデータ ''' def __init__(self): self.value = '' self.name = '' self.type = ''
とりあえず新規シーンをMaya Asciiで保存します。
今回のコードは自分の必要な部分だけなので、取得できるのはノードの情報のみです。
使い方は以下のような感じ。
nodes = Node(r'c:\test.ma', 'camera').search()
これでシーン内のカメラの情報を取得します。
情報をprint文で書き出してみましょ。
4 =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- [node name]perspShape [parent node]persp [attr name]ncp [value]10 [type] [attr name]imn [value]persp [type]string [attr name]den [value]persp_depth [type]string [attr name]v [value]no [type] [attr name]coi [value]180.75837217595105 [type] [attr name]hc [value]%camera [type]string [attr name]fl [value]128.07659939244272 [type] [attr name]man [value]persp_mask [type]string =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- [node name]topShape [parent node]top [attr name]ncp [value]10 [type] [attr name]o [value]yes [type] [attr name]rnd [value]no [type] [attr name]ow [value]30 [type] [attr name]den [value]top_depth [type]string [attr name]v [value]no [type] [attr name]coi [value]100.1 [type] [attr name]hc [value]%camera [type]string [attr name]imn [value]top [type]string [attr name]man [value]top_mask [type]string =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- [node name]frontShape [parent node]front [attr name]ncp [value]10 [type] [attr name]o [value]yes [type] [attr name]rnd [value]no [type] [attr name]ow [value]30 [type] [attr name]den [value]front_depth [type]string [attr name]v [value]no [type] [attr name]coi [value]100.1 [type] [attr name]hc [value]%camera [type]string [attr name]imn [value]front [type]string [attr name]man [value]front_mask [type]string =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- [node name]sideShape [parent node]side [attr name]ncp [value]10 [type] [attr name]o [value]yes [type] [attr name]rnd [value]no [type] [attr name]ow [value]30 [type] [attr name]den [value]side_depth [type]string [attr name]v [value]no [type] [attr name]coi [value]100.1 [type] [attr name]hc [value]%camera [type]string [attr name]imn [value]side [type]string [attr name]man [value]side_mask [type]string
## -*- coding: utf-8 -*- import site import os import re site.addsitedir(os.path.dirname(os.path.abspath(__file__)) + r'\win32maya2015') import win32com.client as com # ------------------------------------------------ # 数字をアルファベットに変換 1 -> A 27 -> AA def num2char(num): quotient, remainder = divmod(num, 26) chars = '' if quotient > 0: chars = chr(quotient + 64) if remainder > 0: chars = chars + chr(remainder + 64) return chars # ------------------------------------------------ # アルファベットを数字に変換 A -> 1 AA -> 27 def char2num(chars): num = 0 for c in chars: num = num * 26 + (ord(c) - 64) return num '''--------------------------------------------------------------------------------''' class App(object): XLMAXIMIZED = -4137 def __init__(self, visible=False): self.__excel_app = com.Dispatch("Excel.Application") #警告メッセージを表示しない。 self.__excel_app.DisplayAlerts = False self.__excel_app.Visible = visible if(visible==True): self.__excel_app.WindowState = self.XLMAXIMIZED self.__excel_app.Visible = True # ------------------------------------------------ # Excelブックを新規作成 def add(self): book = self.__excel_app.Workbooks.Add() return Workbook(book) # ------------------------------------------------ # エクセルファイルを開く def open(self, path, sheet=None, readonly=False): book = self.__excel_app.Workbooks.Open(path, 2, readonly) return Workbook(book) # ------------------------------------------------ # エクセルプロセスを終了 # (閉めにこれをやらないとプロセスに__excel_appがいっぱい残るので注意) def quit(self): self.__excel_app.Application.Quit() '''--------------------------------------------------------------------------------''' class Workbook(object): def __init__(self, book): self.__book = book # ------------------------------------------------ # nameをアクセサ付きのプロパティとして登録 sheets = property(doc='sheets property') @sheets.setter def sheets(self): pass @sheets.getter def sheets(self): return self._get_sheet_list() @sheets.deleter def sheets(self): pass # ------------------------------------------------ # ブックを閉じる def close(self): self.__book.Close() self.__book = None # ------------------------------------------------ # 上書き保存 def save(self): self.__book.Save() # ------------------------------------------------ # 名前をつけて保存 def save_as(self, path): self.__book.SaveAs(path) # ------------------------------------------------ # ブック内のシート一覧を取得する def _get_sheet_list(self): list = [] for value in self.__book.Worksheets: list.append(Sheet(value)) return list # ------------------------------------------------ # 名前でシートを検索 def find_sheet(self, name): if isinstance(name, str): name = name.decode('ShiftJIS') for value in self.sheets: if value.name == name: return value return None # ------------------------------------------------ # シート削除(シート名、もしくは番号で指定) def delete_sheet(self, target): if type(target) is str: if not self.find_sheet(target): print u"[error]Designation of the sheet does not exist" return None if type(target) is int: if target < 1 or target > len(self.sheets): print u"[error]Designation of the sheet does not exist" return None self.__book.Worksheets(target).Delete() # ------------------------------------------------ # シート作成 def add_sheet(self, name): ws = self.__book.Worksheets.Add() ws.Name = name return Sheet(ws) '''--------------------------------------------------------------------------------''' class Sheet(object): XLDOWN = -4121 XLUP = -4162 XLTOLEFT = -4159 XLTORIGHT = -4161 def __init__(self, sheet): self.__sheet = sheet self._name = sheet.Name self._index = sheet.Index # ------------------------------------------------ # nameをアクセサ付きのプロパティとして登録 name = property(doc='name property') @name.setter def name(self, value): self._name = value self.__sheet.Name = value @name.getter def name(self): return self._name @name.deleter def name(self): pass # ------------------------------------------------ # indexをアクセサ付きのプロパティとして登録 index = property(doc='index property') @index.setter def index(self): pass @index.getter def index(self): return self._index @index.deleter def index(self): pass # ------------------------------------------------ # セルを取得 def get_cell(self, x, y): return Cell(self.__sheet, x, y) # ------------------------------------------------ # 指定列(x)の最終行を取得する def max_row(self, x): if type(x) is str: x = char2num(x) return self.__sheet.Cells(65536, x).End(self.XLUP).Row # ------------------------------------------------ # 指定行(y)の最終列を取得する def max_colum(self, y): return self.__sheet.Cells(y, 256).End(self.XLTOLEFT).Column # ------------------------------------------------ # 行から指定の値のセルを取得する(横方向に調べる) def find_cell_from_rows(self, y, search_value, start_x=1): cells = [] if isinstance(start_x, str): start_x = char2num(start_x) max_x = self.max_colum(y) for x in range(start_x, max_x): c = self.__comparison_detail(x, y, search_value) if c is not None: cells.append(c) return cells # ------------------------------------------------ # 列から指定の値のセルを取得する(縦方向に調べる) def find_cell_from_columns(self, x, search_value, start_y=1): cells = [] max_y = self.max_row(x) for y in range(start_y, max_y): c = self.__comparison_detail(x, y, search_value) if c is not None: cells.append(c) return cells # ------------------------------------------------ # セルの内容比較 def __comparison_detail(self, x, y, search_value): #cell_value = self.__sheet.Cells(y, x).Value cell = Cell(self.__sheet, x, y) cell_value = cell.text if isinstance(search_value, str): search_value = search_value.decode('ShiftJIS') if cell_value is None: return None f = re.search(search_value, cell_value) if f is None: return None return cell '''--------------------------------------------------------------------------------''' class Cell(object): def __init__(self, sheet, x, y): self.__sheet = sheet self.__cell = sheet.Cells(y, x) self.__range = sheet.Range(num2char(self.x) + str(self.y)) # ------------------------------------------------ # xをアクセサ付きのプロパティとして登録 x = property(doc='x property') @x.setter def x(self): pass @x.getter def x(self): return self.__cell.Column @x.deleter def x(self): pass # ------------------------------------------------ # yをアクセサ付きのプロパティとして登録 y = property(doc='y property') @y.setter def y(self): pass @y.getter def y(self): return self.__cell.Row @y.deleter def y(self): pass # ------------------------------------------------ # valueをアクセサ付きのプロパティとして登録 value = property(doc='value property') @value.setter def value(self, value): self.__cell.Value = value @value.getter def value(self): return self.__cell.Value @value.deleter def value(self): pass # ------------------------------------------------ # textをアクセサ付きのプロパティとして登録 text = property(doc='text property') @text.setter def text(self): pass #表示そのままの文字列を取得するため、RangeオブジェクトにあるTextプロパティを戻す @text.getter def text(self): return self.__range.Text @text.deleter def text(self): pass # ------------------------------------------------ # heightをアクセサ付きのプロパティとして登録 height = property(doc='height property') @height.setter def height(self, value): self.__cell.RowHeight = value @height.getter def height(self): return self.__cell.RowHeight @height.deleter def height(self): pass # ------------------------------------------------ # widthをアクセサ付きのプロパティとして登録 width = property(doc='width property') @width.setter def width(self, value): self.__cell.ColumnWidth = value @width.getter def width(self): return self.__cell.ColumnWidth @width.deleter def width(self): pass # ------------------------------------------------ # colorをアクセサ付きのプロパティとして登録 bg_color = property(doc='bg_color property') @bg_color.setter def bg_color(self, value): #とりあえずcolorインデックスでの指定のみ対応 self.__cell.Interior.ColorIndex = value @bg_color.getter def bg_color(self): pass @bg_color.deleter def bg_color(self): pass
## -*- coding: utf-8 -*- #まずはインポート import Excel #------------------------------------------------------------------------------ #◆Appクラス #これでエクセルを立ち上げる excel = Excel.App() #既存のエクセルファイルを開く→Workbookクラスが戻ってくる book = excel.open(r"エクセルファイルのパス") #新しいブックを開く場合はこう→Workbookクラスが戻ってくる book = excel.add() #すべてが終わったらエクセルを終了しましょう excel.quit() #------------------------------------------------------------------------------ #◆Workbookクラス #これでシートのリストが取得できる book.sheets #ワークブック自身を閉じる book.close() #上書き保存 book.save() #名前を付けて保存 book.save_as(r"セーブするパス") #シートを名前で検索→存在していた場合シートクラスのインスタンスが戻される sheet = book.find_sheet("Sheet1") #シートを削除 book.delete_sheet(名前もしくは番号を指定) #シートを作成→シートクラスのインスタンスが戻される sheet = book.add_sheet() #------------------------------------------------------------------------------ #◆Sheetクラス #シート名 sheet.name #シート名変更 sheet.name = "ほげほげ" #シートのインデックス(番号) sheet.index #セルの取得→Cellクラスが戻る #x : 列(横方向) A〜Zもしくは数字を指定 #y : 行(縦方向) 数字を指定 sheet.get_cell(x ,y) #指定列(x)の最終行を取得する sheet.max_row(x) #指定行(y)の最終列を取得する sheet.max_colum(y) #行から指定の値のセルを取得する(横方向に調べる)→Cellクラスが戻る #y : 縦方向 数字を指定 #search_value : 対象の値(調べるのは表示されている文字列。内部の式などは見ない) #start_x : 何列目からサーチを開始するか。でふぉは1から #現時点ではsearch_valueが含まれていれば検出対象となる。完全一致などのしては出来ない sheet.find_cell_from_rows(y, search_value, start_x) #列から指定の値のセルを取得する(縦方向に調べる) #上記とほとんど一緒 sheet.find_cell_from_columns(x, search_value, start_y) #------------------------------------------------------------------------------ #◆Cellクラス #メソッドはなし。 #セルの列番号(横方向) #読み取り専用 cell.x #セルの行番号(縦方向) #読み取り専用 cell.y #セルの内容 #セルの内容によって戻ってくる型が違う。場合によってはちょっと扱いづらいかも? #1と表示されているセルでも1.0として戻ってきたりする cell.value #セルのテキスト #表示されている値を文字列として取得 #1と表示されているセルは1が戻ってくる。 #読み取り専用 cell.text #セルの縦幅 cell.height #セルの横幅 cell.width #セルの背景色 #設定する場合は番号を指定。そのうち色直指定できるように拡張したい。 cell.bg_color
# -*- coding: utf-8 -*- from PySide import QtCore, QtGui from maya.app.general.mayaMixin import MayaQWidgetBaseMixin #OKってボタンが表示されるだけのUI class GUI(MayaQWidgetBaseMixin, QtGui.QWidget): def __init__(self, parent=None): super(GUI, self).__init__(parent=parent) self.layout = QtGui.QVBoxLayout() self.setLayout(self.layout) self.okButton = QtGui.QPushButton('OK') self.layout.addWidget(self.okButton) if __name__ == '__main__': app = QtGui.QApplication.instance() ui = GUI() ui.show() sys.exit() app.exec_()
from Shiboken.shiboken import wrapInstance
from shiboken import wrapInstance
app = QtGui.QApplication(sys.argv) ui = GUI() ui.show() sys.exit(app.exec_())
app = QtGui.QApplication.instance() ui = GUI() ui.show() sys.exit() app.exec_()
import pymel.core as pm import maya.cmds as cmds #指定ノード以下のシェイプノードのバウンディングボックスを取得 def get_boundingbox(root): if isinstance(root,str) == True: root = pm.PyNode(root) transforms = root.listRelatives(ad=True,type="transform") pm.select(transforms) bbox = cmds.polyEvaluate(b=True, ae=True) return bbox #バウンディングボックスの位置にロケータ作成 def make_bbox_locator(bbox ,root): for x in bbox[0]: for y in bbox[1]: for z in bbox[2]: l = pm.spaceLocator(p=(x, y, z)) pm.parent(l ,root) min = int(pm.Env().minTime) max = int(pm.Env().maxTime) step = 5 #フレームステップ数 obj = pm.selected()[0] root = pm.group(em=True, name='bbox_root') for fr in range(min ,max ,step): pm.currentTime(fr) bbox = get_boundingbox(obj) make_bbox_locator(bbox ,root)]]>
import pymel.core as pm import pymel.core as pm import maya.cmds as cmds def get_boundingbox(root): if isinstance(root,str) == True: root = pm.PyNode(root) transforms = root.listRelatives(ad=True,type="transform") pm.select(transforms) bbox = cmds.polyEvaluate(b=True, ae=True) print bbox files = get_boundingbox(pm.selected()[0])]]>
#!/usr/bin/env python]]>
#!/usr/bin/env python # -*- coding: utf-8 -*- import pymel.core as pm import os import shutil #指定ノード以下に接続されている全テクスチャノードを取得 def get_all_texture_node(root_node): if isinstance(root_node, str) == True: root_node = pm.PyNode(root_node) shapes = root_node.listRelatives(ad=True, type="mesh") file_nodes = [] for i in shapes: shading_engines = i.connections(type="shadingEngine") for s in shading_engines: shader_list = s.surfaceShader.connections() for shader in shader_list: file_node = shader.connections(type="file") file_nodes += file_node return file_nodes #指定ノード以下に接続されているテクスチャパスのリスト def get_use_texture_list(root_node): file_nodes = get_all_texture_node(root_node) files = [] for tex in file_nodes: files.append(tex.fileTextureName.get()) files = list(set(files)) return files #指定ノード以下に接続されているテクスチャを指定のディレクトリに接続し直し def set_texture_to_specified_dir(root_node, dir): file_nodes = get_all_texture_node(root_node) for tex in file_nodes: def_path = tex.fileTextureName.get() base_name = os.path.basename(def_path) new_path = os.path.join(dir, base_name) if os.path.isfile(new_path): tex.fileTextureName.set(new_path) #ファイルリストを指定のディレクトリにコピーする def copy_filelist(list ,copy_dir): os.makedirs(copy_dir) for e in files: shutil.copy(e, copy_dir) dir = r"c:\temp\test\test2" node = pm.selected()[0] files = get_use_texture_list(node) copy_filelist(files ,dir) set_texture_to_specified_dir(node ,dir)