ON-BLOG

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

色々バックアップするツール

ちょいと用があり、昨日書いたスクリプトです。
こんなやつです。
f:id:tommy_on:20180112001639p:plain

シーンファイル、Xgenファイルをテキストベースのメモを残してバックアップするツールです。
Xgenはお知り合いからの要望で含めてます。
処理は簡易です。
セーブして、そのファイルをコピーしているだけです。
コードはこんな感じ。
インポート周りがすごく汚いです…

# -*- coding: utf-8 -*-
#######################################
import maya.cmds as cmds
import sys
import os
import json
import codecs
from functools import partial
import os.path
import shutil
import datetime
import xgenm as xg
#######################################


class Tt_FileBackUp:

    ExFrameLayout = ""
    Tt_windowName = "Tt_FileBackUp"
    In = cmds.playbackOptions(q=True,min=True)
    Out = cmds.playbackOptions(q=True,max=True)
    FilesPath = ""
    OutDirValue = ""
    OutDirButton = ""
    InDirValue = ""

    @classmethod
    def main(self,*args):

        if cmds.window(self.Tt_windowName ,exists=True):
            cmds.deleteUI(self.Tt_windowName ,window=True)

        MainWindow = cmds.window(self.Tt_windowName,t=self.Tt_windowName,w=450,resizeToFitChildren=True)
        cmds.columnLayout()
        self.ExFrameLayout = cmds.frameLayout(l="Export",backgroundColor=[0.8,0.2,0.3],w=450)
        cmds.rowLayout(nc=1)
        cmds.text(l=u"  ■書き出し")
        cmds.setParent("..")

        cmds.rowLayout(nc=3)
        self.OutDirValue = cmds.textField(w=410)
        self.Tt_Path = cmds.workspace(q=True,rootDirectory=True)

        cmds.textField( self.OutDirValue, edit=True,text=self.Tt_Path,enable=False)
        self.OutDirButton = cmds.button(l="...",w=30,enable=False)
        cmds.setParent("..")
        
        cmds.rowLayout(nc=3)
        self.CheckScene = cmds.checkBox(l=u"Sceneファイルも書き出す",v=True)
        self.CheckMemo = cmds.checkBox(l=u"メモファイルも書き出す",v=True,cc=self.ChangeBox)
        self.CheckXGen = cmds.checkBox(l=u"XGenファイルも書き出す",v=True)
        cmds.setParent("..")
        
        cmds.rowLayout(nc=1)
        cmds.text(l=u"  ■メモ (こちらに記載して下さい。)")
        cmds.setParent("..")
        
        cmds.rowLayout(nc=1)
        self.TectBox = cmds.scrollField( editable=True, wordWrap=True, text=u'',h=90,w=440 )
        cmds.setParent("..")
        
        cmds.rowLayout(nc=1)
        cmds.button(l="Export!!",w=440,h=40,backgroundColor=[0.8,0.2,0.3],c=self.BackUPPPPPP)
        cmds.setParent("..")

        cmds.showWindow(MainWindow)
        
    @classmethod
    def ChangeBox(self,*args):
        Value = cmds.checkBox(self.CheckMemo,q=True,v=True)
        if Value:
            cmds.scrollField(self.TectBox, e=True,enable=True)
        else:
            cmds.scrollField(self.TectBox, e=True,enable=False)

    @classmethod
    def BackUPPPPPP(self,*args):
        Flag = cmds.checkBox(self.CheckScene,q=True,v=True)
        FlagXgen = cmds.checkBox(self.CheckXGen,q=True,v=True)
        day = datetime.datetime.now()
        Time = day.strftime("%m-%d_%Hh%Mm%Ss")
        Scene_Name = cmds.file( query=True, sn=True).rpartition( "/" )[0] 
        Scene_Name_Only = cmds.file( query=True, sn=True , shn=True).partition( "." )[0]
        Scene_Exten = cmds.file( query=True, sn=True , shn=True).partition( "." )[-1]
            
        Path = Scene_Name + "/versionFolder/"
        if not os.path.exists(Path): 
            os.makedirs(Path)
        Path2 = Path + "/" + Time + "/"
        if not os.path.exists(Path2): 
            os.makedirs(Path2)

        if Flag:
            cmds.file(save=True, force=True)            
            Rename = str(Path2)+str(Scene_Name_Only)+"_"+str(Time)+"."+Scene_Exten
            Scene_Dir = cmds.file( query=True, sn=True)
            shutil.copyfile(Scene_Dir, Rename)
        
        if FlagXgen:
            Scene_Name = cmds.file( query=True, sn=True).rpartition( "/" )[0]
            XGenPath = Scene_Name.rpartition( "/" )[0] + "/xgen/"

            if not os.path.exists(XGenPath): 
                os.makedirs(XGenPath)

            Sel = cmds.ls(sl=True)[0]
            try:
                xg.exportPalette(str(Sel), str(XGenPath) + "Collection.xgen")
                pass
            except:
                print "NG"
            RenameXgen = str(Path2)+str(Scene_Name_Only)+"_"+str(Time)+".xgen"
            shutil.copyfile(str(XGenPath) + "Collection.xgen", RenameXgen)
        
        FlagMemo = cmds.checkBox(self.CheckMemo,q=True,v=True)
        if FlagMemo:
            f = codecs.open(Path2 + Scene_Name_Only + "_" + Time +".txt",'w','utf-8')
            textVal = cmds.scrollField(self.TectBox,q=True,text=True)
            WriteText = textVal.splitlines()
            print "Export Files!!"
            for i in WriteText:
                f.write(i)
                f.write("\r\n")
            f.close()
        print "OK"
        cmds.headsUpMessage( u'書き出しが完了しました!',time=3.0)
F_BackUp = Tt_FileBackUp
F_BackUp.main()

こんな挙動です。

file_backup from tommy_on on Vimeo.


今後はエクセルだったりに記載できるように、SVNにもあげれるようにしたいところ。
※仕事で使うなら。

以上です。

蛇みたいな構造体を作る

どっかのサイトで見た蛇みたいなリグを作る方法。
スパイン使うよりも簡単で、何よりも軽いのが特徴。

こんなやつ↓

hebi from tommy_on on Vimeo.

やり方は超簡単。
sphereを原点に出して、それを複製して横にずらします。
この時、オブジェクト同士が重なり合うように配置してください。
それを適宜伸ばしたい分配置します。
こんな感じ。
f:id:tommy_on:20170922010348p:plain

配置が終われば、わかりやすいようにリネーム。
リネーム後、1番めのsphere、2番めのsphereを選んで
コンストレイン→ジオメトリを実行。
次は、2番めのsphere、3番目のsphereを選んで
コンストレイン→ジオメトリを実行。
と最後まで続けてください。
f:id:tommy_on:20170922010745p:plain

これで完成です。
超簡単ですよね。
あとは、出力したいときは、骨を仕込んで拘束すれば
書き出しや、調節にも使えますね。


まぁ使わないと思いますが、HDDサルベージしてたら
出てきたので、忘れないようにメモとして
アップしました。

以上。

親にグループを作ってあげる

タイトル通りで、急遽家でも必要になった為
作りました。
半端なやつですが、メモようにこちらにもアップ。

リグ作るときに、トランスフォームを「0」にする為、親のオフセット値を入れるとおもいます。
それの自動化版です。
特になにもしてない、マクロです。

こんな状態のやつが
f:id:tommy_on:20170914011059p:plain
実行すると、
f:id:tommy_on:20170914011130p:plain
こうなります。


以下コード

import maya.cmds as cmds
import maya.OpenMaya as OpenMaya

def DAGPath( objectName ):
	sel = OpenMaya.MSelectionList()
	sel.add( str( objectName ) )
	obj = OpenMaya.MDagPath()
	sel.getDagPath(0,obj)
	return obj

Sel = cmds.ls(sl = True)

for i in Sel:
	DAG = DAGPath(i)
	Full = DAG.fullPathName()

	Grp = cmds.group(empty = True, world = True)
	Point = cmds.pointConstraint(Full,Grp)
	Ori = cmds.orientConstraint(Full,Grp)
	
	cmds.delete(Point,Ori)
	Pa = cmds.listRelatives(i,allParents=True)
	if not Pa:
		cmds.parent(Full,Grp)
	else:
		cmds.parent(Grp,Pa)
		cmds.parent(Full,Grp)
	cmds.setAttr( Grp + ".tx",lock=True)
	cmds.setAttr( Grp + ".ty",lock=True)
	cmds.setAttr( Grp + ".tz",lock=True)
	cmds.setAttr( Grp + ".rx",lock=True)
	cmds.setAttr( Grp + ".ry",lock=True)
	cmds.setAttr( Grp + ".rz",lock=True)
	#cmds.setAttr( Grp + ".rz",lock=True, keyable = False , channelBox = False)
	cmds.rename(Grp,i + "_Grp")
	

cmds.select(Sel)

パスが変わるので、OpenMaya使ってパスを管理してます。
※使わくてもできたかも‥


以上メモかわりでした。

sphinxについて

必要になったのでメモ。
※もう半年前のやつですが…
 実際仕事では、バンバン使ってるので

sphinx自体の説明は割愛。
要はドキュメント生成ツールとだけ。
sphinx-users.jp


まずはインストールから。
前段階だが、Pythonをインストーももちろんしておく。
※バージョンは2.7しか知りません。
その際環境変数を追加。

