ON-BLOG

CGのこと、あれこれ書いてます。

続・オーダー変えるやつ

更新に時間がかかりました‥
実際は8割り程度結構前にできてたんですが
まぁ色々ありまして‥


と前置きはともかくいきなり完成コードを

# -*- coding: utf-8 -*-
import maya.cmds as cmds
from functools import partial
import re
import ast

def CreateLoc(oObj,ver):
	if ver == 0:
		Order = cmds.optionMenu( 'Order_List', q= True, sl= True) -1
		BakeAll = cmds.checkBox("FrameLock",q=True,value=True)
	else:
		Order = cmds.optionMenu( 'Check_Order_List', q= True, sl= True) -1
		BakeAll = cmds.checkBox("Check_FrameLock",q=True,value=True)
	timeIn = cmds.playbackOptions(q=True,min=True)
	timeout = cmds.playbackOptions(q=True,max=True)
	inframe = []
	outframe = []
	Flag = 0
	plotObj = []
	plotDummy = []
	obj_tmp = []
	delConst = []
	for i in oObj:	
		obj_tmp.append(i)
		#キー、オーダー、アトリビュート取得
		Original_Orders = cmds.listAttr( i,r=True, string="Original_Order*" )
		oObj_Order = cmds.getAttr(i+".rotateOrder")
		oKey_Rot = []
		if cmds.findKeyframe( i , c=True, at='rotateX' ) != None:
			oKey_Rot.append(cmds.findKeyframe( i , c=True, at='rotateX' ))
		if cmds.findKeyframe( i , c=True, at='rotateY' ) != None:
			oKey_Rot.append(cmds.findKeyframe( i , c=True, at='rotateY' ))
		if cmds.findKeyframe( i , c=True, at='rotateZ' ) != None:
			oKey_Rot.append(cmds.findKeyframe( i , c=True, at='rotateZ' ))
		if len(oKey_Rot) > 1:
			keys = cmds.keyframe(oKey_Rot[0],query=True)
			if len(keys) > 1:
			#ここからが実行文
				#アトリビュートの設定とオリジナルのキーを保存しておく
				if not cmds.objExists(i+".Original_Order"):
					cmds.addAttr(i,sn="Ori", ln="Original_Order", dt="string" )
					cmds.setAttr(i+".Original_Order",oObj_Order,type="string")
				if not cmds.objExists(i+".Original_Order_RotX"):
					cmds.addAttr(i,sn="RotX", ln="Original_Order_RotX", at= "double")
					cmds.setAttr(i+".Original_Order_RotX", e= True, k= True , cb=False)
					cmds.copyKey(i,at="rotateX",o="curve")
					cmds.pasteKey(i+'.Original_Order_RotX')
				if not cmds.objExists(i+".Original_Order_RotY"):
					cmds.addAttr(i,sn="RotY", ln="Original_Order_RotY", at= "double")
					cmds.setAttr(i+".Original_Order_RotY", e= True, k= True , cb=False)
					cmds.copyKey(i,at="rotateY",o="curve")
					cmds.pasteKey(i+'.Original_Order_RotY')
				if not cmds.objExists(i+".Original_Order_RotZ"):
					cmds.addAttr(i,sn="RotZ", ln="Original_Order_RotZ", at= "double")
					cmds.setAttr(i+".Original_Order_RotZ", e= True, k= True , cb=False)
					cmds.copyKey(i,at="rotateZ",o="curve")
					cmds.pasteKey(i+'.Original_Order_RotZ')

				#ロケータを作ってオーダーを変えてプロット
				oLoc = cmds.spaceLocator(n=i+"_TempObj")
				inframe.append(keys[0])
				outframe.append(keys[-1])
				PointCons = cmds.pointConstraint(i,oLoc,n="Dummy_point")
				OrientCons = cmds.orientConstraint(i,oLoc,n="Dummy_orient")
				cmds.setAttr(i+".rotateOrder",Order)
				Connections = cmds.listRelatives(oLoc,c=True,typ="constraint",fullPath=True)
				delConst.append(Connections)
				if BakeAll == True:
					plotObj.append(i+".rotateX")
					plotObj.append(i+".rotateY")
					plotObj.append(i+".rotateZ")
					dummy = oLoc[0]
					plotDummy.append(dummy+".rotateX")
					plotDummy.append(dummy+".rotateY")
					plotDummy.append(dummy+".rotateZ")
				else:
					cmds.bakeResults([oLoc[0]+".rotateX",oLoc[0]+".rotateY",oLoc[0]+".rotateZ"],sm=False,t=(keys[0],keys[-1]),pok=True)
					for d in Connections:
						cmds.select(d)
						cmds.delete()
					To_OrientCons = cmds.orientConstraint(oLoc,i,n="Dummy_orient")

					cmds.delete(i+"_rotateX")
					cmds.delete(i+"_rotateY")
					cmds.delete(i+"_rotateZ")
					cmds.bakeResults([i+".rotateX",i+".rotateY",i+".rotateZ"],sm=False,t=(keys[0],keys[-1]),pok=True)
					Connections = cmds.listRelatives(i,c=True,typ="constraint",fullPath=True)
					for c in Connections:
						cmds.select(c)
						cmds.delete()
					cmds.delete(oLoc)
					if len(plotDummy) > 0 :
						#配列済なので、sortedでソート実行
						S_in = sorted(inframe)
						S_out = sorted(outframe)
						Sort_in = set(S_in)
						Sort_out = set(S_out)
						Min_Frame = list(Sort_in)[0]
						Max_Frame = list(Sort_out)[-1]
						#Plot
						cmds.bakeResults(plotDummy,sm=False,t=(Min_Frame,Max_Frame),pok=True)
						for d in delConst:
							cmds.select(d)
							cmds.delete()
						if len(obj_tmp) != []:
							delConst = []
							for tmp in obj_tmp:
								OrientCons = cmds.orientConstraint(tmp+"_TempObj",tmp,n="Dummy_orient")
								Connections = cmds.listRelatives(tmp,c=True,typ="constraint",fullPath=True)
								delConst.append(Connections)
							cmds.bakeResults(plotObj,sm=False,t=(Min_Frame,Max_Frame),pok=True)
							for d in delConst:
								cmds.select(d)
								cmds.delete()
							for tmp in obj_tmp:
								cmds.delete(tmp+"_TempObj")
			else:
				cmds.warning(u'キーが2つ以上打たれていません。')
				DirectChange = cmds.confirmDialog( title='ChangeOrder',  m= u'キーが2つ以上打たれていません。そのままオーダーが変えますがよろしいでしょうか?', button=['Yes','No'], defaultButton='Yes', cancelButton='No', dismissString='No' )
				if DirectChange == "Yes":
					cmds.setAttr(i+".rotateOrder",Order)
				else:
					cmds.warning(u'終了しました。')
		else:
			cmds.warning(u'回転XYZの2つ以上キー設定がされてません')
			cmds.confirmDialog( title= 'ChangeOrder', m= u'回転XYZの2つ以上キー設定がされてません', icon= 'warning')
	if BakeAll == True:
		delConect = []
		Min_Frame = sorted(inframe)[0]
		Max_Frame = sorted(outframe)[-1]

		cmds.bakeResults(plotDummy,sm=False,t=(Min_Frame,Max_Frame),pok=True)
		for d in Connections:
			cmds.select(d)
			cmds.delete()
		for i in oObj:
			To_OrientCons = cmds.orientConstraint(i+"_TempObj",i,n="Dummy_orient")
			cmds.delete(i+"_rotateX")
			cmds.delete(i+"_rotateY")
			cmds.delete(i+"_rotateZ")
			delConect.append(cmds.listRelatives(i,c=True,typ="constraint",fullPath=True))
		cmds.bakeResults(plotObj,sm=False,t=(Min_Frame,Max_Frame),pok=True)
		for dc in delConect:
			cmds.select(dc)
			cmds.delete()
		delConect = []
		for tmp in oObj:
			OrientCons = cmds.orientConstraint(tmp+"_TempObj",tmp,n="Dummy_orient")
			Connections = cmds.listRelatives(tmp,c=True,typ="constraint",fullPath=True)
			delConect.append(Connections)
		cmds.bakeResults(plotObj,sm=False,t=(Min_Frame,Max_Frame),pok=True)
		for d in delConect:
			cmds.select(d)
			cmds.delete()
		for tmp in oObj:
			cmds.delete(tmp+"_TempObj")


