2020年1月1日DAZ ScriptDAZ Script,DAZ Studio

前回の記事「ダイアログボックスに画像を表示する」では、ダイアログボックス上に画像を表示させました。

さて、ダイアログボックス上に画像を表示させるのなら、当然ボタンをクリックしたり、DAZ Studio上の状況変化に応じて表示内容を変化させたくなります。

クリック処理をクロージャで実装する

以前の記事「DAZ Scriptでウィンドウを表示する」でもクリック処理は実装しました。

今回はJavaScriptでよく用いられる「クロージャ」というテクニックを用いて、クリック処理を実装します。

次のようにPixmapオブジェクトを切り替える処理をclicked()シグナルに紐付けることで、ボタンをクリックするたびに画像を切り替えることができます。

function SwitchGen(_label, _xyz) {
    var label = _label;
    var xyz = _xyz;
    var index = 0;
    
    //XYZ画像を切り替えるクロージャ
    function inner() {
        index++;
        index = index % xyz.length;
        label.pixmap = xyz[index];
    }
    return inner;
}
(function () {
    var filepath = "C:/dazscript/images/local-user-product.png";
    var pix_X = "C:/dazscript/images/X.png";
    var pix_Y = "C:/dazscript/images/Y.png";
    var pix_Z = "C:/dazscript/images/Z.png";
    
    var dialog = new DzDialog;
    
    //ラベル要素(背景)
    var dialog_label = new DzLabel(dialog);
    var pixImage = new Pixmap(filepath);
    dialog_label.pixmap = pixImage;
    
    //ラベル要素2(XYZ画像)
    var pix_XYZ = [
        new Pixmap(pix_X),
        new Pixmap(pix_Y),
        new Pixmap(pix_Z)
    ];
    
    var dialog_label_xyz = new DzLabel(dialog);
    dialog_label_xyz.pixmap = pix_XYZ[0];
    dialog_label_xyz.x = 20;
    dialog_label_xyz.y = 50;
    
    //ボタン要素(XYZ画像の表示を切り替える。)
    var dialog_button = new DzPushButton(dialog);
    dialog_button.text = "switch dialog_label_xyz";
    
    //XYZ画像を切り替えるクロージャを取得
    var switch_xyz = SwitchGen(dialog_label_xyz, pix_XYZ);
    
    connect(dialog_button, "clicked()", switch_xyz);
    
    //ダイアログ設定
    dialog.width = pixImage.width;
    dialog.height = pixImage.height;
    dialog.exec();
})();

実行結果は次のようになります。
「switch dialog_label_xyz」ボタンをクリックするたびに「X」「Y」「Z」を切り替えます。
なお、PNG画像の透過設定はそのまま適用できます。

dialog_labelはただの背景として用意しただけですので、本流の処理とは関係ありません。dialog_label_xyzが今回メインとなる、切り替えに使用するラベルです。

前回と同様に、画像ファイルをPixmapオブジェクトに読み込み、DzLabel#pixmapに設定していきます。

Pixmapの配列としてpix_XYZ[]を生成しておき、dialog_label_xyz.pixmapには初期値としてpix_XYZ[0](Xの画像)を格納しておきます。

45行目のswitchGen関数が今回のポイントで、切り替え操作対象のラベル(今回はdialog_label_xyz)とPixmap配列のpix_XYZを渡し、それを用いて切り替え処理を行うinner関数を受け取ります。

受け取ったinner関数はswitch_xyzに格納しておき、これが「クロージャ」になります。

connect関数で、切り替え用ボタン(dialog_button)とシグナル(clicked())とコールバック関数(クロージャにしたinner関数ことswitch_xyz)を紐づけます。

exec関数でダイアログを表示したら、スクリプトは一旦停止します。

その後、「switch dialog_label_xyz」ボタンをクリックするたびにinner関数が呼ばれ、操作対象ラベルのpixmapプロパティに格納する画像が順次差し替えられます。

このときinner関数はクロージャにしてあるため、labelとindexとxyzの値は保持され、処理が終了しても値がリセットされることはありません。

ボタンをクリックするたびに順番にX→Y→Z→X…と表示することができます。

まとめ

JavaScriptでよく見られる「クロージャ」というテクニックについては、他に詳しく説明しているサイトが多数ありますので割愛します。

今回のポイントは、DAZ ScriptでもJavaScriptと同じようなテクニックが使えるということです。

これは便利な半面、例えばクロージャになるinner関数内でthisを使うと、上手くxyzを取得できないといった、JavaScript特有の制約もそのままDAZ Scriptでも発生してしまいます。

こういった制約については、すでにJavaScriptで様々なバッドノウハウがありますので、制約に遭遇するたびに検索して解決策を見つけても良いのですが、個人的にはTypeScriptを活用して、素直に組んだコードで開発するほうが、デバッグ環境もおぼつかないDAZ Scriptにおいては効果的だと思います。

先ほどのthisについても、TypeScriptのアロー関数式とclassを組み合わせることで回避可能です。

このやり方については、また別記事で改めて書こうと思います。

2020年1月2日DAZ ScriptDAZ Script,DAZ Studio

DAZ Scriptで開いたダイアログボックスに画像を表示するには、ラベルのpixmapプロパティにPixmapオブジェクトを格納し、ダイアログのexec()を実行します。
具体的には以下のようになります。

(function () {
    var filepath = "C:/dazscript/images/local-user-product.png";
    var dialog = new DzDialog;
    
    //ラベル要素
    var dialog_label = new DzLabel(dialog);
    var pixImage = new Pixmap(filepath);
    dialog_label.pixmap = pixImage;
    
    //ダイアログ設定
    dialog.width = pixImage.width;
    dialog.height = pixImage.height;
    
    dialog.exec();
})();

実行結果は次のようになります。

処理順序としては、まずDzDialogの表示するDzLabelをnewで生成します。
その後、new Pixmap()で「C:/dazscript/images/local-user-product.png」の画像ファイルを読み込み、DzLabelのpixmapに格納します。
あとは、dialogの大きさを調整して、dialog.exec()でダイアログを表示します。
今回はDzLayoutを使用していないためダイアログの大きさを自分で設定する必要があり、設定しない場合は最小サイズでダイアログが表示されてしまいます。

Document Centerにより詳しいサンプルもある[1]How can I display images in a dialog box[2]Simple Image Dialogのですが、スクリプトらしく手軽に表示するだけなら、上記で十分です。

ちなみにDocument Centerのサンプルの実行結果は次のようになります。

2020年1月1日DAZ ScriptDAZ Script,DAZ Studio

DAZ StudioのSceneペインに表示されているNodeや、PosingペインのPropertyは、それぞれにLabel情報とName情報を保持しています。またPropertyはNumericPropertyなどいくつかの種類に分かれており、それぞれ各設定値の取り出し方が異なります。

このため、DAZ Scriptのどのメソッドを使うとどの情報が取り出せるかが、メソッド名から推測しにくい状況にあります。

そこでとりあえず、「ウィンドウ上に見える情報を取得するためのメソッド」をまとめてみました。

No表示内容の概要対応するDAZ Scriptのメソッド
1ParameterのName
通常目にする機会はない情報。
DzProperty#getName()
DzNumericProperty#getName()
2ParameterのLabel
Parameterペインのリストとして表示される。
DzProperty#getLabel()
DzNumericProperty#getLabel()
3ParameterのPath
Parameterペインの階層構造と一致し、そのルートはSceneペインで選択しているNodeになる。
DzProperty#getPath()
DzNumericProperty#getPath()
4対象のParameterを、Parameterペイン上に表示する/しないの設定DzProperty#isHidden()
DzNumericProperty#isHidden()
5Parameterの数値変更可能/不可能DzProperty#isLocked()
DzNumericProperty#isLocked()
6Parameterの数値の制限のOn/Off
DzNumericPropertyクラスでしか設定できない[1]adjust property “Use Limits" in script [SOLVED]
DzNumericProperty#isClamped()
7親NodeのLabel
Sceceペイン上での表示と一致する。
DzNode#getLabel()
また下記のように、Property側からアクセスすることもできる。
DzProperty#getOwner().getLabel()
その他親NodeのName
通常、目にする機会はない情報。
DzNode#getName()
Labelと同様に、Property側からアクセスすることもできる。
DzProperty#getOwner().getName()

紛らわしいことに、Parameterの情報を格納しているクラスなのに、DzProperty~という名称になっています。

2020年1月1日DAZ ScriptDAZ Script,DAZ Studio,TypeScript

※本記事は、qiitaにも投稿しています。

https://qiita.com/lowpolysnow/items/a9bf84cb8fe31afe339b


以前の記事でTypeScriptでDAZ Scriptを書ける環境を整えたので、せっかくなのでクラスを利用してみたいと思います。

class Greeter {
    greeting: string;
    constructor(message: string) {
        this.greeting = message;
    }
    greet() {
        return "Hello," + this.greeting;
    }
}

(function () {
    let greeter = new Greeter("world");
    MessageBox.information(greeter.greet(), "doit()", "OK");
})();

トランスパイルすることで、次のようなJavaScriptがjsファイルで出力されます。 このjsファイルの拡張子をdsaに書き換えることで、DAZ Scriptとして利用できるようにします。 (以前の記事のように環境をつくておくと、自動で書き換えてくれます)

var Greeter = /** @class */ (function () {
    function Greeter(message) {
        this.greeting = message;
    }
    Greeter.prototype.greet = function () {
        return "Hello," + this.greeting;
    };
    return Greeter;
}());
(function () {
    var greeter = new Greeter("world");
    MessageBox.information(greeter.greet(), "doit()", "OK");
})();

実行結果は次のようになります。

TypeScriptのコードなど下記のサイトを参考にさせていただきました。 他のクラスの機能のサンプルも揃っているので、時間があるときにどこまでDAZ Scriptで使えるか試してみようと思います。

TypeScriptのClassについて – 公式ドキュメント日本語訳
https://mae.chab.in/archives/59843#post59843-1

2020年1月1日DAZ ScriptDAZ Script,DAZ Studio

※本記事は、qiitaにも投稿しています。

https://qiita.com/lowpolysnow/items/ef9897a8d3ca4a37c4cd


DAZ Scriptではウィンドウを表示する機能はなく、代わりにモーダルダイアログを利用します。

(function () {
    var dialog = new DzDialog;
    dialog.exec();
})();

実行結果は次のようになります。

ちなみにモーダレスダイアログ(開いている間もアプリを操作できるウィンドウ)は作ることが出来ません。[1]参照:how make a modalless dialog?

ダイアログ要素の追加

new DzDialogで生成したインスタンスを引数に取ることで、ダイアログには様々な要素を追加することが出来ます。 例えば、ラベルを追加するときは、DzLabelオブジェクトをnewし、ボタンを追加するときもDzButtonオブジェクトをnewします。

ラベル要素

(function () {
    var dialog = new DzDialog;

    //ラベル要素
    var dialog_label = new DzLabel(dialog);
    dialog_label.text = "label text";

    //ダイアログの大きさを設定(要素を追加すると、大きさを指定しないとデバッグで表示されません)
    dialog.width = 200;
    dialog.height = 100;

    dialog.exec();
})();

実行結果は以下のようになります。

ボタン要素

(function () {
    var dialog = new DzDialog;

    //ボタン要素
    var dialog_button1 = new DzPushButton(dialog);
    dialog_button1.text = "button1";

    //ダイアログの大きさを設定(要素を追加すると、大きさを指定しないとデバッグで表示されません)
    dialog.width = 200;
    dialog.height = 100;

    dialog.exec();
})();

実行結果は以下のようになります。

グループボックスやレイアウト設定まとめ

(function () {
    var dialog = new DzDialog;

    //各要素を縦に等配置するDzVBoxLayoutオブジェクトを設定します。
    var dialog_layout = new DzVBoxLayout(dialog);
    dialog_layout.autoAdd = true;
    dialog_layout.margin = 5;
    dialog_layout.spacing = 5;

    //ボタン要素
    var dialog_button1 = new DzPushButton(dialog);
    dialog_button1.text = "button1";

    //ラベル要素
    var dialog_label = new DzLabel(dialog);
    dialog_label.text = "label text";

    //グループボックス要素
    var dialog_button_group = new DzVGroupBox(dialog);
    dialog_button_group.title = "Button Group";
    dialog_button_group.columns = 1;

    //  グループボックス要素にボタンを追加します
    var dialog_button2 = new DzPushButton(dialog_button_group);
    dialog_button2.text = "&Exit";
    dialog.setRejectButton(dialog_button2);

    //ダイアログの大きさを設定(要素を追加すると、大きさを指定しないとデバッグで表示されません)
    dialog.width = 200;
    dialog.height = dialog.minHeight;

    dialog.exec();
})();

実行結果は以下のようになります。

ちなみに、DzVBoxLayoutオブジェクトを使用しないと、各要素がすべて上部に配置されてしまいます。 この作り方はQtScript由来のもので[2]参照:QVBoxLayout Class | Qt 4.8、次項でもそうですが、DAZ Scriptは基本的にQtScriptをベースとし、それにDAZ Studio特有の機能を載せていくという形でプログラムを組んでいくことになります。

クリックイベントと実行メソッドの紐づけ

ボタンをクリックしたとき、同じスクリプト内の別の関数が実行されるようにするには、Globalオブジェクトのconnectメソッドを使用します。(Globalオブジェクトについては別記事参照

connectメソッドはオブジェクトとイベントと実行する関数を紐付けます。JavaScriptのaddEventListener()のようなものだと思ってください。

下記の例では、オブジェクトに対し、シグナルというイベント(例ではclicked())を発行し、メソッドを実行します。

(function () {
    var dialog = new DzDialog;
    var doit_button = new DzPushButton(dialog);
    doit_button.text = "call doit()";
    connect(doit_button, "clicked()", doit);
    dialog.exec();
})();

function doit() {
    MessageBox.information("do it.", "doit()", "OK");
    return;
}

実行結果は以下のようになります。

「call doit()」ボタンをクリックすると、cliked()シグナルに紐づけられたdoit()関数が呼び出され、メッセージボックスが表示されます。

シグナルの考え方はQtScriptやそもそものC++ライブラリ「Qt」に由来するのですが、ちょうどいい参考資料がまだ見つからないので、また後日記述します。