RealBasic University

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

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

OOP University:パート 9

今回は、いつもの単調なOOPの講義から一休みして、簡単なお絵かきプログラムを作成しましょう。きっと難しいだろうと思うでしょう。しかし、これをオブジェクト指向プログラミングで行うので、どれほど簡単にできるかに驚くことでしょう。これを作成するのに、下書きからたった20分しか掛かりませんでした!

SimpleDraw

SimpleDrawでの我々の目的は、前回のレッスンで紹介した概念であるオーバーライド(overriding)の威力をお見せすることです。簡略化のため、我々のプログラムでは、円(circle)、長方形(rectangle)、三角形(triangle)の3つの図形だけ扱います(後からいつでも追加できます)。

まずプロジェクトに4つのクラスを追加することからはじめます(ファイルメニューの「新規クラス」)。その名前を次のように変更してください:

ShapeClassは、共通の親クラスになります。他のクラスに対しては、shapeClassのサブクラスに設定します。これはプロパティパレットで、それらのsuperShapeClassに設定することで行います:

これをcircleClassrectClasstriangleClassに対して行いましょう。

それではshapeClassをダブルクリックしてください。次のメソッドとプロパティを追加しましょう(drawg as graphicsのパラメータを持つことに注意してください):

簡単でしょう?ここで便利なのは、shapeClassがこのような項目を持っているので、他のクラスも同じ項目を継承できます!

しかし、shapeClassにはdrawメソッドがあるのですが、実際には何も行いません。結局、何の図形なのでしょうか? 描画するにはあいまいすぎます。

しかし、定義されたそれぞれの図形に対しては、具体的に描画できます。それぞれのサブクラスを開いて、drawメソッドを追加してください。

circleClassのメソッドはこのようになります:

  
sub draw(g as graphics)
g.drawOval left, top, width, height
end sub

rectClassのメソッドもほとんど変わりありません:

  
sub draw(g as graphics)
g.DrawRect left, top, width, height
end sub

triangleClassのメソッドは、少しだけ複雑になります:

  
sub draw(g as graphics)
dim tx, ty, rx, ry, lx, ly as integer

// Top
tx = left + (width / 2)
ty = top

// Bottom right
rx = left + width
ry = top + height

// Bottom left
lx = left
ly = top + height

g.drawLine tx, ty, rx, ry
g.drawLine rx, ry, lx, ly
g.drawLine lx, ly, tx, ty
end sub

次は何でしょうか?これでほとんど完成したのです!後はプログラムのインターフェースを作成して、オブジェクトを操作できるようにするだけです。

まず始めに、コントロールパレットからオブジェクトをドラッグして、インターフェースを作成しましょう。次のようになるように試してみてください:

左側には3つのプッシュボタン(pushButton)があり、中央には分離線(separator)、右側の枠はキャンバス(canvas)(描画領域になります)があります。

さて、ここで一つの重要なステップがあります。プッシュボタンはコントロール配列にするべきです。一番上のものから、すべての名前をaddObjectButtonで統一します。二番目の設定の際、REALbasicはこれをコントロール配列にしたいか聞いてくるはずですので、そこで「はい」を選びましょう。すべて終われば、それぞれのPushButtonIndex(プロパティパレットの名前の下のもの)は、一番目が0、二番目が1、三番目が2になるはずです。もしなっていなければ、indexをこの番号に合うように変更してください(重要です)。

いいですね。これでwindow1にコードを追加し、お絵かきプログラムを作成する準備が整いました。始めに、window1にプロパティを追加しましょう。それは追加するすべてのオブジェクトのリストを持つデータ構造となります。window1を開き、プロパティ(編集メニューの「新規プロパティ」)としてobjectList(0) as shapeClassを追加してください。

次に、コントロールに行き、canvas1Paintイベントに行きます。そこに次のコードを入力します:

  
dim i, n as integer

n = uBound(objectList)
for i = 1 to n
objectList(i).draw(g)
next // i

