« C4D Python R13 テスト:クローナーで複製したオブジェクトをブールで合体… | トップページ | C4D Python R13 テスト:ダイアログの一般的なウィジェットの確認 »

C4D Python R13 テスト:スクリプトでモーダルダイアログをなんとなく使ってみる…

久しぶりで…
全く時間が取れなくて…
C4D R13は、盛り上がっているだろうか?

さて今回は、C4D Pythonスクリプトでモーダルダイアログを使ってみる。

以前書いた記事では、1つのテキスト入力だけのシンプルなダイアログ…
C4D Python R12 テスト:シンプルな入力ダイアログを使ってみる。
c4d.guiモジュールのInputDialog()関数を試してみました。

Inputdialog_01

今回は、c4d.guiモジュールのGeDialogクラスをスクリプトで試してみる。

今回は、こんなスクリプトから始めます…

Python
'''
スクリプトでモーダルダイアログを使用する為のテスト
'''

import c4d
from c4d import gui


#ダイアログのタイトルと初期値
dialog_title = 'TestScript'
v1 = 10
v2 = 25
v3 = 42


#ダイアログの設定
class MyDialogs(gui.GeDialog):
    #ダイアログレイアウトの設定
    def CreateLayout(self):
        self.SetTitle(dialog_title)
       
        #ウィジェット等の設定
        self.GroupBegin(10000, c4d.BFH_CENTER, 2)
       
        self.AddStaticText(1000, c4d.BFH_CENTER, 0, 0, 'value 1', c4d.BORDER_NONE)
        self.AddEditNumberArrows(1001, c4d.BFH_CENTER, 100, 0)
        self.AddStaticText(1002, c4d.BFH_CENTER, 0, 0, 'value 2', c4d.BORDER_NONE)
        self.AddEditNumberArrows(1003, c4d.BFH_CENTER, 100, 0)
        self.AddStaticText(1004, c4d.BFH_CENTER, 0, 0, 'value 3', c4d.BORDER_NONE)
        self.AddEditNumberArrows(1005, c4d.BFH_CENTER, 100, 0)
       
        self.GroupEnd()
       
        #OK/キャンセルボタンの設置
        self.AddDlgGroup(c4d.DLG_OK|c4d.DLG_CANCEL)
        return True



def Execute():
    #ここに実行する処理を書いてみる。
    print 'exe'


def main():
    dlg = MyDialogs()
    dlg.Open(c4d.DLG_TYPE_MODAL)
    c4d.StopAllThreads()
    Execute()


if __name__=='__main__':
    main()
    c4d.EventAdd()

不完全ですが実行するとマウスポインタの位置にダイアログが現れます。

Pythondialog_01_2

エディットボックス内の値の初期値は、まだ処理していないので 0 のままです。

C.O.F.F.E.E.でも以前から使用していたOK/キャンセルボタンを追加するGeUserDialog::AddDlgGroup()メンバ関数。
このメンバ関数に相当するメソッドがc4d Python R12にはありませんでしたが…
c4d Python R13からはguiモジュールのGeDialogクラスに追加されたようで…

確か…C.O.F.F.E.E.では、OK/キャンセルボタンを押すと自動的にダイアログを閉じてくれたと思いますが、c4d Pythonではダイアログが閉じません。
今の状態だとダイアログの右上隅の閉じるボタンを押すしかありません。

ボタン等のGUIのパーツはウィジェットと言い、ウィジェットIDは以下のように設定しました。
OK/キャンセルボタンのAddDlgGroup()を使用するとOKボタンのIDは1…
キャンセルボタンのIDは2に自動的に設定されます。

逆に言えば…OK/キャンセルボタンにID:1、2が割り当てられているので、他のウィジェットIDに使わないように…

Pythondialog_02

MyDialogクラスに次のメソッドを追加します。

  • AskClose() ダイアログが閉じられたときに実行される。
  • Command() ウィジェット等を押したときに実行される。
  • InitValues() ダイアログが開かれた時に実行される。
Python
#上記のスクリプトのMyDialogクラスへの追加分。

    #ダイアログが閉じる時に実行される
    def AskClose(self):
        print 'ac'
        return False
   
    #ダイアログのボタンを押したりしたときに実行される
    def Command(self, id, msg):
        print 'com'
        return True
   
    #ダイアログが開かれた時に実行される
    def InitValues(self):
        print 'iv'
        return True

コンソールを開いて実行すると、ダイアログが開かれコンソールには’iv’が表示されます。
OK/キャンセルボタンを押したり値を変更すると、そのつど'com'が表示されます。
ダイアログ右上隅の閉じるボタンを押すと'ac'が表示されスクリプトは終了します。

上記が確認できれば問題無いと思います。

では、初期値を反映させてみよう。
用意したグローバル変数v1/v2/v3をダイアログのvalue1/value2/value3に設定する。
ダイアログが開かれた時に実行されるInitValue()メソッド内でGeDialog.SetLong()メソッドを使って処理をする。

Python
    #ダイアログが開かれた時に実行される
    def InitValues(self):
        self.SetLong(1001, v1, 0, 100, 1)
        self.SetLong(1003, v2, 0, 100, 2)
        self.SetLong(1005, v3, 0, 100, 5)
        return True

SetLong()に設定した引数は、ウィジェットID、数値、最小値、最大値、増減値です。
最小値と最大値を指定すると、入力の範囲を制限できます。
増減値は入力ボックス右の上下の三角で増減させたときのステップ値です。入力ボックスに直接入力する場合には関係ありません。

実行すると以下のように数値が入力された状態でダイアログが開きます。

Pythondialog_03

では、OK/キャンセルボタンが押されたらダイアログを閉じましょう。
ウィジェットでイベントが起こると実行されるCommand()メソッドを以下のように変更します。
OK/キャンセルボタンのid:1または2の場合、MyDialogのダイアログをClose()メソッドで閉じます。

Python
    #ダイアログのボタンを押したりしたときに実行される
    def Command(self, id, msg):
        if id == 1 or id == 2:
            self.Close()
        return True

実行してOK/キャンセルボタンを押したら、ダイアログが閉じるのを確認できたなら問題ありません。

ダイアログが閉じる時に実行されるAskClose()メソッドを以下のように変更しました。
ダイアログで入力した数値をグローバル変数v1/v2/v3へ戻しておきます。
global v1,v2,v3なしにv1/v2/v3に値を入力すると、v1/v2/v3はローカル変数になります。
AskClose()メソッドのreturnで返す値をTrueにすると、ダイアログが閉じられなくなるので注意が必要だ…

Python
    #ダイアログが閉じる時に実行される
    def AskClose(self):
        global v1, v2, v3
        v1 = self.GetLong(1001)
        v2 = self.GetLong(1003)
        v3 = self.GetLong(1005)
        return False

グローバル変数v1/v2/v3が変更されているのか確認してみる。
main()関数を以下のように変更してみた。

Python
def main():
    dlg = MyDialogs()
    dlg.Open(c4d.DLG_TYPE_MODAL)
    print 'v1', v1
    print 'v2', v2
    print 'v3', v3

実行してダイアログに適当な数値を入力してダイアログを閉じます。

Pythondialog_04

v1 20
v2 60
v3 55

コンソールに変更した数値が表示されれば問題ありません。

今現在、閉じるボタンとOK/キャンセルボタンを押すとダイアログが閉じますが…
ダイアログが閉じた後、OKボタンが押されたのかは分かりません…
C.O.F.F.E.E.には、GeModalDialog::GetResult()があるので、OKボタンが押されたのかが取得できますが…
勉強不足なので、それに相当するc4d Pythonのメソッドがあるのか無いのか分かりません…
なので手っ取り早く、MyDialogクラスにOKボタン用の属性resを追加して…

Python
#ダイアログの設定
class MyDialogs(gui.GeDialog):
    res = False
   
    #ダイアログレイアウトの設定
    def CreateLayout(self):

MyDialog.Command()メソッドを次のように変更。
OKボタンが押されると属性resにTrueが代入されます。

Python
    #ダイアログのボタンを押したりしたときに実行される
    def Command(self, id, msg):
        if id == 1:self.res = True
        if id == 1 or id == 2:
            self.Close()
        return True

さらにmain()関数も以下のように変更します。

Python
def main():
    dlg = MyDialogs()
    dlg.Open(c4d.DLG_TYPE_MODAL)
    if dlg.res:
        c4d.StopAllThreads()
        Execute()

スクリプトでのダイアログは、C.O.F.F.E.E.同様モーダルだけだと思いますが…
他は試した事はまだない。
ダイアログを開くときにモーダルなのでDLG_TYPE_MODALを指定して…

これで、OKボタンを押したときだけExecute()関数が実行されます。
後は、このスクリプトで処理する本体をExecute()関数内に書けばいいのだ…

で…
Execute()で何させようか…

でも、その前に確認したい事がある…
ダイアログを閉じた後に、入力用のウィジェットから値を読み出せるのか?
で、main()関数をこんな具合に追加してみた…

Python
def main():
    dlg = MyDialogs()
    dlg.Open(c4d.DLG_TYPE_MODAL)
    if dlg.res:
        c4d.StopAllThreads()
        Execute()
    print 'ID:1001', dlg.GetLong(1001)
    print 'ID:1003', dlg.GetLong(1003)
    print 'ID:1005', dlg.GetLong(1005)

実行して…

Pythondialog_03

