2011-10-23

After Effectsユーザーのための、プログラミング入門 その10 後編 AE_Dialogs_due C#コントロール集 AEスクリプトのUIデザイン

After Effectsユーザーのための、プログラミング入門 その10 後編 AE_Dialogs_due C#コントロール集 AEスクリプトのUIデザイン

後編です。
今回はAE_Dialogs_dueを使う為に基本的なUI作成方法について投稿します。

AEスクリプトのUIについてはjavascript_tools_guide.pdfにかなり細かく説明されています。ただ、adobe CS汎用のマニュアルなのでAfter Effectsに関係あるところだけを簡単に紹介していきます(英語がわかれば直接読んでもらったほうがわかりやすいです)

あと、javascript_tools_guide.pdfもいつの間にかCS5対応に変わってましたので、今記事ではCS5用もので解説していきます。javascript_tools_guide.pdfは何故かサイトマップから消えてしまいましたが、googleで検索すると簡単に見つけることができます。

なお、以後javascript_tools_guide.pdfの事をtools_guideと表記します。

Window object

UIの基本となるウインドウ(Windows object)についてはtools_guideの第4章User-Interface ToolsのWindow object(P112)にかなり詳しく解説されています。

まずWindows objectを作成する基本構文は次のようになります。

new Window (type [, title, bounds, {creation_properties}]);


[]で囲まれた部分は省略可能という意味になりますがshow()関数で表示する前には最低限bounds/locationプロパティは設定しておかないと実行時エラーになります。

各パラメータは以下のとおりです。

type
作成するウィンドウのタイプを文字列で指定します。
指定できる文字列は以下の3種類です
  • dialog モーダルダイアログ(modal dialog box)
  • palette モードレスダイアログ(modeless dialog box)
  • window 通常のウィンドウ。動作はモードレスダイアログ
これ以外にもフローティングパレットもAfterEffectsはサポートしていますが、それはnew Windowで作成しません。詳細は後で説明します。

title
タイトルバーに表示する文字列。Window objectのtextプロパティの値と同じ。

bounds
boundsは、Bounds objectとして定義されていますが、実は単純な数値配列で、[left,top,right,bottom]のように4個の要素を持ち、ウィンドウのサイズを指定するものです。
ただ注意する点は、この数値はC#でいうClinentRectangleでタイトルバーや縁のフレームのサイズは含みません。ウィンドウのサイズではなくウィンドウ内のコントロールが描画される範囲のサイズになります。

creation_properties
creation_propertiesは、作成時に指定できる動作オプションで、

{maximizeButton:true,resizeable:true}


といった感じに複数のオプションを指定できます。
オプションについてはWindow objectでは以下のものが主に使われます。
  • resizeable ウィンドウのサイズを変更可能にする。
  • maximizeButton ウィンドウに最大化ボタンを付ける
  • minimizeButton ウィンドウに最小化ボタンを付ける
  • borderless 縁やタイトルバーのない形状にする。
他にもオプションがあるのでtools_guideを参照してみてください。

ウィンドウ作成を具体的なコードにすると

var w = new Window("dialog","Test Dialog",[100,100,200,200]);
w.show();


でダイアログが表示されます。

実際に使うときはスクリーンに対して絶対位置で表示されてしまって不便なのでcenter()関数で画面中央に強制表示させてしまうことが多いです。

var w = new Window("dialog","Test Dialog",[0,0,100,100]);
w.center();
w.show();



var w = new Window("dialog","Test Dialog",[0,0,100,100]);
w.opacity = 0.5;
w.center();
w.show();




コントロールとは?

コントロールとはボタンのように画面に表示される要素(エレメント)の事です。
AfterEffectsContorol objectには以下のものが有ります。
  • button
  • checkbox
  • dropdownlist
  • edittext
  • group
  • iconbutton
  • image
  • listbox
  • progressbar
  • radiobutton
  • scrollbar
  • slider

あとTools Guideには以下のものも表記されていますが、After Effectsで使用可能か未確認です。
AE_Dialogs_dueは対応させていないコントロールです。

  • flashplayer
  • tab
  • tabbedpanel
  • treeview



これらのコントロールはWinodw objectやPanel/Groupのようにコンテナを持つオブジェクトのadd()関数で追加と言う形で設定できます。

