Pyside勉強 その四
いままでのツールで一点おかしな部分があります。
それはウィンドウがMAYA画面の後ろにいってしまう点です。
なんでこんなことがおきるんでしょうか?
その原因はMAYAのウィンドウがPySideで
作成されているのが、起因になっています。
前の記事でも書いてますが
PySideは継承してUIを作成していきます、
そのため、MAYAウィンドウを継承せずに
UIを生成すると、MAYAウィンドウと生成UIが
並列になることになる為、裏側にUIがいってしまいます。
こんな感じでUIが立ち上がるが
MAYAのウィンドウの適当なところをクリックすると
裏にいってしまい消えてしまいます。
これを回避するため、MAYAウィンドウを継承してあげます。
やりかたは簡単です。
from maya.app.general import mayaMixin
をインポートして、クラスの第一引数に
mayaMixin.MayaQWidgetBaseMixin
を指定してあげるだけです。
こんな感じ↓
#-'''- coding: utf-8 -'''- from PySide.QtCore import * from PySide.QtGui import * from maya.app.general import mayaMixin class Form(mayaMixin.MayaQWidgetBaseMixin,QDialog): def __init__(self, parent=None): super(Form, self).__init__(parent) self.edit = QLineEdit("Your Name???") self.button = QPushButton("Push!!!") layout = QVBoxLayout() layout.addWidget(self.edit) layout.addWidget(self.button) self.setLayout(layout) self.button.clicked.connect(self.greetings) def greetings(self): print ("Hello", self.edit.text()) if __name__ == '__main__': form = Form() form.show()
実行してもらえばわかりますが
ウィンドウが後ろにいかなくなっています。
これはQDialogがMayaウィンドウの子どもになっているからです。
簡単ですね。
以降のスクリプトではこの記述は、必須ですね。
以上です。
Pyside勉強 その参
次は
Pysideの肝になる継承を含んだ例です。
こちらも前前回の記事に載っているものですね。
#-'''- coding: utf-8 -'''- from PySide.QtCore import * from PySide.QtGui import * class Form(QDialog): def __init__(self, parent=None): super(Form, self).__init__(parent) self.edit = QLineEdit("Your Name???") self.button = QPushButton("Push!!!") layout = QVBoxLayout() layout.addWidget(self.edit) layout.addWidget(self.button) self.setLayout(layout) self.button.clicked.connect(self.greetings) def greetings(self): print ("Hello", self.edit.text()) if __name__ == '__main__': form = Form() form.show()
起動するとこんな感じに
UIに文字を入力すると
入力した値が出力されるといった簡単なものです。
なんか返って来てますね。
では解説を…
class Form(QDialog):
クラスを定義しています。
クラスの説明はややこしそうなので、簡単に雛形として
処理をまとめたりできるもの。として認識してくれればと思います。
Class この部分でクラスを定義しています。
関数だとDefですね。
その後の文字列【Form】がクラス名です。
名前は予約語以外ならなんでもいいです。
(QDialog)この部分が肝です。
クラスを定義する際に、括弧内に入れるものはPysideの場合は
継承をうまい事使ってGUIを構築していきます。
今回の件でいうと、Pysideの【QDialog】(文字入力させたりメッセージ確認等を行う、一時的に開かれるウィンドウのことです。)
を継承しますよ。そして、色々カスタマイズしますよ!と宣言しています。
ちなみに、QDialogの親元はQWidgetでまたその親がQObjectになります。
こういう親子関係がベースになりますので結構重要です。
https://blog.qt.io/jp/2010/05/05/object/Qt をはじめよう! 第7回: Qt のオブジェクトモデルを理解しよう - Qt Japanese Blog
私はここではじめ学んだ気がします。
あとこの図も
ちなみに、PySideは以下の構造になっています。
※一部抜粋
[Qt QObject]
>[Qt QLayout]
>>[Qt QBoxLayout]
>>[Qt QGridLayout]
>[Qt QWidget]
>>[Qt QDialog]
>>[Qt QLineEdit]
では続けて
def __init__(self, parent=None): super(Form, self).__init__(parent)
クラス内の一番初めの2行です。
関数が記載されています。
この関数は __init__となってます。initializeの略ですが、Pythonでは
ダブルアンダースコアで括るルールがあるみたいです。
この処理自体は、クラスを実体化させる、コンストラクタになるのですが
意味合い的には、クラスの情報を初めに定義する。
という意味があり、実際クラスを実行すると
この関数が初めに実行されます。
括弧内ですが、self、parent=Noneとなってますね。
selfは言葉通り、自身。Formクラスのことを示しています。
class Form(QDialogを継承したクラスForm)を初期化しますよ。という意味です。
parent=Noneの意味は、引数parentのデフォルト値がNoneという意味になります。
そもそもこのオブジェクト(クラス)自身、新規のオブジェクトなので
当たり前ですが、親がいません。
なので、継承元のQDialogのコンストラクタ
QDialogの__init__(None)を実行する。という意味になります。
ちなみに【parent】は実はクラス継承とは関係がないみたいで
単純にオブジェクト間の親子関係を表すみたいです。
要はparent引数をNoneにした場合、QDialogが親で
【新規のオブジェクトを作成】ということになります。
※このあたり自信がないので、間違っていたら
ご指摘いただけますと、嬉しいです。
●次の行
super(Form, self).__init__(parent)
こちらは上記の続きになります。
super() は親クラスを示すためのメソッドです。
__init__() メソッドは親クラスでも、子クラスでも定義されています。
このような場合、子クラスが初期化が有線されて上書きされます。
これをオーバーライドと言います。
親クラスのメソッドや変数を呼んだり参照したいケースでsuper()を使用します。
では中身をみてみます。superのプロパティですが以下のようになります。
super(クラス, インスタンス).メソッド
詳しくは以下のサイトを…
www.lifewithpython.com
なので、置き換えると
クラスFormの親【QDialog】の初期化を実行します。
ということになり、この後に続く、QDialogに
パーツを足したりできるようになります。
長くなったので、まとめます。
def __init__(self, parent=None): super(Form, self).__init__(parent)
クラスFormの初期化メソッドで
スーパークラスのQDialogの初期化を実行し、QDialogに
パーツを仕込めるようにした。ということになります。
補足になりますが、クラス【Form】はサブクラスとなり
QDialogがスーパークラスになります。
※サブクラスとは、継承したクラスから新しく作ったクラスで
継承元のクラスがスーパークラス。
続き
self.edit = QLineEdit("Your Name???") self.button = QPushButton("Push!!!")
QDialogにテキスト入力と、ボタンを追加します。
特に説明はいらないかな?括弧の中はウィンドウに表示される文字列です。
次に上記で作成した、パーツをUIの組み込みんで行きます。
layout = QVBoxLayout() layout.addWidget(self.edit) layout.addWidget(self.button) self.setLayout(layout)
まずは
layout = QVBoxLayout()
QVBoxLayoutとは、Vertical(垂直)のレイアウトを作成します。
という意味で、要は縦にパーツを配置していくレイアウトを宣言しています。
layout.addWidget(self.edit) layout.addWidget(self.button)
垂直レイアウトに上記で記載した、テキスト入力パーツとボタンを追加。
という意味になり、UIを構築している状態になります。
self.setLayout(layout)】
上記でコマンドで、UI構築を終了して確定させます。
という意味なるので、UIがこれで構築できました。
self.button.clicked.connect(self.greetings) def greetings(self): print ("Hello", self.edit.text())
この部分は前も記載しましたので、軽く。
ボタンが押されたら、関数【greetings】を
実行。という意味になり、この関数は
テキスト入力されて値を表示する。
といったものになります。
以上で説明は終わりになります。
このスクリプトはPysideもしくはPythonの基本が詰まってるいいスクリプトですね。
Pyside勉強 その弐
続き。
ハローワールドのスクリプトはこれぐらいにして
続いてプッシュボタンについて。
これも前回記載したサイトに載っているものです。
こちらはイキナリMAYAに落とし込んだコードです。↓
# -'''- coding: utf-8 -'''- import sys from PySide.QtCore import * from PySide.QtGui import * def sayHello(): print "Hello World!" button = QPushButton("Click me") button.clicked.connect(sayHello) button.show()
実行すると、こんな感じに
ボタンを押すと、Hello World!って出力される
シンプルなものです。
以下に説明を記載します。
# -'''- coding: utf-8 -'''-
このコマンドを記載することで、UTF8にエンコードしてくれます。
記載しない限りは2バイト文字は文字化けしますので
注意が必要です。
詳しくは、こちらを参照に…
qiita.com
次、
def sayHello():
このコマンドは、def文といい、関数になります。
sayHelloは関数名になります。
この辺りはPythonの基本ですね。
一応note.nkmk.me
button = QPushButton("Click me")
前回のLabel同様に、UIパーツあたります。
QPushButtonはMAYAでいう、cmds.button()と同じです。
Pyside側のボタンを設定するメソッドです。
()中は、ボタンに表示される文字列です。
後から変更する場合は
button.setText("Push!Push!")
とします。
これでボタンの表示が変更されます。
言葉通りですが、ボタンが格納されている変数に対して
【setText】メソッドで中身を替えています。
ボタンにアイコンを仕込む時は
button.setIcon(QIcon('D:/button.jpg'))
とします。
setIconでアイコンをセットする命令を出し()内で
アイコンのファイルを指定しています。
※青い画像を指定しています。
色を変える時は、スタイルシートを使いますが
長くなるので後述します。
button.clicked.connect(sayHello)
ボタンが押された。という変更を取得し、押されたあと
関数【sayHello】に接続しています。
要はボタン押したら、関数実行しますね。って事。
MAYAだと、button(command=Function)としますが
それと同じです。
最後の一行は前と同じなので割愛。
ボタンというパーツを読み込み
クリックされたら、関数実行する。
というかなりシンプルなものです。
はい。
こんな感じです。
関数を使ったぐらいですが、これが肝なので
書いてみました。
以上です。
Pyside勉強 その壱
Pyside自体は今までも使ってきました。
が、その場しのぎでずっと使ってきました。
数日前に数見たらPysideで作ったスクリプトが20個ほど。
ただ、これらはいろんなサンプルから作っただけなので
基本で躓くことが多く、結構時間が掛かってしまってました。
ちょうど2017年の暮頃に、モチオさんが
SiShelf
github.com
を開発されていて、それに影響受けて私もやろうと
メモしたやつが見つかったので、それを再度まとめつつ
どうせなら、うやむやにしてた箇所を、一から学びなおしたいと思います。
すべて記事にすることは難しいですが、一先ず最近セレクターを
複数個作ったので、この勉強でのゴールはPysideでセレクターを作ることにします。
では、初めにいつものハローワールドから。
https://wiki.qt.io/Hello_World_in_PySide/jahttps://wiki.qt.io/Hello_World_in_PySide/ja
こちらに記載しているコードで行います。
import sys from PySide.QtCore import * from PySide.QtGui import * app = QApplication(sys.argv) label = QLabel("Hello World") label.show() app.exec_() sys.exit()
MAYAで実行すると、エラーになります。
なんでおきているかというと、MAYAでは
起動時にPysideを起動しているので
多重起動になってますよ!
ってエラーになっているわけです。
なので、それを回避する為には、以下のようにします。
app = QApplication.instance()
文字通り、インスタンス化して起動させています。
起動すると、以下の感じになります。
無事起動できました。
起動はできましたが、一個一個が説明できないと
理解したことにならないと思うので
ここは一行ずつみていきます。
コード
import sys
Python のインタプリタ、実行環境などの処理をまとめた
モジュールになります。
ファイル、パス操作など、基本的なコマンド多いので
Pythonではよく使います。
from PySide.QtCore import * from PySide.QtGui import *
PysideのGUIやそのコネクション周りのモジュールを読み込む必要があります。
それらをアスタリスクで一括読み込みしています。
アスタリスクで読み込んでいるのは、手を抜いている証拠です苦笑
app = QApplication.instance()
前述したとおり。
インスタンスを作成しています。
label = QLabel("Hello World")
PysideのQLabelというテキストや画像を表示するためのモジュールを
使って括弧内の【Hello World】をテキストとして、描画する!
という設定を行いつつ、labelという変数に格納しています。
label.show()
前述した、ラベルと表示させるコマンドです。
MAYAのShowと同じ感じです。
app.exec_()
このコマンドで、アプリケーションを実行しています。
exec_()は実行コマンドなので、インスタンスを実行する。
ということです。
sys.exit()
スクリプトのプロセスを終了させます。
正直、上記の書き方では無くても動きます。
MAYAで動かす際には
from PySide.QtCore import * from PySide.QtGui import * label = QLabel("Hello World") label.show()
このコマンド郡だけで動きます。
これは前述しましたが、MAYA上で既にPysideが動いていますので
スクリプトエディタ上では
app.exec_() sys.exit()
この二つをスクリプト実行時に、裏で実行している為です。
こんな感じで一番初歩の部分から進めていき
10回ぐらい続けばいいなぁと思います。
以上です。
Jiggleについて
使おう使おうと思って、中々手が出なかった【Jiggle】デフォーマ。
今日というかさっき、テストしたので
メモ代わりに記載。
初めに【Jiggle】とは?
→揺れに当たる。
私はこれを見て、やりたいなぁと思いました。
これは直ジオメトリですが、骨などにも使えそうだなぁと。
そこでさっきやってみました。
まずは元がこれ
JiNoggle from tommy_on on Vimeo.
これにJiggleを設定したやつ。
Jiggle from tommy_on on Vimeo.
これはJiggle_pSphere1にJiggleデフォーマを設定し
次に適当な頂点位置にロケーター【Jiggle_locator1】を置いて
pointOnPolyConstraintを実行。
※このあたりは揺れの移動値が取れればなんでもいい。
Jiggle_pSphere1の子どもにロケータ【Jiggle_locator2】を配置。
そのロケータは上記のロケーター【Jiggle_locator1】に対して、ペアレントコンストしただけです。
このロケータがバインド骨とかをドリブンすれば
いいかな?と思います。
まぁ簡単ですが、とりあえず揺れたんでOKとします。
ちなみにJiggleの設定はこんな感じにしてます。
超適当です。
これをリグに仕込むのはこれから考えますが
Softbodyより軽ければ使いみちがあるかも。
あと、個人的には【SEN_A0527】さんのベルレのやつも気になってます。。。
ベルレを使った揺れ処理ノードを作ろう! - SEN_Aのじゆうちょう
とりあえず今回はここまで。
以上です。
膝パーツの組み方
よくある事例なんですが、いつも接続で?ってなるのでメモ。
画像みたいに、ひざにパーツがついてる時があります。
特にメカ系なんかに多いんですが…
これを足の骨だけでそのまま回すと
こうなるかと思います。
↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
このままだと、硬い物等で違和感が生じる為、骨を足して
処理を追加します。
※膝と同階層に
画像のように配置します。
ウェイトは繋がっていない限り、パーツに全振りします。
んで、この骨に対して接続を行います。
※デモの為、jointOrientをしていません汗・・・・
kneeの回転値Zを各【remapvalue】の【入力値】に接続し、出力値をnodeとしいう階層に接続してます。
この出力値は、追加した骨を拘束しており、ドリブンされています。
とりあえず、【TX】ノードの接続をみていきます。
基本的には画像の通り。
膝の回転値を変換して、最大は動かないように【0】にして最小の値を【-1】にしています。
このように他のチャンネルにも接続すると、
Knee_Rig from tommy_on on Vimeo.
このような挙動になります。
はじめはclampノードを使っていたんですが
clampだと、リミット時にカクッとなる為
代案を考えておりました。
いろんなデータを落としたりしてる中で、IKFKの切り替えで
【remapvalue】を使っている所を確認したので
試した所きれいにいったのでこれをつかってます。
先人達には感謝がつきませんね…
では…
NameSpaceをすべて削除
リグ組んでたり、アニメーションを読み込みしてたりすると
偶にネームスペースが邪魔になることがある。
その都度一々、ネームスペースエディタを開いていたら
面倒なので、マクロを作りました。
※家用。
# -*- encoding: utf-8 -*- import maya.cmds as cmds def main(): NameSpace = cmds.namespaceInfo(recurse=1,listOnlyNamespaces=1) NameSpace.remove(u'UI') NameSpace.remove(u'shared') if len(NameSpace) > 0: for i in NameSpace: cmds.namespace(mergeNamespaceWithRoot = True , removeNamespace = i) cmds.headsUpMessage( u'NameSpaceを削除しました', verticalOffset=20 ) else: print "NotNameSpace"
今後は指定したやつだけ残す機能とか付けたい。
以上です。