ちょっと古めかしい言葉を出してしまいましたが、MSのVisual StudioやNetBeansのMatisseのようなWYSIWYG環境でのソフトウェ開発が、JavaFXでも可能になりました。
それは、NetBeansなどのIDE用に提供されている、JavaFX追加接続(plug-in)プログラムの下見(preview)機能を利用した開発のことです。Visual StudioやMatisseのような、直接操作方式ではありませんが、編集作業と並行して実行時の姿が実時間で見られるので、かなり開発効率が上がります。
事前評価(preview)版のJavaFX追加接続にもその原型はあったのですが、原始コードの編集領域を分割して表示するため編集領域が狭くなる、コードによってはうまく表示ができないなど、正直言って「今一」の感が否めませんでした。しかし、JavaFX 1.0対応版では、これらの点かなり改善さているようです(まだ、それほど試した訳ではないので、問題点の有無を把握し切れてはいませんが)。
少なくとも、下見画面がIDEの窓とは独立した専用の窓に表示されるため、編集領域が狭くなることはありません。直接操作方式ではありませんが、元々JavaFX言語は宣言型の言語であり、VBやJavaに比べれば、画面表示とコードの対応が取りやすいこともあって、それほど苦にはなりません(もちろん、図形部品をマウスで引っ張りまわして好きな位置に配置したり、寸法を自由に変えられることに越したことはないのですが)。
2008年12月24日水曜日
2008年12月23日火曜日
JavaFX 1.0 SDK Update 1公開
JavaFX 1.0 SDK Update 1が、12月19日に公開されました。公開直後は、導入用プログラムが更新前のものしか公開されていなかったのですが、今日改めて見てみたら、JavaFX 1.0 SDK Update 1用の導入プログラムに変更されていましたので、早速導入してしまいました。
2008年12月16日火曜日
狼おじさんになってしまうところでした
昨日、私の不注意から、JavaFXのcontinue式に不具合があるとの投稿をしてしまいました。
再度確認したところ、continue式には、私が指摘したような不具合はないことが分かりました。
ここに、訂正して、お詫びいたします。
なお、誤った情報をこれ以上広めたくありませんので、まことに勝手ながら昨日の投稿は削除いたしました。
貴重なご指摘をいただいた、雪羽 さんどうもありがとうございました。ここに、お礼を述べさせていただきます。
再度確認したところ、continue式には、私が指摘したような不具合はないことが分かりました。
ここに、訂正して、お詫びいたします。
なお、誤った情報をこれ以上広めたくありませんので、まことに勝手ながら昨日の投稿は削除いたしました。
貴重なご指摘をいただいた、雪羽 さんどうもありがとうございました。ここに、お礼を述べさせていただきます。
2008年12月13日土曜日
NetBeans用JavaFX追加接続プログラム日本語表示不具合
JavaFX言語の入門書「JavaFXプログラミング言語の基礎」を書き始め、まだ第1章だけですがWebページで公開しました。
そこに載せている見本コードの動作確認をしていて分かったのですが、NetBeans用JavaFX追加接続プログラム(JavaFX 1.0相当版:JavaFX 1.0バージョン1.4.3、JavaFX SDK for Windowsバージョン1.3.3)の日本語表示には、まだ未修正の不具合があります。
これは、以前の投稿で記したものですが、標準出力に日本語文字列を表示するようなプログラムを一括変換すると誤りが発生するというものです。同じプログラムを、JavaFX 1.0 SDKのjavafxcでコマンド行から一括変換すると正常に変換でき、日本語が表示できます。したがって、Windows上で動作するNetBeans用JavaFX追加接続プログラムの標準出力への日本語表示に、問題が残っているようです。ちなみに、枠付き窓に日本語が表示できない不具合は、修正されています。
話は変わりますが、以前から、JavaFX言語は、表現力が高いばかりでなく、初心者がプログラミングを習得するのにも適した言語であると思っています。Java言語ですと、何はともあれ、「class」を定義しなければなりません。そこで、クラスとは何なのかの話をいきなり始めるか、「とりあえず、おまじない(?)と思って記述してください」と、クラスに関する説明を後回しにするかの選択をしなければなりません。
いずれにしても、説明する方も、また説明される方もあまりすっきりしません。
その点、JavaFXなら、println()関数の説明をするぐらいで「Hello world!」プログラムが書けますので、話が簡単です。また、JavaFX 1.0では、データ型の推定機能が復活しましたので、その点でもプログラミングが楽になります。しかも、動画や多重媒体応用プログラムが簡潔に書けるまでになります。
「JavaFX言語でプログラミングを学ぶ」といった講座が一般的になる、そんな日が早く来るといいのですが。
そこに載せている見本コードの動作確認をしていて分かったのですが、NetBeans用JavaFX追加接続プログラム(JavaFX 1.0相当版:JavaFX 1.0バージョン1.4.3、JavaFX SDK for Windowsバージョン1.3.3)の日本語表示には、まだ未修正の不具合があります。
これは、以前の投稿で記したものですが、標準出力に日本語文字列を表示するようなプログラムを一括変換すると誤りが発生するというものです。同じプログラムを、JavaFX 1.0 SDKのjavafxcでコマンド行から一括変換すると正常に変換でき、日本語が表示できます。したがって、Windows上で動作するNetBeans用JavaFX追加接続プログラムの標準出力への日本語表示に、問題が残っているようです。ちなみに、枠付き窓に日本語が表示できない不具合は、修正されています。
話は変わりますが、以前から、JavaFX言語は、表現力が高いばかりでなく、初心者がプログラミングを習得するのにも適した言語であると思っています。Java言語ですと、何はともあれ、「class」を定義しなければなりません。そこで、クラスとは何なのかの話をいきなり始めるか、「とりあえず、おまじない(?)と思って記述してください」と、クラスに関する説明を後回しにするかの選択をしなければなりません。
いずれにしても、説明する方も、また説明される方もあまりすっきりしません。
その点、JavaFXなら、println()関数の説明をするぐらいで「Hello world!」プログラムが書けますので、話が簡単です。また、JavaFX 1.0では、データ型の推定機能が復活しましたので、その点でもプログラミングが楽になります。しかも、動画や多重媒体応用プログラムが簡潔に書けるまでになります。
「JavaFX言語でプログラミングを学ぶ」といった講座が一般的になる、そんな日が早く来るといいのですが。
2008年12月5日金曜日
JavaFX 1.0 SDKが公開されました
JavaFX 1.0 SDKが公開されましたので、早速導入してみました。
JavaFX 1.0 SDKは、JavaFX.comのStartページから導入できます。なお、同ページには、「Learning the JavaFX Script Programming Language」という指導書への連結が載っていますが、その指導書の中に記載されているJavaFX 1.0 SDK導入ページの連結をたどると、本日(12/5)現在では、事前評価(Preview)版の導入ページに案内されてしまいます。上記のStartページから導入すると、正式版が導入できます。
また、上記の指導書には、JDK 5またはJDK 6が必要と記されていますが、JavaFX 1.0 SDKを導入しようとすると、最新のJDK 6u11を導入するように求められます。
JavaFX 1.0 SDKは、JavaFX.comのStartページから導入できます。なお、同ページには、「Learning the JavaFX Script Programming Language」という指導書への連結が載っていますが、その指導書の中に記載されているJavaFX 1.0 SDK導入ページの連結をたどると、本日(12/5)現在では、事前評価(Preview)版の導入ページに案内されてしまいます。上記のStartページから導入すると、正式版が導入できます。
また、上記の指導書には、JDK 5またはJDK 6が必要と記されていますが、JavaFX 1.0 SDKを導入しようとすると、最新のJDK 6u11を導入するように求められます。
2008年10月8日水曜日
JavaFX Air Hockey program
I made a Air Hockey game in JavaFX.
As for the program, named AirHockey.fx, plese refer to the code (modified to conform to JavaFX 1.0).
When you run the program, an air hockey game table with two aqua color mallets are displayed on your screen. Computer's goal (the upper one) and your goal (the lower one) are displayed in burlywood color.
Push the "SET" button to start the game. Then, a white puck appears on your side.
You can manipulate your mallet by the mouse as long as it resides in your area.
A velocity of your mallet is computed from its displacement by applying the scaling transformation. The scaling is modulated by the velocityScaling factor. You can make your mallet's velocity more real by moving the factor closer to 1.0.
You can also regulate the bounce back speed by adjusting the elastic modulus elastic.
In this program, a computer simply move its mallet back and forth. It has no ability to take the offensive. It is your business to make your opponent cool.
Enjoy yourself!
2008年10月7日火曜日
JavaFXで空中ホッケー遊技(Air hockey game)を作る
頭の中が沸騰しそうになったとき、しばらく空っぽにするのにいいですよ。
プログラムを実行すると、右の図のような空中ホッケーの卓が表示されます。薄茶色で表示されているところが得点圏(goal)で、青緑色で表示されているのが槌(mallet)です。
「SET」ボタンを押すと、図の位置に白い円盤(puck)が表示されます(プログラムを実行した直後や、得点した後は、円盤が表示されません)。
人間様用の槌(下側が「人間様」の領域で、上側が「計算機様」の領域です)をマウスで動かして(人間様の領域内にマウスを持っていくと、その後を槌が追います)、円盤を打ってみてください。
槌を速く動かしすぎると、槌で円盤を引きずるような動作をします。これは、マウスの位置の変化量から槌の速度を計算する際に、スケーリング変換を行っているためです。velocityScalingの値で、変換係数を調節してください。
また、円盤が卓の壁や槌に衝突してはね返されるとき、弾性係数を考慮して動きを計算しています。elasticの値で、弾性率を変えてみてください。
今回のものでは、計算機様は、ただ槌を往復させるだけで、何の芸も持ち合わせていません。計算機様にどんな芸を持たせるかが、これからの楽しみです。何の制限も持たせなければ、計算機様をいくらでも強くできますが、それでは勝負になりません。強過ぎても弱過ぎてもだめですし、(乱数に因らない)偶然の要素もないと面白くない、その辺りが工夫のしどころです。
最後に、原始コードを載せておきます。なお、前回の投稿で、仕立て節に定義した関数がうまく機能しないと記しましたが、(部品化を考慮して)仕立て節を別の台本ファイルで定義したことがまずかっただったようです。このプログラムのように、同じ台本ファイル内に仕立て節を定義する限り、特に問題はありませんでした。
/*
* AirHockey.fx
* The air hockey game.
*
* Created and modified:
* V 1.0.0 2008/09/25
*/
package game;
import javafx.animation.*;
import javafx.application.*;
import javafx.input.*;
import javafx.scene.*;
import javafx.scene.effect.*;
import javafx.scene.effect.light.*;
import javafx.scene.geometry.*;
import javafx.scene.paint.*;
import javafx.scene.text.*;
import java.lang.Math;
import java.lang.System;
/**
* @author terra
*/
var goalCorners: Disc[];
var mouseEvent: MouseEvent;
var tableWidth: Number = 300;
var tableHeight: Number = 400;
var goalWidth: Number = 80;
var goalCornerRadius: Number = 5;
var puckRadius: Number = 10;
var malletRadius: Number = 15;
var hittingAreaDepth: Number = tableHeight / 2 - malletRadius;
var elastic: Number = 0.95;
var velocityScaling = 0.35;
var malletCMaxV = 2; // Maximum velocity of the computer's mallet.
var timingGenerator: Timeline = Timeline {
repeatCount: Timeline.INDEFINITE
keyFrames: KeyFrame {
time: 20ms
action: function(): Void {
malletC.move();
puck.move();
malletH.calcuVelocity();
puck.collide(malletH);
puck.collide(malletC);
for (corner in goalCorners) {
puck.collide(corner);
}
score.judgeGoal();
}
}
};
var puck: Puck = Puck {
// x: goalCornerRadius + tableWidth / 2
// y: goalCornerRadius + 2 * tableHeight/ 3
x: -(puckRadius + 1)
y: goalCornerRadius + tableHeight / 2
radius: puckRadius
vX: 0, vY: 0
}
var malletH: MalletH = MalletH { // Human's mallet.
x: bind mouseEvent.getStageX()
y: bind mouseEvent.getStageY()
radius: malletRadius
color: Color.AQUA
}
var malletC: MalletC = MalletC { // Computer's mallet.
x: goalCornerRadius + tableWidth / 2
y: goalCornerRadius + malletRadius + puckRadius
radius: malletRadius
color: Color.AQUA
vX: malletCMaxV, vY: 0
}
for (i in [0..1]) {
for (j in [0..1]) {
insert Disc {
x: (tableWidth - goalWidth) / 2 + j * goalWidth
y: i * (2 * goalCornerRadius + tableHeight)
radius: goalCornerRadius
color: Color.LIGHTGREY
} into goalCorners;
}
}
var score: Score = Score{}
Frame {
title: "Air hockey"
width: 2 * goalCornerRadius + tableWidth + 56 as Integer
height: 2 * goalCornerRadius + tableHeight + 28 as Integer
visible: true
closeAction: function(): Void {System.exit(0);}
stage: Stage {
width: 2 * goalCornerRadius + tableWidth as Integer
height: 2 * goalCornerRadius + tableHeight as Integer
fill: Color.LIGHTGRAY
content: [
Rectangle { // Jointed goal area.
x: (tableWidth - goalWidth) / 2
y: 0
width: goalWidth
height: 2 * goalCornerRadius + tableHeight +2
fill: Color.BURLYWOOD
},Rectangle { // Table.
x: goalCornerRadius, y: goalCornerRadius
width: tableWidth, height: tableHeight
fill: Color.OLIVE
},
Rectangle { // Human's hittig area.
x: goalCornerRadius + malletRadius
y: goalCornerRadius + tableHeight - hittingAreaDepth
width: tableWidth - 2 * malletRadius
height: hittingAreaDepth - malletRadius
fill: Color.OLIVE
onMouseMoved: function(ev: MouseEvent): Void {
mouseEvent = ev;
}
},
Line { // Center line.
startX: goalCornerRadius
startY: goalCornerRadius + tableHeight / 2
endX: goalCornerRadius + tableWidth -1
endY: goalCornerRadius + tableHeight / 2
stroke: Color.BLUE
},
puck,
malletH,
malletC,
goalCorners,
ScoreIndicator {
x: 2 * goalCornerRadius + tableWidth + 5 as Integer
y: goalCornerRadius + tableHeight / 2 - 24 as Integer
score: bind score.scoreC
},
ScoreIndicator {
x: 2 * goalCornerRadius + tableWidth + 5 as Integer
y: goalCornerRadius + tableHeight / 2 + 5 as Integer
score: bind score.scoreH
},
Rectangle { // "SET" button.
x: 2 * goalCornerRadius + tableWidth + 5 as Integer
y: goalCornerRadius + tableHeight - 30 as Integer
width: 35, height: 30
arcWidth: 8, arcHeight: 8
fill: Color.DARKGREY
effect: Lighting {
light: DistantLight {
azimuth: 225, elevation: 50
}
}
onMousePressed: function(ev: MouseEvent): Void {
puck.x = goalCornerRadius + tableWidth / 2;
puck.y = goalCornerRadius + 2 * tableHeight/ 3;
puck.vX = 0;
puck.vY = 0;
timingGenerator.start();
}
},
Text {
x: 2 * goalCornerRadius + tableWidth + 10 as Integer
y: goalCornerRadius + tableHeight - 20 as Integer
textOrigin: TextOrigin.TOP
fill: Color.YELLOW
font: Font {
size: 14
name: "Monospaced", style: FontStyle.BOLD
}
effect: DropShadow {
offsetX: 3, offsetY: 3, radius: 2
color: Color.BLACK
}
content: "SET"
}
]
}
}
class Disc extends CustomNode {
attribute x: Number;
attribute y: Number;
attribute radius: Number;
attribute color: Color = Color.WHITE;
attribute vX: Number;
attribute vY: Number;
attribute lastX: Number = x;
attribute lastY: Number = y;
function create(): Node {
return Circle {
centerX: bind x, centerY: bind y
radius: bind radius
fill: bind color
};
}
}
class Puck extends Disc {
function move(): Void {
x += vX;
y += vY;
if (x + radius > goalCornerRadius + tableWidth) {
x = goalCornerRadius + tableWidth - radius;
vX *= -1.0 * elastic;
} else if (x - radius < goalCornerRadius) {
x = goalCornerRadius + radius;
vX *= -1.0 * elastic;
}
if (x >= 0) {
if (y > 2 * goalCornerRadius + tableHeight) {
// It crossed the Human's goal line.
x = -(radius + 1);
y = 2 * goalCornerRadius + tableHeight + radius;
vX = 0;
vY = 0;
} else if (y + radius > tableHeight + goalCornerRadius) {
if ((x < (tableWidth - goalWidth) / 2)
or (x > (tableWidth + goalWidth) / 2)) {
y = tableHeight + goalCornerRadius - radius;
vY *= -1.0 * elastic;
}
} else if (y < 0) {
// It crossed the computer's goal line.
x = -(radius + 1);
y = -radius;
vX = 0;
vY = 0;
} else if (y - radius < goalCornerRadius) {
if ((x < (tableWidth - goalWidth) / 2)
or (x > (tableWidth + goalWidth) / 2)) {
y = goalCornerRadius + radius;
vY *= -1.0 * elastic;
}
}
}
}
function collide(disc: Disc): Void {
var distX: Number = disc.x - x;
var distY: Number = disc.y - y;
var minDist: Number = disc.radius + radius;
var dist2: Number = distX * distX + distY * distY;
var minDist2: Number = minDist * minDist;
if (dist2 < minDist2) {
var colAngle: Number = Math.atan2(distY, distX);
var sinColAngle: Number = Math.sin(colAngle);
var cosColAngle: Number = Math.cos(colAngle);
var expres1: Number = disc.vX * cosColAngle + disc.vY * sinColAngle;
var expres2: Number = vX * sinColAngle - vY * cosColAngle;
var expres3: Number = vX * cosColAngle + vY * sinColAngle;
x = disc.x - minDist * cosColAngle;
y = disc.y - minDist * sinColAngle;
vX = (1+ elastic) * expres1 * cosColAngle + expres2 * sinColAngle
- elastic * expres3 * cosColAngle;
vY = (1+ elastic) * expres1 * sinColAngle - expres2 * cosColAngle
- elastic * expres3 * sinColAngle;
}
}
}
class MalletH extends Disc {
function calcuVelocity(): Void {
vX = velocityScaling * (x - lastX);
vY = velocityScaling * (y - lastY);
lastX = x;
lastY = y;
}
}
class MalletC extends Disc {
function move(): Void {
x += vX;
if (x + radius > 2 * goalCornerRadius + tableWidth / 2
+ goalWidth / 2) {
x = 2 * goalCornerRadius + tableWidth / 2 + goalWidth / 2
- radius;
vX *= -1.0;
} else if (x - radius < tableWidth / 2 - goalWidth / 2) {
x = tableWidth / 2 - goalWidth / 2 + radius;
vX *= -1.0;
}
}
}
class Score {
attribute scoreC: Integer = 0; // Computer's score.
attribute scoreH: Integer = 0; // Human's score.
function judgeGoal(): Void {
if (puck.x < 0) {
if (puck.y > 2 * goalCornerRadius + tableHeight) {
scoreC += 1;
} else if (puck.y < 0) {
scoreH += 1;
}
puck.y = goalCornerRadius + tableHeight / 2;
timingGenerator.stop();
}
}
}
class ScoreIndicator extends CustomNode {
attribute score: Integer = 0;
attribute x: Integer = 0;
attribute y: Integer = 0;
attribute width: Integer = 21;
attribute height: Integer = 18;
attribute fillColor: Color = Color.WHITE;
attribute borderColor: Color = Color.YELLOWGREEN;
attribute scoreTextColor: Color = Color.BLACK;
function create(): Node {
Group {
content: [
Rectangle {
x: x, y: y
width: width, height: height
arcWidth: 6, arcHeight: 6
stroke: borderColor, fill: fillColor
},
Text {
x: x + 4 * height / 18 as Integer
y: y + 5 * height / 18 as Integer
textOrigin: TextOrigin.TOP
fill: scoreTextColor
font: Font {
size: 14 * height / 18 as Integer
name: "Monospaced", style: FontStyle.BOLD
}
content: bind "{%2d score}"
}
]
}
}
}
2008年9月9日火曜日
仕立て節がうまく機能しない
前回の投稿で、SCADA UIプログラム例をPAC類型に基づいて構成しなおしました。
そこで、SCADA UIの構成部品(数値指示計や棒図式指示計など)をjavafx.scene.CustomNodeクラスの派生クラスとしてPACエージェント化しようとしたのですが、どうもうまくいきません。
CustomNodeの派生クラスで、新たに関数を定義しているのですが、その関数が思ったように機能してくれません。関数の機能を限定し、「回避策」的なプログラミングを行えば、少なくともこれまでご紹介したSCADA UIプログラム例と同程度の機能を持った仕立て節を作ることは可能です。ですが、せっかく「仕立てる」のですから、あらが目立つようではどうも…。というわけで、もう少し一括変換器の成熟を待つことにしました。
JavaFX言語V1.0における変更点についても、これまでにご紹介したもの以外の変更があるのですが、すでにご紹介した内容についても更なる変更が生じていますので、こちらについても落ち着くのを待ってご紹介したほうが良さそうです。まだ「2008年の秋」は始まったばかりですから…。
そこで、SCADA UIの構成部品(数値指示計や棒図式指示計など)をjavafx.scene.CustomNodeクラスの派生クラスとしてPACエージェント化しようとしたのですが、どうもうまくいきません。
CustomNodeの派生クラスで、新たに関数を定義しているのですが、その関数が思ったように機能してくれません。関数の機能を限定し、「回避策」的なプログラミングを行えば、少なくともこれまでご紹介したSCADA UIプログラム例と同程度の機能を持った仕立て節を作ることは可能です。ですが、せっかく「仕立てる」のですから、あらが目立つようではどうも…。というわけで、もう少し一括変換器の成熟を待つことにしました。
JavaFX言語V1.0における変更点についても、これまでにご紹介したもの以外の変更があるのですが、すでにご紹介した内容についても更なる変更が生じていますので、こちらについても落ち着くのを待ってご紹介したほうが良さそうです。まだ「2008年の秋」は始まったばかりですから…。
2008年8月18日月曜日
JavaFX言語V1.0における変更:利用修飾子
JavaFX言語のV1.0における変更点の内、利用修飾子について、その内容をご案内したいと思います。
var x;
var x : String;
var x = z + 22;
var x = bind f(q);
変数を宣言します。変数は、オブジェクト直定数で初期化したり、派生クラスで再定義したり、その値を読んだり、値を割り当てたり、もしくは既定値の式またはオブジェクト直定数の初期化で結合することができます。既定で利用可能なのは台本内からで、利用修飾子がない場合は、同じ台本内からのみ初期化、再定義、読むこと、割り当て、または結合が可能です。
def x = 100;
def x = a + b;
def x = bind a + b;
変数を定義します。定義内容は変更できません。この種の変数は、オブジェクト直定数で初期化したり、派生クラスで再定義したり、値を割り当てたりすることができません。既定で利用可能なのは台本内からで、利用修飾子がない場合は、同じ台本内からのみ読むこと、または結合が可能です。
public var x;
公開変数を宣言します。この変数は、どこからでも、初期化、再定義、読むこと、割り当て、もしくは結合が可能です。
public def x = "Hi";
公開変数を定義します。この定義は、どこからでも読むこと、および結合が可能ですが、値を割り当てたり、オブジェクト直定数で初期化したり、または派生クラスで再定義することはできません。
protected var x;
同じパッケージまたは派生クラスから利用可能な変数を宣言します。
protected def x = bind z;
同じパッケージまたは派生クラスから読むこと、または結合が可能な変数を定義します。
package var x;
同じパッケージから利用可能な変数を宣言します。
package def x = 42;
同じパッケージから読むこと、または結合が可能な変数を定義します。
public-readable var x;
どこからでも読むことはできますが、既定で利用可能な台本内からのみ、初期化または書くこと(割り当てまたは結合)が可能な変数を宣言します。
public-readable package var x;
どこからでも読むことはできますが、同じパッケージ内からのみ初期化または書くこと(割り当てまたは結合)が可能な変数を宣言します。
non-writable public var x = 1.0;
non-writable public var x;
公に利用できますが、書くこと(割り当てまたは結合)ができない変数を宣言します。どこからでも、初期化、再定義、または読むことは可能ですが、その値を変更することはできません。この場合、初期化は書くことにはあたりません。
non-writable var x = 1.0;
同じ台本内から初期化または読むことはできますが、値の割り当てまたは結合はできない変数を宣言します。
var x;
var x : String;
var x = z + 22;
var x = bind f(q);
変数を宣言します。変数は、オブジェクト直定数で初期化したり、派生クラスで再定義したり、その値を読んだり、値を割り当てたり、もしくは既定値の式またはオブジェクト直定数の初期化で結合することができます。既定で利用可能なのは台本内からで、利用修飾子がない場合は、同じ台本内からのみ初期化、再定義、読むこと、割り当て、または結合が可能です。
def x = 100;
def x = a + b;
def x = bind a + b;
変数を定義します。定義内容は変更できません。この種の変数は、オブジェクト直定数で初期化したり、派生クラスで再定義したり、値を割り当てたりすることができません。既定で利用可能なのは台本内からで、利用修飾子がない場合は、同じ台本内からのみ読むこと、または結合が可能です。
public var x;
公開変数を宣言します。この変数は、どこからでも、初期化、再定義、読むこと、割り当て、もしくは結合が可能です。
public def x = "Hi";
公開変数を定義します。この定義は、どこからでも読むこと、および結合が可能ですが、値を割り当てたり、オブジェクト直定数で初期化したり、または派生クラスで再定義することはできません。
protected var x;
同じパッケージまたは派生クラスから利用可能な変数を宣言します。
protected def x = bind z;
同じパッケージまたは派生クラスから読むこと、または結合が可能な変数を定義します。
package var x;
同じパッケージから利用可能な変数を宣言します。
package def x = 42;
同じパッケージから読むこと、または結合が可能な変数を定義します。
public-readable var x;
どこからでも読むことはできますが、既定で利用可能な台本内からのみ、初期化または書くこと(割り当てまたは結合)が可能な変数を宣言します。
public-readable package var x;
どこからでも読むことはできますが、同じパッケージ内からのみ初期化または書くこと(割り当てまたは結合)が可能な変数を宣言します。
non-writable public var x = 1.0;
non-writable public var x;
公に利用できますが、書くこと(割り当てまたは結合)ができない変数を宣言します。どこからでも、初期化、再定義、または読むことは可能ですが、その値を変更することはできません。この場合、初期化は書くことにはあたりません。
non-writable var x = 1.0;
同じ台本内から初期化または読むことはできますが、値の割り当てまたは結合はできない変数を宣言します。
2008年8月17日日曜日
JavaFX言語V1.0における変更
JavaFX言語のV1.0(初版)における変更点について、ご案内したいと思います。
まだ実装されていないものもあります。また、実装済みのものについては、古い構文も(警告はされるが)ある期間は引き続き機能するようです。ただし、最終的には、警告ではなく誤りになり、その切り替わり時点は決まっていないということのようです。
不等価演算子
不等価演算子「<>」は「!=」に置き換えられました。この件と次の剰余演算子の変更については、以前の投稿でも触れました。
剰余演算子
剰余演算子「%」は「mod」に置き換えられました。これに関連して、剰余代入演算子「%=」は、(「mod=」に置き換えられるのではなく)剰余代入演算子そのものが支援されなくなるため、「x = x mod y」のように記述することになります。
ちなみに、JavaFXの剰余演算はJavaのそれ(%)と同じで、剰余の符号は被除数の符号に合わせられます(被除数および/または除数が負数の場合の、剰余の符号に関係します)。
台本の取込み
import文は、Java言語の「import static」(J2SE 5.0で支援された、クラスの静的要素の取り込み)機能を含むように、拡張されました。
「+」による文字列連結の廃止
「+」演算子による文字列の連結は廃止されます。String型の変数aとbに格納した文字列の連結は、(「a + b」ではなく)「"{a}{b}"」と表します。
関数再定義
クラスの要素関数を再定義するときは、「override」鍵語が必要になります。
変数宣言
変数を宣言するときは、それがどこであれ、鍵語「var」と「def」が使用され、鍵語「attribute」(クラス属性を宣言)と「static」(台本水準の変数を定義)はなくなります。「def」を用いて定義した変数に(初期化以外で)値を割り当てると誤りになります。
利用修飾子
利用修飾子は、「public」、「package」、「protected」、「public-readable」および「non-writable」の5つとなり、「private」はなくなります。既定の利用可能性は、「台本内から利用可」です。
利用修飾子については、別途投稿します。
オブジェクト直定数構文
「オブジェクト直定数の振る舞いがいくぶんか限定され、構文も厳密になる」ようです。
再帰取込み
「import パッケージ名.*」構文は、「import パッケージ名.**」を支援するように拡張されます。これは、「指定されたパッケージとその下位パッケージに含まれるすべてを取り込む」ことを意味します。
仮引数の不変化
関数の仮引数は不変で、それらに値を割り当てると誤りになります。
まだ実装されていないものもあります。また、実装済みのものについては、古い構文も(警告はされるが)ある期間は引き続き機能するようです。ただし、最終的には、警告ではなく誤りになり、その切り替わり時点は決まっていないということのようです。
不等価演算子
不等価演算子「<>」は「!=」に置き換えられました。この件と次の剰余演算子の変更については、以前の投稿でも触れました。
剰余演算子
剰余演算子「%」は「mod」に置き換えられました。これに関連して、剰余代入演算子「%=」は、(「mod=」に置き換えられるのではなく)剰余代入演算子そのものが支援されなくなるため、「x = x mod y」のように記述することになります。
ちなみに、JavaFXの剰余演算はJavaのそれ(%)と同じで、剰余の符号は被除数の符号に合わせられます(被除数および/または除数が負数の場合の、剰余の符号に関係します)。
台本の取込み
import文は、Java言語の「import static」(J2SE 5.0で支援された、クラスの静的要素の取り込み)機能を含むように、拡張されました。
「+」による文字列連結の廃止
「+」演算子による文字列の連結は廃止されます。String型の変数aとbに格納した文字列の連結は、(「a + b」ではなく)「"{a}{b}"」と表します。
関数再定義
クラスの要素関数を再定義するときは、「override」鍵語が必要になります。
変数宣言
変数を宣言するときは、それがどこであれ、鍵語「var」と「def」が使用され、鍵語「attribute」(クラス属性を宣言)と「static」(台本水準の変数を定義)はなくなります。「def」を用いて定義した変数に(初期化以外で)値を割り当てると誤りになります。
利用修飾子
利用修飾子は、「public」、「package」、「protected」、「public-readable」および「non-writable」の5つとなり、「private」はなくなります。既定の利用可能性は、「台本内から利用可」です。
利用修飾子については、別途投稿します。
オブジェクト直定数構文
「オブジェクト直定数の振る舞いがいくぶんか限定され、構文も厳密になる」ようです。
再帰取込み
「import パッケージ名.*」構文は、「import パッケージ名.**」を支援するように拡張されます。これは、「指定されたパッケージとその下位パッケージに含まれるすべてを取り込む」ことを意味します。
仮引数の不変化
関数の仮引数は不変で、それらに値を割り当てると誤りになります。
2008年8月14日木曜日
SCADA UIプログラム例のJavaFX SDK対応書き換え:PAC類型適用
先日の投稿で、SCADA UIプログラム例のJavaFX SDK対応の書き換えは、一応済みました。
今回は、プログラム構造を基本設計におけるPAC(Presentation-Abstraction-Control)類型を参考にして構成し直してみました。ただし、Presentation要素とAbstraction要素が、結合機能により、(Control要素を介さない)直接的なつながりを持っているので、純粋にはPAC類型と呼べないかも知れません。
しかし、機能的なまとまりをPACエージェントとして構成し、プログラム全体をそれらPACエージェントの階層化された木構造で構成しているという点においては、PAC類型に準じたものです。PAC類型については、F. ブッシュマン、ほか著の『ソフトウェア アーキテクチャ ソフトウェア開発のためのパターン体系』(株式会社トッパン,1999)に説明されています。
PACエージェントとして構成するため、Javaのプログラムを2つに分けました。
まずは、依頼機側の接続エージェントとなるJavaのプログラムを示します。
次に、警報鳴動エージェントとなるJavaのプログラムを示します。
最後に、利用者インタフェース(UI)最上位エージェントである、UI枠付き窓エージェントのJavaFXプログラムを示します。
今回は、プログラム構造を基本設計におけるPAC(Presentation-Abstraction-Control)類型を参考にして構成し直してみました。ただし、Presentation要素とAbstraction要素が、結合機能により、(Control要素を介さない)直接的なつながりを持っているので、純粋にはPAC類型と呼べないかも知れません。
しかし、機能的なまとまりをPACエージェントとして構成し、プログラム全体をそれらPACエージェントの階層化された木構造で構成しているという点においては、PAC類型に準じたものです。PAC類型については、F. ブッシュマン、ほか著の『ソフトウェア アーキテクチャ ソフトウェア開発のためのパターン体系』(株式会社トッパン,1999)に説明されています。
PACエージェントとして構成するため、Javaのプログラムを2つに分けました。
まずは、依頼機側の接続エージェントとなるJavaのプログラムを示します。
/*
* ClientSideConnection.java
* The Client-Side Connection agent for a SCADA application.
*
* Created and modified:
* V 1.0.0 2008/07/31
*/
package client;
/**
* @author terra
*/
class ClientSideConnection {
private int i = -1;
/**
* Create and return pseudo SCADA data as if received from a server.
*/
double receiveScadaData() {
i += 1;
if (i >= 360) {
i = 0;
}
return (double) Math.sin(i * Math.PI / 180) * 100;
}
/**
* Main method for testing this class.
*
* @param args the command line arguments
*/
public static void main(String[] args) {
ClientSideConnection scc = new ClientSideConnection();
for (int i = 0; i < 5; i++) {
System.out.println(scc.receiveScadaData());
}
}
}
次に、警報鳴動エージェントとなるJavaのプログラムを示します。
/*
* AlarmSounder.java
* The Temporary Alarm Sounder agent for a SCADA application.
* At some point, this agent will be rewritten in the JavaFX language.
*
* Created and modified:
* V 1.0.0 2008/08/11
*/
package client;
import java.applet.Applet;
import java.applet.AudioClip;
import javax.swing.JOptionPane;
/**
* @author terra
*/
class AlarmSounder {
private AudioClip ac = Applet.newAudioClip(
AlarmSounder.class.getResource("ALERT.WAV"));
/**
* Start sounding the alarm.
*/
public void startAlarm() {
ac.loop();
}
/**
* Stop sounding the alarm.
*/
public void stopAlarm() {
ac.stop();
}
/**
* Main method for testing this class.
*
* @param args the command line arguments
*/
public static void main(String[] args) {
AlarmSounder as = new AlarmSounder();
as.startAlarm();
JOptionPane.showMessageDialog(null, "Alarm is sounding!",
"Alarm test", JOptionPane.WARNING_MESSAGE);
as.stopAlarm();
}
}
最後に、利用者インタフェース(UI)最上位エージェントである、UI枠付き窓エージェントのJavaFXプログラムを示します。
/*
* ScadaClientUI.fx
* The User Interface Frame (UIF) agent for a SCADA application.
*
* Created and modified:
* V 1.0.0 2008/08/01
*/
package client;
import javafx.animation.*;
import javafx.application.*;
import javafx.input.*;
import javafx.scene.*;
import javafx.scene.geometry.*;
import javafx.scene.paint.*;
import javafx.scene.text.*;
import java.lang.System;
/**
* @author terra
*/
var csc = new ClientSideConnection(); // Client-Side Connection agent.
var alarmSounder = new AlarmSounder(); // Alarm Sounder agent.
/*
* The Abstraction compenent of the UIF agent.
*/
class UIFAbstraction {
attribute numericDatum: Number;
attribute numericData: Number[] = for (i in [0..180]) 0.0;
attribute alarting: Boolean;
function getDisplayColor(value: Number): Color {
var c: Color = Color.GREEN;
if (value > 90.0) {
c = Color.RED;
} else if (value > 75.0) {
c = Color.color(1.0, 0.6, 0.0);
}
return c;
}
function setNumericDatum(value: Number): Void {
numericDatum = value;
delete numericData[0];
insert value into numericData;
if (numericDatum > 90.0 and not alarting) {
alarting = true;
alarmSounder.startAlarm();
alarmDialog.toFront();
alarmDialog.visible = true;
}
}
function setAlarting(alarting: Boolean): Void {
this.alarting = alarting;
}
}
var uifa = UIFAbstraction {};
/*
* The Control compenent of the UIF agent.
*/
class UIFControl {
attribute schedular: Timeline = Timeline{
repeatCount: Timeline.INDEFINITE
keyFrames: KeyFrame {
time: 200ms
action: function(): Void {
uifa.setNumericDatum(csc.receiveScadaData());
}
}
}
}
var uifc = UIFControl {};
uifc.schedular.start();
/*
* The Presentation compenent of the UIF agent.
*/
Frame {
title: "SCADA UI Example"
width: 800, height: 632
visible: true
closeAction: function(): Void {System.exit(0);}
stage: Stage {
width: 800, height: 600
fill: Color.color(0.9, 0.9, 0.9)
content: [
Group {
translateX: 50, translateY: 50
content: [
Rectangle {
width: 55, height: 18
arcWidth: 4, arcHeight: 4
stroke: Color.BLUE, fill: Color.WHITE
},
Text {
x: 4, y: 5
textOrigin: TextOrigin.TOP
fill: bind uifa.getDisplayColor(uifa.numericDatum)
font: Font {
name: "Monospaced", size: 14, style: FontStyle.PLAIN
}
content: bind "{%7.2f uifa.numericDatum}"
},
Text {
x: 60, y: 6
textOrigin: TextOrigin.TOP
font: Font {
name: "Monospaced", size: 14, style: FontStyle.PLAIN
}
content: "m3/s"
}
]
},
Group {
translateX: 200, translateY: 50
content: [
Rectangle {
width: 60, height: 120
stroke: Color.BLUE, fill: Color.WHITE
},
Rectangle {
x: 10, y: 10
width: 10, height: 100
fill: LinearGradient {
startX: 0.0, startY: 0.0
endX: 0.0, endY: 1.0
stops: [
Stop {offset: 0.05, color: Color.RED},
Stop {offset: 0.0875, color: Color.color(1.0, 0.6, 0.0)},
Stop {offset: 0.125, color: Color.LIMEGREEN},
]
}
},
Rectangle {
x: 10, y: 10
width: 10, height: bind 50 - uifa.numericDatum / 2
fill: Color.BLACK
},
Line {
startX: 23, startY: 10
endX: 23, endY: 110
stroke: Color.BLACK
},
Group {
translateX: 23, translateY: 10
content: for (i in [-100..100 step 50]) Line {
translateY: i / 2 + 50
startX: 0, startY: 0
endX: 3, endY: 0
stroke: Color.BLACK
}
},
Group {
translateX: 32, translateY: 10
content: for (i in [-100..100 step 50]) Text {
translateY: 53 - i / 2
content: "{i}"
font: Font {
name: "Monospaced", size: 10, style: FontStyle.PLAIN
}
horizontalAlignment: HorizontalAlignment.LEADING
verticalAlignment: VerticalAlignment.TOP
}
}
]
},
Group {
translateX: 50, translateY: 200
content: [
Rectangle {
width: 230, height: 120
stroke: Color.BLUE, fill: Color.WHITE
},
Rectangle {
x: 10, y: 22
width: 181, height: 88
fill: Color.BLACK
},
Rectangle {
x: 10, y: 15
width: 181, height: 7
fill: Color.GRAY
},
Rectangle {
x: 10, y: 10
width: 181, height: 5
fill: Color.DARKGRAY
},
Line {
startX: 194, startY: 10
endX: 194, endY: 110
stroke: Color.BLACK
},
Group {
translateX: 194, translateY: 10
content: for (i in [-100..100 step 50]) Line {
translateY: i / 2 + 50
startX: 0, startY: 0
endX: 3, endY: 0
stroke: Color.BLACK
}
},
Group {
translateX: 203, translateY: 11
content: for (i in [-100..100 step 50]) Text {
translateY: 53 - i / 2
content: "{i}"
font: Font {
name: "Monospaced", size: 10, style: FontStyle.PLAIN
}
horizontalAlignment: HorizontalAlignment.LEADING
verticalAlignment: VerticalAlignment.TOP
}
},
Group {
translateX: 10, translateY: 10
content: for (i in [0..179]) Line {
translateX: i
startX: 0, startY: bind 50 - uifa.numericData[i] / 2
endX: 1, endY: bind 50 - uifa.numericData[i + 1] / 2
strokeWidth: 2
stroke: bind uifa.getDisplayColor(uifa.numericData[i + 1])
}
}
]
}
]
}
}
/*
* The Alarm Dialog agent.
*/
var alarmDialog = Dialog {
title: "Alarm"
width: 300, height: 60
visible: false
closeAction: function(): Void {
alarmSounder.stopAlarm();
uifa.alarting = false;
}
stage: Stage {
content: Text {
x: 10, y: 10
textOrigin: TextOrigin.TOP
fill: Color.RED
font: Font {size: 14}
content: "Flow rate exceeded the second upper limit!"
}
}
}
2008年8月10日日曜日
第一段 デフラグをする前に
Windowsをお使いの皆さん、「ディスク デフラグ」はどうされていますか。
私はWindows XPを使っていますが、以前は、毎週定期的に磁気円盤上のファイルの控えをとった後に、そのままデフラグを行っていました。
しかし、磁気円盤上に情報が正常に読み書きできない部分がある状態でデフラグを行うと、最悪の場合、Windows自体が損傷を受け、個人用計算機が立ち上がらなくなってしまうことがあるようです。
Windows 98では、磁気円盤上の情報の読み書きで誤りを検出すると、デフラグの途中で「スキャン ディスクを行え」というような窓が表示され、デフラグが中断したように記憶していました。それで、磁気円盤の誤り検査を行わずに、いきなりデフラグを行っていたのですが、これが危険なことのようなのです。実際、私は痛い目にあってしまいました。
それ以来、デフラグを行う前には、磁気円盤の「エラー チェック」を行うようにしています(「そんなことは、常識だろ」といわれそうですが。なお、「エラー チェック」の方法をお知りになりたい場合は、「スタート」ボタンを押して表示される機能選択肢の「ヘルプとサポート」で検索してみてください)。私は、誤り検査だけしか行わない(修復や回復は行わない)ので、それほど時間はかかりません(修復や回復を行うように選択すると、駆動機構によってはWindowsを再起動する必要があります。再起動が必要ない駆動機構の場合でも、検査だけよりかなり時間がかかります)。
私にとっては、「そういうことは、早く教えてよ~」なことでした。「何事も先達はあらまほし…」といいますので、私の轍を踏むことのないようにと、老婆心ながら投稿しました(もっとも、誤り検出を行って正常なことを確認した上でデフラグを行えば安全だ、という保証があるわけではないのですが)。
なお、(私は経験していませんが、)「エラー チェック」で異常を検出すると、修復と回復を選択して、「エラー チェック」をし直すよう促す窓が表示されるようです。また、そもそもデフラグはあまり頻繁に行わないほうがよいと書かれているWebページもあります(頻繁に行ってもあまり効果がないとか、磁気円盤記憶装置の寿命を縮めるというのもありました。本当のところはどうなのでしょうか)。私は、分析の結果デフラグを行う必要がないと表示されたときは、デフラグを行わないようにしています(以前は、必要がないといわれてもデフラグを行っていましたが…)。
私はWindows XPを使っていますが、以前は、毎週定期的に磁気円盤上のファイルの控えをとった後に、そのままデフラグを行っていました。
しかし、磁気円盤上に情報が正常に読み書きできない部分がある状態でデフラグを行うと、最悪の場合、Windows自体が損傷を受け、個人用計算機が立ち上がらなくなってしまうことがあるようです。
Windows 98では、磁気円盤上の情報の読み書きで誤りを検出すると、デフラグの途中で「スキャン ディスクを行え」というような窓が表示され、デフラグが中断したように記憶していました。それで、磁気円盤の誤り検査を行わずに、いきなりデフラグを行っていたのですが、これが危険なことのようなのです。実際、私は痛い目にあってしまいました。
それ以来、デフラグを行う前には、磁気円盤の「エラー チェック」を行うようにしています(「そんなことは、常識だろ」といわれそうですが。なお、「エラー チェック」の方法をお知りになりたい場合は、「スタート」ボタンを押して表示される機能選択肢の「ヘルプとサポート」で検索してみてください)。私は、誤り検査だけしか行わない(修復や回復は行わない)ので、それほど時間はかかりません(修復や回復を行うように選択すると、駆動機構によってはWindowsを再起動する必要があります。再起動が必要ない駆動機構の場合でも、検査だけよりかなり時間がかかります)。
私にとっては、「そういうことは、早く教えてよ~」なことでした。「何事も先達はあらまほし…」といいますので、私の轍を踏むことのないようにと、老婆心ながら投稿しました(もっとも、誤り検出を行って正常なことを確認した上でデフラグを行えば安全だ、という保証があるわけではないのですが)。
なお、(私は経験していませんが、)「エラー チェック」で異常を検出すると、修復と回復を選択して、「エラー チェック」をし直すよう促す窓が表示されるようです。また、そもそもデフラグはあまり頻繁に行わないほうがよいと書かれているWebページもあります(頻繁に行ってもあまり効果がないとか、磁気円盤記憶装置の寿命を縮めるというのもありました。本当のところはどうなのでしょうか)。私は、分析の結果デフラグを行う必要がないと表示されたときは、デフラグを行わないようにしています(以前は、必要がないといわれてもデフラグを行っていましたが…)。
2008年8月7日木曜日
JavaFX台本型プログラミング言語基準(草案):改訂版
以前に和訳を投稿した「The JavaFX™ Script Programming Language Reference (Draft)」が改訂されましたので、訳の方も改めました。改訂内容としては、単に記述項目を追加しただけでなく、以前の記述内容や表現の見直しも行われています。
-------------------------------------------
JavaFX台本型プログラミング言語基準(草案)
目次
1.言語概要
2. クラスとオブジェクト
3.属性と関数
4.変数と基本データ型
5.式と演算子
6.列を宣言する
7.列を利用する
8.データ結合
9.引き金
10.動画
A.鍵語と予約語
1.言語概要
この章では、JavaFX™台本型プログラミング言語(訳注:以下、「JavaFX言語」と略記)の概要について説明する。ここでは、このプログラミング言語の主な特徴を高い水準で述べ、特定の構成概念についての詳述は後の章で行う。この本は、ウェブ(Web)ページにおいて、Java™ Web Startソフトウェアとしてまたは従来型の机上応用プログラムとして実行される、表現力豊かなインタネット依頼機応用プログラムや要素の、設計者および開発者を対象としたものである。読者がJavaScriptまたはJava™プログラミング言語(訳注:以下、「Java言語」と略記)のいずれか、または両者に精通していることを想定している。この文書は公式な言語仕様を定めたものではないが、現在支援されている言語の特徴のすべてに対する完全な基準とみなすことができる。
JavaFX言語は、次の特徴を有している。
以下の節では、JavaFX言語について駆け足で紹介する。しかるべき点についてはJava言語と比較および対比させながら、その核となる構文と機能についての一般的な導入を与える。そして、それぞれの主題は後の章で更に詳しく取り上げられる。
台本
JavaFX言語において、「台本」は1つ以上の宣言または式である。台本を評価することは、宣言または式を順序を追って評価することである。
これは、次のように表示する。
Java言語で書かれた応用プログラムと異なり、台本は、いかなるクラス宣言または関数も含む必要がない。
クラス
クラスの定義は、Java言語と多くの共通点を持っているが、いくつかの相違点は注目に値する。たとえば、情報は、領域ではなく、属性に格納される。振る舞いは、操作ではなく、関数によってあらわにされる。次の例は、それぞれの基本構文を示す単純な矩形クラスを定義している。
JavaFX言語は多重継承を支援しており、必要なら複数のクラスを継承することができる。
クラスは、第2章で詳しく取り上げられる。
属性と関数は、第3章で詳しく取り上げられる。
オブジェクト
オブジェクト直定数は、クラスの実体化のための簡便な構文を提供する。次のコードは、前述した矩形クラスの単一の実体を生成し、その幅と高さの属性を100に初期化する(newを必要としない点に留意すること)。
このオブジェクトへの参照を格納するには、var鍵語を使う。
オブジェクトは、第2章で詳しく取り上げられる。
変数と基本データ型は、第4章で詳しく取り上げられる。
式と演算子
他のプログラミング言語と同様に、JavaFX言語は、式と演算子を支援する。
第5章は、JavaFX言語において利用可能な式と演算子について論じる。
列
列は、オブジェクトの順序付けられた一覧を保持する。これは、Java言語の配列にほぼ類似している。両方とも複数の値を保持し、0から始まる索引により検索される。
部分列も支援されている。
第6章は列の宣言の基本を取り上げており、一方、第7章は列の利用に焦点を当てている。
データ結合
データ結合は、複数オブジェクトの状態を同期させるための簡便な構文を提供する。2つのオブジェクトが互いに結合されると、1番目のオブジェクトが更新されたときはいつも2番目のオブジェクトの値が自動的に変更される。データ結合の一般的な利用法は、GUI部品をその基となるデータに同期させ続けることである。
データ結合は、第8章で詳しく取り上げられる。
引き金
引き金は、ある条件が満たされると実行されるコードの区画である。たとえば、ある属性の値が不適切なものに設定された場合には、注意を喚起してほしいと思うかもしれない。次の例は、その基本的な引き金の構文を示す。
引き金は、第9章で詳しく取り上げられる。
2.クラスとオブジェクト
クラスを取り込む
取り込み文は、Java言語と同様に機能する。その構文は、
および
である。
取り込み文が存在する場合は、それ以外のすべての応用コードの前になければならない。JavaFX言語は、自身の組み込みライブラリクラスのための名前空間(パッケージjavafx.*)を定義しているが、標準的なJava言語のクラスを取り込むこともできる。
クラスを定義する
クラスを指定するための構文は、class鍵語に続けたクラス名、任意選択としてのextends鍵語とカンマで区切った基底クラス名の一覧、左中括弧、それぞれセミコロンで終結している属性と関数の一覧、および右中括弧からなる。
JavaFX言語は多重継承を支援しており、そうしたものとして、新たな用語と規定を定義している。
注記:現在のところ、単純クラスはJavaクラスに変換され、一方、複合クラスはJavaクラスとJavaインタフェースに変換される。
クラスはpublicと宣言することができ、それは、そのクラスが台本のどこからでも利用できることを意味する。さもなければ、そのクラスを含んでいる台本からのみ利用できる(これが既定)。
JavaFX言語は構築子を支援していない。その代わりに、オブジェクト直定数を使用しなければならない。構築子の作用をまねるためには、新たなオブジェクトを返す関数を定義し、その関数を呼び出す。
オブジェクトを定義する
前の章で述べたように、クラス実体化の推奨方法はオブジェクト直定数を使うことである。この形式のオブジェクト割り当ては、クラス名とその後に続く中括弧でくくった属性初期化子の一覧から構成される宣言的構文を用いる。それぞれの初期化子は、属性名に、コロンとその属性の値を定義する式を続けて構成する。しかしながら、オブジェクトを生成する際にnew鍵語を使うこともできる。
3.属性と関数
属性
属性はオブジェクトの状態を定める。それらは、attribute鍵語に、属性名、コロン(任意選択)、属性の型(任意選択)を続け、セミコロンで終結させたものを用いて宣言される。
属性の既定値を宣言することができる(後で、オブジェクト直定数で規定される値に定義しなおすことができる)。初期化子は、新たに生成されるオブジェクトの状況に即して、クラス宣言内で属性が指定された順に評価される。
明示的な初期化が行われないと、それぞれの属性は妥当な既定値に設定される。
上記コードは、次の既定値を画面に表示する(注記:最後の行は、空文字列を表示する)。
データ型は、第4章で取り上げられている。
関数
関数はオブジェクトの振る舞いを定める。関数は次の形式を取る。
ここで、本体は任意の式でかまわない。
関数は第一級オブジェクトである(たとえば、変数に割り当てたり、引数としてほかの関数へ渡すことができる)。
第1章で、何も引数を取らず、そして何も値を返さないgrow関数を定義している簡単な例を示した。
その例証では、利用者が特定の寸法を指定できるようにした、関数の多重定義版も与えた。
関数は、無名関数にもなりうる。無名関数は、GUI構成部品のaction属性に振る舞いを割り当てる際に、しばしば利用される。
4.変数と基本データ型
変数
第1章で論じたように、var鍵語は、新たな変数をプログラムに導入するために用いられる。変数の型は、それを宣言する際に指定してもよいが、指定は任意選択である。宣言から変数の型が省略されると、その型は変数の用いられ方からを推測することができる。
変数宣言は、次の形式を取る。
例:
変数の命名法の慣例は、Java言語における慣例と同一である。クラス名は、各単語の最初の文字を大文字にする(MyClass)。関数名は小文字で始まるが、後に続く各単語の最初の文字は大文字にする(myFunctionName)。定数は、下線文字で区切られた単語により、すべて大文字で表されなければならない(MY_CONSTANT)。
二重山括弧<<>>で閉じられた(空白文字を含む)いかなる文字列も、識別子として扱われる。これにより、JavaFX言語の鍵語(または、ほかの通常では不正な識別子)を、クラス、変数、関数、または属性の名前として用いることができる。
例:
また、JavaFX言語の鍵語と同じ名前の(Java言語で書かれた)操作を呼び出すこともできる。
変数の存続期間は、少なくとも、それを含むコード区画の存続期間と同じである。
基本データ型
JavaFX言語では、原始型(プリミティブ型)という用語は使わない。代わりに、5つの基本データ型を定義しており、応用コードで常に利用することができる。
5つの基本データ型は、次のようにJava言語に対応付けられる。
最初の4つのデータ型は、Java言語でもしばしば使われるので、ほとんどの開発者には既になじみのあるものであろう。しかし、Duration型はこれまでになく、JavaFX言語に特有のものである。javafx.lang.Durationクラスは、時間の単位(ミリ秒、秒、分、または時間)を表す。Durationクラスを実体化するための簡略表現である、時間直定数も支援している。
整数を表す型については、引数または戻り値をJava言語で書かれた操作と受け渡しするときに、自動的に強制型変換が行われる。
文字列に関しては、一重引用符または二重引用符のいずれかを用いて文字列直定数を指定することができる。
一重引用符と二重引用符は対称性を持っている。一重引用符を二重引用符の中に埋め込むことができるし、二重引用符を一重引用符の中に埋め込むこともできる。どちらにも、中括弧「{}」を用いて式を埋め込むことができる。
埋め込まれた式自体が引用符で囲まれた文字列を含むことができるので、次々に、さらなる埋め込み式を含むことができる。
Java言語と異なり、JavaFX言語の文字列直定数は、改行文字を含むことができる。
文字列式に付加的な書式設定接頭辞を与えることにより、数値と日付をどのように文字列に変換するかについても制御することができる。この付加的な書式設定接頭辞は、java.util.Formatterの仕様に従う。
訳注:原文では、「var dateStr = "{%tc date}";」文末のセミコロン(;)が抜け落ちている。また、日本では、日付は上記コメントの書式で表示される。
5.式と演算子
式
Java言語と異なり、JavaFX言語は式言語である。すべての実行文は、0個以上の入力とそれに続く0個(または1個)の出力から構成される式である。これには、条件、繰り返し、およびコード区画も含まれる。
次に実例を示す。
訳注:原文では、剰余演算子として「%」を使用しているが、表 5.1に載っているように、これは「mod」に変わっている。したがって、上記のコード例もmodで書き換えている。
区画式
区画式は、中括弧で囲まれた、セミコロン区切り文(宣言または式でもよい)の羅列から構成される。(区画内の)最後の文が式なら、その式の値が区画式の値となる。さもなければ、区画式はvoid型となる。
次の区画式は、数個の数を加え、その結果をtotalという名前の変数に格納する。
値域式
値域式は、次の構文により、等差数列を形成している数値の列を定める。
上記の式は、数1から数2までの整数(両端含む)からなる列を定める。
値域式の簡単な例は、次のようなものであろう。
既定では各数値間の刻み幅は1だが、異なる刻み幅を指定することもできる。たとえば、次の式は、1から10の間の奇数からなる列を定める。
降順に並んだ値域を生成するためは、終了値が開始値より小さいことを確認し、負数の刻み幅を指定する。
次の宣言文は、実際には空の列を宣言することに留意せよ。
条件式
if式は、Java言語におけるifと似ている。
Java言語は、if式と条件式(たとえば、a < b ? a : b)の両方を含んでいる。JavaFX言語のif式は、その両者に代わるものである。
繰り返し式
for式
for式は列とともに用いられるので、第7章で論じる。
while式
while式はJava言語の場合と同様であるが、whileの本体を囲む中括弧が常に必要である。
例:
そのほかの式
return式は、Java言語において見られるものと同一である。
throw式は、Java言語のものと似ているが、java.lang.Throwableを拡張したオブジェクトだけが投げられたり捕捉されたりできる。
try式とcatch式は、Java言語のものと似ているが、JavaFX言語の変数宣言の構文を用いる。
break式とcontinue式は、Java言語のものと似ているが、ラベルは支援されていない。
例:
演算子
JavaFX言語は、Java言語にみられるものと同様の標準的な演算子を提供する。次の表は、Java言語における等価演算子と比較させながら、優先順位に従ってそれらの演算子を一覧表にしたものである。
例:
訳注:原文で使用している「<>」演算子と「%」演算子は、それぞれ、「!=」と「mod」に書き換えた。
6.列を宣言する
先に述べた5つの基本データ型に加え、JavaFX言語は、「列」として知られたデータ構造を提供する。列はJava言語の配列に似ているが、異なる点もある。
次のコードはいくつかの例を示す。
列は、オブジェクトの順序付けられた一覧を表す。しかしながら、列そのものはオブジェクトではないため、入れ子構造を取ることはできない。列は、値により等価であるとみなされる。列の長さが等しく、かつそれらの要素が同一であるとき、列は等しい。(上記のdaysの初期化におけるように)入れ子になった列を形成する式は、自動的に平準化される。
さらに、単一のオブジェクトは、1つのオブジェクトからなる列に等価である。
列の型は、「[]」注釈を用いて宣言される。
列の要素は共通の型を持たねばならず、その型はオブジェクトであっても良い。列は、Java言語の配列のように、索引付けることがきる。
次の例のように、その要素が等差数列をなしている列に対する「..」を用いた簡便な表記法もある。
この簡略記法により、各要素を手入力する必要がなくなる。
[]演算子は、述語形式の選択も表す。述語は、次の形式を取る。
例:
この手の式は、元の列の要素の内、述部の条件を満たすものから構成される新たな列を返す。
最後になったが、部分列は、列の一部を利用できるようにする。
次の章で述べるように、いくつもの異なる方法で、列または部分列を利用および/または変更することができる。
7.列を利用する
forを用いた列の繰り返し
JavaFX言語は、for演算子を用いた列内包を支援する。列内包は、1つ以上の入力列、任意選択としてのろ過器、および式から構成される。各入力列は変数と関連している。列内包の結果は、元となる列の要素の組み合わせの内で、ろ過器の条件を満たすものに、式を適用した結果であるところの新たな列である。
次のプログラムはこの構文の実例を示したもので、曲集の一覧から曲の一部が表題となっているものを特定するためにfor演算子を用いている。
出力:
TITLE TRACK = A Hard Day's Night
Track = I Should Have Known Better
Track = If I Fell
Track = I'm Happy Just To Dance With You
Track = And I Love Her
Track = Tell Me Why
Track = Can't Buy Me Love
Track = Any Time At All
Track = I'll Cry Instead
Track = Things We Said Today
Track = When I Get Home
Track = You Can't Do That
Track = Heart Like A Wheel
Track = Get On Home
Track = Baby Wanna Dance
TITLE TRACK = Circle Of Love
Track = Macho City
次に示すのは、ろ過器を用いた別の例である。これは、数を受け取ってその因数の一覧を返す関数を定義したものである。
訳注:原文の「%」演算子を、「mod」に書き換えた。
forの内包の中で、indexof演算子を用いることができる。その構文は「indexof 名称」で、「名称」は繰り返し変数の名前である。その値は、基となる列における繰り返しの「索引」値である。
次の例に示したように、indexof演算子は列の抽出に使用することもできる。
列変数を変更する
insertは、列に新たな要素を挿入する。
deleteは、列から要素を取り除く。
次のコードは、挿入と削除の双方の例である。
訳注:原文では、最後の「delete nums[0..<];」文が「delete nums[0..>];」と誤記されている。
列の要素を逆順に並べ替えることもできる。
訳注:2008年7月23日時点のNetBeans 6.1用JavaFX追加接続(Plugin)プログラムで動作確認したところ、「reverse nums;」だけでは、元のnums の要素の並びは変わらなかった(並び替えた結果を利用するためには、「nums = reverse nums;」のように列変数に代入しなおす必要がある。その意味では、delete演算子とは用法が異なる)。
8.データ結合
bind鍵語は、対象となる変数の値を別の変数の値に結びつける。
これは、someExpressionの結果を変数xに結びつける。someExpressionの値が変わると、xの値も自動的に更新される。結び付けられる変数は、ある基本データ型の単純な値、式の結果、区画式の結果、もしくは結合関数でもよい(let鍵語は、それらに割り当てることができない変数を導入する)。
再計算して更新する
ある状況では、更新がどのように行われるか、あるいはsomeExpressionが変化することはどういう意味なのかを厳密に知っている必要ある。結び付けられる値が変化すると、最小限の再計算が実行される。この再計算は、ある限られた状況において問題になるだけである。たとえば、オブジェクトの生成を必要とする結合が実行される場合で、オブジェクトの同一性の理由から、新たなオブジェクトが実際に生成されたかどうかが問題となるような場合である。
expr2が変化するとsumは再計算されるが、expr1は再計算されない。expr1に対する値はすでに格納されており、単にその値を再度取ってくるだけである。
次に実例を示す。
このプログラムの出力は次のようになる。
yが7に設定されたとき、関数ten()は変化していないので再度呼び出されることはない。関数値は記憶されており再利用される。
結合と区画式
bindにおいて、区画式の中に存在しうる文は変数宣言だけである。bind内では、(1増および1減を含め)値の割り当ては禁止されていることに留意せよ。したがって、結合区画式は次のような形式を取る。
結合された式に対するいかなる変化も更新を引き起こし、その更新は最小限なので、変数は効率的に結合される。while、insertおよびdeleteは式なので、bindの中に現れてはならないことに留意せよ。
結合と条件式
次の例を考えてみよ。
condExprが変わると、if式のどちらの分岐が評価されるのかが変わる。expr1またはexpr2に対する変化は、どちらも、他方の式の再計算を引き起こすことはない。
結合とfor式
次の例を考えてみよ。
もしseqが変わっても、それでもseqに残っている要素に対応するnewSeqの要素は、再計算されない。言い換えれば、要素がseqに挿入されると、その要素にexprを適用した結果がnewSeqの相当する位置に挿入され、newSeqのほかの要素は再計算されない。
この例外は、exprがindexof elemを使用しているために、索引の値が変化した要素を更新する必要がある場合であるが、最小限の更新という規定には合致している。
この場合、次の再計算が実行される。
結合とオブジェクト直定数
オブジェクト直定数は、演算子や非結合関数のように機能する。オブジェクト直定数の引数の
myXが変わると、JavaFX言語は、新たなPointオブジェクトを生成する。これは、不変オブジェクトに対して予期された振る舞いでもある。
新たなオブジェクトを生成せずに、xの値がmyXの値を追跡するようにするためには、結合が必要になる。
この例に対しては、おそらくyも同様に結合したいと思うだろう。
ここで、ptは常に同じPointオブジェクトの実体と結合したままである。それは、最初の結合がなくても同様である。
結合と関数
非結合関数は、bound鍵語が前置されていない関数である。Java言語の操作やJavaFX言語の関数の呼び出しに関して、その引数のいずれかが変更されると、JavaFX言語はその関数を再度呼び出すが、関数の本体は暗箱である。関数が、その入力となる引数以外に持っている依存性により再計算が引き起こされることはない。
出力:
引数myXを変更すると、関数makePointは再度呼び出される。しかし、関数makePointは暗箱である。よって、scaleに割り当てられた値を変更しても、当然ではあろうが、関数は再計算されない。これこそが、結合関数が意図したものである。結合関数は予期された更新を引き起こす。
結合関数は、その本体として、結合された区画式を持っている(それゆえに、結合区画式に関する上述の制限を有している)。結合関数に結びつけると、引数以外の変化も更新を引き起こし、かつ引数の変更も調べられる。上述の関数makePointは結合関数にしてもよい。
そうすれば、scaleに割り当てられた値の変化は更新(20.0)を引き起こす。myXが変わるとx0 * scaleだけが再計算され、y0 * scaleは再計算されないことに留意せよ。
結合とは別のところで結合関数を呼び出すことは、非結合関数を呼び出すことと同じことである。
9.引き金
置換引き金
置換引き金に対する構文は次のとおりである。
置換引き金は、引き金を取り付けた変数に対するすべての変更の後に呼び出される。構文上の非終端子である旧値、最低索引値、最高索引値、および新要素は、その本体が区画から成る関数に対する事実上の仮引数である。
引数「[最低索引値..最高索引値]=新要素」は、引き金が取り付けられた変数が列型であるときのみ許される。
旧値と新要素は非対称であることに留意せよ。旧値は、引き金が取り付けられた変数の事前の値であり、一方、新要素は、変更対象となる要素のみから成る、その変数の新値の部分列である。
置換引金の構文は、部分列の割り当てを思い起こさせるようになっている。たとえば:
すると、oldVal はsaveに、loはiに、hiはjに、そしてnewValsはyに結びつけられた上で、expが評価される。
統合置換引き金は、部分列の割り当てとうまく機能する。たとえば、次のように結合を定義することができる。
これは次と等価である。
次のように、yがxの写像だとする。
これは次と等価である。
列の要素のある範囲を削除または置換すること、もしくは列を一箇所に挿入することは、すべて単一の引き金呼び出しに帰着する。述語条件を満たすすべての要素の削除のような、別の演算のあるものは、複数の引き金呼び出しに分解できるかもしれない。その場合、それぞれの引き金呼び出しから見える状態は一致している。特に、プログラマに可視の状態は、あたかも(たとえば)述語削除が独立な部分列削除演算の組として実装されているかのようである。
10.動画
動画概要
JavaFXは、主こま(齣)動画(key frame animation)を支援する。これは、プログラマが、各場面の動画による状態遷移を、時系列上のある点における状態の断面(主こま)を宣言することにより表現する宣言様式である。JavaFX言語は、主こま動画の2つの基本的な種類、離散と補間を支援する。その違いは、後者では、専用の補間関数がそれぞれの動画のこま間に存在する状態を計算する。どちらの場合も、処理系は自動的に動画を実行し、要求に従って動画を停止、中断、再開、反転、または繰り返す。
高次では、主こま動画は次のように表現することができる。
注記:Timelineオブジェクトは、図式動画とは無関係な、汎用の時間に基づく演算に対しても使用することできる。
離散動画
離散動画では、特定の性質の値が、主こまの時刻に、主こまで与えられた値に瞬時に変化する。たとえば、よく知られた「転げ回るデューク」の動画を考えてみよう。そこでは、動きの錯覚が、一組の画像を単にパラパラめくりすることによりもたらされる。こうした動画は、それぞれの動画のこまの「中間」を計算しないので、「離散的」である。
最初に、このプログラムは画像を記憶装置に読み込み、現在の画像を表すための変数(currDuke)を生成する。次に、時系列を生成し、いくつかの主こま(画像当たり1つ)を設定する。それぞれの主こまは100 msずつ離れて配置され、その画像をcurrDukeに割り当てる。最後に、GUIはその表示画像をcurrDukeと結合し、応用プログラムが動画のこまを繰り返すにつれて、現在の画像が画面上に表示されることを保証する。
補間動画
動画には、動的に計算しなければならない状態を持っているものもある。次に取り上げる例は、二次元の矩形をその寸法を変えながら回転させ、かつある範囲の表示色を繰り返す。この動画は、動画の経路に沿ったすべての可能な位置に主こまを生成する代わりに、プログラマが数個の主こま(開始、中間、終了)を単に宣言し、処理系にその中間のすべてを計算させるので、補間であるとされる。
主こま動画は通常のオブジェクトから生成されるが、作業をより容易にするため、特有の構文、すなわち、=>、tween、およびat演算子が提供されることに留意せよ。この例では、主こまを「at」演算子を用いて生成した。これは、KeyFrameオブジェクトの直定数構築子である。
A.鍵語と予約語
この付録は、JavaFX言語において予約された単語を記録したものである。
-------------------------------------------
JavaFX台本型プログラミング言語基準(草案)
目次
1.言語概要
2. クラスとオブジェクト
3.属性と関数
4.変数と基本データ型
5.式と演算子
6.列を宣言する
7.列を利用する
8.データ結合
9.引き金
10.動画
A.鍵語と予約語
1.言語概要
この章では、JavaFX™台本型プログラミング言語(訳注:以下、「JavaFX言語」と略記)の概要について説明する。ここでは、このプログラミング言語の主な特徴を高い水準で述べ、特定の構成概念についての詳述は後の章で行う。この本は、ウェブ(Web)ページにおいて、Java™ Web Startソフトウェアとしてまたは従来型の机上応用プログラムとして実行される、表現力豊かなインタネット依頼機応用プログラムや要素の、設計者および開発者を対象としたものである。読者がJavaScriptまたはJava™プログラミング言語(訳注:以下、「Java言語」と略記)のいずれか、または両者に精通していることを想定している。この文書は公式な言語仕様を定めたものではないが、現在支援されている言語の特徴のすべてに対する完全な基準とみなすことができる。
JavaFX言語は、次の特徴を有している。
・図式利用者インタフェース(GUI)構成部品の指定に宣言型構文を
使用しており、開発者のプログラムコードをGUIの実際の配置に
ぴたりと対応させることを可能にしている。
・宣言型データ結合と増分評価を使用しており、個々の構成部品を
容易に作成および構成することを可能にしている。応用プログラムの
データとGUIの構成部品は、自動的に同期がとられる。
・静的に型付けされており、Java言語において大規模プログラムの
作成と保守を可能にしているのと同様のコード構造、再利用および
情報隠ぺいなどの特性のほとんどを有している。
・どのような規模または複雑さのGUIも支援することができる。
・Swingの利用を容易にする。
以下の節では、JavaFX言語について駆け足で紹介する。しかるべき点についてはJava言語と比較および対比させながら、その核となる構文と機能についての一般的な導入を与える。そして、それぞれの主題は後の章で更に詳しく取り上げられる。
台本
JavaFX言語において、「台本」は1つ以上の宣言または式である。台本を評価することは、宣言または式を順序を追って評価することである。
var ten : Integer = 10;
java.lang.System.out.println("Twice {ten} is {2 * ten}.");
これは、次のように表示する。
Twice 10 is 20.
Java言語で書かれた応用プログラムと異なり、台本は、いかなるクラス宣言または関数も含む必要がない。
クラス
クラスの定義は、Java言語と多くの共通点を持っているが、いくつかの相違点は注目に値する。たとえば、情報は、領域ではなく、属性に格納される。振る舞いは、操作ではなく、関数によってあらわにされる。次の例は、それぞれの基本構文を示す単純な矩形クラスを定義している。
class Rectangle {
attribute width: Integer;
attribute height: Integer;
function grow(): Void {
grow(1);
}
function grow(amount: Integer): Void {
width += amount;
height += amount;
}
}
JavaFX言語は多重継承を支援しており、必要なら複数のクラスを継承することができる。
クラスは、第2章で詳しく取り上げられる。
属性と関数は、第3章で詳しく取り上げられる。
オブジェクト
オブジェクト直定数は、クラスの実体化のための簡便な構文を提供する。次のコードは、前述した矩形クラスの単一の実体を生成し、その幅と高さの属性を100に初期化する(newを必要としない点に留意すること)。
Rectangle {
width: 100
height: 100
}
このオブジェクトへの参照を格納するには、var鍵語を使う。
var myRect = Rectangle {
width: 100
height: 100
}
オブジェクトは、第2章で詳しく取り上げられる。
変数と基本データ型は、第4章で詳しく取り上げられる。
式と演算子
他のプログラミング言語と同様に、JavaFX言語は、式と演算子を支援する。
第5章は、JavaFX言語において利用可能な式と演算子について論じる。
列
列は、オブジェクトの順序付けられた一覧を保持する。これは、Java言語の配列にほぼ類似している。両方とも複数の値を保持し、0から始まる索引により検索される。
var week = ["Monday","Tuesday","Wednesday","Thursday",
"Friday","Saturday","Sunday"];
var mon = week[0];
var wed = week[2];
var fri = week[4];
部分列も支援されている。
var week = ["Monday","Tuesday","Wednesday","Thursday",
"Friday","Saturday","Sunday"];
var weekdays = week[0..4]; // 第1部分列
var weekend = week[5..6]; // 第2部分列
第6章は列の宣言の基本を取り上げており、一方、第7章は列の利用に焦点を当てている。
データ結合
データ結合は、複数オブジェクトの状態を同期させるための簡便な構文を提供する。2つのオブジェクトが互いに結合されると、1番目のオブジェクトが更新されたときはいつも2番目のオブジェクトの値が自動的に変更される。データ結合の一般的な利用法は、GUI部品をその基となるデータに同期させ続けることである。
import javafx.application.Frame;
import javafx.application.Stage;
import javafx.scene.text.Text;
var myString = "Hello World!";
Frame {
width: 50
height: 50
visible: true
stage: Stage {
content: Text {
content: bind myString
}
}
}
// コードのほかの部分でmyStringを変更すると、
// GUIの文字列も自動的に変更される。
データ結合は、第8章で詳しく取り上げられる。
引き金
引き金は、ある条件が満たされると実行されるコードの区画である。たとえば、ある属性の値が不適切なものに設定された場合には、注意を喚起してほしいと思うかもしれない。次の例は、その基本的な引き金の構文を示す。
import java.lang.System;
ReplaceDemo {
mySensitiveData: "Will anyone notice?"
}
class ReplaceDemo {
attribute mySensitiveData: String
on replace {
System.out.println("I noticed a change!");
};
// 応用特有の保護手段コードがここに書かれる
}
引き金は、第9章で詳しく取り上げられる。
2.クラスとオブジェクト
クラスを取り込む
取り込み文は、Java言語と同様に機能する。その構文は、
import パッケージ名.クラス名;
および
import パッケージ名.*;
である。
取り込み文が存在する場合は、それ以外のすべての応用コードの前になければならない。JavaFX言語は、自身の組み込みライブラリクラスのための名前空間(パッケージjavafx.*)を定義しているが、標準的なJava言語のクラスを取り込むこともできる。
import javafx.application.*;
import java.lang.System;
...
クラスを定義する
クラスを指定するための構文は、class鍵語に続けたクラス名、任意選択としてのextends鍵語とカンマで区切った基底クラス名の一覧、左中括弧、それぞれセミコロンで終結している属性と関数の一覧、および右中括弧からなる。
JavaFX言語は多重継承を支援しており、そうしたものとして、新たな用語と規定を定義している。
・単純クラスは、Java言語で書かれたクラスを直接的または間接的に
拡張した任意のクラスである。
・複合クラスは、単純クラスに当てはまらないすべてのクラスである。
・既定では、JavaFX言語で書かれたクラスは複合クラスである。
・クラスは、1つの単純クラスだけを拡張することができる。
そうしたクラスは、単純クラスとなる。クラスはまた、任意の数の
複合クラスまたはJava言語のインタフェースを拡張することもできる。
注記:現在のところ、単純クラスはJavaクラスに変換され、一方、複合クラスはJavaクラスとJavaインタフェースに変換される。
クラスはpublicと宣言することができ、それは、そのクラスが台本のどこからでも利用できることを意味する。さもなければ、そのクラスを含んでいる台本からのみ利用できる(これが既定)。
JavaFX言語は構築子を支援していない。その代わりに、オブジェクト直定数を使用しなければならない。構築子の作用をまねるためには、新たなオブジェクトを返す関数を定義し、その関数を呼び出す。
オブジェクトを定義する
前の章で述べたように、クラス実体化の推奨方法はオブジェクト直定数を使うことである。この形式のオブジェクト割り当ては、クラス名とその後に続く中括弧でくくった属性初期化子の一覧から構成される宣言的構文を用いる。それぞれの初期化子は、属性名に、コロンとその属性の値を定義する式を続けて構成する。しかしながら、オブジェクトを生成する際にnew鍵語を使うこともできる。
import java.io.File;
var tmpPath = "/home/users/docs/tmp.txt"
var myFile = new File("tmp.txt");
3.属性と関数
属性
属性はオブジェクトの状態を定める。それらは、attribute鍵語に、属性名、コロン(任意選択)、属性の型(任意選択)を続け、セミコロンで終結させたものを用いて宣言される。
attribute 属性名 : 属性型 ;
属性の既定値を宣言することができる(後で、オブジェクト直定数で規定される値に定義しなおすことができる)。初期化子は、新たに生成されるオブジェクトの状況に即して、クラス宣言内で属性が指定された順に評価される。
import java.lang.System;
class X {
attribute a: Number = 10;
attribute b: Number = -1;
}
var x = X { };
System.out.println(x.a); // 10.0を表示する
System.out.println(x.b); // -1.0を表示する
明示的な初期化が行われないと、それぞれの属性は妥当な既定値に設定される。
import java.lang.System;
class DefaultValuesDemo {
attribute a: Number;
attribute b: Integer;
attribute c: Boolean;
attribute d: String;
}
var demo = DefaultValuesDemo {};
System.out.println("Default Value: " + demo.a);
System.out.println("Default Value: " + demo.b);
System.out.println("Default Value: " + demo.c);
System.out.println("Default Value: " + demo.d);
上記コードは、次の既定値を画面に表示する(注記:最後の行は、空文字列を表示する)。
Default Value: 0.0
Default Value: 0
Default Value: false
Default Value:
データ型は、第4章で取り上げられている。
関数
関数はオブジェクトの振る舞いを定める。関数は次の形式を取る。
function 関数名 (属性名 : 属性型, ...): 戻り型 本体
ここで、本体は任意の式でかまわない。
関数は第一級オブジェクトである(たとえば、変数に割り当てたり、引数としてほかの関数へ渡すことができる)。
第1章で、何も引数を取らず、そして何も値を返さないgrow関数を定義している簡単な例を示した。
...
function grow(): Void {
grow(1);
}
...
その例証では、利用者が特定の寸法を指定できるようにした、関数の多重定義版も与えた。
...
function grow(amount: Integer): Void {
width += amount;
height += amount;
}
...
関数は、無名関数にもなりうる。無名関数は、GUI構成部品のaction属性に振る舞いを割り当てる際に、しばしば利用される。
// GUIにおいて
...
action: function() {
System.out.println("Click!");
}
...
4.変数と基本データ型
変数
第1章で論じたように、var鍵語は、新たな変数をプログラムに導入するために用いられる。変数の型は、それを宣言する際に指定してもよいが、指定は任意選択である。宣言から変数の型が省略されると、その型は変数の用いられ方からを推測することができる。
変数宣言は、次の形式を取る。
var 変数名 : 型 = 初期化子;
例:
var num = 1; // 推測された型
var num : Number = 2;
var firstName = "John"; // 推測された型
var lastName : String = "Doe";
変数の命名法の慣例は、Java言語における慣例と同一である。クラス名は、各単語の最初の文字を大文字にする(MyClass)。関数名は小文字で始まるが、後に続く各単語の最初の文字は大文字にする(myFunctionName)。定数は、下線文字で区切られた単語により、すべて大文字で表されなければならない(MY_CONSTANT)。
二重山括弧<<>>で閉じられた(空白文字を含む)いかなる文字列も、識別子として扱われる。これにより、JavaFX言語の鍵語(または、ほかの通常では不正な識別子)を、クラス、変数、関数、または属性の名前として用いることができる。
例:
var <<delete>> = 100;
また、JavaFX言語の鍵語と同じ名前の(Java言語で書かれた)操作を呼び出すこともできる。
import javax.swing.JTextArea;
var textArea = new JTextArea();
textArea.<<insert>>("Hello", 0);
変数の存続期間は、少なくとも、それを含むコード区画の存続期間と同じである。
基本データ型
JavaFX言語では、原始型(プリミティブ型)という用語は使わない。代わりに、5つの基本データ型を定義しており、応用コードで常に利用することができる。
5つの基本データ型は、次のようにJava言語に対応付けられる。
表 4.1 基本データ型
JavaFX言語 Java言語
String java.lang.String
Boolean java.lang.Boolean
Number java.lang.Number
Integer byte,short,int,long,BigInteger
Duration 該当なし
最初の4つのデータ型は、Java言語でもしばしば使われるので、ほとんどの開発者には既になじみのあるものであろう。しかし、Duration型はこれまでになく、JavaFX言語に特有のものである。javafx.lang.Durationクラスは、時間の単位(ミリ秒、秒、分、または時間)を表す。Durationクラスを実体化するための簡略表現である、時間直定数も支援している。
5ms; // 5ミリ秒
10s; // 10秒
30m; // 30分
1h; // 1時間
整数を表す型については、引数または戻り値をJava言語で書かれた操作と受け渡しするときに、自動的に強制型変換が行われる。
文字列に関しては、一重引用符または二重引用符のいずれかを用いて文字列直定数を指定することができる。
var s1 = 'Hello';
var s2 = "Hello";
一重引用符と二重引用符は対称性を持っている。一重引用符を二重引用符の中に埋め込むことができるし、二重引用符を一重引用符の中に埋め込むこともできる。どちらにも、中括弧「{}」を用いて式を埋め込むことができる。
var name = 'Joe';
var s = "Hello {name}"; // s = 'Hello Joe'
埋め込まれた式自体が引用符で囲まれた文字列を含むことができるので、次々に、さらなる埋め込み式を含むことができる。
var answer = true;
var s = "The answer is {if (answer) "Yes" else "No"}";
// s = 'The answer is Yes'
Java言語と異なり、JavaFX言語の文字列直定数は、改行文字を含むことができる。
文字列式に付加的な書式設定接頭辞を与えることにより、数値と日付をどのように文字列に変換するかについても制御することができる。この付加的な書式設定接頭辞は、java.util.Formatterの仕様に従う。
import java.util.Date;
var hexStr = "hex of 13 is 0x{%02X 13}";
// hexStr = "hex of 13 is 0x0D"
var date = new Date(107, 10, 11);
var dateStr = "{%tc date}";
// dateStr = "日 11 11 00:00:00 JST 2007"
訳注:原文では、「var dateStr = "{%tc date}";」文末のセミコロン(;)が抜け落ちている。また、日本では、日付は上記コメントの書式で表示される。
5.式と演算子
式
Java言語と異なり、JavaFX言語は式言語である。すべての実行文は、0個以上の入力とそれに続く0個(または1個)の出力から構成される式である。これには、条件、繰り返し、およびコード区画も含まれる。
次に実例を示す。
import java.lang.Math;
import java.lang.System;
var rand = (Math.random() * 100).intValue();
var s:String = null;
if (rand mod 2 == 0) {
s = "rand is even"
} else {
s = "rand is odd"
};
System.out.println(s);
訳注:原文では、剰余演算子として「%」を使用しているが、表 5.1に載っているように、これは「mod」に変わっている。したがって、上記のコード例もmodで書き換えている。
区画式
区画式は、中括弧で囲まれた、セミコロン区切り文(宣言または式でもよい)の羅列から構成される。(区画内の)最後の文が式なら、その式の値が区画式の値となる。さもなければ、区画式はvoid型となる。
次の区画式は、数個の数を加え、その結果をtotalという名前の変数に格納する。
import java.lang.System;
var nums = [5, 7, 3, 9];
var total = {
var sum = 0;
for (a in nums) { sum += a };
sum;
}
System.out.println("Total is {total} ");
値域式
値域式は、次の構文により、等差数列を形成している数値の列を定める。
[数1..数2]
上記の式は、数1から数2までの整数(両端含む)からなる列を定める。
値域式の簡単な例は、次のようなものであろう。
import java.lang.System;
var nums = [0..3];
System.out.println(nums == [0,1,2,3]); // trueと表示する
既定では各数値間の刻み幅は1だが、異なる刻み幅を指定することもできる。たとえば、次の式は、1から10の間の奇数からなる列を定める。
[1..10 step 2]
降順に並んだ値域を生成するためは、終了値が開始値より小さいことを確認し、負数の刻み幅を指定する。
import java.lang.System;
var nums = [3..0 step -1];
System.out.println(nums == [3,2,1,0]); // trueと表示する
次の宣言文は、実際には空の列を宣言することに留意せよ。
var nums1 = [3..0 ];
var nums2 = [3..0 step 1];
条件式
if式は、Java言語におけるifと似ている。
if (condition1) {
System.out.println("Condition 1");
} else if (condition2) {
System.out.println("Condition2");
} else {
System.out.println("not Condition 1 or Condition 2");
}
Java言語は、if式と条件式(たとえば、a < b ? a : b)の両方を含んでいる。JavaFX言語のif式は、その両者に代わるものである。
繰り返し式
for式
for式は列とともに用いられるので、第7章で論じる。
while式
while式はJava言語の場合と同様であるが、whileの本体を囲む中括弧が常に必要である。
例:
import java.lang.System;
var i = 0;
while (i < 10) {
if (i > 5) {
break;
}
System.out.println("i = {i}");
i += 1;
}
そのほかの式
return式は、Java言語において見られるものと同一である。
function add(x, y) {
return x + y;
}
throw式は、Java言語のものと似ているが、java.lang.Throwableを拡張したオブジェクトだけが投げられたり捕捉されたりできる。
import java.lang.Exception;
function foo() {
throw new Exception("this is a java exception");
}
function bar() {
throw "just a string";
}
try式とcatch式は、Java言語のものと似ているが、JavaFX言語の変数宣言の構文を用いる。
try {
throw "Hello";
} catch (s:String) {
System.out.println("caught a String: {s}");
} catch (any) {
System.out.println("caught something not a String: {any}");
} finally {
System.out.println("finally...");
}
break式とcontinue式は、Java言語のものと似ているが、ラベルは支援されていない。
例:
import java.lang.System;
function foo() {
for (i in [0..10]) {
if (i > 5) {
break;
}
if (i mod 2 == 0) {
continue;
}
System.out.println(i);
}
}
function bar() {
var i = 0;
while (i < 10) {
if (i > 5) {
break;
}
if (i mod 2 == 0) {
continue;
}
System.out.println(i);
i += 1;
}
}
演算子
JavaFX言語は、Java言語にみられるものと同様の標準的な演算子を提供する。次の表は、Java言語における等価演算子と比較させながら、優先順位に従ってそれらの演算子を一覧表にしたものである。
表 5.1 演算子の優先順位
優先度 JavaFX演算子 演算 Java演算子 評価順序
1 function() JavaFX関数
() 括弧内の式
new オブジェクト生成
{オブジェクト オブジェクト生成
直定数} と初期化
2 ++ (後置) 1増後代入 ++ 右から左
-- (後置) 1減後代入 --
3 ++ (前置) 1増前代入 ++ 右から左
-- (前置) 1減前代入 --
not 論理否定
sizeof 列の要素数
reverse 列の反転
indexof 列要素の索引
4 * 乗算 * 左から右
/ 除算 /
mod 剰余 %
5 + 加算 + 左から右
- 減算 -
6 == 等価 == 左から右
!= 不等価 !=
< 小なり <
<= 以下 <=
> 大なり >
>= 以上 >=
7 instanceof 型検査 instanceof
as 型変換
8 or 論理OR || 右から左
9 and 論理AND && 右から左
10 += 加算代入 +=
-= 減算代入 -+
*= 乗算代入 *=
/= 除算代入 /=
%= 剰余代入 %=
11 = 代入 =
例:
import java.lang.System;
import java.lang.Math;
var x = 2;
var y = 4;
var a = true;
var b = false;
System.out.println(x == y); // falseを表示する
System.out.println(x != y); // trueを表示する
System.out.println(x < y); // trueを表示する
System.out.println(x > y); // falseを表示する
System.out.println(x >= y); // falseを表示する
System.out.println(x <= y); // trueを表示する
System.out.println(x + y); // 6を表示する
System.out.println(x - y); // -2を表示する
System.out.println(x * y); // 8を表示する
System.out.println(x / y); // 0を表示する
System.out.println(x mod y); // 2を表示する
System.out.println(a and b); // falseを表示する
System.out.println(a or b); // trueを表示する
System.out.println(not a); // falseを表示する
System.out.println(sizeof [x,y]); // 2 を表示する
System.out.println([x,y][e | indexof e == 0]); // [ 2 ]を表示する
System.out.println(if (a) x else y); // 2を表示する
System.out.println(for(q in [x, y] where q < 3) q); // [ 2 ]を表示する
System.out.println(Math.max(x, y)); // 4を表示する
System.out.println("abc".toUpperCase()); // ABCを表示する
System.out.println(x); // 2を表示する
訳注:原文で使用している「<>」演算子と「%」演算子は、それぞれ、「!=」と「mod」に書き換えた。
6.列を宣言する
先に述べた5つの基本データ型に加え、JavaFX言語は、「列」として知られたデータ構造を提供する。列はJava言語の配列に似ているが、異なる点もある。
次のコードはいくつかの例を示す。
var weekDays = ["Mon","Tue","Wed","Thur","Fri"];
var days = [weekDays, ["Sat","Sun"]];
列は、オブジェクトの順序付けられた一覧を表す。しかしながら、列そのものはオブジェクトではないため、入れ子構造を取ることはできない。列は、値により等価であるとみなされる。列の長さが等しく、かつそれらの要素が同一であるとき、列は等しい。(上記のdaysの初期化におけるように)入れ子になった列を形成する式は、自動的に平準化される。
days == ["Mon","Tue","Wed","Thur","Fri","Sat","Sun"]; // trueを返す
さらに、単一のオブジェクトは、1つのオブジェクトからなる列に等価である。
1 == [1]; // trueを返す
列の型は、「[]」注釈を用いて宣言される。
var xs:Number[]; // 数の列
var strs:String[]; // 文字列の列
列の要素は共通の型を持たねばならず、その型はオブジェクトであっても良い。列は、Java言語の配列のように、索引付けることがきる。
var wednesday = days[2];
次の例のように、その要素が等差数列をなしている列に対する「..」を用いた簡便な表記法もある。
var nums = [1..100];
この簡略記法により、各要素を手入力する必要がなくなる。
[]演算子は、述語形式の選択も表す。述語は、次の形式を取る。
列[変数名| 論理式]
例:
var nums = [1,2,3,4];
var numsGreaterThanTwo = nums[n|n > 2];
この手の式は、元の列の要素の内、述部の条件を満たすものから構成される新たな列を返す。
最後になったが、部分列は、列の一部を利用できるようにする。
seq[a..b] // 索引aとbの間(両端含む)の列
seq[a..<b] // 索引a(含む)とb(含まない)の間の列
seq[a..] // seq[a..<sizeof seq]に同じ
seq[a..<] // 一貫性を保つため、seq[a..<sizeof seq-1] に同じ
次の章で述べるように、いくつもの異なる方法で、列または部分列を利用および/または変更することができる。
7.列を利用する
forを用いた列の繰り返し
JavaFX言語は、for演算子を用いた列内包を支援する。列内包は、1つ以上の入力列、任意選択としてのろ過器、および式から構成される。各入力列は変数と関連している。列内包の結果は、元となる列の要素の組み合わせの内で、ろ過器の条件を満たすものに、式を適用した結果であるところの新たな列である。
次のプログラムはこの構文の実例を示したもので、曲集の一覧から曲の一部が表題となっているものを特定するためにfor演算子を用いている。
class Album {
attribute title: String;
attribute artist: String;
attribute tracks: String[];
}
var albums =
[Album {
title: "A Hard Day's Night"
artist: "The Beatles"
tracks:
["A Hard Day's Night",
"I Should Have Known Better",
"If I Fell",
"I'm Happy Just To Dance With You",
"And I Love Her",
"Tell Me Why",
"Can't Buy Me Love",
"Any Time At All",
"I'll Cry Instead",
"Things We Said Today",
"When I Get Home",
"You Can't Do That"]
},
Album {
title: "Circle Of Love"
artist: "Steve Miller Band"
tracks:
["Heart Like A Wheel",
"Get On Home",
"Baby Wanna Dance",
"Circle Of Love",
"Macho City"]
}];
for (album in albums, track in album.tracks) {
if (album.title == track)
java.lang.System.out.println("TITLE TRACK = {track}")
else
java.lang.System.out.println("Track = {track}")
}
出力:
TITLE TRACK = A Hard Day's Night
Track = I Should Have Known Better
Track = If I Fell
Track = I'm Happy Just To Dance With You
Track = And I Love Her
Track = Tell Me Why
Track = Can't Buy Me Love
Track = Any Time At All
Track = I'll Cry Instead
Track = Things We Said Today
Track = When I Get Home
Track = You Can't Do That
Track = Heart Like A Wheel
Track = Get On Home
Track = Baby Wanna Dance
TITLE TRACK = Circle Of Love
Track = Macho City
次に示すのは、ろ過器を用いた別の例である。これは、数を受け取ってその因数の一覧を返す関数を定義したものである。
function factors(n:Number) {
return for (i in [1 .. n/2] where n mod i == 0) i;
}
訳注:原文の「%」演算子を、「mod」に書き換えた。
forの内包の中で、indexof演算子を用いることができる。その構文は「indexof 名称」で、「名称」は繰り返し変数の名前である。その値は、基となる列における繰り返しの「索引」値である。
次の例に示したように、indexof演算子は列の抽出に使用することもできる。
var nums = [1..5];
var numsExceptTheFirstTwo = nums[n|indexof n > 1]; // [ 3, 4, 5 ]を返す
列変数を変更する
insertは、列に新たな要素を挿入する。
insert x into seq
insert x before seq[idx]
insert x after seq[idx]
deleteは、列から要素を取り除く。
delete seq
delete x from seq
delete seq[idx]
delete seq[a..b]
次のコードは、挿入と削除の双方の例である。
// 挿入の例
var nums = [1..5];
var x = 6;
insert x into nums; // 結果は、[ 1, 2, 3, 4, 5, 6 ]
x++;
insert x before nums[0]; // 結果は、[ 7, 1, 2, 3, 4, 5, 6 ]
x++;
insert x after nums[3]; // 結果は、[ 7, 1, 2, 3, 8, 4, 5, 6 ]
// 削除の例
nums = [1..5];
delete 2 from nums; // 結果は、[ 1, 3, 4, 5 ]
delete nums[0];// 結果は、[ 3, 4, 5 ]
nums = [1..10]; // 結果は、[ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]
delete nums[3..7]; // 結果は、[ 1, 2, 3, 9, 10 ]
delete nums; // 結果は、[ ]
nums = [1..10];
delete nums[5..]; // 結果は、[ 1, 2, 3, 4, 5 ]
delete nums[0..<]; // 結果は、[ 5 ]
訳注:原文では、最後の「delete nums[0..<];」文が「delete nums[0..>];」と誤記されている。
列の要素を逆順に並べ替えることもできる。
var nums = [1..5];
reverse nums; // [ 5, 4, 3, 2, 1 ]を返す
訳注:2008年7月23日時点のNetBeans 6.1用JavaFX追加接続(Plugin)プログラムで動作確認したところ、「reverse nums;」だけでは、元のnums の要素の並びは変わらなかった(並び替えた結果を利用するためには、「nums = reverse nums;」のように列変数に代入しなおす必要がある。その意味では、delete演算子とは用法が異なる)。
8.データ結合
bind鍵語は、対象となる変数の値を別の変数の値に結びつける。
let x = bind someExpression;
これは、someExpressionの結果を変数xに結びつける。someExpressionの値が変わると、xの値も自動的に更新される。結び付けられる変数は、ある基本データ型の単純な値、式の結果、区画式の結果、もしくは結合関数でもよい(let鍵語は、それらに割り当てることができない変数を導入する)。
再計算して更新する
ある状況では、更新がどのように行われるか、あるいはsomeExpressionが変化することはどういう意味なのかを厳密に知っている必要ある。結び付けられる値が変化すると、最小限の再計算が実行される。この再計算は、ある限られた状況において問題になるだけである。たとえば、オブジェクトの生成を必要とする結合が実行される場合で、オブジェクトの同一性の理由から、新たなオブジェクトが実際に生成されたかどうかが問題となるような場合である。
let sum = bind expr1 + expr2;
expr2が変化するとsumは再計算されるが、expr1は再計算されない。expr1に対する値はすでに格納されており、単にその値を再度取ってくるだけである。
次に実例を示す。
import java.lang.System;
var y = 3;
function ten() : Integer { 10 }
let sum = bind ten() + y;
System.out.println(sum);
y = 7;
System.out.println(sum);
このプログラムの出力は次のようになる。
13
17
yが7に設定されたとき、関数ten()は変化していないので再度呼び出されることはない。関数値は記憶されており再利用される。
結合と区画式
bindにおいて、区画式の中に存在しうる文は変数宣言だけである。bind内では、(1増および1減を含め)値の割り当ては禁止されていることに留意せよ。したがって、結合区画式は次のような形式を取る。
bind { var a = expr; var b = expr; var c = expr; expr }
結合された式に対するいかなる変化も更新を引き起こし、その更新は最小限なので、変数は効率的に結合される。while、insertおよびdeleteは式なので、bindの中に現れてはならないことに留意せよ。
結合と条件式
次の例を考えてみよ。
let x = bind if (condExpr) expr1 else expr2;
condExprが変わると、if式のどちらの分岐が評価されるのかが変わる。expr1またはexpr2に対する変化は、どちらも、他方の式の再計算を引き起こすことはない。
結合とfor式
次の例を考えてみよ。
let newSeq = bind for (elem in seq) expr;
もしseqが変わっても、それでもseqに残っている要素に対応するnewSeqの要素は、再計算されない。言い換えれば、要素がseqに挿入されると、その要素にexprを適用した結果がnewSeqの相当する位置に挿入され、newSeqのほかの要素は再計算されない。
この例外は、exprがindexof elemを使用しているために、索引の値が変化した要素を更新する必要がある場合であるが、最小限の更新という規定には合致している。
import java.lang.System;
var min = 0;
var max = 3;
function square(x : Integer) : Integer { x*x }
let values = bind for (x in [min..max]) square(x);
System.out.println(values);
max = 5;
System.out.println(values);
min = 1;
System.out.println(values);
min = 0;
System.out.println(values);
この場合、次の再計算が実行される。
・0から3の2乗を計算する。
・4と5の2乗を計算する(maxが変わっても、0から3の2乗は再計算されない)。
・ほかの値は再計算せず、0の2乗を削除する。
・0の2乗を戻す。これは、再計算を必要とする。
この動作は、代わりにinsertやdeleteを用いても同様である。
結合とオブジェクト直定数
オブジェクト直定数は、演算子や非結合関数のように機能する。オブジェクト直定数の引数の
let pt = bind Point { x: myX y: myY }
myXが変わると、JavaFX言語は、新たなPointオブジェクトを生成する。これは、不変オブジェクトに対して予期された振る舞いでもある。
新たなオブジェクトを生成せずに、xの値がmyXの値を追跡するようにするためには、結合が必要になる。
let pt = bind Point { x: bind myX y: myY }
この例に対しては、おそらくyも同様に結合したいと思うだろう。
let pt = bind Point { x: bind myX y: bind myY }
ここで、ptは常に同じPointオブジェクトの実体と結合したままである。それは、最初の結合がなくても同様である。
let pt = Point { x: bind myX y: bind myY }
結合と関数
非結合関数は、bound鍵語が前置されていない関数である。Java言語の操作やJavaFX言語の関数の呼び出しに関して、その引数のいずれかが変更されると、JavaFX言語はその関数を再度呼び出すが、関数の本体は暗箱である。関数が、その入力となる引数以外に持っている依存性により再計算が引き起こされることはない。
import java.lang.System;
class Point {
attribute x : Number;
attribute y : Number;
}
var scale = 1.0;
function makePoint(x0 : Number, y0 : Number) : Point {
Point {
x: x0 * scale
y: y0 * scale
}
}
var myX = 3.0;
var myY = 3.0;
let pt = bind makePoint(myX, myY);
System.out.println(pt.x);
myX = 10.0;
System.out.println(pt.x);
scale = 2.0;
System.out.println(pt.x);
出力:
3.0
10.0
10.0
引数myXを変更すると、関数makePointは再度呼び出される。しかし、関数makePointは暗箱である。よって、scaleに割り当てられた値を変更しても、当然ではあろうが、関数は再計算されない。これこそが、結合関数が意図したものである。結合関数は予期された更新を引き起こす。
結合関数は、その本体として、結合された区画式を持っている(それゆえに、結合区画式に関する上述の制限を有している)。結合関数に結びつけると、引数以外の変化も更新を引き起こし、かつ引数の変更も調べられる。上述の関数makePointは結合関数にしてもよい。
bound function makePoint(x0 : Number, y0 : Number) : Point {
そうすれば、scaleに割り当てられた値の変化は更新(20.0)を引き起こす。myXが変わるとx0 * scaleだけが再計算され、y0 * scaleは再計算されないことに留意せよ。
結合とは別のところで結合関数を呼び出すことは、非結合関数を呼び出すことと同じことである。
9.引き金
置換引き金
置換引き金に対する構文は次のとおりである。
"on" "replace"
[旧値]
[ "[" 最低索引値 ".." 最高索引値 "]" "=" 新要素 ]
区画
置換引き金は、引き金を取り付けた変数に対するすべての変更の後に呼び出される。構文上の非終端子である旧値、最低索引値、最高索引値、および新要素は、その本体が区画から成る関数に対する事実上の仮引数である。
・旧値は変数の事前の値であり、変数と同じ型を持つ。
・最低索引値と最高索引値は、旧値の置換対象となる部分の範囲を
区切る。それらの型はIntegerである。純然たる挿入に対しては、
「最高索引値==最低索引値-1」である。
・新要素は、部分列「旧値[最低索引値..最高索引値]」を
置き換える値の列である。その型は変数と同じである。
引数「[最低索引値..最高索引値]=新要素」は、引き金が取り付けられた変数が列型であるときのみ許される。
旧値と新要素は非対称であることに留意せよ。旧値は、引き金が取り付けられた変数の事前の値であり、一方、新要素は、変更対象となる要素のみから成る、その変数の新値の部分列である。
置換引金の構文は、部分列の割り当てを思い起こさせるようになっている。たとえば:
attribute x
on replace oldVal[lo..hi]=newVals { exp };
var save = x;
x[i..j] = y;
すると、oldVal はsaveに、loはiに、hiはjに、そしてnewValsはyに結びつけられた上で、expが評価される。
統合置換引き金は、部分列の割り当てとうまく機能する。たとえば、次のように結合を定義することができる。
attribute x;
attribute y = bind x;
これは次と等価である。
attribute x =
on replace [i..j]=n
{ y[i..j]=n };
attribute y = [];
次のように、yがxの写像だとする。
attribute x;
attribute y = bind for (xi in x) f(xi);
これは次と等価である。
attribute x =
on replace [i..j]=n
{ y[i..j] = for (k in n) f(k) };
attribute y = [];
列の要素のある範囲を削除または置換すること、もしくは列を一箇所に挿入することは、すべて単一の引き金呼び出しに帰着する。述語条件を満たすすべての要素の削除のような、別の演算のあるものは、複数の引き金呼び出しに分解できるかもしれない。その場合、それぞれの引き金呼び出しから見える状態は一致している。特に、プログラマに可視の状態は、あたかも(たとえば)述語削除が独立な部分列削除演算の組として実装されているかのようである。
10.動画
動画概要
JavaFXは、主こま(齣)動画(key frame animation)を支援する。これは、プログラマが、各場面の動画による状態遷移を、時系列上のある点における状態の断面(主こま)を宣言することにより表現する宣言様式である。JavaFX言語は、主こま動画の2つの基本的な種類、離散と補間を支援する。その違いは、後者では、専用の補間関数がそれぞれの動画のこま間に存在する状態を計算する。どちらの場合も、処理系は自動的に動画を実行し、要求に従って動画を停止、中断、再開、反転、または繰り返す。
高次では、主こま動画は次のように表現することができる。
・動画は、javafx.animation.Timelineオブジェクトにより表される
時系列に沿って再生される。
・それぞれの時系列は、javafx.animation.KeyFrameオブジェクト
により表される2つ以上の主こまを含む。
・それぞれの時系列は、それを表現するある種の属性(autoReverse、
repeatCount、toggle、など)と、再生を制御するいくつかの関数
(start()、stop()、pause()、およびresume())を提供する。
・それぞれの主こまには、それを含んでいる時系列における相対時刻が
付随し、それ自身、3つのもの、主値の一覧、従時系列、および
「引き金」を含む。含まれている従時系列のそれぞれは、主こまの
主時刻からの相対開始点で評価される。それゆえ、時系列は、
階層的に構成し、従時系列を含むことができる。従時系列自身、
同じことを繰り返すことができる。「引き金」は、それが存在すれば、
主こまで指定された時刻に実行される手続きコードの区画である。
・javafx.animation.KeyValueオブジェクトは、ある特性に対し、
その前の主こまからの相対で「中割り」の値を計算するために
用いられる関数とともに、主こまのその時刻におけるその特性の
最終状態を表す。
注記:Timelineオブジェクトは、図式動画とは無関係な、汎用の時間に基づく演算に対しても使用することできる。
離散動画
離散動画では、特定の性質の値が、主こまの時刻に、主こまで与えられた値に瞬時に変化する。たとえば、よく知られた「転げ回るデューク」の動画を考えてみよう。そこでは、動きの錯覚が、一組の画像を単にパラパラめくりすることによりもたらされる。こうした動画は、それぞれの動画のこまの「中間」を計算しないので、「離散的」である。
import javafx.application.Frame;
import javafx.application.Stage;
import javafx.scene.paint.Color;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
// 画像データを読み込む
var images = for (i in [1..17]) {
Image { url: "file:images/T{i}.gif"};
// 画像は手元に格納されていると仮定する
// ウェブから画像を読み込むには、代わりに次の行
// (訳注:原文では1行)を使用する
// Image {url: "http://java.sun.com/applets/other/
// TumblingDuke/images/T{i}.gif"};
}
// 最初の画像を設定する
var currDuke = images[0];
// 画像を繰り返すための時系列を生成する
var timeline = Timeline {
toggle: true
keyFrames: for (image in images) {
KeyFrame {
time: 100ms* indexof image
// 各こまは100ms離れる
action: function(){currDuke=image;}
}
}
}
Frame {
title: "Click on Duke!"
width: 250
height: 150
visible: true
stage: Stage {
content: ImageView {
image: bind currDuke;
onMouseClicked: function(e) {timeline.start();}
}
}
}
最初に、このプログラムは画像を記憶装置に読み込み、現在の画像を表すための変数(currDuke)を生成する。次に、時系列を生成し、いくつかの主こま(画像当たり1つ)を設定する。それぞれの主こまは100 msずつ離れて配置され、その画像をcurrDukeに割り当てる。最後に、GUIはその表示画像をcurrDukeと結合し、応用プログラムが動画のこまを繰り返すにつれて、現在の画像が画面上に表示されることを保証する。
補間動画
動画には、動的に計算しなければならない状態を持っているものもある。次に取り上げる例は、二次元の矩形をその寸法を変えながら回転させ、かつある範囲の表示色を繰り返す。この動画は、動画の経路に沿ったすべての可能な位置に主こまを生成する代わりに、プログラマが数個の主こま(開始、中間、終了)を単に宣言し、処理系にその中間のすべてを計算させるので、補間であるとされる。
import javafx.application.Frame;
import javafx.application.Stage;
import javafx.scene.geometry.Rectangle;
import javafx.scene.paint.Color;
import javafx.scene.transform.Transform;
import javafx.scene.Cursor;
import javafx.animation.Timeline;
import javafx.animation.Interpolator;
Frame {
title: "Animation Demo";
width: 500
height: 400
visible: true
stage: Stage {
content: Rectangle {
// 変数を宣言する
var rotation = 0.0;
var size = 1.0;
var color = Color.GREEN;
// 属性を初期化する
cursor: Cursor.HAND
height: 50
width: 50
fill: bind color
transform: bind [Transform.translate(100,100),
Transform.scale(size,size),
Transform.rotate(rotation,25,25)]
// 動画の時系列を生成する
var myTimeline = Timeline {
toggle: true
// 主こま
var begin = at (0s) {
size => 1.0;
color => Color.GREEN;
rotation => 0.0;
}
var mid = at (0.5s) {
color => Color.PURPLE
tween Interpolator.EASEBOTH;
}
var end = at (1s) {
size => 3.0 tween Interpolator.LINEAR;
color => Color.RED
tween Interpolator.EASEBOTH;
rotation => 360.0
tween Interpolator.EASEBOTH;
}
keyFrames: [begin,mid,end]
}
onMouseClicked: function(e) {
myTimeline.start();
}
}
}
}
主こま動画は通常のオブジェクトから生成されるが、作業をより容易にするため、特有の構文、すなわち、=>、tween、およびat演算子が提供されることに留意せよ。この例では、主こまを「at」演算子を用いて生成した。これは、KeyFrameオブジェクトの直定数構築子である。
A.鍵語と予約語
この付録は、JavaFX言語において予約された単語を記録したものである。
表 A.1 鍵語と予約語
実装済み鍵語
abstract attribute bind break
class continue delete false for
function if import init insert
new not null package
private public return
super sizeof this throw
try true var while after
and as before catch
dur else exclusive extends
finally in bound
indexof into inverse lazy
on or replace step
with where instanceof override at
then tween
予約済み鍵語
assert by do first from
last let protected readonly typeof
lazy
2008年8月3日日曜日
SCADA UIプログラム例のJavaFX SDK対応書き換え:残り
新たな書き換えの要因として、次のものがあります。
・差分が1と異なる列の指定のしかたが、たとえば、[0,20..100]から[0..100 step 20]のように変わった。
・ConfirmDialogがないので、 javafx.application.Dialogに変えた。
SCADA UI構成単位のJavaFXファイルを置き換えて、企画を再構築して実行すると、図のような画面が表示されます。
次に、SCADA UI構成単位のJavaFXファイルを示します。
/*
* ScadaClientUI.fx
* An example of a User Interface (UI) module of the client-side
* Presentation component for a SCADA application.
*
* Created and modified:
* V 1.0.0 2008/08/01
*/
package client;
import javafx.animation.*;
import javafx.application.*;
import javafx.scene.*;
import javafx.scene.geometry.*;
import javafx.scene.paint.*;
import javafx.scene.text.*;
import java.lang.System;
import client.ScadaClientControl;
/**
* @author terra
*/
var scc: ScadaClientControl = new ScadaClientControl();
/*
* A control compenent for SCADA Data in the UI module.
*/
class ScadaDataControl {
attribute newNumericDatum: Number;
attribute numericData: Number[] = for (i in [0..180]) 0.0;
attribute alarting: Boolean;
attribute schedular: Timeline = Timeline{
repeatCount: Timeline.INDEFINITE
keyFrames: KeyFrame {
time: 200ms
action: function(): Void {
newNumericDatum = scc.receiveData();
delete numericData[0];
insert newNumericDatum into numericData;
if (newNumericDatum > 90.0 and not alarting) {
alarting = true;
scc.startAlarm();
Dialog {
title: "Alarm"
width: 300, height: 60
visible: true
closeAction: function() {
scc.stopAlarm();
alarting = false;
}
stage: Stage {
content: Text {
x: 10, y: 10
textOrigin: TextOrigin.TOP
fill: Color.RED
font: Font {size: 14}
content: "Flow rate exceeded the second upper limit!"
}
}
}
}
}
}
}
function getDisplayColor(value: Number): Color {
var c: Color = Color.GREEN;
if (value > 90.0) {
c = Color.RED;
} else if (value > 75.0) {
c = Color.color(1.0, 0.6, 0.0);
}
return c;
}
}
var sdc = ScadaDataControl {};
sdc.schedular.start();
Frame {
title: "SCADA UI Example"
width: 800, height: 632
visible: true
closeAction: function(): Void {System.exit(0);}
stage: Stage {
width: 800, height: 600
fill: Color.color(0.9, 0.9, 0.9)
content: [
Group {
translateX: 50, translateY: 50
content: [
Rectangle {
width: 55, height: 18
arcWidth: 4, arcHeight: 4
stroke: Color.BLUE, fill: Color.WHITE
},
Text {
x: 4, y: 5
textOrigin: TextOrigin.TOP
fill: bind sdc.getDisplayColor(sdc.newNumericDatum)
font: Font {
name: "Monospaced", size: 14, style: FontStyle.PLAIN
}
content: bind "{%7.2f sdc.newNumericDatum}"
},
Text {
x: 60, y: 6
textOrigin: TextOrigin.TOP
font: Font {
name: "Monospaced", size: 14, style: FontStyle.PLAIN
}
content: "m3/s"
}
]
},
Group {
translateX: 200, translateY: 50
content: [
Rectangle {
width: 60, height: 120
stroke: Color.BLUE, fill: Color.WHITE
},
Rectangle {
x: 10, y: 10
width: 10, height: 100
fill: LinearGradient {
startX: 0.0, startY: 0.0
endX: 0.0, endY: 1.0
stops: [
Stop {offset: 0.05, color: Color.RED},
Stop {offset: 0.0875, color: Color.color(1.0, 0.6, 0.0)},
Stop {offset: 0.125, color: Color.LIMEGREEN},
]
}
},
Rectangle {
x: 10, y: 10
width: 10, height: bind 50 - sdc.newNumericDatum / 2
fill: Color.BLACK
},
Line {
startX: 23, startY: 10
endX: 23, endY: 110
stroke: Color.BLACK
},
Group {
translateX: 23, translateY: 10
content: for (i in [-100..100 step 50]) Line {
translateY: i / 2 + 50
startX: 0, startY: 0
endX: 3, endY: 0
stroke: Color.BLACK
}
},
Group {
translateX: 32, translateY: 10
content: for (i in [-100..100 step 50]) Text {
translateY: 53 - i / 2
content: "{i}"
font: Font {
name: "Monospaced", size: 10, style: FontStyle.PLAIN
}
horizontalAlignment: HorizontalAlignment.LEADING
verticalAlignment: VerticalAlignment.TOP
}
}
]
},
Group {
translateX: 50, translateY: 200
content: [
Rectangle {
width: 230, height: 120
stroke: Color.BLUE, fill: Color.WHITE
},
Rectangle {
x: 10, y: 22
width: 181, height: 88
fill: Color.BLACK
},
Rectangle {
x: 10, y: 15
width: 181, height: 7
fill: Color.GRAY
},
Rectangle {
x: 10, y: 10
width: 181, height: 5
fill: Color.DARKGRAY
},
Line {
startX: 194, startY: 10
endX: 194, endY: 110
stroke: Color.BLACK
},
Group {
translateX: 194, translateY: 10
content: for (i in [-100..100 step 50]) Line {
translateY: i / 2 + 50
startX: 0, startY: 0
endX: 3, endY: 0
stroke: Color.BLACK
}
},
Group {
translateX: 203, translateY: 11
content: for (i in [-100..100 step 50]) Text {
translateY: 53 - i / 2
content: "{i}"
font: Font {
name: "Monospaced", size: 10, style: FontStyle.PLAIN
}
horizontalAlignment: HorizontalAlignment.LEADING
verticalAlignment: VerticalAlignment.TOP
}
},
Group {
translateX: 10, translateY: 10
content: for (i in [0..179]) Line {
translateX: i
startX: 0, startY: bind 50 - sdc.numericData[i] / 2
endX: 1, endY: bind 50 - sdc.numericData[i + 1] / 2
strokeWidth: 2
stroke: bind sdc.getDisplayColor(sdc.numericData[i + 1])
}
}
]
}
]
}
}
2008年8月2日土曜日
SCADA UIプログラム例のJavaFX SDK対応書き換え:数値指示計
次に、SCADA UIプログラムの例を、JavaFX SDK対応に書き換えてみます。今回は、数値指示計の部分を書き換えました。
逐次解釈型では、Textオブジェクトで「%m.nf」形式の書式付き出力を行うと、頭の空白文字が無視されという不備な点がありましたが、それは修正されています。
NetBeansで、JavaFX企画を作成し、SCADA UI構成単位のJavaFXファイルと依頼機を模擬する制御構成単位のJavaファイルを作成し、警報音源ファイルを置いて、企画を構築して実行すると、図のような数値指示計の画面が表示されます。
次に、SCADA UI構成単位のJavaFXファイルを示します。
/*
* ScadaClientUI.fx
* An example of a User Interface (UI) module of the client-side Presentation
* component for a SCADA application.
*
* Created and modified:
* V 1.0.0 2008/08/01
*/
package client;
import javafx.animation.*;
import javafx.application.*;
import javafx.scene.*;
import javafx.scene.geometry.*;
import javafx.scene.paint.*;
import javafx.scene.text.*;
import java.lang.System;
import client.ScadaClientControl;
/**
* @author terra
*/
var scc: ScadaClientControl = new ScadaClientControl();
/*
* A control compenent for SCADA Data in the UI module.
*/
class ScadaDataControl {
attribute numericData: Number;
attribute schedular: Timeline = Timeline{
repeatCount: Timeline.INDEFINITE
keyFrames: KeyFrame {
time: 200ms
action: function(): Void {
numericData = scc.receiveData();
}
}
}
function getDisplayColor(value: Number): Color {
var c: Color = Color.GREEN;
if (value > 90.0) {
c = Color.RED;
} else if (value > 75.0) {
c = Color.color(1.0, 0.6, 0.0);
}
return c;
}
}
var sdc = ScadaDataControl {};
sdc.schedular.start();
Frame {
title: "SCADA UI Example"
width: 800, height: 632
visible: true
closeAction: function(): Void {System.exit(0);}
stage: Stage {
width: 800, height: 600
fill: Color.color(0.9, 0.9, 0.9)
content: [
Group {
translateX: 50, translateY: 50
content: [
Rectangle {
width: 55, height: 18
arcWidth: 4, arcHeight: 4
stroke: Color.BLUE, fill: Color.WHITE
},
Text {
x: 4, y: 5
textOrigin: TextOrigin.TOP
fill: bind sdc.getDisplayColor(sdc.numericData)
font: Font {name: "Monospaced", size: 14, style: FontStyle.PLAIN}
content: bind "{%7.2f sdc.numericData}"
},
Text {
x: 60, y: 6
textOrigin: TextOrigin.TOP
font: Font {name: "Monospaced", size: 14, style: FontStyle.PLAIN}
content: "m3/s"
}
]
}
]
}
}
また、模擬依頼機制御構成単位のJavaファイルを示します。
/*
* ScadaClientControl.java
* An example of the client-side Control component for a SCADA application.
*
* Created and modified:
* V 1.0.0 2008/07/31
*/
package client;
import java.applet.Applet;
import java.applet.AudioClip;
import javax.swing.JOptionPane;
/**
* @author terra
*/
public class ScadaClientControl {
private AudioClip ac = Applet.newAudioClip(
ScadaClientControl.class.getResource("ALERT.WAV"));
private int i = -1;
/**
* Create and return pseudo SCADA data as if received from a server.
*/
public double receiveData() {
i += 1;
if (i >= 360) {
i = 0;
}
return (double) Math.sin(i * Math.PI / 180) * 100;
}
/**
* Start sounding the alarm.
*/
public void startAlarm() {
ac.loop();
}
/**
* Stop sounding the alarm.
*/
public void stopAlarm() {
ac.stop();
}
/**
* Main method for testing this class.
*
* @param args the command line arguments
*/
public static void main(String[] args) {
ScadaClientControl scc = new ScadaClientControl();
for (int i = 0; i < 5; i++) {
System.out.println(scc.receiveData());
}
scc.startAlarm();
JOptionPane.showMessageDialog(null, "Alarm is sounding!", "Alarm test",
JOptionPane.WARNING_MESSAGE);
scc.stopAlarm();
}
}
2008年7月30日水曜日
目覚まし時計プログラムをJavaFX SDK対応に書き換える
以前に投稿した、目覚まし時計プログラムをJavaFX SDK対応に書き換えてみました。
JavaFX SDKのAPI文書に、Spinnerクラスが見つからなかったので、Sliderクラスに置き換えてみました。また、パッケージ名を「timer」から「clock」に変更しています。
NetBeansで、JavaFX企画を作成し、目覚まし時計プログラムの本体JavaFXファイルと警報鳴動Javaファイルを作成し、警報音源ファイルを置いて、企画を構築して実行すると、図のような画面が表示されます。
次に、本体の目覚まし時計JavaFXファイルを示します。
package clock;
import javafx.animation.Timeline;
import javafx.animation.KeyFrame;
import javafx.application.Frame;
import javafx.application.Stage;
import javafx.ext.swing.BorderPanel;
import javafx.ext.swing.Button;
import javafx.ext.swing.Canvas;
import javafx.ext.swing.FlowPanel;
import javafx.ext.swing.GridPanel;
import javafx.ext.swing.Label;
import javafx.ext.swing.Slider;
import javafx.ext.swing.SwingFrame;
import javafx.ext.swing.Slider;
import javafx.ext.swing.TextField;
import javafx.scene.Font;
import javafx.scene.FontStyle;
import javafx.scene.Group;
import javafx.scene.HorizontalAlignment;
import javafx.scene.VerticalAlignment;
import javafx.scene.geometry.Circle;
import javafx.scene.geometry.Line;
import javafx.scene.geometry.Rectangle;
import javafx.scene.layout.VBox;
import javafx.scene.Orientation;
import javafx.scene.paint.Color;
import javafx.scene.text.Text;
import javafx.scene.transform.Translate;
import javafx.scene.transform.Rotate;
import java.lang.Math;
import java.lang.System;
import java.util.Date;
import clock.Sound;
public class Alarm {
private attribute elapsed: Number;
public attribute seconds: Integer = 0;
public attribute minutes: Integer = 0;
public attribute hours: Integer = 0;
public attribute count: Number = 0;
public attribute startTime: Number;
public attribute elapsedTime: Number;
public attribute remainingTime: Number;
public attribute alarmEnabled: Boolean = false;
public attribute running: Boolean = false on replace {
if ((alarmEnabled == true) and (remainingTime <= 0)) {
alarmEnabled = false;
Sound.alarm();
}
};
public attribute timer: Timeline = Timeline {
repeatCount : Timeline.INDEFINITE
keyFrames : KeyFrame {
time: 1s
action: function(): Void {
var now = new Date();
elapsedTime = now.getTime() / 1000 - startTime;
remainingTime = count - elapsedTime;
seconds = remainingTime mod 60 as Integer;
minutes = (remainingTime / 60) mod 60 as Integer;
hours = (remainingTime / 60) / 60 as Integer;
if (elapsedTime >= count) {
timer.stop();
running = false;
hours = 0;
minutes = 0;
seconds = 0;
} else {
running = true;
}
}
}
};
}
SwingFrame {
var alarm = Alarm {}
title: "Alarm Clock"
width: 250
height: 350
visible: true
closeAction: function(): Void {
System.exit(0);
}
content: BorderPanel {
center: Canvas {
content: Group {
var font = Font {
name: "Dialog"
style: FontStyle.PLAIN
size: 15
};
var secs: Number = bind alarm.seconds
var mins: Number = bind alarm.minutes + secs / 60
var hrs: Number = bind alarm.hours + mins / 60
content: [
Rectangle {
width: 250, height: 215
fill: Color.color(0.9, 0.9, 0.9)
},
Circle {
centerX: 120, centerY: 90, radius: 80
fill: Color.WHITE, stroke: Color.BLACK
strokeWidth: 1},
Group {
transform: Translate {x: 120, y: 100}
content: for (i in [1..12]) Text {
var radians = Math.toRadians(30 * i - 90)
transform: Translate {x: 70 * Math.cos(radians),
y: 70 * Math.sin(radians)}
content: "{i}"
horizontalAlignment: HorizontalAlignment.CENTER
verticalAlignment: VerticalAlignment.CENTER
}
},
Group {
transform: [Translate {x: 120, y: 90}]
var hourHand = Line {
startX: 0, startY: 0, endX: 0, endY: -35,
strokeWidth: 4, stroke: Color.BLACK
transform: bind Rotate {angle: hrs * 30, x: 0, y: 0}
}
var minuteHand = Line {
startX: 0, startY: 0, endX: 0, endY: -55,
strokeWidth: 2, stroke: Color.BLUE,
transform: bind Rotate {angle: mins * 6, x: 0, y: 0}
}
var secondHand = Line {
startX: 0, startY: 0, endX: 0, endY: -75,
strokeWidth: 1, stroke: Color.RED,
transform: bind Rotate {angle: alarm.seconds * 6, x: 0, y: 0}
}
content: [hourHand, minuteHand, secondHand]
},
Circle {
centerX: 120, centerY: 90, radius: 3
fill: Color.BLACK, stroke: Color.BLACK
}
]
},
}
bottom: GridPanel {
rows: 4
columns: 1
content: [
FlowPanel {
background: Color.WHITE
content: [
Slider {
minimum: 0, maximum: 11
value: bind alarm.hours with inverse
enabled: bind not(alarm.running)
},
Label {
enabled: bind not(alarm.running)
text: "hour"
}
]
},
FlowPanel {
background: Color.WHITE
content: [
Slider {
minimum: 0, maximum: 59
value: bind alarm.minutes with inverse
enabled: bind not(alarm.running)
},
Label {
enabled: bind not(alarm.running)
text: "min "
}
]
},
FlowPanel {
background: Color.WHITE
content: [
Slider {
minimum: 0, maximum: 59
value: bind alarm.seconds with inverse
enabled: bind not(alarm.running)
},
Label {
enabled: bind not(alarm.running)
text: "sec "
}
]
},
FlowPanel {
background: Color.WHITE
hgap: 40
content: [
Button {
enabled: bind alarm.running
text: "Stop"
action: bind function(): Void {
alarm.running = false;
alarm.timer.stop();
}
},
Button {
enabled: bind not(alarm.running)
text: "Start"
action: function(): Void {
var date = new Date();
alarm.count = alarm.hours * 3600 + alarm.minutes * 60
+ alarm.seconds;
alarm.startTime = date.getTime() / 1000;
alarm.remainingTime = alarm.count;
if (alarm.count != 0) {
alarm.alarmEnabled = true;
alarm.running = true;
alarm.timer.start();
}
}
}
]
}
]
}
}
}
また、次に警報鳴動Javaファイルを示します。
package clock;
import java.applet.Applet;
import java.applet.AudioClip;
import javax.swing.JOptionPane;
public class Sound {
public static void alarm() {
AudioClip ac
= Applet.newAudioClip(Sound.class.getResource("TestMusic.wav"));
ac.loop();
JOptionPane.showMessageDialog(null, "時間ですよ!", "警告!警告!",
JOptionPane.WARNING_MESSAGE);
ac.stop();
}
public static void main(String[] args) {
Sound.alarm();
}
}
上述のJavaFX API文書は、実装されているものとクラス名や属性名が異なっていたり、未実装の機能が存在します。そのため、現状では、多少の試行錯誤が必要です。
また、「The JavaFX™ Script Programming Language Reference (Draft)」の「Chapter 5. Expressions and Operators」に載っている演算子の表は、「JavaFX台本型プログラミング言語基準(草案):第5章」を投稿した後に、変更されています。演算子の剰余は「%」から「mod」に、不等価は「<>」から「!=」になりました。変更があったことについては記されておらず(元元、Draft(草案)ですから、仕方ありませんが…)、同ページに載っているコード例では、前の演算子がそのまま使われているので、注意が必要です(「表の方が最新です」と言いたいのですが、「以上」の演算子「>=」が「=>」(動画の補間直定数生成演算子)になっているなど、いくつか誤記があります。…)。
登録:
投稿 (Atom)