RealBasic University

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

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

SimplePaint: パート I

私が働いているプリントショップの上司は、時々、彼の子供のうちの一人を数時間お店に連れてきます。一番幼い子はいま5才なのですが、数年前には一番上の子がその年齢でした。幼い子供は集中力が短い間しか持たないので、彼らが問題を起こさないように、何か面白いものを見つけることは常に難題でした。

その点、お絵かきはいつも彼らを楽しませました。そして私たちは、子供たちがコンピュータでお絵かきすることに惹かれているのに気がつきました。だから私は持て余しているマックでペイントプログラムを立ち上げ、それで彼らに遊ばせました。まず始めに私は、ペイント・プログラムの必要最小限度であると思っているAdobe Photoshopを試しました。もちろんそれは女の子にはかなり複雑だったので、次にクラリス・ワークスに変えました。それはいくらか満足いくものだったのですが、彼らから目が離せないことに気がつきました。目を離すと、ウィンドウの外をクリックしてFinderを表示し、フォルダーを自由に移動させたり、ファイル名をでたらめに書き換えたり、ありとあらゆる大損害をもたらすのです。

3、4秒の間、私はインターネットでフリーの"KidPaint"のようなプログラムを探してみました。しかしそれらを見つけて、すべて試してみるには私は不精すぎです。その代わり、すぐにアイデアが閃きました――自分で作ってしまおうと。

私はREALbasicを立ち上げ、ほんの数分で極めて基本的なペイント・プログラムを手にしていました。それはとても楽しくて、私はすぐにいくつかの高度な機能的を加えました。最終的には、子供向けの特別な極めてシンプルなペイント・プログラムができました。おそらくもっと素晴らしくて良いプログラムもあると思いますが、これは私がまさに欲しかったもので、書いた価値がありました。

プログラムの仕様

このプログラムは子供をターゲットとしたので、仕様に関してわずかな留意点がありました。私は以下のような基準リストを思いつきました。

女の子はすべての"ツールパレット"に混乱する(それは基本的にモーダルです)ことから、私は従来からのお絵かきプログラムのオプションであった、円、直線や長方形を描かせる機能を付けないことにしました。その代わり、唯一のエンピツツールがあります。このプログラムでは、ひとつだけのモードがあります――ピクセルで描く、ただそれだけです。

女の子は前段階に戻って、作品を描き直そうとは滅多にしないので、私はわざわざ前に描いた絵を再び開いて始める方法をとらないことにしました。女の子は何か絵を描き、保存して、スクリーンを消去した後、再び描き始めるでしょう。

それから絵をプリントする方法を作成することにも意味がありませんでした。もし絵を印刷したい場合は、保存した画像をフォトショップから印刷しましょう。

ブランク・スクリーン

昔のREALbasicでは、メニューバーを隠すことは、システムコールやプラグインを必要とする厄介な作業でした。しかしそれはREALbasicのバージョン4では簡単なことです。ウィンドウのMenuBarVisible属性のチェックを外すだけです!

新しいプロジェクトを作成してください。window1paintWindowと改名して、次の設定をしてください:

HasBackColor属性にチェックを入れると、絵画領域として望ましい白い背景のウィンドウになることを覚えておきましょう。

それから先に進んで、プロジェクトを"Simple Paint"フォルダー内の"SimplePaint.rb"というファイル名で保存してください。(".rb"はREALbasicがMac OS Xでのプロジェクト・ファイルに使う拡張子なので、それ以前のOSでも使用するのがいいでしょう。)

変数の準備

私たちはお絵かきについて様々な種類の操作(保存、編集、その他)をするので、画像変数(picture variable)内にそれを記憶させておくのが良いでしょう。

paintWindowを開いて、新しい属性(Editメニューの"New Property")にp as pictureを加えてください。ここで、いくつかの必要な他の属性を加えましょう。

最低限のペイント・プログラムとしてSimplePaintを動作させるには、これらをここで定義する必要はありませんが、先に考慮しておくと良いでしょう。c属性は描いている現在の色を保持します。xWidth属性とyWidth属性は使っている絵筆のサイズを表します。結局のところ、この属性を変更する方法を加えるので、子供は描く時に応じて違った色を選択できるし、絵筆のサイズを変えることもできます。

初期化

画像変数内に手を加える前に、それをまず初期化しなければなりません(専門用語では、変数にメモリ[記憶領域]を割り当てる[蓄えておく]といいます)。それをnewPictureメソッドで行いましょう。画像はウィンドウと同じくらい大きい(ウィンドウのFullScreen属性を指定したので、それは主スクリーンと同様のサイズ)に違いありません。さらにまた、画像はフルカラー表示したいのでnewPictureのパラメータは32とします(画像が32ビットのカラー画像になることを意味します)。(メモリの少ないマシンでは、16あるいは8を選択するといいでしょう。8ビットカラーはたった256色しかありませんが。)

これはプログラムを立ち上げて始めに行わなければならないので、描画ウィンドウのOpenイベントに入力しましょう。このコードをpaintWindowのOpenイベントに入力してください:

 p = newPicture(screen(0).width, screen(0).height, 32) 

