Section 3.18 時計の実装(その4)

前回で時計の機能は一通り作れたわけだけど…
だけど…?
起動するたびにいちいちアラームファイルを設定しなくちゃいけないよね。
そういえば、それってちょっと面倒だよね。
というワケで、今回は設定ファイルを作ってみるね。
設定ファイル?
うん。設定ファイルを作って、アラームファイルの設定を書き込んどくの。
そうすれば、起動する時に設定ファイルを読み込んで、自動的にアラームファイルを設定できるでしょ。
あ、なるほどね。
でも、どうやって設定ファイルを作るの?
§2.8§2.9で作った DictionaryIO クラスを使って設定ファイルを作るの。
あ〜、そういえば辞書配列をファイルに保存したり、ファイルから読み込んだりするクラス作ったね。
あれを使えば簡単に設定ファイルが作れるからね。
で、今回作るメソッドはこの2つ。

<設定ファイル処理のためのメソッド>
メソッド名引数戻り値処理内容
saveConfigなしなしアラームファイル名をファイルに保存する
loadConfigなしなしアラームファイル名をファイルから読み込んで開く

設定を保存する saveConfig メソッドと、設定を読み込む loadConfig メソッドを作るんだね。
そう。
で、まず ClockWindow クラスにこの2つのメンバ変数を追加するね。

ClockWindow クラスに追加するメンバ変数(以下のスクリプトはコンストラクタ内に追加します)>

// 設定を読み書きするための DictionaryIO クラスのオブジェクトを作ります
dicIO = new DictionaryIO();

// 設定はこの辞書配列にセットしておきます
config = new Dictionary();

あと、設定ファイル名を取得するために configFile っていうプロパティを作るね。

configFile プロパティ(ClockWindow クラス内で定義します)>

property configFile
{
    getter()
    {
        // 設定ファイル名(吉里吉里本体があるフォルダ内の "clock.dat" という名前のファイル)を返します
        return System.exePath + "clock.dat";
    }
}

このプロパティって何のために作ってるの?
こうやってプロパティで設定ファイル名を取得するようにしとくと、 設定ファイルの名前を変えたくなった時に、いちいち DictionaryIO クラスの save, load メソッドの引数を書き換えなくても、 このプロパティのゲッターの戻り値を書き換えるだけですむでしょ。
あ、そっか。
でも、それなら普通の変数にファイル名を代入しといてもいいんじゃないの?
設定ファイル名はスクリプト実行中に変わっちゃったらまずいでしょ。
え?
う〜ん、それはそうだけど、設定ファイル名が変わっちゃうことってあるの?
勝手に変わっちゃうってことはないけど、普通の変数は自由に値を書き換えられるから、 もしかしたら、その変数の値を書き換えちゃいけないってことを忘れて、 スクリプトのどこかでその変数に値を代入しちゃうかもしれないでしょ。
でもそれって、その変数の値を書き換えないように気をつければいいんじゃない?
単純なスクリプトだったら確かにそうすればいいけど、 複雑なクラスとかだと、メンバ変数が何十個とかになることもあるからね。
そうなっても、どれが書き換えてもいい変数で、どれが書き換えちゃいけない変数かちゃんと覚えてられる?
う〜ん、それはちょっと無理そうかも…
だから、書き換えちゃいけない値は、読み取り専用プロパティで取得できるようにしとけば安全になるってワケ。
そっか。それでプロパティにしてるんだね。
うん、そういうこと。
それじゃ、まずは saveConfig メソッドから作ってみて。
えっと、config っていう辞書配列に設定の内容が入ってるから、これを保存すればいいんだよね?
ん、そうそう。
ってことは…

saveConfig メソッド>

function saveConfig()
{
    dicIO.save(config, configFile);  // 設定を書き込んである辞書配列を設定ファイルに保存します
}

これでいいんだよね?
ん、これで OK だよ。
じゃ、次は loadConfig メソッドなんだけど、 その前に setAlarmFile メソッド を修正しとこっか。
setAlarmFile メソッドって、どこか直さなくちゃいけないの?
ちょっとこの辺をね。

setAlarmFile メソッドの修正点>

引数をつけるの?
うん。
setAlarmFile メソッド は action メソッドと loadConfig メソッドの両方から呼び出されるようになるわけだよね。
そうなの?
loadConfig メソッドはアラームファイル名を設定ファイルから読み込んだ後、そのアラームファイルを開くわけだからね。
あ、そっか。アラームファイルを開くために setAlarmFile メソッドを呼び出すんだね。
そ。でも、ファイルの選択ダイアログを表示する必要があるのは action メソッドから呼び出された場合だけだから、 ダイアログを表示するかどうかを引数で指定できるようにするの。
なるほど、だから引数を作るんだね。
それじゃ、setAlarmFile メソッドを書き直してみて。
えっと、この引数ってデフォルト値が void だから、 最初に引数をチェックして、void だったらファイルの選択ダイアログボックスを表示すればいいんだよね?
ん、そう。
で、ファイルが選択されたら、alarmFile にファイル名を代入してね。
キャンセルされたら、何もやることがないからすぐに return ね。
うん、わかった。

setAlarmFile メソッド・改>

function setAlarmFile(alarmFile = void)
{
    // alarmFile が void(引数が省略されている)の場合は、ファイル選択ダイアログを表示します
    if(alarmFile === void)
    {
        if(selector.openFile())
            alarmFile = selector.name;  // ファイルが選択されたら、ファイル名を alarmFile に代入します
        else
            return;
    }

    try
    {
        buffer.open(alarmFile);  // アラームファイルを開きます
        config.alarmFile = alarmFile;  // 設定を更新します
        System.inform("アラームファイルを設定しました");
    }
    catch(e)
    {
        // 例外が発生した場合は、例外メッセージを表示します
        System.inform(e.message);
    }
}

ん〜、こんな感じかなぁ?
うん、ちゃんとできてるよ。
じゃあ、今度はこの setAlarmFile メソッドを使って loadConfig メソッドを作ってみて。
dicIOload メソッドを使えばいいんだよね。
うん。
設定ファイルが読み込めたら、戻り値の辞書配列の中身を config にコピーしてね。
確か、辞書配列のメソッドって、普通に使えないんだったよね?
そ。Dictionary クラスに直接属してる assign メソッドを config のコンテキストで実行しなきゃいけないんだ。
§1.15 とか §2.11 で説明したよね。
えっと、(Dictionary.assign incontextof config)(dic); っていう感じだったよね?
うん、そう。その dicload メソッドの戻り値になるわけだね。
で、アラームファイル、つまり config.alarmFile が設定されてたら、setAlarmFile メソッドを呼び出してね。
config.alarmFile を引数にすればいいんだよね?
ん。ただ、config.alarmFilevoid だったら、 アラームファイルが設定されてないってことだから、setAlarmFile メソッドは呼び出さなくていいよ。
うん、わかった。
じゃあ、こんな感じかな。

loadConfig メソッド>

function loadConfig()
{
    var dic = dicIO.load(configFile);  // 設定ファイルを読み込みます
    if(dic !== void)
    {
        // 設定ファイルが読み込めたら、設定ファイルの内容を config にコピーします
        (Dictionary.assign incontextof config)(dic);

        if(config.alarmFile !== void)
            setAlarmFile(config.alarmFile);  // アラームファイルを設定します
    }
}

ん、これも OK だね。
じゃあ、最後は saveConfig メソッドを呼び出す部分だね。
あ、そういえば loadConfig メソッドと saveConfig メソッドってどこで呼び出すの?
設定ファイルだから、起動した時に読み込んで、終了する時に保存することにしよっか。
起動した時に読み込むのは、コンストラクタで loadConfig メソッドを呼び出せばいいと思うけど、 終了する時に保存するのってどうやるの?
Window クラスの onCloseQuery っていうメソッドを使うの。
onCloseQuery メソッド?
それって名前が on で始まってるから、もしかしてイベント?
そのとーり。
onCloseQuery メソッドはウィンドウが閉じられる時に呼び出されるんだ。
へぇ、そういうイベントもあるんだね。
ま、本来 onCloseQuery メソッドはウィンドウを閉じるかどうかの確認のために使うんだけどね。
それってどういうこと?
スーパークラスの onCloseQuery メソッドの引数に true を指定すると普通にウィンドウが閉じるんだけど、 false を指定するとウィンドウは閉じないんだ。
あ、そうなんだ。
KAG のウィンドウって、閉じる時に「終了しますか?」って聞かれるでしょ。
うん。確認のダイアログボックスが表示されるよね。
あのダイアログボックスを表示するスクリプトは onCloseQuery メソッドの中に書いてあるんだ。
ってことは…「はい」を選ぶと、スーパークラスの onCloseQuery メソッドの引数が true になって、 「いいえ」を選ぶと false になるの?
ん、そういうこと。
今回は終了確認はしないけど、ウィンドウが閉じられる時に onCloseQuery メソッドが呼び出されるっていうことを利用して、設定ファイルを保存するってワケ。
なるほどね〜。
んじゃ、onCloseQuery メソッドをオーバーライドしてみて。
終了確認はしないから、スーパークラスの onCloseQuery メソッドの引数は true にしといてね。
うん、わかった。

onCloseQuery メソッド(オーバーライド)>

function onCloseQuery()
{
    saveConfig();    // 設定を保存します
    super.onCloseQuery(true);  // スーパークラス(Window クラス)の onCloseQuery メソッドを呼び出します
}

これでおっけー?
ん、おっけー。それじゃ、実行してみよっか。
実行に必要なスクリプトはここに置いとくね。
このスクリプトをプロジェクトフォルダに置いてから実行、だよね?
ん、そうだね。
じゃ、実行!
今回は、アラームファイルを設定してから、一旦終了してみて。
おっけー。
アラームファイルを "alarm.wav" に設定してから終了、と。
うん、できたよ。
じゃあ、krkr.eXe があるフォルダを見てみて。

<krkr.eXe があるフォルダの中身>

krkr.eXe があるフォルダの中身

あっ、clock.dat っていうファイルができてる!
ちょっとそのファイルを開いてみて。
うん。

<clock.dat の中身>

clock.dat の中身

えっと、これって alarmFile っていう要素の値が "file://./c/tjs/test/alarm.wav" になってるってことなんだよね?
ん、そうだよ。
つまり、ちゃんと設定が保存できてるってことだね。
じゃあ、もう一回起動して、アラームファイルを設定せずに、アラームを1分に設定してみて。
は〜い。
1分後。
あっ、アラームが鳴った!
うん、読み込みの方も OK だね!
これで完成だねっ!
ん、そうだね。
第3章も長かったよねぇ。
まぁ色んなクラスを使ったからね〜。
ちなみに、第3章はもうちょっと続くから。
えっ、まだやることあるの?
ん、ちょっと補足しときたいことがあるから。
というワケで、また次回ね!


前へ | TOP | 次へ