windowOrContainerObj.add (type [, bounds, text, { creation_props } ]);


type
コントロールの種類の文字列
bounds
コントロールの位置サイズ。親オブジェクトの基準点からの相対位置
text
コントロールに表示される文字列。種類によって無いものもある
creation_props
作成時に指定できるオプション
これは追加するコントロールによって変わります。tools_guideに詳しく解説されています。ただ、若干用途が不明、設定しても変化がないオプションがあり要注意です。
AE_Dialogs_dueでも、主だったものは指定できるようになっています。

Boundsについて

コントロールの位置・サイズを指定する Bounds objectは単純に4個の要素を持つ数値の配列になります。[左,上,右,下]と言った順に数値が並んでいます。
C#のRectangleだと[左,上,横幅,縦幅]なので、僕はたまに勘違いします。注意しましょう。

Boundsは親オブジェクトの基本位置(左上)からの相対位置となります。Panel/Group objectにアイテムを追加するときには注意してください。

Button イベントと変数のスコープ

まず簡単にボタンを作ってみます。

var w = new Window("dialog","Button Test",[0,0,150,50]);
w.center();
var b = w.add("button",[10,10,150-10,50-10],"ボタン");
w.show();


これにイベントを追加します。

var name = "このダイアログ";
var w = new Window("dialog","Button Test",[0,0,150,50]);
w.center();
var b = w.add("button",[10,10,150-10,50-10],"ボタン");
b.onClick = function(){alert(name);}
w.show();


onClickイベントに無名関数でイベントを追加しています。

今度はこれを関数化してみます。変数はあえて全てthisで宣言します。

function showDialog()
{
	this.name = "このダイアログ";
	this.w = new Window("dialog","Button Test",[0,0,150,50]);
	this.w.center();
	this.b = this.w.add("button",[10,10,150-10,50-10],"ボタン");
	this.b.onClick = function()
	{
		alert(this.name); //ここでエラー
	}
	this.show = function(){return this.w.show();}
}
var sd = new showDialog;
sd.show();


単純に書き換えただけですが、実は上のコードは期待通りには動きません。
実行すればわかりますが、ボタンを押してもundefinedと表示されます。
onClick関数内ではthisthis.b変数のボタン自身を意味するので、上で定義されているthis.nameにはアクセルできないのです。

UI関係を関数化して処理したい時に結構ハマる変数スコープの問題です。
UIの問題ではないのですがcontrolのイベントを扱う時には必ずぶつかる問題なので、それの対処法を以下に説明していきます。



その1ローカル変数にする
this宣言をしなければボタンobject内のスコープの問題は発生しません。

function showDialog()
{
	var name = "このダイアログ";
	var w = new Window("dialog","Button Test",[0,0,150,50]);
	w.center();
	b = w.add("button",[10,10,150-10,50-10],"ボタン");
 
	b.onClick = function() { alert(name); }
	this.show = function()
	{
		return this.w.show();
	}
	this.setName = function(s) { name = s;}
	this.getName = function() { return name;}
}
var sd = new showDialog;
sd.show();


これなら何も考えないでコーディングできます。なんか全てグローバル変数で処理って感じで消極的な方法ですが、最近僕はこのやり方が多いです。

ライブラリとか作る時に一番重宝します。ただこの関数外から変数にアクセスする為には専用の関数を定義しないといけません。
setter/getter関数の定義(プロパティの定義)はAEスクリプトではまだ対応してないので、早く対応して欲しいですな。


その2ボタンに変数を設定する
button objectもオブジェクトの一つなので、それに変数を追加していく方法です。

function showDialog()
{
	this.w = new Window("dialog","Button Test",[0,0,150,50]);
	this.w.center();
	this.b = this.w.add("button",[10,10,150-10,50-10],"ボタン");
	this.b.name = "このダイアログ";
	this.b.onClick = function()
	{
		alert(this.name);
	}
	this.show = function()
	{
		return this.w.show();
	}
}
var sd = new showDialog;
sd.show();


複数のボタンを扱うときには便利なやり方で、JavaScriptっぽいかなり通な使い方になります。

例えば以下のような使い方が出来ます

function showDialog()
{
	this.w = new Window("dialog","Button Test",[0,0,150,90]);
	this.w.center();
	this.b1 = this.w.add("button",[10,10,150-10,40],"ボタン");
	this.b2 = this.w.add("button",[10,50,150-10,80],"ボタン");
	this.b1.tag = 1;//ここでボタン固有の数値を設定
	this.b2.tag = 2;
	function exec(){alert(this.tag);}
	this.b1.onClick = exec;
	this.b2.onClick = exec;
	this.show = function()
	{
		return this.w.show();
	}
}
var sd = new showDialog;
sd.show();




その3変数・イベントは全部外で管理

function showDialog()
{
	this.w = new Window("dialog","Button Test",[0,0,150,50]);
	this.w.center();
	this.b = this.w.add("button",[10,10,150-10,50-10],"ボタン");
	this.show = function()
	{
		return this.w.show();
	}
}
var sd = new showDialog;
var name = "このダイアログ";
sd.b.onClick= function(){ alert(name);}
sd.show();


関数内はダイアログの作成だけでその他の処理はすべて別に行っています。AE_Dialogsの初期のバージョンはこのやり方を想定していました。

デザインとコードを無理やり分離できるので、一見見難いコードになりますが意外と楽に処理できます。



と主だったテクニックを解説しましたが、実際にはこのやり方を複合したコーディングになります。

ダイアログの基本操作

以下はダイアログから値を獲得するサンプルになります。

var w = new Window("dialog","Dialog Test",[0,0,150,130]);
w.center();
var e = w.add("edittext",[10,10,150-10,40]);
var btnOK = w.add("button",[10,50,150-10,80],"OK",{name:'ok'});
var btnCancel = w.add("button",[10,90,150-10,120],"Cancel",{name:'cancel'});
if ( w.show() < 2){
	alert(e.text);
}else{
	alert("cancel");
}


特に説明することはありません。
button objectのオプションでdefaultElement/cancelElementの設定をしているので、リターンキーでOKボタンが、エスケープキーでcancekボタンが押されるようになっています。
defaultElement/cancelElementを設定しておくと、show()関数の返り値が押されたキーによって変わるのでそれを利用しています。

これを使いやすくするためにクラス化すると以下のようになります。
さりげなく、テキストエディットのデフォルト値も設定できるようにして有ります。

function showDialog()
{
	var w = new Window("dialog","Button Test",[0,0,150,130]);
	w.center();
	var e = w.add("edittext",[10,10,150-10,40]);
	var btnOK = w.add("button",[10,50,150-10,80],"OK",{name:'ok'});
	var btnCancel = w.add("button",[10,90,150-10,120],"cancel",{name:'cancel'});
	this.show = function(def)
	{
		e.text = def;
		if ( w.show() < 2){
			return e.text;
		}else{
			return "";
		}
	}
}
var dlg = new showDialog
var result = dlg.show("After Effects");
if ( result !=""){
	alert(result);
}else{
	alert("Cancel");
}


上のサンプルではthis.show()の返り値が獲得した値にするようにしています。

今度はテキストエディットに数字以外が入力されていたらエラー表示されるようにしてみます。

ボタンのonClickイベントにチェック用のコードを指定します。ただ、onClickイベントを設定するとdefaultElementに設定してあってもwindowが閉じてくれないので、close()関数で自前で閉じる必要が有ります。

さらに返り値がNumberになるようにしてあります。

function showDialog()
{
	var w = new Window("dialog","Button Test",[0,0,150,130]);
	w.center();
	var e = w.add("edittext",[10,10,150-10,40]);
	var btnOK = w.add("button",[10,50,150-10,80],"OK",{name:'ok'});
	var btnCancel = w.add("button",[10,90,150-10,120],"cancel",{name:'cancel'});
 
	w.defaultElement = btnOK;
	//数字が入力されているか確認
	function chk(){return ( (e.text!="")&amp;&amp;(isNaN(e.text)==false))}
 
	btnOK.onClick = function()
	{
		if (chk()==true){
			w.close();
		}else{
			alert("数字じゃない");
		}
	}
 
	this.show = function(def)
	{
		e.text = def + "";
		var r = w.show();
		if ( r < 2){
			return e.text * 1;
		}else{
			return Number.NaN;
		}
	}
}
 
var dlg = new showDialog
 
var result = dlg.show("");
if ( isNaN(result) ==false){
	alert(result);
}else{
	alert("Cancel");
}