def ChangeOrder_exe(ver):
	#選択を取得
	oObj = cmds.ls(sl=True,type="transform")
	#メニューからもらえるオーダーの値
	if not oObj == []:
		FirstObj = oObj[0].split("_")[-1]
		if FirstObj != "TempObj":
			CreateLoc(oObj,ver)
		else:
			oObj_Sel = cmds.getAttr(oObj[0]+".Original_Selection")
			date = ast.literal_eval(oObj_Sel)
			cmds.select(clear=True)
			for sel in range(0,len(date)):
				cmds.select(date[sel],add=True)
			cmds.delete(oObj)
			oObj = cmds.ls(sl=True)
			CreateLoc(oObj,ver)
		cmds.select(oObj)
		cmds.headsUpMessage( u'完了しました。', verticalOffset=20 )
	else:
		cmds.warning(u'選択して実行してください。')
		cmds.confirmDialog( title= 'ChangeOrder', m= u'選択して実行してください。', icon= 'warning')


def Bake_BeforeOrder(self):
	oObj = cmds.ls(sl=True,type="transform")
	for i in oObj:
		oObj_Order = cmds.getAttr(i+".Original_Order")
		cmds.setAttr(i+".rotateOrder",int(oObj_Order))
		cmds.delete(i+"_rotateX")
		cmds.delete(i+"_rotateY")
		cmds.delete(i+"_rotateZ")
		cmds.copyKey(i,at="Original_Order_RotX",o="curve")
		cmds.pasteKey(i+'.rotateX')
		cmds.copyKey(i,at="Original_Order_RotY",o="curve")
		cmds.pasteKey(i+'.rotateY')
		cmds.copyKey(i,at="Original_Order_RotZ",o="curve")
		cmds.pasteKey(i+'.rotateZ')
		cmds.deleteAttr(i+".Original_Order")
		cmds.deleteAttr(i+".Original_Order_RotX")
		cmds.deleteAttr(i+".Original_Order_RotY")
		cmds.deleteAttr(i+".Original_Order_RotZ")
	cmds.select(oObj)
	cmds.headsUpMessage( u'完了しました。', verticalOffset=20 )

