簡単なPySideプログラム

# -*- coding: utf-8 -*-

import maya.cmds as cmds
import maya.mel as mel

from PySide.QtCore import * 
from PySide.QtGui import * 
from shiboken import wrapInstance 
from maya import OpenMayaUI as omui 
from maya.app.general.mayaMixin import MayaQWidgetBaseMixin
from pprint import pprint

import maya.OpenMaya as OpenMaya
import maya.OpenMayaMPx as OpenMayaMPx

mayaMainWindowPtr = omui.MQtUtil.mainWindow()
mayaMainWindow = wrapInstance(long(mayaMainWindowPtr), QWidget) 

class CreatePolygonUI(QWidget):    
    def __init__(self, *args, **kwargs):        
        super(CreatePolygonUI, self).__init__(*args, **kwargs)
        
        #Parent widget under Maya main window        
        self.setParent(mayaMainWindow)        
        self.setWindowFlags(Qt.Window)   
        
        #Set the object name     
        self.setObjectName('CreatePolygonUI_uniqueId')        
        self.setWindowTitle('Create polygon')        
        self.setGeometry(50, 50, 250, 150)        
        self.initUI()        
        self.cmd = 'polyCone'

    def initUI(self):        
        #Create combo box (drop-down menu) and add menu items 
        self.combo = QComboBox(self)        
        self.combo.addItem( 'Cone' )        
        self.combo.addItem( 'Cube' )        
        self.combo.addItem( 'Sphere' )        
        self.combo.addItem( 'Torus' )        
        self.combo.setCurrentIndex(0)        
        self.combo.move(20, 20)        
        self.combo.activated[str].connect(self.combo_onActivated)        

        #Create 'Create' button
        self.button = QPushButton('Create', self)        
        self.button.move(20, 50)        
        self.button.clicked.connect(self.button_onClicked)                    

    #Change commmand string when combo box changes
    def combo_onActivated(self, text):        
        self.cmd = 'poly' + text + '()'            

    #Execute MEL command when button is clicked
    def button_onClicked(self):        
        mel.eval( self.cmd )    

class HelloWorld(OpenMayaMPx.MPxCommand):
    def __init__(self):
        OpenMayaMPx.MPxCommand.__init__(self)
 
    def doIt(self, argList):
        ui = CreatePolygonUI()
        ui.show()
        return
 
def creator():
    return OpenMayaMPx.asMPxPtr( HelloWorld() )
 
def initializePlugin(obj):
    plugin = OpenMayaMPx.MFnPlugin(obj, 'plugin', '1.0', 'Any')
    try:
        plugin.registerCommand('helloWorld', creator)
    except:
        raise RuntimeError
 
def uninitializePlugin(obj):
    plugin = OpenMayaMPx.MFnPlugin(obj)
    try:
        plugin.deregisterCommand('helloWorld')
    except:
        raise RuntimeError

プラグインとしてロードして、MELスクリプトからhelloWorldで呼び出すと、ダイアログが表示されプリミティブを作れます。

Lightwaveでボタンとボタンイベントを作る

簡単なボタンコントロールの作り方。

RESOURCE = '\04(k:"%s" c:LWPy)'

class HelloClass(lwsdk.IGeneric):
    def __init__(self, context):
        super(HelloClass, self).__init__()

    def func(self,ctl,data):
        ...

    def process(self, ga):
        ui = lwsdk.LWPanels()
        panel = self.ui.create(RESOURCE % 'test')

        btn1 = self.panel.button_ctl(RESOURCE % 'button label')
        btn1.set_event(self.func)

        panel.align_controls_vertical([btn1])
       
        if panel.open(lwsdk.PANF_BLOCKING | lwsdk.PANF_CANCEL) == 0:
            ui.destroy(self.panel)
            return lwsdk.AFUNC_OK

        return lwsdk.AFUNC_OK

ボタンのサンプルが無かったので。

Lightwaveで外部のモジュールを使いたい。

LightwaveGUIにPySideを使いたい、というモチベーションで調査開始。

LightwavePythonは環境パスのPythonではなく、

C:\Program Files\NewTek\LightWave_2015.2J\bin

(環境によるので注意)にあるdllを呼んでいるっぽい。
PySideを使おうと思ったのだけれど、当然入っていない。

LightwavePythonと本体にインストールされているPythonのバージョンが厳密に一致している必要がある。
LightwavePythonのバージョンは最初にLayoutツールを起動して、Utilities-Python-Open Consoleでウィンドウ起動したときの、一行目に出てくる。2015.2(64bit)版を利用している私の場合はPython2.7.7であった。
この2.7.7であることが重要で、3.4がNGなことは当然として2.7.11でもだめである。