以上のサンプルは入力をedittextだけで行なっていましたが、他のコントロールでも同じように出来ます。

Radiobutton

次はRadiobuttonのサンプルです。
ここまでくればコードを読めば何をしてるかわかると思います。

function showDialog()
{
	var selectedIndex = -1;
	var w = new Window("dialog","Button Test",[0,0,150,210]);
	w.center();
	var rb0 = w.add("radiobutton",[10,10,150-10,40],"rb0");
	var rb1 = w.add("radiobutton",[10,50,150-10,80],"rb1");
	var rb2 = w.add("radiobutton",[10,90,150-10,120],"rb2");
	var btnOK = w.add("button",[10,130,150-10,160],"OK",{name:'ok'});
	var btnCancel = w.add("button",[10,170,150-10,200],"cancel",{name:'cancel'});
	rb0.value = true;	//初期値を設定
	selectedIndex = 0;
	rb0.tag = 0;
	rb1.tag = 1;
	rb2.tag = 2;
 
	rb0.onClick =
	rb1.onClick =
	rb2.onClick = function(){ selectedIndex = this.tag;}
 
	this.result = function(){ return selectedIndex;}
	this.show = function(){return (w.show() < 2);}
}
 
var dlg = new showDialog;
if (dlg.show() == true){
	alert("rb"+dlg.result() + "が選ばれた");
}


DropDownList

最後のサンプルはDropDownListです。
DropDownList選択されたアイテムのインデックスの獲得で昔苦労したのでその方法のサンプルになります。
方法は、もう総当たりで調べる方法とindexプロパティを使う方法がありますが、下の例では総当たりで調べる関数を作成してあります。
indexプロパティの方がonChangeイベントで使うには楽ですが、まぁこんな方法もあるという例です。

function showDialog()
{
	var selectedIndex = -1;
	var dList = [ "ほむ","まど","さ","まみ"];
	var w = new Window("dialog","Button Test",[0,0,150,130]);
	w.center();
	var dl = w.add("dropdownlist",[10,10,150-10,40],dList);
	var btnOK = w.add("button",[10,50,150-10,80],"OK",{name:'ok'});
	var btnCancel = w.add("button",[10,90,150-10,120],"cancel",{name:'cancel'});
	dl.items[0].selected = true;	//初期値を設定
	selectedIndex = 0;
 
	function selectedAt(cnt)
	{
		var ret = -1;
		if (cnt instanceof DropDownList){
			for ( var i=0; i<cnt.items.length; i++)
			{
				if (cnt.items[i].selected == true) {
					ret = i;
					break;
				}
			}
		}
		return ret;
	}
	dl.onChange = function()
	{
		selectedIndex = selectedAt(dl);
	}
/*
	dl.onChange = function()
	{
		//こっちでも獲得できます
		selectedIndex = dl.selection.index;
	}
*/
	this.result = function(){ return selectedIndex;}
	this.show = function(){return ((w.show() < 2)&&(selectedIndex>=0));}
}
 
var dlg = new showDialog;
if (dlg.show() == true){
	alert("Doropdownlistの"+dlg.result() + "が選ばれた");
}


フローティングパレット

CS3からフローティングパネルにも対応しています。
aftereffectscs3_scripting_guide.pdfの7ページに詳しく説明されています。

具体的にはScriptsフォルダ内に”ScriptUI Panels folder“を作成しその中に拡張子がjsxのスクリプトファイルを入れておくと、そのスクリプトをウィンドウメニューから呼び出せるようになります。その方法で呼び出したスクリプトは親オブジェクトがPanel objectになっています。

var myPanel = this;
myPanel.add("button", [10, 10, 100, 30], "Tool #1");
myPanel.show();


上の例のように普通にコントロールをadd()関数で作成し表示できます。

ただ、このままのコードで通常のスクリプトのように「スクリプトを実行」で動かすとエラーになるので、
以下のサンプルのように親がPanelかどうか判別して処理を分ける必要があります。

var thisObj = this;
var myPanel = ( thisObj instanceof Panel) ? thisObj : new Window("palette", "My Tools",[100, 100, 300, 300]);
myPanel.add("button", [10, 10, 100, 30], "Tool #1");
if (thisObj instanceof Panel) myPanel.show();


リソース

WindowsやMacのプログラミングではUIのデータはリソースとして管理されています。
まぁリソースエディタあってのリソース管理なので、ろくなリソースエディタのないAEスクリプトではあまり使われませんが、機能として同じようにリソースとしてUIを設計することも出来ます。

tools_guideのP.78のResource specificationsで解説されています。

具体的にはリソースを文字変数として定義して、new Winodw()やadd()のパラメータに渡すだけです。

var winRes =
"palette {\
	text: 'palette',\
	bounds :[100,100,600,400],\
	properties:{resizeable:true }\
}";
// \で改行コードを無効化している。
//var winRes = "palette {text: 'palette',bounds :[100,100,600,400],properties:{resizeable:true }}";と同じ意味になる。
 
var btnRes=
"Button {\
	text: 'button1',\
	bounds :[20,20,120,60]\
}";
//1行で書くとこうなる
//var btnRes= "Button {text: 'button1',bounds :[20,20,120,60]}";
 
var rw = new Window (winRes);
var btn1 = rw.add(btnRes);
rw.show();


優秀なリソースエディタがあればかなりコーディングを楽に出来ます。
まぁ1箇所にたらだらとコードを書くよりデータを分散できる為、リサイタブルに設計したUIの場合はリソースで管理した方が楽になると思います。

AE_Dialogs_dueでも採用しようかと悩みましたが、僕自身がリソース形式になれていなかったので見送りましたが、tools_guideの最新版を読んでるといろいろ便利なサンプルがあったので、今後対応させようかなとか思ってます。
また、AE_Dialogs_dueを使わないで手作業で作成するときは、リソース使ったほうがいいかなとか思います。


最後に

今回はAE_Dialogs_dueを使うための最低限のUI作成方法の解説でした。
教科書相当のjavascript_tools_guide.pdfが知らない間にCS5対応になってて慌てましたが、特に問題なくて助かりました。

あと、AE_Dialogs_dueの対応はCS4なんで注意のことです。まぁ、コーディングに注意すれば大丈夫と思いますが。

次はやっと「1行スクリプト」です。もう50行分作成済みですので、近いうちに投稿できる筈です。




タグ : , , [タグを追加する]


この記事の投稿者について:bryful

フィードバック

1 Star2 Stars3 Stars4 Stars5 Stars
(評価回数:2 , 平均:5.00)
Loading...Loading...

この記事に対する 7 の反応があります


  1. 1 aeusers 10月 23rd, 2011 at 08:42

    新着記事 : After Effectsユーザーのための、プログラミング入門 その10 後編 AE_Dialogs_due C#コントロール集 AEスクリプトのUIデザイン http://t.co/wCR44ubX

  2. 2 古橋宏 10月 23rd, 2011 at 08:47

    AEPProjectに記事を投稿しました。http://t.co/cmqSwoHq

  3. 3 藤田 枝里 10月 23rd, 2011 at 08:50

    新着記事 : After Effectsユーザーのための、プログラミング入門 その10 後編 AE_Dialogs_due C#コントロール集 AEスクリプトのUIデザイン http://t.co/wCR44ubX

  4. 4 Adobe Creative Suite 11月 17th, 2011 at 07:15

    「After Effectsユーザーのための、プログラミング入門 その10 後編 AE_Dialogs_due C#コントロール集 AEスクリプトのUIデザイン」AEP Project http://t.co/r0XpyFIa #CS5_jp

  5. 5 Adobe Creative Suite 11月 17th, 2011 at 07:28

    スクリプトは経験があるけれど他の言語はさわったことがないユーザーを対象に、C#/AEのJavaScriptをメインにプログラム全般を解説する連載の第10回後編です。前回に続いて「AE_Dialogs_due」解説です。http://t.co/r0XpyFIa

  1. 1 AfterEffectsユーザーのための、プログラミング入門その10後編AE_Dialogs_dueC#コントロール集AEスクリプトのUIデザイン | スクリプト - After Effects - 動画編集ソフト - アプリケーション | Jishuu.net Pingback on 4月 10th, 2012 at 06:23
  2. 2 After Effectsユーザーのための、プログラミング入門その10後編AE_Dialogs_dueC#コントロール集AEスクリプトのUIデザイン | スクリプト - After Effects - 動画編集ソフト - アプリケーション | Jishuu.net Pingback on 10月 3rd, 2012 at 20:59

コメントをどうぞ!




特集