オーダー変えるやつ
続けて次はアニメーションの結果を変えずに
オーダーを切り替えるスクリプトを書いていきたいと思います。
SI時代にも作成していて、さらに昨年MAYA版も作ったんですが
色々あり、闇に消えてしまったので一から作り直してみたいと思います。
※今回は色々時間がないので、さらにスローに作ります‥
では、このスクリプトの仕様を見ていきましょう。
お題:オーダー変更スクリプト
制作理由:オーダーを変えるのが、地味に面倒。
外部に出して、変更しないとアニメーションの結果が変わる。
一度プロットしないといけない。
複数実行ができない。
実装方法:
●選択したものを取得して、ロケータを取得→コンストかけて
オーダーを変えれるように
またイテレーションをできるだけ、少なくしたいので
オーダーを確認できるようにテストモードを実装。
ボタンで一時的に確認できるようにする。
●テストモードは、実行と同じで、選択物に対して
ロケータを生成して、コンストをかける。
そのロケーターでプルダウンを選ぶ度に、オーダーが変えれるようにして
目視で実行後のイメージが見えるようにします。
●後は、この時のエラー処理をどうしようかと‥
選択を固定する事はできないので、スクリプトジョブかなんかで
監視する事が必要かも‥まだモヤモヤしてる箇所。
あとアニメーションしていない、物は弾く。
●さらにキーが一個ならプロットせずに
オーダーのそのまま変えるように設定。
オーダー変えた際に、デフォルトのオーダーも
記憶しておかないと、いけないのでアトリビュートを追加
●オーダー変更自体は、一時的に作ったロケータをプロットして
選択コントローラーを逆コンストする形で実装。
ただ、複数あるとプロット祭りになるので
一度配列にコントローラーを追加して、プロット自体は一気に行う。
またコンスト自体も同様に一括で行う。
以上な感じで。
今回はぱっとUIを先作ってみました。
こんな感じ。
タブ切り替えると
現状のイメージでここにコマンド入れ込んでいきたいですね。
あと、マニュピレーターをボタンかなんかで変えれるようにした方がいいのか‥
また考えます。
一応コード
※すみません。汚いです。。。
# -*- coding: utf-8 -*- import maya.cmds as cmds windowname = "OrderCheck" WindowWidth_Size = 230 WindowHight_Size = 300 if cmds.window(windowname,exists=True): cmds.deleteUI(windowname,window=True) Window = cmds.window(windowname,title=windowname,sizeable=False,mxb=False,w=WindowWidth_Size) #Helpの為にメインメニューバーを追加します cmds.menuBarLayout() cmds.menu(label=u"Menu",tearOff=False) cmds.menuItem(label=Window+u"の解説") cmds.separator(w = WindowWidth_Size) cmds.setParent("..") cmds.columnLayout(w=WindowWidth_Size+3) tab = cmds.tabLayout(w=WindowWidth_Size) tab1_B = cmds.frameLayout("ChangeOrder",label="ChangeOrder",bgc=[1.0,0.3,0.2]) cmds.columnLayout( columnAttach= ('left',2)) cmds.rowLayout( 'Orders', numberOfColumns= 4, columnWidth2= [100,40]) cmds.optionMenu( 'Order_List', l= '', w= 100,h=50) cmds.menuItem( l= '0. xyz') cmds.menuItem( l= '1. yzx') cmds.menuItem( l= '2. zxy') cmds.menuItem( l= '3. xzy') cmds.menuItem( l= '4. yxz') cmds.menuItem( l= '5. zyx') cmds.optionMenu( 'Order_List', e= True, bgc= [0.5,0.2,0.2], ebg= False) cmds.button( 'exe', l= u'実行!', h= 40, w= 110) cmds.separator( height= 1) cmds.setParent(tab1_B) tab_2 = cmds.frameLayout("OrderCheck",label="OrderCheck",bgc=[0.2,0.4,0.6]) cmds.columnLayout() cmds.text("") cmds.text(label=u"オーダー変更後の確認が行えます。") cmds.button( 'exe_test', l= u'オーダー確認', h= 35, w= 220) cmds.separator( h= 15) cmds.setParent( tab ) tab2_B = cmds.frameLayout("Plot",label=u"Plot処理",bgc=[0.9,0.1,0.1]) cmds.columnLayout() cmds.separator( h= 15) cmds.button( 'remove', l= u'オーダーを元にもどす', h= 40, w= 220) cmds.separator( h= 15) cmds.button( 'test', l= u'模索中‥', h= 40, w= 220) cmds.tabLayout( tab , edit=True, tabLabel=((tab1_B, u'オーダー変更'), (tab2_B,u"プロット")) ) cmds.showWindow(Window)
今回は短いですが、以上です。
完・続・スクリプト書いてみようかと
前回までで、UIとコマンドができたので、組み込みと
テストをして、完成させたいと思います。
では早速、まずは関数化していきます。
ある程度前回の記事で仕様と、どうすればいいのか決まってるので
それらを踏まえ、加筆しました。
と書きましたが、まぁエラー出まくりで
めちゃ時間かかりました。。。
2日間、時間でいうと8時間くらい‥
情けない‥
とりあえずテストも終えたコードを↓
# -*- coding: utf-8 -*- import maya.cmds as cmds import codecs import os.path from functools import partial class savepose: radioB = "" TextValue = "" radio = "" flag = "" #saveFunction def savepose_cmd(self,*args): currentF = cmds.currentTime( query=True ) text_get = self.TextValue flag = args[0] oSel = cmds.ls(sl=True,l=True,type='transform') radioCollections = cmds.radioCollection(self.radioB,q=True,select=True) radioQuery = cmds.radioButton(self.radio, q = True, sl = True) ScenePath = cmds.internalVar(uad = True) + 'SavePose' if oSel != []: if not os.path.exists(str(ScenePath)): os.makedirs(str(ScenePath)) if flag == 0: text_split = "0" else: InputTime = cmds.textField(text_get, q=True, tx=True ) Brank_Check = InputTime.strip() text_split = Brank_Check.split(",") for F in range(len(text_split)): if flag == 1: try: cmds.currentTime( text_split[F] ) except: cmds.confirmDialog( title= 'SavePose', m= u'半角数字を入力してください。', icon= 'warning') cmds.warning(u'半角数字を入力してください。') return Select_Value = [] ImportText = "import maya.cmds as cmds" Select_Value.append(ImportText) for i in oSel: list = cmds.listAttr(i, unlocked = True , visible = True , keyable = True, connectable = True, scalar = True, write = True, hd = True) for att in list: gettype = cmds.getAttr(i + "." + att,type=True) if gettype == "bool": getValue = cmds.getAttr(i + "." + att) else: getValue = cmds.getAttr(i + "." + att) AttrStr = str(i) + "." + str(att) Value = "(\'" + str(AttrStr) + "\'," + str(getValue) + " , clamp = True)" Select_Value.append("cmds.setAttr" + Value) if flag == 0: if radioQuery == True: TextOpen = codecs.open(ScenePath+"\SavePose.py","w","utf-8") else: basicFilter = "*.py" Export_File = cmds.fileDialog2(dir=ScenePath,okCaption=u"書き出し!",ds=2,fm=0,caption=u"ポーズを保存",fileFilter=basicFilter, dialogStyle=2) if Export_File != None: TextOpen = open(Export_File[0], 'w') for val in Select_Value: TextOpen.write(val) TextOpen.write("\r\n") TextOpen.close() else: TextOpen = codecs.open(ScenePath+"\\"+ text_split[F] +".py","w","utf-8") for val in Select_Value: TextOpen.write(val) TextOpen.write("\r\n") TextOpen.close() if flag == 1: cmds.currentTime(currentF) else: cmds.warning(u'選択して実行してください。') cmds.confirmDialog( title= 'SavePose', m= u'選択して実行してください。', icon= 'warning') return #LoadFunction def LoadPose_cmd(self,*args): loadPath = cmds.internalVar(uad = True) + 'SavePose' radioCollections = cmds.radioCollection(self.radioB,q=True,select=True) radioQuery = cmds.radioButton(self.radio, q = True, sl = True) if radioQuery == True: ScenePath = cmds.internalVar(uad = True) + 'SavePose\SavePose.py' else: basicFilter = "*.py" Import_File = cmds.fileDialog2(dir=loadPath,okCaption=u"読み込み!",ds=2,fm=1,caption=u"ポーズを読み込み",fileFilter=basicFilter, dialogStyle=2) if Import_File != None: ScenePath = Import_File[0] script_code = [] script_err = [] for line in open(ScenePath, 'r').xreadlines(): lines = line.rstrip() script_code.append(lines) for i in script_code: try: exec(i) except: print i+"は存在していない為、実行できませんでした。" script_err.append(i) #HelpWindow def HelpWindow_Page(self,*args): Helpwindow = "SavePose_Help" Help_WindowWidth_Size = 350 Help_WindowHight_Size = 200 if cmds.window(Helpwindow,exists=True): cmds.deleteUI(Helpwindow,window=True) Help_MakeWindow = cmds.window(Helpwindow,title=Helpwindow,sizeable=False,mxb=False,wh=[Help_WindowWidth_Size,Help_WindowHight_Size]) cmds.columnLayout() cmds.separator(w = Help_WindowWidth_Size,h=20) cmds.text(label=u" SavePoseのHelpページ!!") cmds.separator(w = Help_WindowWidth_Size,h=30) cmds.text(label=u"●選択したノード保存するスクリプトです。") cmds.text(label=u" 複数可能で、ペーストは必ず、対になっているものに限ります。") cmds.separator(w = Help_WindowWidth_Size,h=10) cmds.text(label=u"●保存先はボタンを【ユーザー】に変える事で、変更可能です。") cmds.separator(w = Help_WindowWidth_Size,h=10) cmds.text(label=u"●タブを【連番に】変更すると、複数フレームを書き出せます。") cmds.text(label=u" 入力はカンマ「,」で区切っていただくと事で、出力がなされます。") cmds.text(label=u" 数字以外は入力しないで下さい。") cmds.showWindow(Help_MakeWindow) #folderOpen def folderOpen(self,*args): SystemPath = cmds.internalVar(uad = True) + 'SavePose/' if not os.path.exists(SystemPath): os.makedirs(SystemPath) SystemPath2 = SystemPath.replace("/", "\\\\") os.popen("explorer " + str(SystemPath2)) #cmdがでるから、お休み #os.system('explorer ' + "\"" + SystemPath2 + "\"") def MenuMake(self,*args): Pose_window = "SavePose" WindowWidth_Size = 300 WindowHight_Size = 140 if cmds.window(Pose_window,exists=True): cmds.deleteUI(Pose_window,window=True) MakeWindow = cmds.window(Pose_window,title=Pose_window,sizeable=False,mxb=False,wh=[WindowWidth_Size,WindowHight_Size]) #Helpの為にメインメニューバーを追加します cmds.menuBarLayout() cmds.menu(label=u"Menu",tearOff=False) cmds.menuItem(label=Pose_window+u"の解説",c=self.HelpWindow_Page) cmds.setParent("..") #複数フレーム対応の為、タブを生成 cmds.columnLayout(w=WindowWidth_Size+3) tab = cmds.tabLayout(w=WindowWidth_Size) #続けてボタンなど配置 tab1_B = cmds.columnLayout(w=WindowWidth_Size) cmds.rowLayout(numberOfColumns=2) self.radioB = cmds.radioCollection() self.radio = cmds.radioButton(label="システム") RadioB2 = cmds.radioButton(label="ユーザー") cmds.setParent( '..' ) cmds.separator(w = WindowWidth_Size) cmds.rowColumnLayout(numberOfColumns=4 , columnWidth=[(1,15), (2, 120), (3, 15),(4, 120)]) cmds.text(l = '') cmds.button(l = u'セーブ',c=partial(self.savepose_cmd, 0)) cmds.text(l = '') cmds.button(l = u'ロード',c=self.LoadPose_cmd) cmds.setParent( '..' ) cmds.separator(w = WindowWidth_Size) cmds.rowColumnLayout(numberOfColumns=2 , columnWidth=[(1,150), (2, 120)]) cmds.text(l = '') cmds.button(l = u'システムフォルダを開く',c=self.folderOpen) cmds.setParent( tab ) tab2_B = cmds.columnLayout(w=WindowWidth_Size) cmds.rowColumnLayout(numberOfColumns=4 , columnWidth=[(1,5),(2,150),(3,8),(4,100)]) cmds.text(l = '') cmds.text(l = u'フレーム【カンマで区切って入力】') cmds.text(l = '') self.TextValue = cmds.textField() cmds.textField( self.TextValue, edit=True,text="0,100" ) cmds.setParent( '..' ) cmds.separator(w = WindowWidth_Size) cmds.rowColumnLayout(numberOfColumns=2 ,columnWidth=([1,65],[2,150])) cmds.text(l = '') cmds.button(l = u'セーブ',c=partial(self.savepose_cmd, 1)) cmds.setParent( '..' ) cmds.separator(w = WindowWidth_Size) cmds.rowColumnLayout(numberOfColumns=2 , columnWidth=[(1,65), (2, 150)]) cmds.text(l = '') cmds.button(l = u'システムフォルダを開く',c=self.folderOpen) #各所設定 cmds.radioCollection(self.radioB,e=True,select=self.radio) cmds.tabLayout( tab , edit=True, tabLabel=((tab1_B, u'フレーム'), (tab2_B,u"連番")) ) cmds.showWindow(MakeWindow) def main(): classCommad = savepose() classCommad.MenuMake() main()
メイン画面
連番画面
Helpウィンドウ
※入力の値について、エラー処理対応したので
この画像は間違いですね‥
出力されたファイル達
といった感じになりました。
とにかくメニューの値と関数の参照が欲しくて
クラス化にしたんですが
まぁ、エラーに悩まされました。
※何個かはMAYA自体のエラーとかもあったんですが。(再起動で治るパターン)
あと、今回パソコンの関係で外部エディタが使えなかったので
MAYAのスクリプトエディタで作業したんですが
マジで地獄でした。
個人的にハマったのが、ラジオボタンのセレクトの値をどうすればいいのかという事。
今回は2個だったんで(わからなかったので2個にしました。)一つの条件で仕分けができたんですが‥
今後の課題です。。
また、UI内に「システムフォルダ」を開くってボタンありますが
これちゃんと動いてません。
【cmds.internalVar(uad = True) + 'SavePose/'】
でディレクトリとってきてるんですが、そのまま使っても
ダメで、更に文字列操作して
エスケープ文字も対応したんですが、それでもダメでした。
なぜなんでしょうか‥
ちなみに、文字列操作した
値と同じ文字列を別途用意すると
そちらはちゃんとパスが通りました。
なんでやねん‥と。
とりあえずあまり支障がないので、ほっときますが、今後また調べてみます。
私の勘違いです。
文字列操作の
【SystemPath2 = SystemPath.replace("/", "\\\\")】
の部分に誤りがありました。。
正しくは
【replace('/', '\\')) 】
です。
SIの感覚でやってしまいしました…
この部分を変えてもらうと動きますね…
@horonigさん
@eco_haganeさん
ご助言感謝致します!
ありがとうございました。
あと、UIのデザインも。
できれば、タブで切り分ける事なく、UIのアニメーションとかで
表現できたらいいなぁーと思ってたんですが、Pysideを触らないとできないっぽいので
今回は流しました。
と愚痴を書いてますが、とりあえず完成しました。
本当はカスタムアトリビュートのコピーや、スキンの除外、シェリフへの書き出しなんかやりたかったんですが
それはまた機会みて実装します。
使い方なんかは前の記事を見ていただければと思います。
※簡単ですが。
では、次はオーダー変えるスクリプトを作り直したいと思います。
※昔作ったやつは闇に消えたので‥
以上です。
新・続・スクリプト書いてみようかと
前回までで、書き出しと読み込みのコードが【とりあえず】できたので
それらをテストも含めて、関数化してみます。
まずは書き出し。
関数にする前に、一番はじめに書いた仕様を確認。
3.外部に保存するコマンドとそうではない、イテレーションが早いコマンドも必要。パスはドキュメントで
4.外部保存の形式はなんでもいいが、中身はPythonで実装。
5.できれば、複数フレームの一斉書き出しがほしい。
これらを見ていると、3のイテレーションが早いコマンドは現状で問題ないのですが
ファイルの名前、ディレクトリ指定が現状だと、できていないですね。
あと5の複数フレームにも対応してません。
また複数フレームについて、読み込み時どうするのかもまだ決まってませんね。
※現状だと複数読み込みは対応しないかなー。意味ないし。
では、ファイル名とディレクトリ変更を含めて関数化してみます。
まずは、現状のコード↓
# -*- coding: utf-8 -*- import maya.cmds as cmds import codecs import os.path oSel = cmds.ls(sl=True,l=True,type='transform') Select_Value = [] print oSel ImportText = "import maya.cmds as cmds" Select_Value.append(ImportText) for i in oSel: #name print i list = cmds.listAttr(i, unlocked = True , visible = True , keyable = True, connectable = True, scalar = True, write = True, hd = True) print list for att in list: gettype = cmds.getAttr(i + "." + att,type=True) if gettype == "bool": getValue = cmds.getAttr(i + "." + att) else: getValue = cmds.getAttr(i + "." + att) AttrStr = str(i) + "." + str(att) Value = "(¥'" + str(AttrStr) + "¥'," + str(getValue) + " , clamp = True)" Select_Value.append("cmds.setAttr" + Value) ScenePath = cmds.internalVar(uad = True) + 'SavePose' if not os.path.exists(ScenePath): os.makedirs(ScenePath) TextOpen = codecs.open(ScenePath+"\SavePose.py","w","utf-8") for val in Select_Value: TextOpen.write(val) TextOpen.write("\r\n") TextOpen.close()
まずはファイル名の変更について変更してみます。
ファイル名はUIからのフラグによって共通か個別か決めるので
スクリプト内の
「TextOpen = codecs.open(ScenePath+"\SavePose.py","w","utf-8")」の
"SavePose.py"
を変数に変数指定に変更して、さらに条件分岐すればよさそうですね。
加えて、ディレクトリの同様に【ScenePath】を、条件分岐によって
変更できるように(上書き)すれば問題なさそうです。
あとは複数フレームですが、こればカレントフレームを動かすという
行動が必要な為、加筆が必須だと思います。
まずは指定のフレームに移動するコードですが
シンプルにこれでいけますね。
import maya.cmds as cmds cmds.currentTime(10)
10F目に移動してますね。
あとは、そのフレームをUIから、入力値を取得して、Forで回して
セーブポーズすればいいはずです。
※カレントの場合は常に1にしておけばOk。
では関数にしようかな?
って思ったんですが、まずはUIを用意した方が
良さそうなので、UIからやってみます。
で、まずUIで必要な情報はなにか?を列挙してみます。
1.Save実行ボタン
2.ロード実行ボタン
3.ファイル指定のラジオボタン
4.複数か単フレームかのラジオボタンないし、タブ区分け
5.保存パス※個人的にほしいので
6.あとHelpボタンないし、説明するタブ?
こんなもんかと。
では早速上記を含めてコード書いてみました。
# -*- coding: utf-8 -*- import maya.cmds as cmds def MenuMake(self): Pose_window = "SavePose" WindowWidth_Size = 300 WindowHight_Size = 180 if cmds.window(Pose_window,exists=True): cmds.deleteUI(Pose_window,window=True) MakeWindow = cmds.window(Pose_window,title=Pose_window,sizeable=False,mxb=False,w=WindowWidth_Size) #Helpの為にメインメニューバーを追加します cmds.menuBarLayout() cmds.menu(label=u"Menu",tearOff=False) cmds.menuItem(label=Pose_window+u"の解説") cmds.setParent("..") #複数フレーム対応の為、タブを生成 cmds.columnLayout(w=WindowWidth_Size+3) tab = cmds.tabLayout(w=WindowWidth_Size) #続けてボタンなど配置 tab1_B = cmds.columnLayout(w=WindowWidth_Size) cmds.rowLayout(numberOfColumns=2) Radio = cmds.radioCollection() RadioB1 = cmds.radioButton(label="システム") RadioB2 = cmds.radioButton(label="ユーザー") cmds.setParent( '..' ) cmds.separator(w = WindowWidth_Size) cmds.rowColumnLayout(numberOfColumns=4 , columnWidth=[(1,15), (2, 120), (3, 15),(4, 120)]) cmds.text(l = '') cmds.button(l = u'セーブ') cmds.text(l = '') cmds.button(l = u'ロード') cmds.setParent( tab ) tab2_B = cmds.columnLayout(w=WindowWidth_Size) cmds.rowColumnLayout(numberOfColumns=4 , columnWidth=[(1,5),(2,150),(3,8),(4,100)]) cmds.text(l = '') cmds.text(l = u'フレーム【カンマで区切って入力】') cmds.text(l = '') cmds.textField(text="0,100") cmds.setParent( '..' ) cmds.separator(w = WindowWidth_Size) cmds.rowColumnLayout(numberOfColumns=2 ,columnWidth=([1,65],[2,150])) cmds.text(l = '') cmds.button(l = u'セーブ') #各所設定 cmds.radioCollection(Radio,e=True,select=RadioB1) cmds.tabLayout( tab , edit=True, tabLabel=((tab1_B, u'フレーム'), (tab2_B,u"連番")) ) cmds.showWindow(MakeWindow) MenuMake(" ")
こんな感じに。
連番が
こうなりました。
細かい説明はいらないかな。。
すべてHELPに載ってることを実装しているだけなので・・・
ちなみにSaveする際に、ディレクトリを指定するには
以下のコードを、出力関数に足す必要がある。
# -*- coding: utf-8 -*- import maya.cmds as cmds basicFilter = "*.pose" Export_File = cmds.fileDialog2(okCaption=u"書き出し!",ds=2,fm=0,caption=u"ポーズを保存",fileFilter=basicFilter, dialogStyle=2) if Export_File != None: savePose_file = open(Export_File[0], 'w') savePose_file.write("test") savePose_file.close()
※現状デフォルトのパスは指定しておらず、書き出す中身も適当。
これはセーブだが、ロードの際は、Openを"r"としてreadすればOK。
とまぁ、UI書いてたら、結構長くなっちゃたので
ここで一旦切ります。
次回で組み込みやって、テストやって終わりたいと思います。
では…
続・スクリプト書いてみようかと
前回までで、選択したものの値などを外部に保存することまでできました。
今回は引き続きコードを書いていきたいと思います。
次は、まず前回までで、できたものを整理します。
始めに現状だと一個のオブジェクトしか対応していないので
それを対応する為
配列を用意して、その配列に対して処理を行うようにします。
こんな感じで整理してみました。
※まだまだ改良の余地がありますが
シェイプの件などもあるので一旦これで
# -*- coding: utf-8 -*- import maya.cmds as cmds import codecs import os.path oSel = cmds.ls(sl=True,l=True,type='transform') Select_Value = [] ImportText = "import maya.cmds as cmds" Select_Value.append(ImportText) for i in oSel: #name list = cmds.listAttr(i, unlocked = True , visible = True , keyable = True, connectable = True, scalar = True, write = True, hd = True) for att in list: Tmpsel = cmds.ls(sl=True,type='transform')[0] gettype = cmds.getAttr(Tmpsel + "." + att,type=True) if gettype == "bool": getValue = cmds.getAttr(Tmpsel + "." + att) else: getValue = cmds.getAttr(Tmpsel + "." + att) AttrStr = str(i) + "." + str(att) Value = "(¥'" + str(AttrStr) + "¥'," + str(getValue) + " , clamp = True)" Select_Value.append("cmds.setAttr" + Value) ScenePath = cmds.internalVar(uad = True) + 'SavePose' if not os.path.exists(ScenePath): os.makedirs(ScenePath) TextOpen = codecs.open(ScenePath+"\SavePose.py","w","utf-8") for val in Select_Value: TextOpen.write(val) TextOpen.write("\r\n") TextOpen.close()
複数選択してから実行すると、以下の画像みたいになるはずです。
変更点は配列「Select_Value」を追加し、値を追加したものを
外部へ出力する際に、Forで繰り返し出力しただけです。
これである程度まとまりができたので、テストを行います。
まずはすべてのコントローラーを選択して、実行してみます。
すると‥
はい。エラーになります。
エラー文を読みと
「# エラー: line 1: ValueError: file
はい。
これは15行目でエラーになってることが分かります。
gettype = cmds.getAttr(Tmpsel + "." + att,type=True)
もっと詳しくみていくと、エラーが発生するコントローラーがあります。
右手のUPVですね。
書いてる通りアトリビュートの「blendOrient1」で問題が出ていることが分かります。
が、ここで改めてエラー文を見ると
「名前と一致するオブジェクトがありません: ALL.blendOrient1 # 」
とあります。
ただ、Allには ALL.blendOrient1はなく、エラーが出ているのは右手のUPVです。
このコトから、スクリプトの記載順番や
構造に問題があるんじゃないか?と想定します。
そう!簡単なミスです。
15行目のコマンドがエラーしているということは
その記述が間違っていなかったら
原因はそのコマンドに渡している
引数ないし、値に問題あるんですね。
ここでいうと、「Tmpsel 」か「att」に絞られます。
経験者なら、読んだ瞬間間違いに気づいていると思いますが
私は10分は悩んでしまいましたw
原因はTmpsel の中身です。
ここでTmpsel = cmds.ls(sl=True)[0] と
一番始めに選択しているものを再度取得した為
Allにはブレンドがないのに!
とエラーで弾かれてたわけです。
正しくは以下の中身になります。
# -*- coding: utf-8 -*- import maya.cmds as cmds import codecs import os.path oSel = cmds.ls(sl=True,l=True,type='transform') Select_Value = [] print oSel ImportText = "import maya.cmds as cmds" Select_Value.append(ImportText) for i in oSel: #name print i list = cmds.listAttr(i, unlocked = True , visible = True , keyable = True, connectable = True, scalar = True, write = True, hd = True) print list for att in list: gettype = cmds.getAttr(i + "." + att,type=True) if gettype == "bool": getValue = cmds.getAttr(i + "." + att) else: getValue = cmds.getAttr(i + "." + att) AttrStr = str(i) + "." + str(att) Value = "(¥'" + str(AttrStr) + "¥'," + str(getValue) + " , clamp = True)" Select_Value.append("cmds.setAttr" + Value) ScenePath = cmds.internalVar(uad = True) + 'SavePose' if not os.path.exists(ScenePath): os.makedirs(ScenePath) TextOpen = codecs.open(ScenePath+"\SavePose.py","w","utf-8") for val in Select_Value: TextOpen.write(val) TextOpen.write("\r\n") TextOpen.close()
はい実行するとこんな感じ。
無事通りました。
再度テストを込めて、保存したものを別フレームで実行してみます。
ただ、今は読み込みの機能をつけていないので
データを開いてスクリプトエディタに
直接ペーストして実行します。
この状態で実行すると
こうなりました。
という点から、とりあえず問題ないように思います。
※本当はいっぱいありますが…
では、続けて読み込みのコマンドを用意してみます。
読み込みはすでにPythonコードが用意されているので
やり方は複数あります。
1.execfileを使ってスクリプトを実行
2.Openで開いてから読み込むで実行
3.execで読み込んで実行
以上のパターンがありました。
※本当はもっとあると思います。
今回のスクリプトで、一番相性がいいのが
どれかと考えていたのですが
結局エラー処理も同時にしたいので
一行ずつ読み込むようにします。
簡単に用意しました。こんな感じ。
import maya.cmds as cmds import codecs import os.path import sys script_code = [] script_err = [] ScenePath = cmds.internalVar(uad = True) + 'SavePose\SavePose.py' for line in open(ScenePath, 'r').xreadlines(): line = line.rstrip() script_code.append(line) for i in script_code: try: exec(i) except: print i+"は存在していない為、実行できませんでした。" script_err.append(i)
はい。これで読み込みまでいけたはずです。
ただ、細かいエラー処理などはまだ組み込んでないです。
それらは、UI実装時に合わせて行います。
とまた一旦ここらで切ります。
次回以降はコマンドを関数化し、UIを実装してみます。
では…
スクリプト書いてみようかと
超久しぶりにブログ書きます。
というのも色々本当にありまして…
…
さておき
では今回は割とガッツリスクリプトを書いてみようかと思います。
はじめに仕様面を決めてみようかと思います。
お題:ポーズセーブスクリプト
制作理由:MAYAデフォルトでポーズだけ保存したり、外部に残しておける方法がないため。
実装方法:今回は選択した物と、その子ども以下の階層のSRTとアトリビュートの値を保存する。
シェイプや、追加アトリビュートについては現状は無しで、完成後追加で実装できればする。
できればカレントのシェリフにも追加したい。
ポーズ保存と実行なので、以下の注意点がある。
1.選択したオブジェクトはフルパスし、重複するオブジェクトはエラーで出力
2.実行する際は、階層の持ち方に注意する。
3.外部に保存するコマンドとそうではない、イテレーションが早いコマンドも必要。パスはドキュメントで
4.外部保存の形式はなんでもいいが、中身はPythonで実装。
5.できれば、複数フレームの一斉書き出しがほしい。
プロトタイプとして、まずは値の取得から行う。
# -*- coding: utf-8 -*- import maya.cmds as cmds oSel = cmds.ls(sl=True,l=True, type='transform') for i in oSel: #name print i #Rotate ro = cmds.xform(i,q=True,ro=True) #Trans tr = cmds.xform(i,q=True,t=True) #Vis vis = cmds.getAttr(i+".visibility") #Scale sc = cmds.xform(i,q=True,s=True) print sc
これで値自体は取れた。
ただ、ロックしている値まで取ってきているので、処理的に問題にもなりそう。
ということで、ロック情報を取得するか、除外する方法を考える。
SDK等みていると、「listAttr」というコマンドが使えそう。
このコマンドのフラグにて、
unlocked(u) boolean create
ロックが解除されているアトリビュートのみがリストされます。
このフラグがそのまま使えそうですね。
しかもありがたい事に、keyableや、channelBoxなどもありますね。
ではこれを用いて、再度値の取得を書いてみました。
# -*- coding: utf-8 -*- import maya.cmds as cmds oSel = cmds.ls(sl=True,l=True,type='transform') print oSel for i in oSel: #name print i list = cmds.listAttr(i, unlocked = True , visible = True , keyable = True, connectable = True, scalar = True, write = True, hd = True) print list
で結果。
[u'visibility', u'translateX', u'translateY', u'translateZ', u'rotateX', u'rotateY', u'rotateZ', u'scaleX', u'scaleY', u'scaleZ']
あれ…値が。。。
そうなんです。このままだと値が取れないので、ここからさらにGetAttrをかまして値を取ってみます。
# -*- coding: utf-8 -*- import maya.cmds as cmds oSel = cmds.ls(sl=True,l=True,type='transform') print oSel for i in oSel: #name print i list = cmds.listAttr(i, unlocked = True , visible = True , keyable = True, connectable = True, scalar = True, write = True, hd = True) print list for att in list: Tmpsel = cmds.ls(sl=True)[0] getValue = cmds.getAttr(Tmpsel + "." + att) print getValue
結果こんな感じ。
[-1.4327737,100.82367,-26.672975]
うん。取れました。
あとこれは余談ですが、個人的にアトリビュートのタイプは分けて処理したほうがいいので、以下の感じで
一回アトリビュートのタイプで篩をかけています。
※大きく意味はないのですが、一応。
# -*- coding: utf-8 -*- import maya.cmds as cmds oSel = cmds.ls(sl=True,l=True,type='transform') print oSel for i in oSel: #name print i list = cmds.listAttr(i, unlocked = True , visible = True , keyable = True, connectable = True, scalar = True, write = True, hd = True) print list for att in list: Tmpsel = cmds.ls(sl=True,type='transform')[0] gettype = cmds.getAttr(Tmpsel + "." + att,type=True) if gettype == "bool": BoolValue = cmds.getAttr(Tmpsel + "." + att) print BoolValue else: getValue = cmds.getAttr(Tmpsel + "." + att) print getValue
では、以上で値が取れたきた事を確認しました。
続いては、この情報を選択名とくっつけて、テキスト保存し拡張子を変更しましょう。
まずは、値を実行できるように、「SetAttr」としてまとめていきます。
現状取得できているのは、
「i」で選択物の名前
「att」でアトリビュート名
「getValue」でその値
が取得できています。
ここまで取得できてると、setattr用に簡易にまとめれますね。
こんな感じでまとめました。
# -*- coding: utf-8 -*- import maya.cmds as cmds oSel = cmds.ls(sl=True,l=True,type='transform') print oSel for i in oSel: #name print i list = cmds.listAttr(i, unlocked = True , visible = True , keyable = True, connectable = True, scalar = True, write = True, hd = True) print list for att in list: Tmpsel = cmds.ls(sl=True,type='transform')[0] gettype = cmds.getAttr(Tmpsel + "." + att,type=True) if gettype == "bool": getValue = cmds.getAttr(Tmpsel + "." + att) print getValue else: getValue = cmds.getAttr(Tmpsel + "." + att) print getValue AttrStr = str(i) + "." + str(att) Value = "(¥'" + str(AttrStr) + "¥'," + str(getValue) + " , clamp = True)" SetValue = "cmds.setAttr" + Value #exec(SetValue)
こんな感じで値をまとめれました。
実行したらこんな感じに↓
cmds.setAttr('|Monster_Rig|ALL|COG|Stomach|Chest|Neck|Head.rotateX',-11.0883595763 , clamp = True) cmds.setAttr('|Monster_Rig|ALL|COG|Stomach|Chest|Neck|Head.rotateY',0.0 , clamp = True) cmds.setAttr('|Monster_Rig|ALL|COG|Stomach|Chest|Neck|Head.rotateZ',0.0 , clamp = True)
あとはこれをテキストに保存します。
保存の仕方はGoogle先生なんかでよくでてくる感じでまとました。↓
またその際に、保存するディレクトリーを作成し、配置させてます。
※ディレクトリーがない場合は、作成する方向で
# -*- coding: utf-8 -*- import maya.cmds as cmds import codecs import os.path oSel = cmds.ls(sl=True,l=True,type='transform') for i in oSel: #name list = cmds.listAttr(i, unlocked = True , visible = True , keyable = True, connectable = True, scalar = True, write = True, hd = True) for att in list: Tmpsel = cmds.ls(sl=True,type='transform')[0] gettype = cmds.getAttr(Tmpsel + "." + att,type=True) if gettype == "bool": getValue = cmds.getAttr(Tmpsel + "." + att) else: getValue = cmds.getAttr(Tmpsel + "." + att) AttrStr = str(i) + "." + str(att) Value = "(¥'" + str(AttrStr) + "¥'," + str(getValue) + " , clamp = True)" SetValue = "cmds.setAttr" + Value #exec(SetValue) ScenePath = cmds.internalVar(uad = True) + 'SavePose' ImportText = "import maya.cmds as cmds" if not os.path.exists(ScenePath): os.makedirs(ScenePath) TextOpen = codecs.open(ScenePath+"\SavePose.py","w","utf-8") TextOpen.write(ImportText) TextOpen.write("\r\n") TextOpen.write(SetValue) TextOpen.write("\r\n") TextOpen.close() """ 使わないけど、拡張子変更する方法も記載。 file_path = ScenePath+"\SavePose.txt" basefile, ext = os.path.splitext(file_path) basefile, ext = os.path.splitext( os.path.basename(file_path) ) print ScenePath + "\\" + basefile + ".py" os.rename(file_path, ScenePath + "\\" + basefile + ".py") """
すげぇ汚いっすね。
今後スクラッチかけますわ‥
とりあえず、リグの頭を選んで実行すると、以下の画像みたくなります。
※拡張子がテキストになってますが、間違いです。。。
正式には、「.py」になってます。
はい。
これでテキスト出力までできました。
次はこれらを複数できるようにカスタムし、実行文とUIをやっていきます。
が、長くなったので一旦ここで切ります。
大体ここまでで2時間ぐらいでしょうか‥
それでは‥
フェースを表示トグルするツール
久しぶりに更新します。
とある事で必要に駆られたので作りました。
※WEBにもっといいやつありますし、あたらしいエクステンションには標準で搭載されてますが…
ポリゴン表示をトグルです。
一応ルールがあります。
1.初めてフェースを非表示にする時は、オブジェクトのフェース(消したい所)を選んで実行して下さい。
2.実行後は、フェース選択モードからオブジェクトモードに戻して下さい。
3.1回実行した後は、アウトライナーに【Poly_Hide_Set】という名のSetができるので、それを選んでスクリプトを実行して下さい。
それで非表示だった箇所が表示されます。
4.もう1回【Poly_Hide_Set】という名のSetを選んで実行すると、再度非表示になります。
これを繰り返してお使い下さい。
5.提出、納品時は【Poly_Hide_Set】という名のSetを選んで【Ctrl】キーを押してスクリプトを実行して下さい。
これで、setsと、ヒストリーが削除され、綺麗になります。
あと、連打実行はNGで、【Poly_Hide_Set】という名のSetの名前は絶対に変えないで下さい。
というより、名前が変わった際は、手動で消して下さい。
詳しくは動画にて。
SelPoly_Hide from tommy_on on Vimeo.
以下コード
# -*- coding: utf-8 -*- import maya.cmds as cmds import maya.mel as mel def SelPoly_Hide(): #選択をリストに格納 oSel = cmds.ls(sl=True) #フラグ立て Check = 0 #選択されていなかったら if oSel == []: return #キーボードステートをチェック mods = cmds.getModifiers() #コントロール押していたら、関数に飛んで終了 if (mods & 4) > 0: if oSel[0] == "Poly_Hide_Set": Toggle(oSel) Clean() return #選択がセットで且つ、コントロール押していなかったら。 if oSel[0] == "Poly_Hide_Set": Toggle(oSel) return #フェースを選択 oSelFace = cmds.filterExpand(sm=34) #フェースじゃなかったら if oSelFace == None: return #フェースからオブジェクトを取得 const = oSelFace[0].rpartition(".") #ヒストリーを取得 oList = cmds.listHistory(const[0]) for i in oList: #穴開けるヒストリーがあったら、フラグを立てる if i == "Hide_Poly": Check = 1 #フラグが立っていたら if Check == 1: cmds.polyHole(assignHole=0) #なかったら else: mel.eval('ToggleHoleFaces;') FaceSet = cmds.sets(n="Poly_Hide_Set") cmds.polyHole(assignHole=1) cmds.rename("polyHoleFace1","Hide_Poly") #単純に表示を切り替えているだけ def Toggle(oSel): cmds.select(hierarchy=True) mel.eval('ToggleHoleFaces;') cmds.select(cl=True) #ヒストリとセットを削除 def Clean(): cmds.delete("Hide_Poly") cmds.delete("Poly_Hide_Set") SelPoly_Hide()
【覚書】maya python入門 其の十三
今回はmaya.cmdsを使ってXMLの簡単な例を書いてみたいと思います。
SIではJSを使えば、結構簡単に出力、読み込みができましたが
Pythonでやるのが、未経験だったのでメモに。
今回は、シーンのディスプレイレイヤーを取得し
それをXMLとして出力してみます。
早速コード
# -*- coding:utf-8 -*- import maya.cmds as cmds import xml.dom.minidom as xml import codecs imp_XML = xml.getDOMImplementation() doc = imp_XML.createDocument(None, 'SceneLayers', None) top_element = doc.documentElement Disp_Layers = cmds.ls(typ ="displayLayer") for i in Disp_Layers: Member = cmds.editDisplayLayerMembers( i, query=True ) Layer = doc.createElement('Layer') Layer_attr = doc.createAttribute("layer_name") Layer_value = doc.createTextNode(str(Member)) Layer.appendChild(Layer_value) Layer_attr.value = str(i) Layer.setAttributeNode(Layer_attr) top_element.appendChild(Layer) print(doc.toxml('UTF-8')) # ファイルに保存 f = codecs.open('sample.xml', 'w', 'utf-8') doc.writexml(writer=f, encoding='UTF-8', newl='\n', addindent='\t') f.close()
上記のコードを実行するとmayaのデフォルトフォルダ
C:\Program Files\Autodesk\Mayaバージョン\bin
にXMLが保存されます。
※必要に応じてディレクトリーは変えてください。
そうすると、こんな感じで出力されています。
出力はこんな感じ。
細かいコマンドについては、他を参照いただければと思います。
超カンタンです。
では、次は簡単な解析を
# -*- coding:utf-8 -*- from xml.dom import minidom # ----- DOM生成 ----- # DOM 実装を取得 doc = minidom.parse('c:\\Program Files\\Autodesk\\Maya2016\\bin\\sample.xml') elements = doc.getElementsByTagName("Layer") for i, element in enumerate(elements) : print(element.childNodes[0].data )
XMLファイルを取得して、【Layer】以下のタグを取得しているだけ。
ここからMAYAで色んな処理を、噛ますと色々できるかと思います。
私は今回MAYA←→SIでレイヤー構造がうまくいかないので
作成しておりました。
細かい仕様は書かないですが、レイヤー以外にも
マテリアルや、モーションにも使えそうですね。
以上です。