2.7.10以降?ではpipがデフォルトインストールだが、2.7.7では入っていないため
https://pip.pypa.io/en/latest/installing/
よりget_pip.pyをダウンロード、

python get_pip.py

でpipのインストール。

一番わかりやすいnumpyをLightwave上から呼べるようにする。

pip install numpy

で、numpyのインストール。するとC:\Python27\Lib\site-packages\numpyにインストールされる。
このsite-packagesディレクトリには、Pythonのバージョンが一致している限りはLightwaveから参照されているため、Consoleからnumpyを呼び出してもちゃんと動くことが確認できる。

PySideについて

うまくいかない。pip install PySideで、PySideがインストール

import sys
from PySide.QtGui import *
from PySide.QtCore import *

app = QApplication(sys.argv)
# Create a Label and show it
label = QLabel("Hello World")
label.show()
# Enter Qt application main loop
app.exec_()

Pythonコンソールで試してみると
QtGUIコマンドを実行したところでLayoutツールがフリーズを起こす。

PySideはあきらめたほうがよさそう。。。

LightwaveのPythonプラグイン

LightwaveのPluginをPythonで作ってみている。

import lwsdk

__lwver__ = "11"

class hello_world(lwsdk.IGeneric):
    def __init__(self, context):
        super(hello_world, self).__init__()

    def process(self, generic_access):
        ui = lwsdk.LWPanels()
        panel = ui.create('Test')

        if panel.open(lwsdk.PANF_BLOCKING | lwsdk.PANF_CANCEL) == 0:
            ui.destroy(panel)
            return lwsdk.AFUNC_OK

        ui.destroy(panel)
        return lwsdk.AFUNC_OK

ServerTagInfo = [
    ( "Python Hello World!", lwsdk.SRVTAG_USERNAME | lwsdk.LANGID_USENGLISH ),
    ( "Hello, World!", lwsdk.SRVTAG_BUTTONNAME | lwsdk.LANGID_USENGLISH ),
    ( "Utilities/Python", lwsdk.SRVTAG_MENU | lwsdk.LANGID_USENGLISH )]
ServerRecord = { lwsdk.GenericFactory("LW_PyHelloWorld", hello_world) : ServerTagInfo }

[Utilities]タブの[Plugins]-[Add Plugins]で上記のPythonスクリプトを読み込む。
[Plugins]-[Adittional]に"Python Hello World!"というものが一番下?に追加されているので、呼び出すと、OKとCancelボタンがあるダイアログが表示される。

Inspectorにconstな変数を表示したい。

class Global : MonoBehaviour{
    const float fVariables = 1.0f;
}

のような定数をInspectorで調整したい。でも、プログラムからは変更できないようにしたい。
みたいな状況が起こったのでどうしようかいろいろ考えた。

そもそもInspector上でconstな変数やstaticな変数は表示できない。プロパティの編集もできない。
publicやprivateな変数しか現状はInspector上でいじれない、ということである。
余談ではあるが、プロパティをInspector上で編集させてくれ、という要望も却下されてしまったようである。
staticなパラメータをInspector上で編集できるようにするにはSingletonにすればよいらしい。

しかし、何も考えずにC++的なSingletonを実装したところ、MonoBehaviourはnewが禁止されていることが発覚。結構ここではまる人は多いようである。
で、調べたところ
http://naichilab.blogspot.jp/2013/11/unitymanager.html
のように実装すればいけることがわかった。

試したところ、無事うまくいった。

まとめ

    public class SingletonMonoBehaviour<T> : MonoBehaviour where T : MonoBehaviour
    {
        private static T instance;
        public static T Instance
        {
            get
            {
                if (instance == null)
                {
                    instance = (T)FindObjectOfType(typeof(T));

                    if (instance == null)
                    {
                        Debug.LogError(typeof(T) + "is nothing");
                    }
                }

                return instance;
            }
        }
    }

    class Global : SingletonMonoBehaviour<Global>
    {
        public float VARIABLES = 0.0f;
    }

してHierarchy上に空のGameObjectを配置、Globalをコンポーネントとして追加すれば、VARIABLESがインスペクターに表示されるはずである。

スクリプトから呼ぶときは

float variable = Global.Singleton.VARIABLES;

となる。

とはいえ、結局Instance経由で変数の変更はできてしまうので、staticではあってもconstではない状況。
なにかいい方法はないものか。。。

C#でconstant value

C++だと

#define CONSTANT_VALUE 1
const int CONSTANT_VALUE = 1;

のように書きたくなる定数ですが、C#の場合は

class Global{
    public const int CONSTANT_VALUE = 1;
}

とクラスのメンバにするのが良さそう。

自分で作ったクラスの場合は

public static readonly MyClass myClass = new MyClass();

とするのがよさげ。