def ChangeSelect(self):
	sel = []
	oObj = cmds.ls()
	for i in oObj:
		Artr_Check = cmds.attributeQuery('Original_Order', node=i, exists=1)
		if Artr_Check == True :
			sel.append(i)
	if sel == []:
		cmds.confirmDialog( title= 'ChangeOrder', m= u'存在しませんでした。', icon= 'warning')
	else:
		cmds.select(sel)
		print u"選択しました。"


def ParamChange(self):
	try:
		oObj = cmds.ls(sl=True,type="transform")
		FirstObj = oObj[0].split("_")[-1]
	except:
		cmds.warning(u'選択して実行してください。')
		cmds.confirmDialog( title= 'ChangeOrder', m= u'選択して実行してください。', icon= 'warning')
		return
	if FirstObj != "TempObj":
		oLoc = cmds.spaceLocator(n=oObj[0]+"_TempObj")[0]
		cmds.addAttr(oLoc,sn="OriSel", ln="Original_Selection", dt="string" )
		cmds.setAttr(oLoc+".Original_Selection",oObj,type="string")
		PointCons = cmds.pointConstraint(oObj[0],oLoc,n="Dummy_point")
		OrientCons = cmds.orientConstraint(oObj[0],oLoc,n="Dummy_orient")
	else:
		oLoc = cmds.ls(sl=True)[0]
	cmds.setToolTo( 'RotateSuperContext' )
	Rotate_Mode = cmds.manipRotateContext( 'Rotate', q= True, mode= True)
	cmds.manipRotateContext( 'Rotate', e= True, mode= 2)
	oOrder = cmds.optionMenu( 'Check_Order_List', q= True, sl= True)
	if oOrder == 1:
		cmds.setAttr(oLoc+".rotateOrder",0)
	elif oOrder == 2:
		cmds.setAttr(oLoc+".rotateOrder",1)
	elif oOrder == 3:
		cmds.setAttr(oLoc+".rotateOrder",2)
	elif oOrder == 4:
		cmds.setAttr(oLoc+".rotateOrder",3)
	elif oOrder == 5:
		cmds.setAttr(oLoc+".rotateOrder",4)
	elif oOrder == 6:
		cmds.setAttr(oLoc+".rotateOrder",5)

