Skip to content

Instantly share code, notes, and snippets.

@ShikouYamaue
Last active April 20, 2019 11:14
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ShikouYamaue/77d16b25d60ea77d0c5953585ae3c319 to your computer and use it in GitHub Desktop.
Save ShikouYamaue/77d16b25d60ea77d0c5953585ae3c319 to your computer and use it in GitHub Desktop.
Sample Editor 3 MayaAPI2.0 + QTableView + QStandardItemModel
# -*- coding: utf-8 -*-
from maya import cmds
from maya.app.general.mayaMixin import MayaQWidgetBaseMixin
import maya.api.OpenMaya as om2
import maya.api.OpenMayaAnim as oma2
import time
import imp
try:
imp.find_module('PySide2')
from PySide2.QtWidgets import *
from PySide2.QtGui import *
from PySide2.QtCore import *
except ImportError:
from PySide.QtGui import *
from PySide.QtCore import *
#時間計測用関数
def timer(func):
def wrapper(*args, **kwargs):
global WINDOW
start = time.time()#開始時間
func(*args, **kwargs)#関数実行
end = time.time()#終了時間
msg = func.__name__ + ' : ' + str(end - start)
print '-'*50
print msg
print '-'*50
WINDOW.time_label.setText(msg)
return wrapper
#ウィンドウ呼び出し
def main():
global WINDOW
WINDOW = SampleEditor()
WINDOW.resize(600, 400)
WINDOW.show()
#サンプルエディタークラス
class SampleEditor(MayaQWidgetBaseMixin, QMainWindow):
#コンストラクタ
def __init__(self, parent=None):
super(self.__class__, self).__init__(parent)
self.setWindowTitle('Sample Editor API')
#ラッパーウイジェットとレイアウトを設定
central_widget = QWidget()
self.setCentralWidget(central_widget)
self.main_layout = QVBoxLayout()
central_widget.setLayout(self.main_layout)
#ウェイト取得するボタン
self.get_button = QPushButton('Get Weights')
self.main_layout.addWidget(self.get_button)
self.get_button.clicked.connect(self.get_weights)
#ウェイトテーブルの変更を実際のウェイト値に反映するボタン
self.set_button = QPushButton('Set Weights')
self.main_layout.addWidget(self.set_button)
self.set_button.clicked.connect(self.set_weights)
#ウェイトテーブルビュー
self.table_view = QTableView()
self.main_layout.addWidget(self.table_view)
#時間計測結果のお知らせ
self.time_label = QLabel('Execution time :')
self.main_layout.addWidget(self.time_label)
#選択コンポーネントのウェイトデータを取得してテーブルに反映
def get_weights(self):
self.get_skin_data()#まずはウェイトデータの取得
self.table_setup()#テーブルにデータセット
#QTableWidgetにデータ設定
@timer
def table_setup(self):
#StandardItemModel作成
self.weight_model = QStandardItemModel()
self.weight_model.dataChanged.connect(self.update_weights)
#モデルにデータをセット
for row, weights in enumerate(self.weights_list):
items = []
for column, w in enumerate(weights):
item = QStandardItem(str(w))
items.append(item)
self.weight_model.appendRow(items)
#選択管理モデルを作る
self.sel_model = QItemSelectionModel(self.weight_model)
#モデルをビューに設定
self.table_view.setModel(self.weight_model)
self.table_view.setSelectionModel(self.sel_model)
#選択頂点、ウェイト、インフルエンス、スキンクラスタを取得
@timer
def get_skin_data(self):
#現在選択しているオブジェクト、コンポーネントをAPIで取得
sList = om2.MGlobal.getActiveSelectionList()
#選択リストのイテレータ
iter = om2.MItSelectionList(sList)
#イテレーションしてメッシュのDagPathとコンポーネントをセットで取得
while not iter.isDone():
meshDag, component = iter.getComponent()
iter.next()#nextでイテレーションを進める
#今回は1メッシュのみサポート(書式をわかりやすくするため)
self.meshDag = meshDag
#MFnSkinMeshクラス、頂点アレイ、スキンクラスタ名を取得する関数呼び出し
self.skinFn, self.skin_cluster = self.om_get_skin_cluster(self.meshDag)
self.vertexArray= self.convert_to_vertex(self.meshDag, component)
#指定の頂点をコンポーネントとして取得する
singleIdComp = om2.MFnSingleIndexedComponent()#SingleIDを格納するクラス
self.vertexComp = singleIdComp.create(om2.MFn.kMeshVertComponent)#頂点コンポーネント(SingleID)を格納する函
singleIdComp.addElements(self.vertexArray)#頂点アレイを格納
#インフルエンスを取得して情報整理
infDags = self.skinFn.influenceObjects()#インフルエンスを全取得
self.infIndices = om2.MIntArray( len(infDags), 0 )#インフルエンスIDを格納するMIntArrayを用意、インフルエンスの数のIntArray
for x in xrange(len(infDags)):
self.infIndices[x] = int(self.skinFn.indexForInfluenceObject(infDags[x]))
#インフルエンス名をフルパスで取得
self.full_path_influences = [infDags[x].fullPathName() for x in range(len(self.infIndices))]
#ショート名に変換(列ヘッダー表示用)
self.influences = [inf.split('|')[-1] for inf in self.full_path_influences]
#ウェイト取得
self.weights = self.skinFn.getWeights(self.meshDag , self.vertexComp)
#ウェイトデータ(1次元配列)を頂点ごとの2次元配列にリシェイプ
self.weights_list = self.conv_weight_shape(len(self.infIndices), self.weights[0])
#頂点番号名を生成(行ヘッダー表示用)
mesh_name = self.meshDag.fullPathName().split('|')[-1]
self.vertices = [mesh_name + '.vtx[' + str(vid) + ']'for vid in self.vertexArray]
#ウェイトをバーテックス単位の2次元配列にリシェイプする関数
def conv_weight_shape(self, shape, weights):
return [[weights[i+j*shape] for i in xrange(shape)] for j in xrange(len(weights)/shape)]
#ウェイトテーブルが更新されたらクラス内変数で保有しているウェイト値も更新する
def update_weights(self, top_left, bottom_right):
row_start = top_left.row()
row_end = bottom_right.row() + 1
column_start = top_left.column()
column_end = bottom_right.column() + 1
for row in range(row_start, row_end):
for column in range(column_start, column_end):
new_value = float(self.weight_model.item(row, column).data(0))
self.weights_list[row][column] = new_value
#コンポーネントやメッシュリストをバーテックスリストに変換する
def convert_to_vertex(self, meshDag, component):
cmpType = None
#選択されているコンポーネントタイプを取得
if component.hasFn(om2.MFn.kMeshVertComponent):
cmpType = "vtx"
elif component.hasFn(om2.MFn.kMeshEdgeComponent):
cmpType = "edge"
elif component.hasFn(om2.MFn.kMeshPolygonComponent):
cmpType = "face"
if cmpType:
compFn = om2.MFnSingleIndexedComponent(component)
#コンポーネントを頂点情報に変換してIDリストに格納する
meshFn = om2.MFnMesh(meshDag)
#頂点選択はそのまま取得
if "vtx" == cmpType:
vtxArray = compFn.getElements()
#エッジを頂点IDに変換する
elif "edge" == cmpType:
eid = compFn.getElements()
eSet = []
for e in eid:
evid = meshFn.getEdgeVertices(e)
eSet.extend(evid)
vids = list(set(eSet))
vtxArray = om2.MIntArray()
[vtxArray.append(id) for id in vids]
#フェースを頂点IDに変換する
elif "face" == cmpType:
fid = compFn.getElements()
fSet = []
for f in fid:
vid = meshFn.getPolygonVertices(f)
fSet.extend(vid)
vids = list(set(fSet))
vtxArray = om2.MIntArray()
[vtxArray.append(id) for id in vids]
#メッシュ選択の場合
else:
vids = range(meshFn.numVertices)
vtxArray = om2.MIntArray()
[vtxArray.append(id) for id in vids]
return vtxArray
#スキンクラスタ名とMFnSkinClusterクラスを返す
def om_get_skin_cluster(self, dagPath=None):
skinCluster = cmds.ls(cmds.listHistory(dagPath.fullPathName()), type='skinCluster')
clusterName = skinCluster[0]
sellist = om2.MGlobal.getSelectionListByName(clusterName)
skinNode = sellist.getDependNode(0)
skinFn = oma2.MFnSkinCluster( skinNode )
return skinFn, clusterName
#ウェイト値をスキンメッシュに反映
@timer
def set_weights(self):
#変更されたウェイト値をMDoubleArrayにキャスト
new_weights = om2.MDoubleArray()
for weights in self.weights_list:
new_weights += weights
#書き込み
self.skinFn.setWeights(self.meshDag, self.vertexComp, self.infIndices, new_weights, False)
#実行
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment