ON-BLOG

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

StudioLibraryをPythonコマンドでSAVE LOADしてみる

すんごい細かいメモ。


qiita.com
ここのカバーとなります。
ほとんど書かれてますが…

Saveは上記通りで大丈夫かと思います。

from studiolibrarymaya import animitem

def LoadAnim(path,Selections):
    path    = path
    animitem.load(
            path,
            objects=Selections,
            namespaces=[],
            connect=True,
            currentTime=False,
            option="replace")

Selections= cmds.ls(sl=True) 
ExportPath   = "D:\\StudioLibrary"
File= "test..anim"
path = os.path.join(ExportPath  , File)
LoadAnim(path,Selections)

コード的にはこれでいけます。
実際はこのまま使うことはないのですが、基本的にこのテンプレートかと。
loadの引数はSaveと基本同じですが、違いがあるとすれば
studiolibraryのウィンドウに出てくる、Optionのパラメータと同じになります。

これでLoadはできるので、一部のツールにSaveとLoadを入れ込むといいかもですね。

コンスト先とか元とか

昔作ったやつ
実行で、コンスト元を選択し
Ctrl押しながらだと、コンスト先を取得する。
現状再帰的処理は入れていないし
色々不備はあるかと。

今度修正。

# -*- encoding: utf-8 -*-
import maya.cmds as cmds
import sys

def ConnectionList(KeyState,List,hako,Sel):
    #重複処理
    S_ListNode = list(set(List))
    for Node in S_ListNode:
        #拘束先を取得
        if KeyState == 4:
            #コンストだけ抽出
            if cmds.objectType(Node,isType="orientConstraint") or cmds.objectType(Node,isType="pointConstraint") or cmds.objectType(Node,isType="parentConstraint") or cmds.objectType(Node,isType="scaleConstraint"):
                #targetParentMatrixはバッティングする可能性が高いので修正案件 
                hako.append(cmds.listConnections(Node + ".target[0].targetParentMatrix",d=False))
        #拘束元を取得
        elif KeyState == 0:
            if cmds.objectType(Node,isType="orientConstraint") or cmds.objectType(Node,isType="pointConstraint") or cmds.objectType(Node,isType="parentConstraint") or cmds.objectType(Node,isType="scaleConstraint"):   
                Source = cmds.listConnections(Node + ".target[0].targetParentMatrix",d=False)[0]
                if Source == Sel:
                    hako.append(cmds.listConnections(Node + ".constraintParentInverseMatrix"))
    return hako

def Constraint_Source_Select():
    hako = []
    ListNode = []
    KeyState = cmds.getModifiers()
    #0-None
    #1-Shift
    #2-CapsLock
    #4-Ctrl
    #8-Alt
    Sels = cmds.ls(sl=True)
    for Sel in Sels:
        #source拘束元←←←←←←←←destination拘束される側
        #Ctrl押されてたら = 拘束先を取得
        if KeyState == 4:
            ListNode = cmds.listConnections(Sel,source=True,destination=False)
        #NotPusyKey = 拘束元を取得
        elif KeyState == 0:
            ListNode = cmds.listConnections(Sel,source=False,destination=True)
        #例外処理
        if ListNode == None:
            cmds.select(cl=True)
            cmds.confirmDialog(message = u'そもそも接続がないみたいです',button=['OK'], defaultButton='OK')
            sys.exit()
        
        #ここを再帰的にすれば、最後まで検索可能。
        List = ConnectionList(KeyState,ListNode,hako,Sel)
        print List
        if len(List) > 0:
            cmds.select(List[0])
            cmds.confirmDialog(message = u"選択しました。\n" + str(List[0]) ,button=['OK'], defaultButton='OK')
        else:
            cmds.confirmDialog(message = u'それ以上接続がないみたいです',button=['OK'], defaultButton='OK')
        
Constraint_Source_Select()

Pyside勉強 その十(終)

それでは、最後に前回作成したセレクターに
ボタンの処理となんとなく選択しているボタンの視認性を変更してみて
最後にします。

まずはコード。

import os
import sys

from PySide import QtCore, QtGui
from PySide.QtUiTools import QUiLoader
from maya.app.general.mayaMixin import MayaQWidgetBaseMixin

CURRENT_PATH = "D:/"

class GUI(MayaQWidgetBaseMixin, QtGui.QMainWindow):

    def __init__(self, parent=None):
        super(GUI, self).__init__(parent)
        loader = QUiLoader()
        uiFilePath = os.path.join(CURRENT_PATH, 'UI.ui')
        self.UI = loader.load(uiFilePath)
        self.setCentralWidget(self.UI)
        self.UI.BackGound.setPixmap(QtGui.QPixmap("D:/Selector.png"))
        self._filter = Filter()
        self.UI.widget.installEventFilter(self._filter)
        self.setWindowTitle("UI_testWindow")
        self.resize(660, 840)
        
        self.UI.CTRL_Head.clicked.connect(self.Push)
        self.UI.CTRL_L__Collar.clicked.connect(self.Push)
        self.UI.CTRL_R__Collar.clicked.connect(self.Push)
        self.UI.CTRL_Chest.clicked.connect(self.Push)
        self.UI.CTRL_Body.clicked.connect(self.Push)
        self.UI.CTRL_Hips.clicked.connect(self.Push)
        self.UI.CTRL_L__ElbowPole.clicked.connect(self.Push)
        self.UI.CTRL_L__Hand.clicked.connect(self.Push)
        self.UI.CTRL_R__ElbowPole.clicked.connect(self.Push)
        self.UI.CTRL_R__Hand.clicked.connect(self.Push)
        self.UI.CTRL_L__KneePole.clicked.connect(self.Push)
        self.UI.CTRL_L__Foot.clicked.connect(self.Push)
        self.UI.CTRL_R__KneePole.clicked.connect(self.Push)
        self.UI.CTRL_R__Foot.clicked.connect(self.Push)

        self.UI.ALL.clicked.connect(self.ALL_Sel)
        
    def Push(self):
        Sender =self.sender()
        modifiers = QtGui.QApplication.keyboardModifiers()
        if modifiers == QtCore.Qt.ControlModifier:
            cmds.select(Sender.objectName(),add=True)
        else:
            cmds.select(Sender.objectName())

    def ALL_Sel(self):
        cmds.select("CTRL*")


class Filter(QtCore.QObject):
    def eventFilter(self, widget, event):
        if event.type() == QtCore.QEvent.MouseButtonPress:
            self.origin = event.pos()
            self.rubberBand = QtGui.QRubberBand(QtGui.QRubberBand.Rectangle,widget)
            self.origin.setX(self.origin.x())
            self.origin.setY(self.origin.y())
            self.rubberBand.setGeometry(QtCore.QRect(self.origin,QtCore.QSize()))
            self.rubberBand.show()
            print "Click"
            print self.origin.x()
            print self.origin.y()
            
        elif event.type() == QtCore.QEvent.MouseMove:
            if self.rubberBand.isVisible():
                self.movePos = event.pos()
                self.movePos.setX(self.movePos.x()+ widget.x())
                self.movePos.setY(self.movePos.y()+ widget.y())
                self.rubberBand.setGeometry(QtCore.QRect(self.origin,self.movePos).normalized())
                print "Move"
                print event.x()
                print event.y()
            
        elif event.type() == QtCore.QEvent.MouseButtonRelease:
            print "Release"
            print event.x()
            print event.y()
            self.rubberBand.hide()
            rect = self.rubberBand.geometry()
            rect.setX(rect.x()-widget.x())
            rect.setY(rect.y()-widget.y())
            rect.setWidth(rect.width()-widget.x())
            rect.setHeight(rect.height()-widget.y())
            selected = []
            #ウィンドウ内のQPushButtonをすべて取得
            for child in widget.findChildren(QtGui.QPushButton):
                if rect.intersects(child.geometry()):
                    selected.append(child)
                    child.setChecked(True)
            if selected:
                self.rectSelection(selected)
            else:
                cmds.select(cl=True)
                for child in widget.findChildren(QtGui.QPushButton):
                    if child.objectName() != "ALL":
                        child.setStyleSheet("background-color: rgb(225,225,225);color: rgb(0, 0, 0);")
        return False

    def rectSelection(self,sel):
        selectBox = []
        for i in sel:
            print i.objectName()
            if i.objectName() == "ALL":
                pass
            else:
                print i.objectName()
                selectBox.append(i.objectName())
                i.setStyleSheet("background-color: rgb(20,20,20);color: rgb(255, 255, 255);")
        try:
            cmds.select(selectBox)
        except:
            pass


def main():
    app = QtGui.QApplication.instance()
    ui = GUI()
    ui.show()

if __name__ == '__main__':
    main()

実行するとこんな感じ。
f:id:tommy_on:20190429012657p:plain
前回からUIが変わってます。
といいつつも、中身は正直ボタンと色変化が入っただけで、何も変わってません。
f:id:tommy_on:20190429012755p:plain

実際実行すると、キチンと矩形選択ができており
のボタンの色も変わってます。
f:id:tommy_on:20190429013340p:plain

さらに下部にある、【SelectAll】も動いていることが確認できます。
f:id:tommy_on:20190429013428p:plain


色が変わったボタンも、何もない箇所で矩形選択をすると
通常状態に戻ります。
f:id:tommy_on:20190429013547p:plain


では今回も同様に、変わったところ、追加したところを記載してみます。

self.UI.CTRL_Head.clicked.connect(self.Push)
self.UI.CTRL_L__Collar.clicked.connect(self.Push)
self.UI.CTRL_R__Collar.clicked.connect(self.Push)
self.UI.CTRL_Chest.clicked.connect(self.Push)
self.UI.CTRL_Body.clicked.connect(self.Push)
self.UI.CTRL_Hips.clicked.connect(self.Push)
self.UI.CTRL_L__ElbowPole.clicked.connect(self.Push)
self.UI.CTRL_L__Hand.clicked.connect(self.Push)
self.UI.CTRL_R__ElbowPole.clicked.connect(self.Push)
self.UI.CTRL_R__Hand.clicked.connect(self.Push)
self.UI.CTRL_L__KneePole.clicked.connect(self.Push)
self.UI.CTRL_L__Foot.clicked.connect(self.Push)
self.UI.CTRL_R__KneePole.clicked.connect(self.Push)
self.UI.CTRL_R__Foot.clicked.connect(self.Push)
self.UI.ALL.clicked.connect(self.ALL_Sel)

ボタンのコネクション処理を追加しています。
一番下以外は共通の関数に飛んでいます。

def Push(self):
	Sender =self.sender()
	modifiers = QtGui.QApplication.keyboardModifiers()
	 if modifiers == QtCore.Qt.ControlModifier:
		cmds.select(Sender.objectName(),add=True)
	else:
		cmds.select(Sender.objectName())

上記ボタンの押したときに飛ぶ関数です。
内容的には、ボタンにあったコントローラーを選択しているだけです。
一行目の

Sender =self.sender()

はクリックされたボタンを返しています。
詳しく記載すると、HELPを見る限り
【直前のシグナルを送信したウィジェットを返す】
と翻訳できます。
なので、押されたボタンを返す。という意味になります。
Qtデザイナー上で、コントローラーの名前をボタンにしているので
押されたボタン=コントローラーということになります。

次に

modifiers = QtGui.QApplication.keyboardModifiers()
if modifiers == QtCore.Qt.ControlModifier:

このコマンドはキーボードの入力を取得する関数と
その入力に対する、分岐処理です。
Ctrlキーを押されてたらという分岐になり
その後の

	cmds.select(Sender.objectName(),add=True)
else:
	cmds.select(Sender.objectName())

で、追加選択か、通常選択を切り替えています。

次に

def ALL_Sel(self):
	cmds.select("CTRL*")

この関数は【SelectAll】というボタンの処理になります。
内容的には、リグシーン内コントローラーは必ず
プレフィックスに【CTRL】がついているので
その名がついたものを選択しています。
このリグ特有の処理になります。
個人的には、コントローラーはアトリビュート追加した方が
いいと思います…


次は少し飛んで

if child.objectName() != "ALL":
	child.setStyleSheet("background-color: rgb(225,225,225);color: rgb(0, 0, 0);")

矩形選択後の処理になり、矩形選択したボタンの中で【SelectAll】
を除く、ボタンの色を【setStyleSheet】で色変えをしています。
https://dftalk.jp/?p=16108
StyleSheetについては、こちらが参考になります!
処理的には、Defの色に戻しているだけです。

最後になります。

 def rectSelection(self,sel):
        selectBox = []
        for i in sel:
            #print i.objectName()
            if i.objectName() == "ALL":
                pass
            else:
                #print i.objectName()
                selectBox.append(i.objectName())
                i.setStyleSheet("background-color: rgb(20,20,20);color: rgb(255, 255, 255);")
        try:
            cmds.select(selectBox)
        except:
            pass

矩形選択部分になります。

if i.objectName() == "ALL":

【SelectAll】
は矩形選択から除外したいので、無視しています。

以下のコマンドは前回も出ていますが、setStyleSheetが追加されています。
矩形選択選択後、選択されているものは、ボタンの色を変えています。

selectBox.append(i.objectName())
 i.setStyleSheet("background-color: rgb(20,20,20);color: rgb(255, 255, 255);")


こんなもんでしょうか…
とりあえず、簡単ではありますが、矩形選択ができるセレクターを作るまで
は記事にできました。
ただ、このコマンドだけでは、実務ではまだまだまだまだ耐えれません。
※実感として

最低限【ネームスペース対応】【UndoRedo対応】【モーション補助ツールの追加】
【ビューポート選択と、UI選択のリンク】【タブ対応】【UIのズーム機能】が必須になるかと思います。
こちらは記載するよりも、いろんなセレクターが世にでてますので
それらを参考にしていただければと、思います。


とりあえず、平成の間に書き終えてよかった…
以上です。

Pyside勉強 その九