変数名: Path
変数値: ;C:\Python27;C:\Python27\Scripts

ローカルでパスが通っているか確認の為
コマンドプロンプト
python】と入力し
詳細が出るか確認。
エラーが出てなければOK。


続けてsphinxのインストール。
今回はpipで。
コマンドプロンプトで以下のコマンドを実行。

【pip install Sphinx

仮の場所として、【C:\Python27\Doc】でコマンドプロンプトを開く。

sphinx-quickstart】
を実行。
するとたくさん質問が出てきます。
それらは一個一個答える必要があります。
詳しくは以下のリンクにて。
http://sphinx-users.jp/gettingstarted/sphinxquickstart.html

その中でも重要なのが
プロジェクト名、名前。
あとは、拡張子の設定でデフォだと
「.rst」となっているところを
「.txt」に設定しておくと便利かも。

で、すべての項目が終わると
開いた階層に、プロジェクト階層ができる。
そこにできる「index.rst or txt」を開きます。
内部のテキストを変更すると、HTMLに反映される感じです。


冒頭の【.. 】以降に書いている
テキストはコメントアウトなので
気になさらず。
重要なのは
Welcome to HelpPage's documentation!
====================================
これでタイトルエリアの設定。


これはタイトルです!
====================================
とするとわかりやすいですね。

次に

.. toctree::
:maxdepth: 2
:caption: Contents:
この部分ですが
まずは【toctree】
これは、子どものタイトル情報を持ってきて、リンクを生成できます。
maxdepthはその階層の深さ。
captionはそのままの意味となります。

まぁとりあえず、うだうだ書いてますが
上記をを踏まえ以下の感じでテストしてみます。

用意ができれば、再度コマンドプロンプトを指定のディレクトリで立ち上げ
以下のコマンドを実行します。

make html
これでビルドされてHTMLが生成されます。

画像とか、動画も貼れるので
HELPとかには、すげぇ使えますね。

以上。


とか書いてますが、正直このレベルは
WEBに腐る程ありますね。



…。

FormLayoutについて

FormLayout結構使ってるんですが、たまに意図しない配置になったりするので
昔書いた絵をサルベージしました。

って書いてますが正直Help見ればいいですが…
とりあえずその昔(MAYAを本格的に触り始めた10ヶ月前ぐらい)書いた絵を
f:id:tommy_on:20161007003253j:plain


要はアタッチ系がよくわかっていませんでした。
アタッチなので、枠にひっついている。という単純な事だったんですけどね‥

上記の上部ボタンに関しては、
ボタンSIZE = (windowSize[0] -20)/2,height
とすれば、キレイに当分してくれますね。
※windowSizeは(横、縦)のウィンドウサイズの配列で「20」は左右のオフセット5px+ボタンの間(隙間)10Pxの合計値

FormLayoutは私的にすごく重要なのです。
例えば、超簡単セレクタ

import maya.cmds as cmds
def Sel(*args):
    print "selection"
 
window = cmds.window()
form = cmds.formLayout()
JPG = cmds.image(image = 'c:/test.jpg')
b1 = cmds.button(l="R_Hund" , bgc = [1.0,0.5,0.3] , c=Sel)
b2 = cmds.button(l="L_Hund" , bgc = [0.0,0.5,1.0] ,c=Sel)
b3 = cmds.button(l="Head" , bgc = [0.0,1.0,0.2] ,c=Sel)
cmds.formLayout( form, edit=True, 
							attachForm=[(JPG, 'top', 1),(JPG, 'left', 1),(JPG, 'right', 1),(JPG, 'bottom', 1),(b1,'top',140),(b1,'right',10),(b2,'top',140),(b2,'left',5),(b3,'top',20)],
							attachPosition=[(b3,'left',0,45),(b2,'right',30,50),(b1,'left',30,50)] )
cmds.showWindow( window )

こんなやつが出てきます。
f:id:tommy_on:20161007011237p:plain

以前にも書いた気がしますが・・

ようはFormLayoutで配置をするわけなんですが
これをすべてFormでやるとなると、正直結構死ねます。

ってことで、次【時間あれば】、Qtデザイナー使って
セレクター作ってみたいと思います。

以上。

Pythonのメモ

こんな事できるなんて…と思ったのをメモに

【リストを変数に個々に格納】

Sel1,Sel2,Sel3,Sel4 = ["polyShpere1","pTorus1","pCylinder1","pCube1"]
print Sel2

結果↓

pTorus1

こんな事できたとは…
あと、内包表記。
今まで少しだけ使ってたんですが、いろんなチュートリアル
スクリプトを見てると、上手いこと内包表記を使われてる方が多い。
なので、復習がてらにメモ。