// Draw border
g.drawRect(0, 0, g.width - 1, g.height - 1)

信じられないかもしれませんが、このわずか数行ですべてのオブジェクトを描画します!ここではオブジェクトのリスト全体をループし、それぞれに描画するように伝えているだけだからです。とても単純です!

しかし、もちろんこの描画ルーチンは、描画するオブジェクトがなければ何も描画しません。オブジェクトの生成方法を追加する必要があります。

オブジェクトの追加

ここはSimpleDrawのもっとも複雑な部分です。しかし、それでも少しも難しくないことがわかるでしょう!

始めに、objectList配列の領域を増やします。これは、後から追加する新しいオブジェクトのためのものです。

次に、オブジェクトのサイズと位置をランダムに生成します。私は簡単化のためにこうしましたが、もちろんsliderコントロールを追加して(あるいは、あなたの望みの方法で)、ユーザに新しいオブジェクトのサイズを設定させることもできます。

サイズと位置が決まれば、制作中のオブジェクト(circle、rectangle、triangle)の新しいインスタンスを作成します。addObjectButtonはコントロール配列なので――この一つのコードで3つのすべてのオブジェクトを作成することを覚えておきましょう。コントロール配列のindexを渡してあるので、どのオブジェクト(0 = circle, 1 = rectangle, 2 = triangle)でするかテストすることができます。

コントロール配列作成中に混乱した場合に備えて(例えばボタンをさらに追加した場合)、indexが他のどれとも一致しないときにはshapeClassオブジェクトを作成します。shapeClassは何も実行しないので、shapeClassのインスタンスを追加しても何の害もありません。(ただメモリーにオブジェクトとして存在するだけです)

最後のステップは、オブジェクトにサイズと位置の値を割り当てるだけです。すべてのオブジェクトは同じプロパティを持つので、このコードはすべてのオブジェクトで同じものになります。

ここにaddObjectButtonActionイベントのコードを示します:

  
dim l, t, w, h, n as integer

// オブジェクト追加のための準備
n = uBound(objectList) + 1
redim objectList(n)

// サイズと位置のランダム生成
w = round(rnd * 20) + 10
h = round(rnd * 20) + 10
l = rnd * (canvas1.width - w)
t = rnd * (canvas1.height - h)

// オブジェクト作成
select case index
case 0 // circle
objectList(n) = new circleClass
case 1 // rect
objectList(n) = new rectClass
case 2 // triangle
objectList(n) = new triangleClass
else
// 万が一に備えて
objectList(n) = new shapeClass
end select

objectList(n).left = l
objectList(n).top = t
objectList(n).width = w
objectList(n).height = h
canvas1.refresh // 新しいオブジェクトで再描画

難しかったですか?最後の行はただcanvas1に再描画するよう伝えているだけです。objectListに新しいオブジェクトをちょうど追加した後なので、その新しいオブジェクトが直ちに描画されます。ユーザへの影響は、描画領域に円、長方形、または三角形を追加したことになります!

プログラムを実行してボタンをクリックすれば、次のように素敵なアートワークが生成されます:

もちろん、これはまだ本格的なお絵かきプログラムではありません。しかし、驚くほど完成しています。オブジェクトの選択、移動、サイズ変更の方法を追加すれば、殆ど完成です。

次は何でしょうか?プログラムに少しの変更を加えると、それらの動作を簡単に追加できます!信じられませんか?次回のレッスンで、SimpleDrawSuperDrawにアップグレードします!

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

次週

簡単な修正を加えるだけで、SimpleDrawSuperDrawへアップグレードします!

News

グッドニュースです!REALbasic Developerマガジンの発展を見守ってきたくれた読者の方はご存知でしょうが、我々はより安くアメリカ国外に雑誌を配送できるよう、定期刊行物郵送許可を受けるために何ヶ月間も待っていました。そして、ついに米国郵政公社(USPS)が我々の申請を認めてくれました!