前回の記事をベースに矩形選択で取得した
ボタンの挙動を実装してみます。
まずは、ここでコントローラーのセレクトをしたいので
こちらの
3dfiggins.com
スパイダーマンリグをお借りしました。
ありがとうございます。
ちなみにここのサイトでリグ何個か買いましたが
めちゃくちゃ勉強になりました…
その節もありがとうございました。。。。。

このリグを見るとこんな感じで
かなりシンプルです。
f:id:tommy_on:20190429010651p:plain

では、このリグのセレクターをつくっていきたいと思います。
まずは、MAYA上でキャプチャーして
QtDesignerにて、前回まで使っていたUIファイルに画像を読み込みます。
f:id:tommy_on:20190429010718p:plain

背面は色のついた板をおいただけです。
この状態で、各コントローラー位置に
PushButtonを配置していきます。
こんな感じ。
f:id:tommy_on:20190429010751p:plain

ボタンの表示名(text)はわかりやすい文字で大丈夫です。
各ボタンのオブジェクト名をMAYAでセレクトするときの名前に
書き換えます。
左手のコントローラーなら
f:id:tommy_on:20190429010806p:plain
【CTRL_L__Hand】となっているので
オブジェクトも同様に変えます。
全部変えるとこんな感じになりました。
f:id:tommy_on:20190429010831p:plain

これで準備は完了です。
このオブジェクト名を見て、MAYA上でセレクトします。
このとき、ダイナミックプロパティなどを併用すると
いろんな分岐も持てるので、オススメです。
※今回は割愛

次はMAYAへ
以下のコードを実行してください。

import os
import sys

from PySide import QtCore, QtGui
from PySide.QtUiTools import QUiLoader
from maya.app.general.mayaMixin import MayaQWidgetBaseMixin

CURRENT_PATH = "D:/"

class GUI(MayaQWidgetBaseMixin, QtGui.QMainWindow):

    def __init__(self, parent=None):
        super(GUI, self).__init__(parent)
        loader = QUiLoader()
        uiFilePath = os.path.join(CURRENT_PATH, 'UI.ui')
        self.UI = loader.load(uiFilePath)
        self.setCentralWidget(self.UI)
        self.UI.BackGound.setPixmap(QtGui.QPixmap("D:/Selector.png"))
        self._filter = Filter()
        self.UI.widget.installEventFilter(self._filter)
        self.setWindowTitle("UI_testWindow")
        self.resize(660, 840)
        
    def test(self):
        print "Push!"

class Filter(QtCore.QObject):
    def eventFilter(self, widget, event):
        if event.type() == QtCore.QEvent.MouseButtonPress:
            self.origin = event.pos()
            self.rubberBand = QtGui.QRubberBand(QtGui.QRubberBand.Rectangle,widget)
            self.origin.setX(self.origin.x())
            self.origin.setY(self.origin.y())
            self.rubberBand.setGeometry(QtCore.QRect(self.origin,QtCore.QSize()))
            self.rubberBand.show()
            print "Click"
            print self.origin.x()
            print self.origin.y()
            
        elif event.type() == QtCore.QEvent.MouseMove:
            if self.rubberBand.isVisible():
                self.movePos = event.pos()
                self.movePos.setX(self.movePos.x()+ widget.x())
                self.movePos.setY(self.movePos.y()+ widget.y())
                self.rubberBand.setGeometry(QtCore.QRect(self.origin,self.movePos).normalized())
                print "Move"
                print event.x()
                print event.y()
            
        elif event.type() == QtCore.QEvent.MouseButtonRelease:
            print "Release"
            print event.x()
            print event.y()
            self.rubberBand.hide()
            rect = self.rubberBand.geometry()
            rect.setX(rect.x()-widget.x())
            rect.setY(rect.y()-widget.y())
            rect.setWidth(rect.width()-widget.x())
            rect.setHeight(rect.height()-widget.y())
            selected = []
            #ウィンドウ内のQPushButtonをすべて取得
            for child in widget.findChildren(QtGui.QPushButton):
            	if rect.intersects(child.geometry()):
                    selected.append(child)
            if selected:
                self.rectSelection(selected)

        return False

    def rectSelection(self,sel):
        selectBox = []
        for i in sel:
            selectBox.append(i.objectName())

        cmds.select(selectBox)


def main():
    app = QtGui.QApplication.instance()
    ui = GUI()
    ui.show()

if __name__ == '__main__':
    main()


実行すると以下のウィンドウが表示されます。
f:id:tommy_on:20190429011344p:plain
UIがめちゃくちゃでかいですが、気にしないでくださいw
矩形選択でボタンを含めると、コントローラーが選択できているはずです。
f:id:tommy_on:20190429011056p:plain
↓↓↓↓↓↓↓↓
f:id:tommy_on:20190429011013p:plain

ではコードの説明を
実はほとんど前回から変わってないです。
以下の関数の箇所だけです。

    def rectSelection(self,sel):
        selectBox = []
        for i in sel:
            selectBox.append(i.objectName())

        cmds.select(selectBox)

