RealBasic University

このコラムはStone Table Softwareのオーナーであり、またREALbasic Developerの編集者でもあるMarc Zeedar氏により書かれたものを、著者の許可を得て翻訳したものです。この翻訳はHREM Researchにより提供されています。この日本語版へのご意見はRBU-Jまでご連絡下さい。

URL: http://www.applelinks.com/rbu/057/
INDEXに戻る

SimplePaint: パート IV

子供向けのペイント・プログラムは順調に進んでいますが、さらに価値あるプログラムにするために、ひとつの重要な機能を導入する必要があります。それは画像を保存する機能です。

画像を保存する

SimplePaintの背後にある設計理念は、"ファイル保存"ダイアログで混乱することがあってはならないということです。そこでわれわれは画像を保存する簡単な方法を思いつきました。私の選んだ方法は、ある決まった名前で決まったフォルダーに、それぞれの画像を保存することです。 欠点はユーザが画像の名前を決定できないこと、また保存する場所を決められないことですが、その利点は簡便さと"ファイル保存"ダイアログが要らないことです。

画像の保存は難しくはありませんが、いくつかのコードが必要でしょう。まず始めに、SimplePaintプロジェクトを開いて、それからpaintWindowのコード・エディタを開いてください (開いたpaintWindow上をダブルクリックしてください)。KeyDownイベントにおいて、次のコードを加えてください(他のif-then間でなければ特にどこでも構いません)。

   if key = "S" then 
savePict
end if

ここでしているのは、ユーザが"S"キーを押した場合に、savePictメソッドを呼び出すことです。(私たちはキー判定に大文字の"S"を用いていますが、REALbasicの初期設定では大文字・小文字を区別しないので、"s"と"S"のどちらでも動きます。)

さてここでメソッドを作成する必要があります。Editメニューで、"New Method"を選択し、savePictと命名しましょう(パラメータはそのままにして、フィールドはブランクにしておきましょう)。

保存ルーチンが働く仕組みは次のようになります。まずSimplePaintアプリケーションと同じフォルダーに保存された画像を保管しておきます。そして各々が個別の名前を持つように、画像に番号を振っていきます。

しかし鋭い読者はそのシステムには問題があると気づくでしょう。それはシングル・セッションではうまく機能します。しかしながら2、3の画像が保存され、プログラムが再起動されると、ハードディスクにはすでに"Picture 1"があるのに、また1からカウントしていきます!新しい画像は古い画像を上書きしてしまうので、それは信じられないほど乱暴な方法ですね。

だから、どうにかして古い画像の上に新しい画像を保存しないようにしなければなりません。その解決策はハードディスク上にある一番大きい画像番号を探し出すことです。その番号に1を加えることで、新しい画像番号を得ることができます。ここに、そのルーチンがどのように機能するのか示しましょう。

getNumberと呼ばれる新しいメソッドを作成し、それにintegerを返すようにしてください。ここでコードは次のようになります。

   dim i as integer 
dim f as folderItem

i = 0
do
i = i + 1
f = getFolderItem("").child("Picture " + str(i))
loop until not f.exists

return i

見てもわかるように、これは非常にシンプルです。ここですることは、i値を1つずつ増加させていき、"Picture " + iのファイル名が存在するかどうかをチェックしていきます。存在するときは、iに1を加えていきます。"Picture i"が存在しないなら、その名前で新しい画像を保存することができるということなので、iの値を返します!

ではsavePictに戻り、次のコードを入力してください。

   dim f as folderItem 

f = getFolderItem("").child("Picture " + str(getNumber))
if f <> nil then
f.saveAsPicture(p)
msgBox "'" + f.name + "' Saved!"
else
beep
end if // f = nil

これは、デフォルトのフォルダ内に新しいfolderItemを作成します(getFolderItem("")はアプリケーションのフォルダを返します)。folderItemの名前は"Picture "+"値"(getNumberメソッドによって計算される)です。

画像を保存するには、画像pを渡してfolderItemのsaveAsPictureメソッドを使用します。ただそれだけです!

ダイアログ・ボックスを取り除く

われわれの保存ルーチンは、ダイアログ・ボックスを表示することに気がづくと思います。それは保存を確認するだけのメッセージ・ボックスですが、子供にとってそれは混乱の原因になるでしょう。彼らはモーダルなダイアログに気がつかず、どうしてこれ以上お絵かきができないのかわからないかもしれません。