if p = nil then
beep
msgBox "Sorry, there's not enough memory to run SimplePaint."
msgBox "I must quit now."
quit
end if

self.backdrop = p

xWidth = 9
yWidth = 9

pに対する十分なメモリー領域がないと、プログラムを停止することに注意しましょう。この方法は停止するのに必ずしも丁寧な方法ではありませんが、クラッシュしてしまうよりはましです。(あなたがお望みならば、最初32ビットの画像を割り当て、p = nilの場合に再び16ビットで割り当てることができるでしょう。この時はまたpnilでないことを確認してください。)

有効なpを手に入れたら、それをpaintWindowのbackdrop属性に指定します。それは画像pがどのように見えようともpaintWindowの背景になることを意味します。これによりpaintWindowが描画や再描画を必要とするときは自動的に行われますので、私たちは描画をする必要はなくなります。

最後に、xWidth値とyWidth値の初期サイズを設定します。

マウスの動きを見る

すべてのペイントプログラムは、同じ基本原理に基づいて動きます。それはユーザがマウスのボタンをクリックするときに、クリックされた場所でピクセルが描かれるということです。私たちのも違いはありません。

paintWindowMouseDownイベントへ行って、これを加えてください:

 return true 

これはMouseDownイベントを扱います、ということをREALbasicに伝えているだけです。 規定値はMouseDownを無視する意味のfalseを返します。それをtrueに返すことによって、REALbasicはMouseDragイベントを受け入れます。そしてそれがこの仕事のほとんどの作業部分です。

詳細

なぜMouseDownの代わりにMouseDragを使うと思いますか?

それはMouseDownがマウスボタンが始めにクリックされたときに一度だけ起こるイベントなのに対して、MouseDragは繰り返し起きるイベントだからです。一旦マウスボタンが押されていれば、MouseDragはマウスカーソルの現在の座標をユーザがマウスを動かすたびに伝えてくれます。それが私たちの必要とするデータです。

MouseDragイベントでは、このコードを加えましょう:

 dim x2, y2 as integer 

x2 = x
y2 = y

drawPixel(p.graphics, x2, y2)

ただxyを直接drawPixelに渡す方が簡単なのに、なぜx2y2を宣言するのか不思議に思うかもしれませんね。

確かにその通りですが、それは先を考えてのことです。つまり今後はxyを操作したいかもしれないからです。

最終行はgraphicsオブジェクト(pictureであるpの)と描画する座標をdrawPixelルーチンに渡します。drawPixelルーチンとは何でしょうか?まさに私たちがこれから作ろうとしているものです!

ピクセルを描く

何かを実際に描かせるためには、たった今呼び出したdrawPixelルーチンを作る必要があります。新しいメソッド (Editメニューの"New Method")を加えて、drawPixelと命名しましょう。パラメータの行に、g as graphics, x as integer, y as integerと入力してください。コードはとても簡単です:

 dim n1, n2 as integer 

n1 = xWidth \ 2
n2 = yWidth \ 2

g.foreColor = c
g.fillOval(x - n1, y - n2, xWidth, yWidth)

self.refreshRect(x - n1, y - n2, xWidth, yWidth)

n1n2とは何でしょうか?それはこれから描こうとしている"ピクセル"の中心点です。私たちの言う"ピクセル"とは本当の1ピクセルのサイズではないことを覚えておいてください。それは実際にはxWidthyWidth(初期設定では9に設定しています)です。REALbasicのfillOvalメソッドを利用して特別大きなピクセルを描きましょう。

REALbasicが円を描画するときには、その円を描き始める座標は円が描かれる四角い外枠の左上端になります。例えば、下の赤い円は外部の四角い枠の寸法を与えることによって描画されます(左上端の座標、幅と高さです)。

ある意味、円は非常に丸みを持ったただの四角い箱です!

しかし目的に適うためには、ユーザがクリックしたx座標とy座標を円の中心としたいですね。それには描画する座標を、簡単な計算によりオリジナルの座標から円の幅と高さの半分だけずらします。このように、左上端から円を描画し始めることによって、その円の中心はオリジナルの座標になります。

次に、描画する色をあらわすcを設定して円を描きます(幅と高さを異なるようにすれば楕円です)。最後に、paintWindowに描画領域を再描画(アップデート)するように伝えます。

簡単ですね?では試してみましょう。プログラムが動いて、こんないたずら書きもできますよ:

しかし描画色や絵筆のサイズの変更方法、あるいは画像を保存する方法がありません。これについては次回行いましょう。

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

次週

カラーパレットやその他の特色を加えてSimplePaintの機能を高めます。

News

REALbasic Developerマガジンの進行具合は上々です。記事はすでに書き上がり編集も済んで、いまは初版のレイアウトに取り組んでいます。ここで初版のカバーがどのようなものになるかこっそりお見せしましょう。

予約購読は正規の定価の25%割引でご利用になれます。

Letters