内容はシンプルで
矩形内のボタンを取得して
それのForで回して、ボタンのオブジェクト名を
配列に格納し、最後に選択しています。
まぁ単純です。

が、これでおおよそ目的は達したと思います。
ただ今はボタンを押しても効かないので
最後にボタンのクリックなどを実装してみたいと思います。


以上です

Pyside勉強 その八

次は、読みこんだUIに、マウスイベントを入れてみます。
色々手法はあるみたいですが
とりあえず、Filterを使ってWidgetのイベントを
オーバーライドする形にしてみます。
まぁはじめこの部分すんごいはまりましたw
まずはマウスイベントだけですが
なかなか文字数増えてますね汗

import os
import sys

from PySide import QtCore, QtGui
from PySide.QtUiTools import QUiLoader
from maya.app.general.mayaMixin import MayaQWidgetBaseMixin

CURRENT_PATH = "D:/"

class GUI(MayaQWidgetBaseMixin, QtGui.QMainWindow):

    def __init__(self, parent=None):
        super(GUI, self).__init__(parent)
        loader = QUiLoader()
        uiFilePath = os.path.join(CURRENT_PATH, 'UI.ui')
        self.UI = loader.load(uiFilePath)
        self.setCentralWidget(self.UI)
        self.UI.BackGound.setPixmap(QtGui.QPixmap("D:/GR.jpg"))
        self._filter = Filter()
        self.UI.widget.installEventFilter(self._filter)
        self.UI.pushButton.clicked.connect(self.test)
        self.setWindowTitle("UI_testWindow")
        self.resize(460, 610)
        
    def test(self):
        print "Push!"

class Filter(QtCore.QObject):
    def eventFilter(self, widget, event):
        if event.type() == QtCore.QEvent.MouseButtonPress:
            self.origin = event.pos()
            self.rubberBand = QtGui.QRubberBand(QtGui.QRubberBand.Rectangle,widget)
            self.origin.setX(self.origin.x())
            self.origin.setY(self.origin.y())
            self.rubberBand.setGeometry(QtCore.QRect(self.origin,QtCore.QSize()))
            self.rubberBand.show()
            print "Click"
            print self.origin.x()
            print self.origin.y()
            
        elif event.type() == QtCore.QEvent.MouseMove:
            if self.rubberBand.isVisible():
                self.movePos = event.pos()
                self.movePos.setX(self.movePos.x()+ widget.x())
                self.movePos.setY(self.movePos.y()+ widget.y())
                self.rubberBand.setGeometry(QtCore.QRect(self.origin,self.movePos).normalized())
                print "Move"
                print event.x()
                print event.y()
            
        elif event.type() == QtCore.QEvent.MouseButtonRelease:
            print "Release"
            print event.x()
            print event.y()
            self.rubberBand.hide()
            rect = self.rubberBand.geometry()
            rect.setX(rect.x()-widget.x())
            rect.setY(rect.y()-widget.y())
            rect.setWidth(rect.width()-widget.x())
            rect.setHeight(rect.height()-widget.y())
            selected = []
            #ウィンドウ内のQPushButtonをすべて取得
            for child in widget.findChildren(QtGui.QPushButton):
            	if rect.intersects(child.geometry()):
                    selected.append(child)
            if selected:
                self.rectSelection(selected)

        return QtGui.QMainWindow.eventFilter(self, widget, event)

    def rectSelection(self,sel):
    	print sel


def main():
    app = QtGui.QApplication.instance()
    ui = GUI()
    ui.show()

if __name__ == '__main__':
    main()

実行するとこんな感じ
f:id:tommy_on:20190428005822p:plain
前回同様に画像が張ってあって、ボタンが真ん中に。
といったUI。
見た目は何も変わってません。

これにマウスイベントが組み込まれています。
実際に矩形選択すると、ログにはこう出ています。
f:id:tommy_on:20190429012126p:plain
マウスの位置と、ボタンが取得できていますね。
成功です。

では解説。

from PySide.QtUiTools import QUiLoader
from maya.app.general.mayaMixin import MayaQWidgetBaseMixin
CURRENT_PATH = "D:/"

UIファイルを読み込むモジュールを
インポートしておきます、
【CURRENT_PATH 】で絶対パスを記載して、あとでファイルと結合して
UIファイルを読み込みます。
この方が管理しやすいので、変更しました。

loader = QUiLoader()

ロードするコマンドをインスタンス化します。

uiFilePath = os.path.join(CURRENT_PATH, 'UI.ui')
self.UI = loader.load(uiFilePath)

上記で宣言したパスと、ファイル名を合体して
ロードを実行。読み込んだUIファイルを変数に格納しておきます。

self.setCentralWidget(self.UI)

継承したウィンドウ(メインウィンドウ)に読み込んだ
UIをセットしています。

self.UI.BackGound.setPixmap(QtGui.QPixmap("D:/GR.jpg"))

前回同様画像ファイルを設定

self._filter = Filter()
self.UI.widget.installEventFilter(self._filter)

この部分が今回の一番の追加点になります。
マウスイベントを【installEventFilter】を
使用して、イベントを管理しています。
【installEventFilter】については以下が、めちゃくちゃわかりやすいです。
ヲドリテヒヅル eventFilter de のっとーり


で、中身ですが、
まずはマウスの処理をまとめたクラスを読み込み
変数self._filterに格納しておきます。
次にeventFilterを使ってイベントを全取得して、上記クラスに処理を投げています。
細かく書くと

self.UI.widget.installEventFilter(self._filter)

(メインウィンドウの子どもにあるパーツにinstallEventFilterで読み込んだクラスを
 割り当てる)といった流れです。
f:id:tommy_on:20190429004958p:plain

eventFilterはイベントを全部取ってくることができる、とても便利な機能です。
他のやり方もあるみたいですが、私はこのやり方しかやったことがないので、割愛。

self.UI.pushButton.clicked.connect(self.test)
self.setWindowTitle("UI_testWindow")
self.resize(460, 610)

ボタンクリックの挙動と、ウィンドウの名前
UIサイズの変更を行っています。

def test(self):
	print "Push!"

ボタン挙動の関数です。
今見ると、押すとPush!!ってアホですねw


では次に、追加したクラスの中をみていきます。

class Filter(QtCore.QObject):
    def eventFilter(self, widget, event):

マウス処理を記載したクラスです。
ここで少しハマったので自信ありませんw
とりあえず動いているので進めます苦笑
クラスの名前は適当です。
関数名も同様です。
引数でウィジェットとイベントを指定しています。

if event.type() == QtCore.QEvent.MouseButtonPress:

event.type()なので、クラスに渡されたイベントが
マウスボタンが押されたという、イベントだったら
という分岐になります。

self.origin = event.pos()

イベントが発生したポジションを取得しています。
今回でいうと、マウスボタンが押された場所です。

self.rubberBand = QtGui.QRubberBand(QtGui.QRubberBand.Rectangle,widget)

widget上で、QRubberBandで矩形選択時に出るコレ↓を描画しています。
f:id:tommy_on:20190429005033p:plain

self.origin.setX(self.origin.x())
self.origin.setY(self.origin.y())

マウスボタンが押された場所をXとYで分解して
矩形選択の開始位置として、セットしています。

self.rubberBand.setGeometry(QtCore.QRect(self.origin,QtCore.QSize()))

矩形のジオメトリなので、線の部分かと思います
QtCore.QRectについては以下が一番わかり易いです。
QRect - qtmemo @ ウィキ - アットウィキ
あとここも
http://melpystudio.blog82.fc2.com/blog-entry-191.htmlQRect - qtmemo @ ウィキ - アットウィキ
引用すると
""
QtCore.QRect(x, y, width, height)
でQRectを作成します。xが矩形の左上X座標、yが左上Y座標、width/heightが矩形の幅/矩形の高さをそれぞれ表しています。
""
なので、QtCore.QRect(self.origin,QtCore.QSize())では
x, y,をself.originで。
width, heightQtCore.QSize()で指定います。

self.rubberBand.show()

最後に実勢の表示をこのコマンドで実行。

print "Click"
print self.origin.x()
print self.origin.y()

こちらはデバッグ用です。特に意味はありません。

elif event.type() == QtCore.QEvent.MouseMove:

マウスが動いたら。という分岐になります。

if self.rubberBand.isVisible():

クリック時に表示していた、矩形オブジェクトが表示状態なら=クリックが実行されて
いたらという意味になります。

self.movePos = event.pos()
self.movePos.setX(self.movePos.x()+ widget.x())
self.movePos.setY(self.movePos.y()+ widget.y())

一行目で現在のポジションを取得し
2、3行目でXYそれぞれの最新の値を更新しています。
(self.movePos.x()+ widget.x())この部分ですが
self.movePos.x()(現在位置) + widget.x()(ウィジェットのX開始位置)
と言うことになるので、ウィジェットの開始位置からの移動値を
取得しているといったながれになります。

self.rubberBand.setGeometry(QtCore.QRect(self.origin,self.movePos).normalized())

上記で取得した位置を元に、ジオメトリを更新しています。
最後のnormalized()は正規化です。
正しい値にしているだけですので、実はなくても動きます。
保険かと思います。
サンプルに記載していたので、つけてますw


次に

elif event.type() == QtCore.QEvent.MouseButtonRelease:

マウスボタンが離されたらという分岐。

self.rubberBand.hide()

リリースなので、ジオメトリを非表示にしています。

rect = self.rubberBand.geometry()
rect.setX(rect.x()-widget.x())
rect.setY(rect.y()-widget.y())
rect.setWidth(rect.width()-widget.x())
rect.setHeight(rect.height()-widget.y())

この部分ですが、今までと逆のことを行っています。
1行目では、矩形を取得し
2,3、4,5行目で
ウィジェットのローカル空間のXY位置と
横幅のサイズを取得しています。

selected = []

あとで使う空の配列

for child in widget.findChildren(QtGui.QPushButton):
	if rect.intersects(child.geometry()):
		selected.append(child)

widgetの子どもにあるボタンをまず全取得しておきます。
2行目で rect.intersectsで矩形の中にある(厳密には矩形に当たっている)
ジオメトリ(パーツ)なのか?と分岐を噛ましています。
矩形内なら、配列にボタンを追加しています。
まとめると、まずは該当ウィジェットの子どもにある
ボタンを全取得し、それを回し
矩形内に入っていたら(矩形座標内に)配列に足す。という
流れになります。

if selected:
	self.rectSelection(selected)

配列が空じゃなかったら、関数に飛びます。

return QtGui.QMainWindow.eventFilter(self, widget, event)

ここ重要です。
eventFilterはリターンが必要です。
処理を返さないといけないので上記を指定しています。
ここはIFで分岐しているので
return Falseでもいけます。

def rectSelection(self,sel):
	print sel

最後です。
配列にボタンが入っていたら飛ぶ関数です。
特段今は意味がありません。


という感じになります。
eventFilter便利なんですが、調べるのに結構時間かかりました…
ふぅ。。

次はボタンが矩形内にあったときの挙動を作ってみます。

以上です。

Pyside勉強 その七

今回はQtdesignerを使って簡単な画像付のUIを作成し
それをMAYAとリンクさせるところまでやってみたいと思います。

はじめにUIファイルを作成します。
こんな感じの簡単なやつでいきます。
f:id:tommy_on:20190428004054p:plain
内容的にも簡易です。
widget作って、今回はQLabelを配置。
名前を【BackGound】としてしておきます。
さらにpixmapにDドライブ直下においている、適当な画像を指定しています。
今回はローカルにあった将軍の画像。
※将軍やらないとなぁ・・・
f:id:tommy_on:20190403000059j:plain
最後にPushButtonを配置し、表示テキストを
【Hellow】として、Dドライブ直下にUIという名前で保存します。

次はMAYA側。
以下のコードになります。

from PySide.QtCore import *
from PySide.QtGui import *
from PySide.QtUiTools import QUiLoader
from maya.app.general import mayaMixin


class Image(mayaMixin.MayaQWidgetBaseMixin,QDialog):

    def __init__(self, parent = None):
        super(Image, self).__init__(parent)
        self.UIPanel = QUiLoader().load("D:/UI.ui",parentWidget=self)
        self.UIPanel.BackGound.setPixmap(QPixmap("GR.jpg"))

        self.UIPanel.pushButton.clicked.connect(self.test)

    def test(self):
        print "Push!"
    
Window = Image()
Window.UIPanel.show()

実行するとMAYAではこうなります。
f:id:tommy_on:20190428005822p:plain


新たに追加された部分を説明します。

from PySide.QtUiTools import QUiLoader

UIファイルを読み込むモジュールです。
これ以外にも読み込み方はありますが、今回はこれを使います。

クラス定義する際に、

mayaMixin.MayaQWidgetBaseMixin

の記載は忘れずに。

self.UIPanel = QUiLoader().load("D:/UI.ui",parentWidget=self)

QUiLoader().load これがUIファイルを読み込むメソッドになります。
シンプルに読み込んでます。
parentWidget=selfここが重要になります。
簡単なんですが、parentWidgetなので親のウィジェットを指定しています。
Selfなので、class Imageが親になります。
絵にするとこんな感じ。
f:id:tommy_on:20190428005305p:plain

parentWidgetでSelfをしているすることで、【UIPanel】
がMAYAの後ろにいかなくなります。

self.UIPanel.BackGound.setPixmap(QPixmap("D:/GR.jpg"))

ここでQtDesinerで指定した画像がリンク切れを起こしてますので
サイド設定しています。

ほかは特に記載の必要がないと思います。

こんな感じでUIファイルを読み込みます。
次はUIファイルにボタンを追加してますので
マウスジェスチャーを入れてみようかともいます。

以上です。

Pyside勉強 その六

では次はもう少し実践を意識したツールを作ってみます。
前回のUIを意識して、リストに物を追加して
処理を行うものにします。
またボタンを追加して、リストに入っているものを
コンストレインレインを行う。といったツールにします。
細かい処理はいれないので、シンプルなものになるかな?

