RealBasic University

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

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

OOP University:パート 13

この小さなSuperDrawプロジェクトの制作を楽しまれていることと思います。私がこの「OOP University」を開始した際は、オブジェクト指向プログラムに焦点を絞った役立つコラムを想定していましたが、いったんコラムを連載し始めてみると、私は単なる講義になっていることに気がつきました。実際のOOPの実例の方が、私はもっと価値のあるものと思いましたので、SuperDrawを制作するに至りました。

SuperDrawはもともと、2、3のレッスンで終わりにする予定でしたが、それを書いているときに2つのことを見いだしました。第一に、描画要素のサイズ変更のように、私がもっと洗練されるよう改善したくてうずうずしていたこと。第二に、オブジェクト指向でデザインされたSuperDrawは、このような機能を追加するのが極めて容易であったことです。

いくつかの新規オブジェクトを追加してSuperDrawを拡張するという今回のレッスンほど、第二の点をうまく示す例は他にありません。このような新規オブジェクトを追加するには、たった数行のコードを追加するだけです。新規の描画オブジェクトは親オブジェクトであるshapeClassの特性を自動的に継承します!まったく簡単で、すばらしいです。

ポリゴン・オブジェクトの追加

我々が追加する最初のオブジェクトは、シンプルなポリゴン・オブジェクトです。ポリゴンとは、多面体の閉じた図形ですが、採用した図形は私の独断で選びました。もちろんそれとは違うあなたのお気に入りの図形を描画するために、あなた自身でポリゴンをプログラムしても構いません。あるいは、もっと複雑にしたいと思うならば、ポリゴンの側面の数や形を、ユーザが自由に編集できるような方法を作成するのもよいでしょう。実際には、SuperDrawをどのように設計するかで、各々のポリゴンにユニークな形を持たせることもできます!(単に、ポリゴン・オブジェクト内部にポリゴンの描画ポイントの配列を格納するだけです――問題なのは、形を編集する箇所でしょう。)

それでは、前回までのSuperDrawを開くことから始めましょう。それに新規クラス(ファイルメニューの「新規クラス」)を追加します。そのクラスにpolygonClassと名前を付け、そのsupershapeClassに設定します(プロパティ・パレット上で)。

polygonClassを開き(それをダブルクリックする)、eventsのエリアでpaintイベントを見つけてください。paintの内部に、次のコードを入力します:

  
dim points(0) as integer
dim i, n, n2 as integer
dim data, p as string

data = "l,t w,h w,t l,h l,t"

data = replaceAll(data, "l", str(left))
data = replaceAll(data, "t", str(top))
data = replaceAll(data, "w", str(left + width))
data = replaceAll(data, "h", str(top + height))
data = replaceAll(data, "cx", str(left + (width/2)))
data = replaceAll(data, "cy", str(top + (height/2)))

// Number of coordindate pairs
n2 = countFields(data, " ") * 2
redim points(n2)
n = 0
n2 = uBound(points) - 1
for i = 1 to n2 step 2
n = n + 1
p = nthField(data, " ", n)
points(i) = val(nthField(p, ",", 1))
points(i + 1) = val(nthField(p, ",", 2))
next // i

g.drawPolygon points

これはポリゴン・クラスの描画ルーチンです。ここでは水平・垂直方向の座標配列を構築して、それをグラフィック・オブジェクトのdrawPolygonメソッドに渡します。

詳細

私がpoints配列を初期化した(賢い?)方法に気がつきましたでしょうか。第一行目、data =は実際の描画形状を決定します。私はオブジェクトの境界(上、左、幅、高さ、等々)を表すために、文字列を使用しました。これらは後になって、それに該当する実際の数に置き換えられます。したがって、「l」(左 left)は実際には「33」になります(オブジェクトの左側面は、描画領域の左端から33ピクセルのところであると仮定します)。

ここにはまだ使われていないものが2つ残っています:「cx」と「cy」は、その方向の中間点(中心)を表します(cxは水平方向の中心、cyは垂直方向の中心となります)。私はこれを、x翼の形状には使用しませんが、他の形状の時に役立ちます(下記参照)。