数人の方が、前回のプロジェクトであるZoomerがMac OS Xでは動かなかったと知らせてくれました。実際は半分動きます:editFieldは拡大されるのですが、その中のテキストが拡大されません。明らかにMac OS XではdrawIntoメソッドによく存在したいくつかのバグがまだあるようです(過去に、drawIntoeditFieldのほんの始めの数行は描画するのに、スクロールされたテキストは描画しないバグがありました)。

まず始めに、Mac OS XでZoomerをテストしなかったことについてお詫びします。それは私の不注意でした。ますます多くの人が開発にMac OS Xを使用していますので、私はどちらのプラットフォームでもRBUが動くように動作確認したいと思います。最低限、なにか動かないものがあったらお知らせいたします。

第二に、Mac OS XでZoomerを動かしたい場合は、この代替手段を試してみてください。それはeditFieldのテキストを表示しないバグを直すものでありませんが、何かしら拡大できるようになります。

drawZoomメソッドを開いてください。次の設定で検索/置換を行ってください:

"すべて置換(Replace All)"ボタンをクリックしてください。

これがすることは、ZoomereditField1の代わりにウィンドウ全体を拡大させることです。これはあなたにいくつかの考える材料を与えます――お望みならば、ズームして見るためにいくつかのコントロールをwindow1へをドラッグしてください。

次に、JulianさんがpopupMenusについての問題を書いてくれました。

こんにちは

まず初めに、このままよいお仕事をお続けください―あなたのコラムは大変素晴らしく、とても参考になります。私はあなたのテクニックを自身の問題に使うことができています。どうもありがとうございます。

私はリストの階層を上下させる操作をするために、ポップアップメニューを使おうとしていますが、そこで小さな問題が起こりました。

ポップアップメニュー(PopUpMenu)の項目がプログラムにより動的に指定される場合、つぎのようにlistIndexが設定されるまでどんなテキストも表示しないのです:

 andSomeAfterPopup.listIndex = 0 

問題なのは、このアップデートが'change method'を引き起こすことです。私は単にユーザがポップアップをクリックして、新しい値を選んだときに次のようなコードのメソッドを実行したいのです。

 linkField.text = me.text 

私はグローバルな'gMouseWithinAfterPopUp'を設定することにより、その周辺をコード化しました。変更コードは次のようになります:

 if gMouseWithinAfterPopUp = true then 
linkField.text = me.text
else
do nothing
end if

そしてもちろんMouseEnterと、Exitイベントはこのグローバル変数を反転させます。

この方法は問題を解決するのですが、洗練された方法とはいえないでしょう。

私はここで何か見落としたのでしょうか、それとも何かよりよい方法をご存知でしょうか?

さらに私はRBプログラムの多くは読めば判りますが、それぞれの新しいメソッドや属性ダイアログボックスの下の方にコメント/説明のための領域があるといいのではないでしょうか。そしてコメント/説明の印刷の制御機能があるといいですね。私はプログラムについての系図を書きたい時、どちらかと言えば'ダミー'のメソッドを設定しています。

海辺にて

Jules.

あなたの述べている問題は非常に一般的です。あなたの解決法がひとつの方法ではありますが、それは複雑ですね。私がよく用いるもっと簡単な解決法は、コードにユーザの行為ではなくプログラムの変更であることを伝える"myChange"変数を作成することです。

したがって初期化イベントはこのようになるでしょう:

 myChange = true 
andSomeAfterPopup.listIndex = 0
myChange = false

それから、Changeイベント内はこのようなコードになります:

  if not myChange then 
// ユーザがポップアップをここで変更した時、
// 起こしたい出来事を入力してください。
.
.
.
end if

私は取消/再実行(undo/redo)システムを導入するときに、しばしばこのテクニックを用います。それはundoシステムは一般にユーザの行為をある種のバッファーに保存(後で戻ることができるように)しておくからです。問題はユーザの古い行為を前に進める時や、undoを通じて以前の状態にデータを復元する時です。その行為は、ユーザの行為とみなされます。すなわち、プログラムは何かをあなたがプログラム的(コードを通じて)にしたのか、ユーザが何かをしたのか区別がつきません。しかしこれはプログラム的な行為であるとルーチンに伝える論理型変数を作成することによって、ユーザがそうしたときに通常起こるかもしれない出来事を避ける得ることを意味します。

まだこの解決方法はあなたに余計な労力を要求します。良いと思われるものは――私たちはその変数を組み込むようにREAL Software社に要求するべきですが――ルーチンへのコールがプログラム的に行われた場合にtrueを、それがユーザによって行われた場合はfalseを返すグローバルな論理型でしょう。あなたは依然コードをこの変数へのチェックで囲む必要があるでしょうが、自分自身で論理型を設定する必要がなくなります。

それからメソッド内に記述領域を作ることに関してですが、それは悪い考えではありません。REAL Software社がそれを加えるまで、ほとんどの人はクラスを記述するダミーのメソッドを次のように加えるといいでしょう:


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

INDEXに戻る