以下コード。

#-'''- coding: utf-8 -'''-
from PySide.QtCore import *
from PySide.QtGui import *
from maya.app.general import mayaMixin

class Ui_MainWindow(mayaMixin.MayaQWidgetBaseMixin,QDialog):

    def __init__(self, parent=None):
        super(Ui_MainWindow, self).__init__(parent)
        
        #全体のサイズ設定
        self.resize(500,400)

        #レイアウトを宣言
        #全体纏めるレイアウトAll_Layoutと、パーツまとめるレイアウトmain_layout
        self.All_Layout = QVBoxLayout()
        self.main_layout = QHBoxLayout()

        #左側のUI作成
        self.Source_0 = QPushButton("Add")
        self.Scroll_Sorce_List = QListWidget()
        self.Scroll_Sorce_List.resize(250,150)
        self.Left_layout = QVBoxLayout()
        self.Left_layout.addWidget(self.Source_0)
        self.Left_layout.addWidget(self.Scroll_Sorce_List)
        
        #右側のUI作成
        self.Source_1 = QPushButton("Add")
        self.Scroll_Sorce_List_02 = QListWidget()
        self.Scroll_Sorce_List_02.resize(250,150)
        self.Right_layout = QVBoxLayout()
        self.Right_layout.addWidget(self.Source_1)
        self.Right_layout.addWidget(self.Scroll_Sorce_List_02)
        
        #左右のパーツをメインレイアウトに追加
        self.main_layout.addLayout(self.Left_layout)
        self.main_layout.addLayout(self.Right_layout)
        #実行用のボタンを追加して、パーツをレイアウトに追加
        self.ExeButton = QPushButton("Exe")
        self.All_Layout.addLayout(self.main_layout)
        self.All_Layout.addWidget(self.ExeButton)
        #最後に大元のレイアウトに追加
        self.setLayout(self.All_Layout)
        
        self.Source_0.clicked.connect(self.Left_ADD)
        self.Source_1.clicked.connect(self.Right_ADD)
        self.ExeButton.clicked.connect(self.Exe)
        
    def Left_ADD(self):
        sel = cmds.ls(sl=True,type="transform")
        for i in sel:    
            self.Scroll_Sorce_List.addItem(i)

    def Right_ADD(self):
        sel = cmds.ls(sl=True,type="transform")
        for i in sel:
            self.Scroll_Sorce_List_02.addItem(i)
    
    def Exe(self):
        items = []
        items_02 = []                
        if not self.Scroll_Sorce_List.count() == self.Scroll_Sorce_List_02.count():
            print "Not Match"
        else:
            for index in xrange(self.Scroll_Sorce_List.count()):
                 items.append(self.Scroll_Sorce_List.item(index))
            for index in xrange(self.Scroll_Sorce_List_02.count()):
                 items_02.append(self.Scroll_Sorce_List_02.item(index))
            for i in xrange(len(items)):
                Left = items[i].text()
                Right = items_02[i].text()
                cmds.parentConstraint(Left,Right)


if __name__ == '__main__':
    form = Ui_MainWindow()
    form.show()

起動するとこんな感じ。
f:id:tommy_on:20190423215542p:plain

オブジェクトをAddボタンで足せます。
f:id:tommy_on:20190423215818p:plain

最後に下部のボタンを押すと、ペアレントコンストレイントが掛かります。
f:id:tommy_on:20190423215959p:plain

テコレになっていれば、複数も可能です。
逆にちぐはぐなリストだったら、エラーを返します。
チェックボックスとか付けると、リグなどに使えますね。
というか、これの拡張版を今使ってます。

さぁ。
細かく見ていくと、初期化部分の箇所から

self.resize(500,400)

この部分は、ウィンドウの全体サイズを指定しています。
XYとなるので、横500縦400のウィンドウサイズに変更しています。

self.All_Layout = QVBoxLayout()
 self.main_layout = QHBoxLayout()

コメントアウトにても記載していますが
All_Layoutが全体を縦で管理しているレイアウトで
main_layoutがパーツを纏めた横のレイアウトです。

イメージですが、こんな感じ。
f:id:tommy_on:20190423214536p:plain

図で見るとわかりやすいですね。

後は、図通りに追加するだけですね。
なので、次はボタン押したときの挙動の一部を…

self.Scroll_Sorce_List.addItem(i)
self.Scroll_Sorce_List_02.addItem(i)

変数iということは選択したものを
スクロールリストに追加するといった流れになります。
addItemって解りやすいっよね。

後は特に説明は入らない気がするので、割愛。

こんな感じで、PySideでUI組む流れになりました。
次は、Qtdesignerで作成されたUIをMAYAに持ち込んでみたいと思います。

以上です。