それならば、どうしたらダイアログなしでユーザに画像が保存されたと伝えられるでしょうか?単なるビープ音では、画像が正しく保存されたということではなく、エラーと思われるかもしれません。

ちょうどREALbasicのオンラインヘルプに、簡単な解決策が載っています!

REALbasicのオンラインヘルプを表示させ、検索語として"speech"を入力してください。ここに検索されたものを示します(それはヘルプ・ウィンドウの上端ではないので、このコードを見るために少しスクロールしてみなければならないでしょう)。

では、次のような新しいメソッドを作成してください。

speechを実行するために、私はRBのヘルプ・ウィンドウのコードをただ単にドラッグして(点線で囲まれたコードは、コード・エディタに加えるためにドラッグできます)、それを修正しました。

このコードは、Mac OSに組み込まれたspeechルーチンにアクセスするためにdeclare宣言を使用します(それは標準的なREALbasicコマンドではアクセスできません)。一度ルーチンを宣言してしまえば、その他のREALbasicルーチンと同様に呼び出すことができます。REALbasicはコマンドをオペレーティング・システムに渡すだけです。

システム・コマンドは全てのプラットフォームで利用可能というわけではないので、OSコールをする前に、プラットフォームの確認をしなければなりません。これはMac OSあるいはCarbon (Mac OS X)を実行していればtrueを返すREALbasicの特別な"target boolean"値targetMacOStargetCarbonと条件付き編集コマンド#if-#endifを用いて行います。(CarbonアプリはMac OSで実行可能なので、本当にMac OS Xで実行しているのかを見分ける方法はありません――ただ言えることは、Carbonで実行しているということだけです。)

あいにくオンラインヘルプのコードはMac OS Xではうまく働きません(それはアプリケーションを停止させてしまいます)。最初、私はSpeechがCarbonでサポートされていないのかと思いましたが、Appleの開発者向けウェブサイトをちょっとチェックしてみるとSpeakStringコマンドCarbonでサポートされていると書いてありました。それを動かすために、私はtargetCarbon版では"SpeechLib"を"CarbonLib"に置き換えました――そしてうまく機能するようになりました!

(すべてのCarbonコマンドが容易に変換できるものだとは思わないように注意しましょう。時々、Carbonコマンドは新しい名称や他のMac OSとは異なったパラメータを持っていて、さらに変換を難しくしています。関数の宣言に関して、何を行っているか明確に分からない場合、コンピュータがクラッシュするのを見たくないのなら手を加えないのがベストです。)

次に仕上がったルーチンを示します。

   dim i as integer 

#if targetMacOS then
declare function SpeakString lib "SpeechLib" (SpeakString as pstring) as integer
#endif

#if targetCarbon then
declare function SpeakString lib "CarbonLib" (SpeakString as pstring) as integer
#endif

#if targetMacOS then
i = SpeakString(theString)
return true
#endif

return false

speechがうまくいく(利用可能)ならばtrueを返し、それ以外の場合はfalse(それはユーザがspeechの利用可能なOSを使用していない場合に相当します)を返します。

詳細

私がまずtargetMacOSをチェックし、それからtargetCarbonをチェックしたことに気づくと思いますが、それはなぜだか分かりますか?

それはCarbonはMacでのみ利用可能なので、targetMacOSの問い合わせに対して、Carbonで動いているアプリは常にtrueを返します。それはCarbonアプリをクラッシュさせるであろう"SpeechLib"内の関数が定義されることを意味します。しかし、2つ目の宣言はCarbonを走らせているかどうかをチェックしています。もし走らせているならば、関数は"CarbonLib"を呼ぶことによって再定義され、うまく働くようになります。

最後の宣言は、われわれが走らせているどんなMac OSでもSpeakStringコマンドが使えるので、ただtargetMacOSをチェックしているだけです。(例外は異なる宣言を必要とする68K Mac OSですが、REALbasicはすでに68Kのコンパイルをサポートしていないので、私はそれに関しては気に留めていません。しかし古いバージョンのREALbasicを使用しているならば、それをサポートする必要があるでしょう。)

さてsavePictメソッドへいくつかの小さな変更を加える必要があります。そうしてダイアログを表示するかわりに、ユーザへメッセージを伝えるようにします。

   dim f as folderItem 

