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(草案)ですから、仕方ありませんが…)、同ページに載っているコード例では、前の演算子がそのまま使われているので、注意が必要です(「表の方が最新です」と言いたいのですが、「以上」の演算子「>=」が「=>」(動画の補間直定数生成演算子)になっているなど、いくつか誤記があります。…)。

0 件のコメント: