« C4D C.O.F.F.E.E. クラスを使うには…_03 | トップページ | C4D ユーザスクリプトでモーダルダイアログの学習-02 »

C4D ユーザスクリプトでモーダルダイアログの学習

以前にも取り上げたネタなので、知っている人は読み飛ばしてください。

ユーザスクリプトでのユーザからの入力をさせるにはダイアログを使いますね。

C4Dには、モーダルダイアログノンモーダル(モードレス)ダイアログがあります。

モーダルダイアログは、ユーザからの操作はダイアログだけになり、ダイアログを閉じるまでC4D機能の操作ができなくなります。

モードレスダイアログは、ダイアログを開いても、C4Dの機能も操作でき、C4Dレイアウトにも組み込みが可能です。

ただし、ユーザスクリプトで使用できるのは、モーダルダイアログのみです。

では、モーダルダイアログを使用するには、何をするのかと言うと…

GeModalDialogクラスを使います。

/*
乱暴ではありますが、ダイアログが開きます
2009.7.31
*/


main(doc , op)
{
  var dlg = new(GeModalDialog);

  dlg->Open(-1 , -1);
}

たったこれだけで、ダイアログが開きますが、何もできません。
ボタンも何もありません。
閉じるだけです。

Dialog_01

ダイアログは、開いて閉じるだけではなく…当たり前ですね…
ユーザからの入力をしてもらう為の物ですから、ボタンや入力フォームなどのアイテム(ウィジェット)を配置(レイアウト)が必要です。
ダイアログを開いたときの初期値。
入力された値をウィジェットからの読み込み等…

その作業をするには、GeModalDialogクラスのサブクラスを作り、機能を必要とするメンバを上書き(オーバーライド)をします。

※その前にクラスの使い方が知らない人は、必要最低限で良いのでクラスの学習をしてください。

ですから、上のスクリプトは、ダイレクトにGeModalDialogクラスを割り当ててもレイアウトなどの必要な機能を付けられません。

ダイアログ関連クラスの確認

GeBaseDialog
└ GeUserDialog
  └ GeDialog
    └ GeModalDialog

では、GeModalDialogクラスのサブクラスを作りましょう。

/*
GeModalDialogクラスのサブクラスを使って、ダイアログが開きます
2009.7.31
*/



//test_dialogクラス==========================================
class test_dialog:GeModalDialog
{
  public:
    test_dialog();//コンストラクタ
}

//コンストラクタ-----------------
test_dialog::test_dialog()
{
  super();
}


//main==========================================
main(doc , op)
{
  var dlg = new(test_dialog);

  dlg->Open(-1 , -1);
}

ここで、実行しても先ほどのスクリプトとは、何も変わりません。

Dialog_01

GeModalDialogクラスのOpen()メンバ関数は、ダイアログの開く画面上の位置(X , Y)を指定してダイアログを開きます。
ただし、(-1 , -1)はマウスの位置に開きます。

この時点では、コンストラクタは無くてもエラーになりません。

では、簡単なレイアウトを作ってみましょう。
項目と、入力フィールドを追加します…
あと、ダイアログのタイトルも設定しましょう。

ダイアログのレイアウトをするには、GeModalDialogクラスのスーパークラス、GeDialogクラスのCreateLayout()メンバ関数を使います。
test_dialogクラスでオーバーライドします。

/*
GeModalDialogクラスのサブクラスを使って、ダイアログが開きます
2009.7.31
*/



//test_dialogクラス==========================================
class test_dialog:GeModalDialog
{
  public:
    test_dialog();//コンストラクタ
    CreateLayout();//ダイアログのレイアウト
}

//コンストラクタ-----------------
test_dialog::test_dialog()
{
  super();
}

//ダイアログレイアウト-------------
test_dialog::CreateLayout()
{
 
}


//main==========================================
main(doc , op)
{
  var dlg = new(test_dialog);

  dlg->Open(-1 , -1);
}

CreateLayout()を追加しました。
まだ、外枠のみなので実行しても何も起こりません。

Dialog_01