以下のコードは、MAYAのパスにPSDファイルを保存するための
(実際は絶対こんな事はしてはいけないのですが。。。)
コードです。
単純に文字列をくっつけてるだけの例になります。

# -*- coding: utf-8 -*-
import maya.cmds as cmds

ScenePath = cmds.internalVar(uad = True)
File_List = ["File1.psd","File2.psd","File3.psd"]
CreatePath = []
for File in File_List:
	CreatePath.append(ScenePath + File)
print CreatePath


結果がこれ↓

[u'C:/Users/UserName/Documents/maya/File1.psd', u'C:/Users/UserName/Documents/maya/File2.psd', u'C:/Users/UserName/Documents/maya/File3.psd']

今まで結構使ってた手法です。
これでも問題ないのですが、あまりすっきりした書き方がではないです。
なのでここに内包表記をつかってみるとこうなります。

# -*- coding: utf-8 -*-
import maya.cmds as cmds

ScenePath = cmds.internalVar(uad = True)
File_List = ["File1.psd","File2.psd","File3.psd"]
CreatePath = []
CreatePath = [ScenePath + File for File in File_List]
print CreatePath

結果は同じで、結構スッキリしました。
これは今後優先して使っていこう。


【更新】
次にBoolのトグルについて
今までは、IF文を使っていたが以下の書き方にすることで
IF分がいらないようになる。

# -*- coding: utf-8 -*-
import maya.cmds as cmds

Sel = cmds.ls(sl=True,tr=True)
for i in Sel:
	Vis = cmds.getAttr(i+".visibility")
	cmds.setAttr(i+".visibility",(1-int(cmds.getAttr(i+".visibility"))))

要は「1-今の値」をしてるだけという、めちゃシンプルな内容。
昔からあるやり方みたいなんですが、最近知りました。
こうやって簡単に考えないと行けないっすよね‥

●USERパスを取得
単純に便利だなって思ったのでメモ

# -*- coding: utf-8 -*-
import os

FileExtension = 'Anim'

print os.path.join(os.path.expanduser('~'),'Sample.%s'%FileExtension)

結果こうなる。

##C:/Users/tommy_on/Documents\Sample.Anim

うん。
地味に便利。
今後はこっちを使おう。

ちなみに、

print os.path.expanduser('~')

でカレントユーザのホームディレクトリを取得してます。
もっと短く(拡張子の汎用性を無視するなら)

print os.path.expanduser('~/Sample.Anim')

でもいける。
ファイルの有無を簡単に調べるかも。

●Panelのドッキング方法
これ初めて知ったのでメモを‥
全く使ってコなかったなぁと。

パスにシンプルなウィンドウを記載してスクリプトを配置して実行

import maya.cmds as cmds
#パスにtestScript.pyを配置している。中身はクラス化した、簡単なウィンドウ
import testScript
Samplescript = testScript.ClassName()
Samplescript.main()
cmds.toggleWindowVisibility(Samplescript.window)
layout = cmds.paneLayout(configuration='single')
dock = cmds.dockControl(label=Samplescript.title,
						width=Samplescript.size[0],
						allowedArea='all',
						area='left',
						floating=False,
						content=layout)

cmds.control(Samplescript.window,e=True,p=layout)

うん。すごくいい。
ただ、あんま使わないかな‥
とりあえずメモ。

●joinでの'|'の追加
私普通にテキストの接続でやってましたがこっちの方が
キレイですね。
こんな感じ。

base = 'one|two|three'
add = 'five|six'
mix = '|'.join([base,'fore',add])

print mix

結果

one|two|three|fore|five|six

ウン。。
わかりやすいのがいい感じです。



こんな感じで、気になった事を
今後もこのページにメモしていきたいと思います。


以上。

キーワード引数のメモ

キーワード引数にリストを入れるとタプルになって
無理やり文字列操作でリストにしてたんですが
実は引数の前にアスタリスクを入れるだけでよかったという事が
わかったのでメモ。
かなり初歩的な事っぽい。。

def testFunc(*args):
	print(args[0:])


nodelist = ["test1","test2","test3","test4"]

testFunc(nodelist)

これだと

(['test1', 'test2', 'test3', 'test4'],)

とタプルになる。
別に引数が一つならこれで全然問題ないのですが
引数が複数ある関数だと、リストじゃないと
いけないときがちらほらあったりします。

いままでは一回文字列に変換して、まさかの文字列操作で
操ってました。

が、これ実は

def testFunc(*args):
	print(args[0:])


nodelist = ["test1","test2","test3","test4"]

testFunc(*nodelist)

と引数のリスト前に「*」を入れるだけでよかったみたいです。。

('test1', 'test2', 'test3', 'test4')

はい。
こんな事でできるとは…

とい言うことでメモに残しておきます。

以上。