それから、ループを通して配列を構築します。我々は、二次元座標ペアを扱っているので、ひとつおき毎にスキップするようstepは2になっています。現在処理中の場所をカウントするために、nを用います。

データの行では、カンマで区切られた文字対で構成され、それぞれの対はスペースによって分割されていることに気づくでしょう。したがって、スペースによって区切られたフィールドの一対を抽出することができます。我々はそれをp(現座標ペア)として割り当てます。その後、pから1番目と2番目のペア(コンマによって区切られたもの)を抽出し、それを(数として)points配列に割り当てることができます。簡単です!

その結果、data列を変更することで簡単に異なる形状を作成することができます。次で実験をしてみましょう。例えば、これはアスタリスク(星印)を生成します:

data = "l,t cx,cy cx,t cx,cy w,t cx,cy w,cy cx,cy w,h cx,cy cx,h cx,cy l,h cx,cy l,cy, cx,cy l,t"

もちろんもっと複雑な形状を作成するためには、四隅とオブジェクトの中心以外も考慮する必要がありますが、機能を追加するために、データ文字列解析ツールを簡単に拡張することができます。

さて、これで新規オブジェクトの追加が完了しましたが、プログラムはまだそのオブジェクトで何をしたらよいのか分かりません。新しい描画要素をSuperDrawの一部として実際に導入するためには、簡単な2つのステップが必要です。

最初は、このオブジェクトを描画領域に追加するために、インターフェースを修正しなければなりません。したがって、window1をダブルクリックし、3番目(一番下)のpushButtonを選択してください。コマンド+D(または編集メニューから「複製」)を押してください。このコントロールは、コントロール配列の一部なので、REALbasicは自動的にindexを3にします。これで完璧です!それではそのテキストを「New Polygon」に変更し、そのボタンをウィンドウの好きな場所にドラッグしてください。以上でこのステップは完了です!

次に必要なことは、この新しい形状をどのように追加するかをdrawCanvasClassに伝えることです。drawCanvasClassを開き、addObjectメソッドに行ってください。case 2のコードの後と、else行の間に、次のコードを入力してください:

  
case 3 // polygon
objectList.append new polygonClass

次は何でしょう?これで終わりです!オブジェクトは追加されました。驚くべきことに、このオブジェクトはコードをいっさい追加しないでも、直ちに移動やサイズ変更が可能になります!

しかしまだ終わりではありません。これではとても簡単すぎるので、さらにオブジェクトを追加しましょう!今回は少し飛ばしていきますので、しっかりついてきてください。

画像オブジェクトの追加

SuperDrawが画像のインポートをサポートしたら、とても素晴らしいと思いませんか?それなら、どうしてそうしないのでしょう?画像追加は難しいとお考えですか?

克服しなければならない問題はありますが、実際にはそれほど難しくありません。それも数分の内に片付いてしまいます。始めに、オブジェクト・クラスを作成(ファイルメニューの「新規クラス」)し、それにpictClassと名前を付け、shapeClasssuperとしてください。

pictClassを開き、image as pictureのプロパティを追加してください。そしてpaintイベントで、次のコードを入力してください:

  
if image <> nil then
// Draw the picture
g.drawPicture(image, left, top, width, height, 0, 0, image.width, image.height)
else
// Draw empty picturebox
g.foreColor = rgb(255, 255, 255) // White
g.fillRect(left, top, width - 1, height - 1)
g.foreColor = rgb(0, 0, 0) // Black
g.drawRect(left, top, width - 1, height - 1)
g.drawLine(left, top, left + width - 1, top + height - 1)
g.drawLine(left, top + height - 1, left + width - 1, top)
end if

ここでは、画像があればそれを単純に描画します。なければ、空の画像フレームを描画します。

以前と同じように、window1上にもう一つのpushButtonを追加してください。そしてdrawCanvasObjectのaddObjectメソッドにもう一つのcase行を追加します:

  
case 4 // picture
objectList.append new pictClass