ではCreateLayout()に追加していきます。
まずは、ダイアログのタイトル。
ダイアログの一番最初のクラスGeBaseDialogのSetTitle()メンバ関数で文字列を渡します。
ウィジェットは、GeBaseDialogクラスのサブクラスGeUserDialogクラスに全てあります。
項目と入力フィールドは、StaticTextとEditNumberを使いましょう。
引数は、ウィジェットのID番号と配置、大きさ等のウィジェット共通のものと、後は、そのウィジェットの特有なもの…

ウィジェットのIDは重ならないように割り振ってください。
このIDで管理されます。

//ダイアログレイアウト-------------
test_dialog::CreateLayout()
{
  SetTitle("test_dialogs");//ダイアログのタイトル設定

  AddStaticText(1 , 0 ,   0 , 0 , "項目" , 0);
  AddEditNumber(2 , 0 , 100 , 0);
}

これを追加して実行すると、ウィジェットが表示されてはいますが、予想していないレイアウトになっていると思いますが…

Dialog_02

ウィジェットをただ追加すると、下へと追加されるようですね。
これを意図した並びにするには、グループを使います。
このグループは単純で、グループ内で横に何づつ個並べるのか?
それとも、縦に何個づつ並べるのかを指定するだけです。
配置は、左寄せとか、右寄せ、中揃え等をウィジェットで指定できます…

ま、全てをいくつかのグループでまとめると綺麗に並べる事ができます。
グループもウィジェットと同じGeUserDialogクラスにあります。
グループは、開始と終わりを設定します。

//ダイアログレイアウト-------------
test_dialog::CreateLayout()
{
  SetTitle("test_dialogs");//ダイアログのタイトル設定

  AddGroupBeginV(10 , BFH_CENTER , 2 , "group" , 0);
    AddStaticText(1 , 0 ,   0 , 0 , "項目" , 0);
    AddEditNumber(2 , 0 , 100 , 0);
  AddGroupEnd();
}

Dialog_03

ウィジェットの引数を説明していませんでしたね。

AddStaticText()から。
編集不可のテキストで、入力フィールドの項目などに使います。
(ID , 配置 , 幅 , 高さ , 文字 , border)

AddEditNumber()は、数値入力用フィールドです。
文字の場合はAddEditText()を使用します。
(ID , 配置 , 幅 , 高さ)
ほとんどのウィジェットの基本は、(ID , 配置 , 幅 , 高さ)です。

AddGroupBeginV()は、垂直に追加されるグループです。
(ID , 配置 , 横に並べる個数 , 名前 , グループフラグ)
水平に追加するグループは、AddGroupBeginH()です。

AddGroupEnd()はグループの終わりです。
引数はありません。

引数で指定する幅や高さに0を使うと自動で入力されます。

ウィジェットの具体的な使い方は、後ほど…

さて、ダイアログと言うものは、ツールとユーザを対話させる機能です。
初期のダイアログは、実行しますか?それともキャンセルしますか?そんな物でした。
このダイアログにも、実行/キャンセルボタンを追加しましょう。

AddDlgGroup()を使います。

//ダイアログレイアウト-------------
test_dialog::CreateLayout()
{
  SetTitle("test_dialogs");//ダイアログのタイトル設定

  AddGroupBeginV(10 , BFH_CENTER , 2 , "group" , 0);
    AddStaticText(1 , 0 ,   0 , 0 , "項目" , 0);
    AddEditNumber(2 , 0 , 100 , 0);
  AddGroupEnd();

  AddDlgGroup(DR_DLGGROUP_OK | DR_DLGGROUP_CANCEL);
}

Dialog_04

これは、ウィジェットの通常のボタンではく、専用のボタンです。

GeModalDialogのサブクラスで開かれたダイアログが閉じた後、GeModalDialogのGetResult()メンバ関数で(OK/キャンセル)なのかを取得します。

括弧の中はビット演算です。
真ん中の縦棒は「タダの区切りマーク」ではありません。
※実は、始めて見た時に区切りと勘違いしたのは自分です。遥か昔の事ですが、照れますね…

DR_DLGGROUP_OKやDR_DLGGROUP_CANCELがエディタで明示されないのは、何故だろうか…

これで、簡単なダイアログのレイアウトができました。

でも、数値を入力しても、取得する機能がありません。
では、入力した数値を取得しましょう。

えぇ、その前に…
ダイアログが閉じられた後の、OK/キャンセルの分岐をします。

//main==========================================
main(doc , op)
{
  var dlg = new(test_dialog);

  dlg->Open(-1 , -1);
  if(!dlg->GetResult())return;//OKボタン以外で閉じられた場合は終了

  //以下、OKボタンが押されてダイアログが閉じられた場合の処理を…
  println("ok");
}

実行して、OKボタンが押されると、コンソールに"ok"が表示されます…

さて、動作チェックをしてみよう。
動きに問題がある様だけど…気づいただろうか?

OKを押すと"ok"が表示されます。

ok

キャンセルを押すと何も表示されません。

実行して、ただOKやキャンセルを押しているだけだと気付けません。
入力フィールドEditNumberに数値を入力してOKを押してみよう。

何も表示されません。OKを押したにもかかわらず…キャンセル扱いです。
理由か見当たりません。

偶然にも、不具合の要因を見つけたのですが、理由がわかりません。

//ダイアログレイアウト-------------
test_dialog::CreateLayout()
{
  SetTitle("test_dialogs");//ダイアログのタイトル設定

  AddGroupBeginV(10 , BFH_CENTER , 2 , "group" , 0);
    AddStaticText(1 , 0 ,   0 , 0 , "項目" , 0);
    AddEditNumber(3 , 0 , 100 , 0);
  AddGroupEnd();

  AddDlgGroup(DR_DLGGROUP_OK | DR_DLGGROUP_CANCEL);
}

EditNumberのID:2を別の物、3に変えました。

これで、入力フィールドに数値入力してOKを押してもキャンセル扱いにはならず…
正常に動いているようです。"ok"を表示しました。

これは、自分のC4Dだけなのか…?
全く分かりません。

ID番号が若すぎて、ダイアログを構成する他の何かと衝突しているのだろうか?
ウィジェットのIDは、SDKの例コードと同じように、1,000番代や10,000番代を使った方が良いのだろうか?
原因を突き止める為にSDK内を探すのは大変なので保留し、IDを大きい番号を使います。

//ダイアログレイアウト-------------
test_dialog::CreateLayout()
{
  SetTitle("test_dialogs");//ダイアログのタイトル設定

  AddGroupBeginV(10000 , BFH_CENTER , 2 , "group" , 0);
    AddStaticText(10001 , 0 ,   0 , 0 , "項目" , 0);
    AddEditNumber(10002 , 0 , 100 , 0);
  AddGroupEnd();

  AddDlgGroup(DR_DLGGROUP_OK | DR_DLGGROUP_CANCEL);
}

全く問題が無いように動いています。

では、ダイアログに入力された値を取得しコンソールに表示してみよう。

まず、入力された値を保持する為の変数を追加しましょう。

test_dialogクラスにメンバ変数を追加します。

//test_dialogクラス==========================================
class test_dialog:GeModalDialog
{
  public:
    var valu;//入力数値用変数

    test_dialog();//コンストラクタ
    CreateLayout();//ダイアログのレイアウト
}

追加した変数は、privateでも良いでしょう…
ただし、クラスからの入出力メンバ関数が必要になりますが…
グローバル変数にする事も良いでしょう。

では、一応コンストラクタで、初期化しましょう。
適当な値で、初期化しました。0でもかまいません。

//コンストラクタ-----------------
test_dialog::test_dialog()
{
  super();
  valu = 100;
}

さて、まずは、ダイアログに入力された値を、このメンバ変数valuに格納します。

入力された値を格納するには、ダイアログが閉じられる時に取得します。
ダイアログ関連のクラスには、ダイアログが閉じられる時に呼ばれるメンバ関数があります。

GeDialogクラスのAskClose()メンバ関数です。
では、test_dialogに組み込みオーバーライドします。

//test_dialogクラス==========================================
class test_dialog:GeModalDialog
{
  public:
    var valu;//追加します

    test_dialog();//コンストラクタ
    CreateLayout();//ダイアログのレイアウト
    AskClose();//ダイアログが閉じられる時に呼ばれる
}

では、AskClose()を定義します。

//ダイアログを閉じたとき-----------------
test_dialog::AskClose()
{
  valu = GetFloat(10002);
}

GetFloat()で、メンバ変数にウィジェットから値を取得します。

GetFloat()は、GeBaseDialogクラスのメンバ関数で、指定したIDのウィジェットの入力された値を返します。
レイアウトで入力の為に使ったEditNumberのIDは10002です。