そのまま、OK/キャンセルボタンを押してみる。

ID:1001 0
ID:1003 0
ID:1005 0

3個とも0なのね…
もう一度実行して、右上隅の閉じるボタンを押してみた…

ID:1001 10
ID:1003 25
ID:1005 42

どうしてなのか…
閉じるボタンで閉じたときは、ダイアログが閉じた後でも読み出せるね…
理由は解らない…
Close()を実行したのかの違いだけ…
ま、AskClose()でウィジェットから値を取得すれば良いだけの事なんだけど…

では、Execute()を…
今回は何かを処理するために作っているわけではないので、適当に…
立方体をシーンに追加してみよう…面白くない?
通常の追加ではなく、選択オブジェクトの子オブジェクトとして追加してみよう。
ま、とりあえずね…

Execute()を以下のように変更。

Python
def Execute():
    obj = c4d.BaseObject(c4d.Ocube)
    obj[c4d.PRIM_CUBE_LEN] = c4d.Vector(v1, v2, v3)
    obj.SetBit(c4d.BIT_ACTIVE)
   
    if op:op.DelBit(c4d.BIT_ACTIVE)
   
    doc.InsertObject(obj, op, None)
    return

実行すると、選択オブジェクトの子オブジェクトとして立方体(最大サイズ:100×100×100)が追加されます。
選択オブジェクトが無い場合は、オブジェクトマネージャのトップに追加されます。

実行前の選択オブジェクトを選択解除して、追加したオブジェクトを選択オブジェクトにする…

オブジェクトの選択の設定は、BaseObject.SetBit()やBaseObject.DelBit()以外にも、BaseDocument.SetActiveObject()があるんだね…
こんな具合…

Python
def Execute():
    obj = c4d.BaseObject(c4d.Ocube)
    obj[c4d.PRIM_CUBE_LEN] = c4d.Vector(v1, v2, v3)
   
    doc.InsertObject(obj, op, None)
    doc.SetActiveObject(obj, c4d.SELECTION_NEW)
    return

SetBit()はオブジェクトに直接働きかけるのでシーンに追加されていなくても設定できますが…
SetActiveObject()はシーンに働きかけるので、オブジェクトはシーンに追加されていなければならないだろう。
そう思ったのだけど…

Python
def Execute():
    obj = c4d.BaseObject(c4d.Ocube)
    obj[c4d.PRIM_CUBE_LEN] = c4d.Vector(v1, v2, v3)
    doc.SetActiveObject(obj, c4d.SELECTION_NEW)
   
    doc.InsertObject(obj, op, None)
    return

シーンに追加する前に実行してもエラーが起こらないのだ…
どう言う事だろう…わからないね…

それは、さて置き…

複数選択されていたら?
グローバル変数opは複数選択には対応していないので…
グローバル変数docから全ての選択オブジェクトを取得する…
BaseDocument.GetActiveObjects()でね…

取得した全ての選択オブジェクトの子に立方体を追加してみよう。

Python
def Execute():
    a_objs = doc.GetActiveObjects(True)
    if a_objs == []:a_objs = [None]
   
    for id, a_obj in enumerate(a_objs):
        obj = c4d.BaseObject(c4d.Ocube)
        obj[c4d.PRIM_CUBE_LEN] = c4d.Vector(v1, v2, v3)
        if id == 0:
            mode = c4d.SELECTION_NEW
        else:
            mode = c4d.SELECTION_ADD
       
        doc.SetActiveObject(obj, mode)
       
        doc.InsertObject(obj, a_obj, None)
    return

選択オブジェクトをBaseDocument.SetActiveObject()を使って複数設定するには、最初の選択オブジェクトにモードをSELECTION_NEWを使い、他のオブジェクトの選択を解除し…
2個目からはSELECTION_ADDを使って追加します。

でも、SetBit()/DelBit()の方が良いのかなぁ…

Python
def Execute():
    a_objs = doc.GetActiveObjects(True)
    if a_objs == []:a_objs = [None]
   
    for a_obj in a_objs:
        obj = c4d.BaseObject(c4d.Ocube)
        obj[c4d.PRIM_CUBE_LEN] = c4d.Vector(v1, v2, v3)
        obj.SetBit(c4d.BIT_ACTIVE)
        if a_obj:a_obj.DelBit(c4d.BIT_ACTIVE)
       
        doc.InsertObject(obj, a_obj, None)
    return

えぇ、どっちが良いのかな…
まぁ、いいや…

今回は、モーダルダイアログをなんとなく作って見るんだったけど…
趣旨が…

それも、まぁ、いいかぁ…

最後に、「取り消し」のUndoを…
BaseDocument.StartUndo()/AddUndo()/EndUndo()を使うのだ…