#HelpWindow
def HelpWindow_Page(self):
	Helpwindow = "ChangeOrder_Help"
	Help_WindowWidth_Size = 350
	Help_WindowHight_Size = 270
	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"                          ChangeOrderの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.text(label=u"状態を確認することができます。")
	cmds.separator(w = Help_WindowWidth_Size,h=10)
	cmds.text(label=u"●【フレームを保持しない】は、選択した物の総尺を取得し")
	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)
	cmds.separator(w = Help_WindowWidth_Size,h=30)

def CheckOrder(self):
	BakeAll = cmds.checkBox("FrameLock",q=True,value=True)
	oObj=[]
	try:
		oObj = cmds.ls(sl=True,type="transform")[0]
	except:
		cmds.warning(u'選択して実行してください。')
		cmds.confirmDialog( title= 'ChangeOrder', m= u'選択して実行してください。', icon= 'warning')
		return
	Check_windowname = "ChangeOrder"
	Check_WindowWidth_Size = 230
	Check_WindowHight_Size = 300
	if cmds.window(Check_windowname,exists=True):
		cmds.deleteUI(Check_windowname,window=True)
	Check_window = cmds.window(Check_windowname,title=Check_windowname,sizeable=True,mxb=False,wh=[Check_WindowWidth_Size,Check_WindowHight_Size])

	cmds.frameLayout("CheckOrder",label="CheckOrder",bgc=[0.2,0.7,0.1])
	cmds.columnLayout(w=Check_WindowWidth_Size+3,bgc=[0.2,0.2,0.2])
	cmds.separator( height= 1)
	cmds.text(label=u"オーダーを確認できます。")
	cmds.text(label=u"プルダウンを変更して確認ください。")
	cmds.text(label=u"実行ボタンで、変更処理が走ります。")
	cmds.separator( height= 1)
	cmds.rowLayout("Check",numberOfColumns=3,columnWidth2=[100,40])

	cmds.optionMenu( 'Check_Order_List',changeCommand=ParamChange, 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')
	if len(oObj) > 1:
		oObj_Order = cmds.getAttr(oObj+".rotateOrder")
	else:
		oObj_Order = 0
	cmds.optionMenu( 'Check_Order_List', e= True, bgc= [0.1,0.5,0.7], ebg= False,sl=oObj_Order+1)
	cmds.button( 'exe', l= u'実行!', h= 40, w= 110,bgc=[1.0,0.0,0.0],c='ChangeOrder_exe(%d)' % (1))
	cmds.setParent("..")
	cmds.text(label=" ")
	cmds.checkBox("Check_FrameLock", label=u'フレームを保持しない' )
	cmds.text(label="")
	cmds.separator(w = Check_WindowWidth_Size , st = 'in')
	cmds.text(label=" ")
	cmds.button( 'back', l= u'戻る', h= 60, w= Check_WindowWidth_Size,bgc=[0.45,0.45,0.45],c=MainWindow)
	cmds.setParent("..")
	if BakeAll == True:
		cmds.checkBox("Check_FrameLock",e=True,value=True)
	cmds.showWindow(Check_window)


def MainWindow(V):
	print V
	try:
		Chcek_BakeAll = cmds.checkBox("Check_FrameLock",q=True,value=True)
	except:
		Chcek_BakeAll = []
	windowname = "ChangeOrder"
	WindowWidth_Size = 230
	WindowHight_Size = 300
	oObj = []

	if cmds.window(windowname,exists=True):
		cmds.deleteUI(windowname,window=True)
	Window = cmds.window(windowname,title=windowname,sizeable=True,mxb=False,wh=[WindowWidth_Size,WindowHight_Size])

	#Helpの為にメインメニューバーを追加します
	cmds.menuBarLayout()
	cmds.menu(label=u"Menu",tearOff=False)
	cmds.menuItem(label=Window+u"の解説",c=HelpWindow_Page)
	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', 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')
	try:
		oObj = cmds.ls(sl=True,type="transform")[0]
		cmds.setToolTo("RotateSuperContext")
		cmds.manipRotateContext("Rotate",e=True,mode=2)
	except:
		pass
	if len(oObj) > 1:
		oObj_Order = cmds.getAttr(oObj+".rotateOrder")
	else:
		oObj_Order = 0

	cmds.optionMenu( 'Order_List', e= True, bgc= [0.5,0.2,0.2], ebg= False,sl=oObj_Order+1)
	cmds.button( 'exe', l= u'実行!', h= 40, w= 110,c='ChangeOrder_exe(%d)' % (0))
	cmds.separator( height= 1)
	cmds.setParent(tab1_B)
	Lock = cmds.checkBox("FrameLock", label=u'フレームを保持しない' )

	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,c=CheckOrder)
	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= 60, w= 220,c=Bake_BeforeOrder)
	cmds.separator( h= 15)
	cmds.button( 'order_sel', l= u'オーダー変えたやつを選択', h= 60, w= 220,c=ChangeSelect)

	cmds.tabLayout( tab , edit=True, tabLabel=((tab1_B, u'オーダー変更'), (tab2_B,u"プロット")) )
	if Chcek_BakeAll != []:
		if Chcek_BakeAll == True:
			cmds.checkBox("FrameLock",e=True,value=True)
	cmds.showWindow(Window)