これで、ダイアログが閉じられると、AskClose()が呼ばれEditNumberから値をメンバ変数valuで確保されます。

では確認のために、ダイアログがOKボタンで閉じられた後に表示される"ok"に続いて数値を表示してみよう。

//main==========================================
main(doc , op)
{
  var dlg = new(test_dialog);

  dlg->Open(-1 , -1);
  if(!dlg->GetResult())return;//OKボタン以外で閉じられた場合は終了

  //以下、OKボタンが押されてダイアログが閉じられた場合の処理を…
  println("ok");
  println("dlg::valu = " , dlg->valu);
}

これで、OKボタンを押してダイアログが閉じられると、コンソールには"ok"を表示した後…
入力された数値が表示されます。

Dialog_05

ok
dlg::valu = 60.000000

ウィジェットのEditNumberは、プリセットで0~100までになっているのだろうか?

Dialog_06

ok
dlg::valu = 100.000000

では、先ほど入力用の変数をコンストラクタの所で初期化しました。
が、入力用EditNumberには反映されていません。
では、この値がダイアログを開いたときに、入力されるようにします。

ダイアログを開いた時に呼ばれるメンバ関数、GeDialogクラスのInit()メンバ関数を使います。

test_dialogクラスにInit()メンバ関数を追加しオーバーライドします。

//test_dialogクラス==========================================
class test_dialog:GeModalDialog
{
  public:
    var valu;//追加します

    test_dialog();//コンストラクタ
    CreateLayout();//ダイアログのレイアウト
    AskClose();//ダイアログが閉じられる時に呼ばれる
    Init();//ダイアログが開かれる時に呼ばれる
}

Init()メンバ関数の定義をします。

//ダイアログが開いた時に呼ばれる-----
test_dialog::Init()
{
  SetFloat(10002 , valu , 0 , 1000 , 1);
}

SetFloat()は、指定したIDのウィジェットに数値を設定します。
引数は(ID , 数値 , 最小値 , 最大値 , ステップ値)です。

これで実行すると、コンストラクタで設定した初期値をダイアログが開いた時に表示します。

Dialog_07

最後に完成した全体を…

/*
GeModalDialogクラスのサブクラスを使って、ダイアログが開きます
2009.7.31
*/


//test_dialogクラス==========================================
class test_dialog:GeModalDialog
{
  public:
    var valu;//追加します

    test_dialog();//コンストラクタ
    CreateLayout();//ダイアログのレイアウト
    AskClose();//ダイアログが閉じられる時に呼ばれる
    Init();//ダイアログが開かれる時に呼ばれる
}

//コンストラクタ-----------------
test_dialog::test_dialog()
{
  super();
  valu = 100;
}

//ダイアログが開いた時に呼ばれる-----
test_dialog::Init()
{
  SetFloat(10002 , valu , 0 , 1000 , 1);
}

//ダイアログレイアウト-------------
test_dialog::CreateLayout()
{
  SetTitle("test_dialogs");//ダイアログのタイトル設定

  AddGroupBeginV(10000 , BFH_CENTER , 2 , "group" , 0);
    AddStaticText(10001 , 0 ,   0 , 0 , "項目" , 0);
    AddEditNumber(10002 , 0 , 100 , 0);
  AddGroupEnd();

  AddDlgGroup(DR_DLGGROUP_OK | DR_DLGGROUP_CANCEL);
}

//ダイアログを閉じたとき-----------------
test_dialog::AskClose()
{
  valu = GetFloat(10002);
}


//main==========================================
main(doc , op)
{
  var dlg = new(test_dialog);

  dlg->Open(-1 , -1);
  if(!dlg->GetResult())return;//OKボタン以外で閉じられた場合は終了

  //以下、OKボタンが押されてダイアログが閉じられた場合の処理を…
  println("ok");
  println("dlg::valu = " , dlg->valu);
}

今回はダイアログの学習なので、これで終わりです。
ここから何か処理をするスクリプトを作るのであれば、main()関数のprintln("ok");以下を削除して続きの処理を書いてください。

更にダイアログの学習は続く。

|

« C4D C.O.F.F.E.E. クラスを使うには…_03 | トップページ | C4D ユーザスクリプトでモーダルダイアログの学習-02 »