これで、画像オブジェクトを表示させる準備は整いました。しかしながら、そこへ画像を導入する方法がありません。画像を追加する最も簡単な方法は、画像ボックスをダブルクリックすると、選択可能な画像ファイルを表示させる方法でしょう。したがって、次にそれを行っていきましょう。

始めに、ダブルクリックを検知する方法が必要です。それはdrawCanvasObjectで行います。それを開いて、startClick as doubleというプロパティを追加しましょう。

それからmouseDownに行き、一番先頭の部分(dim文の後)に、次を追加してください:

  
dim doubleClicked as boolean

// ダブルクリックのチェック
if ticks - startClick < 15 then
doubleClicked = true
else
startClick = ticks
end if

これは現在のクリックが、その前のクリックから15ティック以内(1/4秒)であるか確認し、そうならばダブルクリックと見なします。

次に、そのダブルクリックで何を実行するかが必要となります。mouseDownを見渡して、すべてのオブジェクトをスキャンしているコードを見つけてください。それはfor i = n downTo 1の行から始まります。そのループ内のyStart = y行の後に、次を挿入してください:

  
if doubleClicked then
currentObject.doubleClicked
end if

これは、ダブルクリックを現在のオブジェクトに渡します。

しかし待ってください!いまこれを実行すると、エラーを発生するでしょう:オブジェクトはdoubleClickedイベントが何なのかわかりません。そのイベントを追加しなければなりません!

shapeClassを開き、doubleClickedという新規イベントを追加してください(編集メニューの「新規イベント」)。そのイベントを呼び出す方法もまた必要ですので、同じ名前のメソッドを追加し、そのコードにdoubleClickedという1行を書き加えましょう(これはdoubleClickedイベントを動作させます)。

pictClassを開き、先ほど使用可能になったdoubleClickedイベントを見つけます。そこへ次のコードを入力してください:

  
dim f as folderItem

// Prompt for picture
f = getOpenFolderItem("image/pict;image/gif;image/jpeg")
if f <> nil then
image = f.openAsPicture
end if

これが動作するには、アプリはいくつかのファイル・タイプを決定しておかなければならないので、次にそれを行いましょう。編集メニューから「ファイルタイプ」を選択し、サーポートしたいグラフィックのファイル・フォーマットを追加するために「追加」ボタンを押します。私の場合は、PICT、GIF、そしてJPEGに限定しましたが、ご自由に追加して構いません(このような標準タイプは、「追加」ダイアログボックスの所定のポップアップ・メニューで利用できます)。

これで完成です!それではSuperDrawを実行してみましょう。描画領域に新規の画像を置いた場合、始めは空のボックスが表れます。しかし、そこをダブルクリックすると、画像ファイルの選択が可能になります。何かひとつを選択すれば、画像は描画領域に表示されるでしょう!

何といっても、画像の移動やサイズ変更ができ、完全に動作します!そして好きなだけの画像を追加することができます(もちろん、メモリーに制限されますが)。これは素晴らしいですね。

今回はこれで終了です。来週は、テキストを描画要素として追加する機能など、さらにいくつか追加します!

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

次週

とても簡単なSuperDraw拡張機能を追加していきます!

ニュース

まだ耳にしていない人のために、REAL Software社はREALbasic 5 for Macintoshがついに発売されることを発表しました。これは今までのREALbasicの中で最も大きな改良が加えられ(新たなコンパイラーも含まれます)、製品の確かな未来を約束するものとなりました。新しい機能の中には、Jaguar(Mac OS X)サポートの向上、新しい通信プロトコル(email、http、等々)、そして新しい言語機能が含まれます。

REAL Software社のウェブサイトでは、REALbasic 5についてのさらに多くの情報を手に入れることができます。アップグレードは$29.95からです。

REALbasic 5の新機能に興味がありましたら、これから発売予定のREALbasic Developerマガジンの4/5月号版で、この重大なアップグレードについて、2つの徹底解説記事が掲載されます。

Letters

今回は、なりたてのREALbasicユーザさんが次のような手紙を書いて送ってくれました:

Hi there,

こんにちは。

私はあなたのコラムのほとんどがお気に入りですが、いくつかがよくわかりません。。私はREALBasicの初心者です。プログラム学習を再開して、アップルの大ファンになる決心をしました。

そこで私は、まずその基礎としてREALBasicを選び、そうこうしているうちにあなたのRBUコラムに出会いました。私は最初のプロジェクトを完了し、現在はノートを見直しているところですが、いくつか質問があります。

1. モジュールをもう少し私に分かりやすく説明してください。それから、どうして配列にするのでしょうか。

2. 初回のプロジェクトで作成したモジュールの各部分について、説明を加えてください。

3. 新規クラス(NewClass)と新規モジュール(NewModule)の違いを、私によく理解できるように教えてください。

よろしくお願いします。

S C Rollins

Christopherさん、コメントをありがとうございます。これからあなたの学習がますます発展していきますように!

あなたの質問には、できる限りお答えしていきましょう。あなたはどのプロジェクトを完了されたのかはっきり明記していなかったので、RBUで行った最初のプロジェクトであるGenderChangerと仮定してお話を進めていきたいと思います。

質問 1: モジュールは簡単なコード(メソッド)、変数、定数の集まりです。その概念は、関連したルーチンをひとつにまとめることによって、すべてがひとかたまりのモジュールとなることです。モジュールは、プロジェクト・ウィンドウへドラッグすることで簡単に他のプロジェクトで再利用することができます。

例えば、私は自身のシェアウェア会社を経営しています。そこではプログラムがすべて同じ外観と操作法を持つことが望ましく、またできる限り多くのコードを再利用したいものです。モジュールへ共通のコードを入力し、モジュールを再利用することで、どちらの目的も達成させることができます。プログラムでは同じコードを使用でき、私は時間を節約することができます。そして、同じコードを使用することで、外観と機能はどのプログラムも同様になります。

モジュールを利用するもう一つの重要な理由は、モジュールは定数(値が変化しない「変数」)を持つことができ、またモジュールに追加された変数(プロパティ)はすべてグローバルとなるからです(要するに、プログラムのどこの部分でも利用できます)。

質問 2: あなたが仰っているものがGenderChangerであるとすれば、globalsModuleには5つの項目があります:プレファレンス・ファイルの名前を示すテキスト定数のkPrefName;プログラムのプレファレンスを読み出し、保存するメソッドのopenFileSaveFile;ダイアログボックスからのデータを渡すために用いる文字プロパティのgDialogReturn(よってグローバルの必要があります:window1のプロパティはwindow1でしか利用できないので、使用することができません);カスタムクラスfileTypeClassの配列であるgTheFileList()です。再度申し上げますが、データ構造はグローバルにする必要がありますので、我々はそれをモジュールに組み込みます。

質問 3: クラスのよい説明としては、現在執筆中の「OOP University」シリーズを最初からご覧ください。簡単に言うと、新規クラスはオブジェクト型の青写真(テンプレート)です。新規モジュールは、単なるコードの集まりです。モジュールは、関連したコードを再利用するためにひとつにグループ化するのを容易にします。

クラスも、それに基づいてオブジェクトを作成でき、他のプロジェクトでそのクラスを再利用できるので、コードの再利用ができます。しかし、クラスは単なるオブジェクトの定義であり、オブジェクトそのものではありません。機能するためのオブジェクトであるインスタンスがなければ、クラスそれ自身ではなにもできません。

つまり、プロジェクトにモジュールとクラスを追加して、それぞれにbeepbeepmyBeepというメソッドを作った場合には、beepbeepはすぐに機能しますが、myBeepはあなたの作成したクラスのオブジェクトを作成し、そのオブジェクトにmyBeepを指示した場合に限り機能します。

これでいいですか?あなたの質問にうまく答えられたでしょうか?

両者の区別がつくようになるまで時間が掛かっても驚かないでください。私はキーボードの気の向くままに説明を書き加えましたが、実践に勝るものはありません。色々と試し、経験することで、ある日突然光が差すように、根底から違いが理解できる時がやってくるでしょう。しかし、始めたばかりではそのようなことはまずありえません。


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

INDEXに戻る