これは海外すべての読者に雑誌が郵送できることを意味しています。通常では、RBDを定期購読したときは、その時点の最新号から郵送が始まります。しかし海外の定期購読者にはまだ一冊も雑誌を郵送していないので、すべての海外定期購読者には創刊号から始めようと思います。

さらに嬉しいことに、新規の海外定期購読者で、来週初め(2003年2月18日)の第一回目の郵送前に定期購読されるならば、同じ取り決めでご提供します!つまりバックナンバー価格を払う必要なしに、すべての既刊誌を手に入れることができます。この提供は、アメリカ国外の読者で、2003年2月17日までに定期購読申し込みした方に限定します。この一生に一度の大奉仕をお見逃しなく!

前回(rbu083)を見逃した人のために、2/3月号の内容を簡単にお知らせいたします。

ソフトウェアの販売戦略
始めの特集記事は、Electric Butterfly社のDave Wooldridge氏(UniHelp fame?)によるソフトウェアの販売戦略についてです。ソフトウェアを販売するために必要な数多くの作業に嫌気がさした経験をお持ちならば、この記事こそあなたの読むべき記事です。ブランドとして確立する手法、サーチ・エンジンの登録方法、プレス・リリースの書き方、広告、報酬の扱いなどについて、Dave氏が解説します。

Aquaのための設計
次に、Mac OS X "Aqua"に適したプログラムを設計することに奮闘しているならば、Mike Benonis氏が役に立つ情報を与えてくれます。さらに、mactester.comのDoug Grinbergs氏によって編集された便利な"Aquaチェックリスト"を入手しました。

TouchCAD制作者へのインタビュー
グラフィックスに興味があるならば、Claes Lundster氏へのインタビューに惹かれることでしょう。彼はボートのデザイナーで、REALbasicで驚くほどの機能を備えたTouchCAD($795)を開発しました。

ポストモーテム(事後分析)
この号のポストモーテムでは、Thomas Reed氏が既存のプログラム(Coffee Break Pro)を、REALbasicで書き換えることについて詳述します。

さらに
そしてもちろん、すべてのレギュラー・コラムも忘れてはなりません:Matt Neuburg氏のコラムはハッシュ・コーディングについてです。Thomas Cunningham氏のコラムは、ビギナーの視点に立った分かりやすいグラフィック入門についてです(これはThomas Reed氏の上級グラフィック講座の補足になります)。そして、今話題のトピックに興味があるならば、Didier氏の"Beyond the Limits"を忘れずに読みましょう。ここでは、REALbasicでステガノグラフィー(電子透かし技術:画像の中に隠れた情報を埋め込む技法)をどのように行うか説明しています。

今すぐ申し込みましょう!

Letters

今週は、getFolderItem関数で問題にぶつかったJoe Riceさんからお便りをいただいています:

こんにちは

問題に突き当たったので、あなたにメールを書いています。あなたに送られてくる質問のメールはとてもたくさんあると思うので、もしこれが迷惑になるようならば申し訳ありません。:)

私が直面している問題とは、GetFolderItem関数についてです。私はRealBasicの初心者(まだ15才です)で、 最終的にはこのスクリプトは動きましたが、それから、どういうわけか動かなくなりました。そこには文字入力欄が6つと、「保存」と「読み込み」のためのプッシュボタンが2つあります。「保存」をクリックすると'preferences.text'というファイルを開き、ここに文字入力欄のデータを書き込んで保存します。それから「読み込み」は、'preferences.text'を開き、データを適切な文字入力欄に書き出します。

そのスクリプトを下の方に添付しておきました。前にも述べたように、これは動くには動きますが、これとは関連の全くないプログラムの他の箇所を編集すると、どういうわけか止まってしまいます。ボタンのひとつをクリックすると、どうしてなのか分からないのですが、'Unhandled NilObject Exception Raised'というエラーになります。私はいま3.1.2を使っています(と思います)。

悲しいことに私は忙しくてRBUのサイトに目を通す時間があまりないので、Eメールでお返事がいただけたらと思います。:)

差し支えなければ、メールとサイトの両方でお返事お願いします。質問の回答を公開することは、同じ問題を抱える人にとって役立ちますので。

お力添えどうもありがとうございます
Joe Rice

スクリプト:
これは「保存」ボタンのactionイベントのものです:

  
dim file as FolderItem
dim fileStream as TextOutputStream
file=GetFolderItem("preferences.text")
if file <> nil then
fileStream=file.CreateTextFile
fileStream.WriteLine companyName.text
fileStream.WriteLine website.text
fileStream.WriteLine email.text
fileStream.WriteLine phone.text
fileStream.WriteLine fax.text
fileStream.WriteLine address.text
end if
end if

これは「読み込み」ボタンのactionイベントのものです:

  
dim folder, file as folderItem
dim path as string
dim fileReadfrom as TextInputStream
file=GetFolderItem("preferences.text")
if file <> nil then
fileReadfrom=file.OpenastextFile
CompanyName.text=fileReadfrom.ReadLine
WebSite.text=fileReadfrom.ReadLine
Email.text=fileReadFrom.ReadLine
Phone.text=fileReadFrom.ReadLine
Fax.text=FileReadFrom.ReadLine
Address.text=FileReadfrom.ReadLine
filereadfrom.close
end if
end if

まず始めに、あなたのスクリプトで明らかにおかしなところは見つかりませんでした。しかし、ある種のエラーをチェックすることを忘れているので、それが原因ではないかと思います。textInputStreamtextOutputStreamの両オブジェクトは、ファイルが開かれたり作成できなかったりするとnilになる可能性があります。そのためにnilObjectErrorが起きているのではないかと思います。これを修正するには、.openAsTextFileの行の次にif fileReadFrom <> nil thenの命令文を挿入するだけです。(他のスクリプトのfileStreamオブジェクトにも同様にしてください。)

なぜこのエラーが起こるかについてはいくつかの理由が考えられますが、一番大きな可能性としては、REALbasicが開くべきファイルを見つけられないことです。どうしてそれが見つけられないのでしょうか?それは、そのための十分な情報を与えていないからです:あなたはただ"preferences.text"(パスがありません)と伝えているだけです。あなたが具体的に追跡可能な場所の情報を与えていないので、REALbasicは初期設定の場所でそれを探します。この点が、特に古いバージョンのREALbasicでややこしくなるところです。IDEにいるときには、REALbasicの初期設定のパスはREALbasicのフォルダです。しかしコンパイルされたアプリケーションでは、REALbasicはコンパイルされたアプリが存在する場所のフォルダを探します。

この一番よい解決策は、このファイルをあなたのPreferencesフォルダ内に置くことです(ファイルの名前が"preferences.text"となっているので、あなたのアプリケーションのPreferencesファイルだと仮定してそのようにしました)。Preferencesフォルダがどこにあるのか分からないですか?問題ありません:RBが教えてくれます!

getFolderitemのコードを次のように変更するだけです:

 file = preferencesFolder.child("preferences.text") 

ここでは、あなたのオペレーティングシステムに関係なく、Preferencesフォルダがどこにあろうと、それを指しているfolderitemを返すためにpreferencesFolder関数を使っています。そこでchildメソッドを加え、あなたの探しているファイルの名前をそこに渡します:このメソッドは親フォルダ内(ユーザのPreferencesフォルダ)で、渡した名前のファイルを探します。

このようにすることで、このコードは問題なく常に動作するようになるでしょう。もちろん、それでもファイルが存在するか常にチェックする必要があります――初めてアプリが実行されたときは、Prefsファイルはそこにないでしょう。それから、より共通の場所に置くことになるので(他のPrefsファイルもたくさんあります)、ファイルの名前を一般的な"preferences.text"から、あなたのプログラムの名前を含む何かユニークなものに変更することをお勧めします。

これで問題が解決するでしょう!

P.S. あらかじめこれをJoeさんにお知らせし、彼からはバグが直ったと連絡をいただきました。


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

INDEXに戻る