f = getFolderItem("").child("Picture " + str(getNumber))
if f <> nil then
f.saveAsPicture(p)
if speakTheString(f.name + " Saved!") then
else
msgBox "'" + f.name + "' Saved!"
end if
else
beep
end if // f = nil  

speechがインストールされていない場合は、ルーチンは以前のように確認ダイアログを表示することに注意しましょう。

スクリーンを消去する

いままでSimplePaintに画像を保存する機能を加えてきましたが、その後は何が必要になってくるでしょうか?たいていの場合、子供は新しい絵を描きはじめたいと思うことでしょう――そこで子供にスクリーンを消去させる方法が必要になります。

paintWindowKeyDownイベントに行き、次の文を挿入してください。

   if key = chr(8) then 
// 全てを消去
p.graphics.clearRect(0, 0, self.width, self.height)
self.refresh
end if

これはユーザがDeleteキー(ASCII 8)を押したかどうかをチェックし、押された場合はclearRectメソッドで画像を消去して、スクリーンを新しくします。簡単です!

消しゴムを加える

もちろん、消去には白色を使うということを子供に伝える方が簡単ですが、より高機能にして消しゴムのキーボード・コマンドを加えましょう。それは現在の描画色を白に変換してしまう方法です。

paintWindowのKeyDownイベントに次を加えてください。

   // 消しゴム 
if key = "E" then
changeColor(rgb(255, 255, 255)) // 白
end if 

"E"を押すことによって、カラーを白色に設定して消しゴムとして使えるようになりました。

今日はここまでにします。次回は、マウス・カーソルやいくつかの音の変更のようなさらなる機能を追加していきます。

今週のREALbasicチュートリアルの完全なプロジェクト・ファイル(リソースを含む)を入手したい場合は、ここからダウンロードしてください。

次週

さらにSimplePaintを洗練させていきます。

Letters

今週の手紙は、アリゾナ州のDavidさんからです。彼は次のように書いてくれました。

こんにちは、Marcさん、

REALbasicチュートリアルをいつもありがとう。楽しく読んでいます。

1つ質問があるのですが、お答えいただければうれしいです。

私は3つのウィンドウを持つRBプログラムを設計しました。Window1はデータが入力されているWindow2へ続く導入ページです。そしてWindow2のデータに基づいて計算をし、結果を表示するWindow3があります。

私はWindow3の式やデータ操作が、Window2がただ表示されているときに入力されたデータを認識して使用するようにさせたいと思います。

Window2でテキスト・フィールドに数値が入力され、そのときテキスト・フィールドの値が変数に割り当てられている場合、どのようにすればこの変数をWindow3で使用することができるでしょうか?

私はこれを試してみましたが、Window3で変数の値がいつもゼロにリセットされてしまいます。

あなたの時間、援助、情報に感謝します。

ありがとう!

平和でありますように、

David

こんにちはDavidさん!あなたの状況下でのいちばん簡単な解決策は、他のオブジェクトの属性のように、他のウィンドウの属性を参照するだけです。複雑そうに聞こえますが、属性名の前にウィンドウ名を入力して、ピリオドで区切るだけです。

例えば、window2のlistBoxのすべての列を削除するためには、次を入力してください。

 window2.listBox.deleteAllRows 

その前にウィンドウ名(ピリオドを続けて)を入力するだけで、他のウィンドウのどんなオブジェクトや属性も参照できるようになります。次に示すようにこれは全てにおいて有効です。

 // editFieldBはwindow1上にあります 
editFieldB.text = window2.editField1.text

// これは前の行と同じです
window1.editFieldB.text = window2.editField1.text

// totalは数値属性です
total = val(window2.editField1.text) * val(window2.editField3.text)

別の方法はグローバル変数を用いることです。モジュールへ属性を追加すれば、それは全てのウィンドウやメソッドからアクセス可能になります。しかしそれは、あるウィンドウからグローバル変数へ内容を保存し、それから別のウィンドウからグローバルにアクセスする必要があるので、より複雑でしょう。別のウィンドウのデータに直接アクセスする方がもっと簡単ですね。


RBU-Jの通知サービス!コラムが発表されるたびに日本語版REALbasic Universityのお知らせの emailがあなたに届きます。登録・削除は ここ から。

INDEXに戻る