def main():
	ver = 1.0
	MainWindow(V=ver)

main()

これで一応できました。
とかく色々エラー処理がめんどくさかったです。
中身はすごい単純なのに‥
※re,partialは使ってないのですが、拡張を考えていて使う予定があるので‥
 【import ast】すげぇ便利でしたよ。


でUIは前回からあまり変わってないです。
テストように一回ウィンドウ作ったぐらいですね。
模索中だったところが、変更したオブジェクトを
選択するものにしました。
こんな感じ。
メインページ
f:id:tommy_on:20160923144702j:plain

プロットページ
f:id:tommy_on:20160923144913j:plain

テストページ
f:id:tommy_on:20160923144924j:plain

HELPページ
f:id:tommy_on:20160923150024j:plain

使い方はHELPにも書いてますが
選択して実行するだけです。
もちろん複数も可能です。


今回は色々とエラー処理やUIについて
学ぶことが多かったです。

ということで、次回はPysideか
法線にものを配置するツールを
作ってみたいと思います。

Pysideはともかく、法線はできるかな‥


以上です。

オーダー変えるやつ

続けて次はアニメーションの結果を変えずに
オーダーを切り替えるスクリプトを書いていきたいと思います。
SI時代にも作成していて、さらに昨年MAYA版も作ったんですが
色々あり、闇に消えてしまったので一から作り直してみたいと思います。
※今回は色々時間がないので、さらにスローに作ります‥

では、このスクリプトの仕様を見ていきましょう。

お題:オーダー変更スクリプト
制作理由:オーダーを変えるのが、地味に面倒。
     外部に出して、変更しないとアニメーションの結果が変わる。
     一度プロットしないといけない。
     複数実行ができない。

実装方法:
●選択したものを取得して、ロケータを取得→コンストかけて
 オーダーを変えれるように
 またイテレーションをできるだけ、少なくしたいので
 オーダーを確認できるようにテストモードを実装。
 ボタンで一時的に確認できるようにする。