Python
def Execute():
    a_objs = doc.GetActiveObjects(True)
    if a_objs == []:a_objs = [None]
   
    doc.StartUndo()
   
    for a_obj in a_objs:
        obj = c4d.BaseObject(c4d.Ocube)
        obj[c4d.PRIM_CUBE_LEN] = c4d.Vector(v1, v2, v3)
        obj.SetBit(c4d.BIT_ACTIVE)
        if a_obj:
            a_obj.DelBit(c4d.BIT_ACTIVE)
            doc.AddUndo(c4d.UNDOTYPE_DEACTIVATE, a_obj)
       
        doc.InsertObject(obj, a_obj, None)
        doc.AddUndo(c4d.UNDOTYPE_NEW, obj)
    doc.EndUndo()
    return

取り消しは、変更箇所を記録する…
StartUndo()で取り消しの記録を始め、AddUndo()で変更箇所を記録しEndUndo()で記録を終了する。
選択解除は、UNDOTYPE_DEACTIVATEを…
シーンへの追加は、UNDOTYPE_NEWを指定してUndoを記録する。

これで、一通りできただろうか…?
やっぱり、趣旨が変わってしまったような…

最後に、変更した全てを示しておきます。

Python
'''
スクリプトでモーダルダイアログを使用する為のテスト
'''

import c4d
from c4d import gui
from c4d import documents


#ダイアログのタイトルと初期値
dialog_title = 'TestScript'
v1 = 10
v2 = 25
v3 = 42


#ダイアログの設定
class MyDialogs(gui.GeDialog):
    res = False
   
    #ダイアログレイアウトの設定
    def CreateLayout(self):
        self.SetTitle(dialog_title)
       
        #ボタン等の設定
        self.GroupBegin(10000, c4d.BFH_CENTER, 2)
       
        self.AddStaticText(1000, c4d.BFH_CENTER, 0, 0, 'value 1', c4d.BORDER_NONE)
        self.AddEditNumberArrows(1001, c4d.BFH_CENTER, 100, 0)
        self.AddStaticText(1002, c4d.BFH_CENTER, 0, 0, 'value 2', c4d.BORDER_NONE)
        self.AddEditNumberArrows(1003, c4d.BFH_CENTER, 100, 0)
        self.AddStaticText(1004, c4d.BFH_CENTER, 0, 0, 'value 3', c4d.BORDER_NONE)
        self.AddEditNumberArrows(1005, c4d.BFH_CENTER, 100, 0)
       
        self.GroupEnd()
       
        #OK/キャンセルボタンの設置
        self.AddDlgGroup(c4d.DLG_OK|c4d.DLG_CANCEL)
        return False
   
    #ダイアログが閉じる時に実行される
    def AskClose(self):
        global v1, v2, v3
        v1 = self.GetLong(1001)
        v2 = self.GetLong(1003)
        v3 = self.GetLong(1005)
        return False
   
    #ダイアログのボタンを押したりしたときに実行される
    def Command(self, id, msg):
        if id == 1:self.res = True
        if id == 1 or id == 2:
            self.Close()
        return True
   
    #ダイアログが開かれた時に実行される
    def InitValues(self):
        self.SetLong(1001, v1, 0, 100, 1)
        self.SetLong(1003, v2, 0, 100, 2)
        self.SetLong(1005, v3, 0, 100, 5)
        return True


def Execute():
    a_objs = doc.GetActiveObjects(True)
    if a_objs == []:a_objs = [None]
   
    doc.StartUndo()
   
    for a_obj in a_objs:
        obj = c4d.BaseObject(c4d.Ocube)
        obj[c4d.PRIM_CUBE_LEN] = c4d.Vector(v1, v2, v3)
        obj.SetBit(c4d.BIT_ACTIVE)
        if a_obj:
            a_obj.DelBit(c4d.BIT_ACTIVE)
            doc.AddUndo(c4d.UNDOTYPE_DEACTIVATE, a_obj)
       
        doc.InsertObject(obj, a_obj, None)
        doc.AddUndo(c4d.UNDOTYPE_NEW, obj)
    doc.EndUndo()
    return


def main():
    dlg = MyDialogs()
    dlg.Open(c4d.DLG_TYPE_MODAL)
    if dlg.res:
        c4d.StopAllThreads()
        Execute()


if __name__=='__main__':
    main()
    c4d.EventAdd()

参考になればよいのですが…

|

« C4D Python R13 テスト:クローナーで複製したオブジェクトをブールで合体… | トップページ | C4D Python R13 テスト:ダイアログの一般的なウィジェットの確認 »

コメント

コメントを書く



(ウェブ上には掲載しません)




« C4D Python R13 テスト:クローナーで複製したオブジェクトをブールで合体… | トップページ | C4D Python R13 テスト:ダイアログの一般的なウィジェットの確認 »