●テストモードは、実行と同じで、選択物に対して
ロケータを生成して、コンストをかける。
 そのロケーターでプルダウンを選ぶ度に、オーダーが変えれるようにして
 目視で実行後のイメージが見えるようにします。
     
●後は、この時のエラー処理をどうしようかと‥
 選択を固定する事はできないので、スクリプトジョブかなんかで
 監視する事が必要かも‥まだモヤモヤしてる箇所。
 あとアニメーションしていない、物は弾く。

●さらにキーが一個ならプロットせずに
 オーダーのそのまま変えるように設定。
 オーダー変えた際に、デフォルトのオーダーも
記憶しておかないと、いけないのでアトリビュートを追加

     
●オーダー変更自体は、一時的に作ったロケータをプロットして
 選択コントローラーを逆コンストする形で実装。
 ただ、複数あるとプロット祭りになるので
 一度配列にコントローラーを追加して、プロット自体は一気に行う。
 またコンスト自体も同様に一括で行う。

以上な感じで。

今回はぱっとUIを先作ってみました。
こんな感じ。
f:id:tommy_on:20160912165233p:plain

タブ切り替えると
f:id:tommy_on:20160912165245p:plain

現状のイメージでここにコマンド入れ込んでいきたいですね。
あと、マニュピレーターをボタンかなんかで変えれるようにした方がいいのか‥
また考えます。
一応コード
※すみません。汚いです。。。

# -*- 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()

メイン画面
f:id:tommy_on:20160910143905j:plain

連番画面
f:id:tommy_on:20160910143914j:plain

Helpウィンドウ
f:id:tommy_on:20160910143937j:plain
※入力の値について、エラー処理対応したので
この画像は間違いですね‥


出力されたファイル達
f:id:tommy_on:20160910144013j:plain

といった感じになりました。


とにかくメニューの値と関数の参照が欲しくて
クラス化にしたんですが
まぁ、エラーに悩まされました。
※何個かは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(" ")

こんな感じに。
f:id:tommy_on:20160909005201p:plain
連番が
f:id:tommy_on:20160909005217p:plain
こうなりました。

細かい説明はいらないかな。。
すべて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()


複数選択してから実行すると、以下の画像みたいになるはずです。
f:id:tommy_on:20160906155833p:plain
変更点は配列「Select_Value」を追加し、値を追加したものを
外部へ出力する際に、Forで繰り返し出力しただけです。


これである程度まとまりができたので、テストを行います。
まずはすべてのコントローラーを選択して、実行してみます。
すると‥

f:id:tommy_on:20160906160504p:plain

はい。エラーになります。
エラー文を読みと
「# エラー: line 1: ValueError: file line 15: 名前と一致するオブジェクトがありません: ALL.blendOrient1 # 」
はい。
これは15行目でエラーになってることが分かります。

gettype = cmds.getAttr(Tmpsel + "." + att,type=True)

もっと詳しくみていくと、エラーが発生するコントローラーがあります。
右手のUPVですね。
f:id:tommy_on:20160906161849p:plain
書いてる通りアトリビュートの「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()

はい実行するとこんな感じ。
f:id:tommy_on:20160906162612p:plain

無事通りました。
再度テストを込めて、保存したものを別フレームで実行してみます。
ただ、今は読み込みの機能をつけていないので
データを開いてスクリプトエディタに
直接ペーストして実行します。

f:id:tommy_on:20160906162821p:plain
この状態で実行すると

f:id:tommy_on:20160906162845p:plain
こうなりました。

という点から、とりあえず問題ないように思います。
※本当はいっぱいありますが…


では、続けて読み込みのコマンドを用意してみます。
読み込みはすでに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
ロックが解除されているアトリビュートのみがリストされます。

f:id:tommy_on:20160905233516p:plain


このフラグがそのまま使えそうですね。
しかもありがたい事に、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」になってます。
f:id:tommy_on:20160906140906p:plain

はい。
これでテキスト出力までできました。
次はこれらを複数できるようにカスタムし、実行文と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()