Canvas を柔軟に使うためのコントロール CanvasEx の製作(1)

Xojo のデスクトップアプリケーションにおいて、画像の表示などで Canvas を使用することが多いですが、Canvas の大きさが変化する場合、その大きさに合わせて再描画を行う必要があります。また、工夫をしないと別ウインドウの下になっていた部分に画像表示の欠けが発生したりする場合があります。このコラムでは「イメージバッファ」を利用して Canvas に描画を行う方法を解説します。

1.用語

Canvas コントロール
Canvas コントロールはデスクトップアプリケーションで画像を表示させる時に一般的に使用されるコントロールです。

Paint イベント
Paint イベント は、別のウインドウが閉じられて自分が最前面に表示されたときなど、Canvas が再描画の必要ありと判断した時に発生するイベントです。引数として g As Graphics が渡されますので、線を引く、画像を貼り付けるなどの描画操作はこの g に対して行います。三角形を描くプログラムをこのイベントに描いておけば、画面が更新されるときはいつでも三角形が描画されることになります。
その他、この Canvas に対して Invalidate メソッドを使用すると、強制的に Paint イベントを呼び出し実行することができます。アニメーションやゲームなどを表示するときのように、別ウインドウとの上下関係などには変更はないが Canvas 内を更新する必要がある場合には Invalidate メソッドを実行することで、強制的に Canvas 内を描き直すことができます。

イメージバッファ
ここで言うイメージバッファとは Canvas に表示する画像を保管しておく Picture を指します。初期化などの際に Picture に画像をあらかじめ読み込んでおくことで、Paint イベントが発生した時にすぐに描画を行うことができます。また、元の画像を保管しておくため、元の画像を Canvas に描画する際に元の画像イメージをそのまま保持したまま任意に拡大縮小転送描画を行うことができます。

2.仕様

今回は Canvas コントロールクラスに、以下の仕様を追加した Canvas クラスを親とした canvasEx コントロールを作成します。

仕様
・コントロール内にイメージバッファとなる Picture プロパティを作成する
・コントロール内に表示したい画像をイメージバッファに転送する初期化用メソッ
ドを作成する。
・Paint イベント発生時には、このイメージバッファの内容を描画する。

3.CanvasEx コントロールの作成

3.1 CanvasEx クラスを作成する

まず新規プロジェクトを用意し、以下の方法で CanvasEx クラスを作成します。

クラスの作成

3.1.1 プロジェクトリスト内で右クリックをしてメニューから「クラス」を選択します。

→ プロジェクト内に Class1 クラスが作成されます。

 

 

 

 

クラス名の変更

3.1.2 Class1 クラスの名称を CanvasEx に変更します。インスペクタボタンをクリックしてクラス名を表示、クラス名を編集して CanvasEx に変更します。また、親クラスを Canvas に変更します。

3.2 イメージバッファ用のプロパティを作成する

プロパティの設定を変更する

クラスにプロパティを追加します。Untitled As Integer というプロパティが作成されますので、3.1.2 と同様に名前を imageBuffer、型を Picture に設定します。

3.3 Init メソッドを作成する

クラスにメソッドを追加します。名称を Init として引数に p As Picture を引き渡すようにします。この p に渡された画像をイメージバッファの中に取り込むプログラムを書きます。

// イメージバッファに Picture のインスタンスを作成する
Self.imageBuffer = New Picture( p.width, p.height )

// 作成した Picture に画像を描画する
Self.imageBuffer.Graphics.DrawPicture( p, 0, 0 )

 

(つづく)

自分で条件つきコンパイルの条件を作りたい

条件付きコンパイル

Xojo では、ある条件に当てはまる場合にのみコードをコンパイルする「条件付きコンパイル」機能があります。例えば、Windows と MacOS で使用するコードを変えたい場合には以下のような「#」で始まる「ディレクティブ」を書きます。

#if TargetWindows then

// Windows のみで有効になるコードをここに書きます
// 例えばタスクトレイの処理など…

#elseif TargetMacOS then

// MacOS のみで有効になるコードをここに書きます
// 例えば Dock の処理など…

#endif

自分の条件で条件付きコンパイル

デバッグのときのみ、プラットフォームの違いなど、特定のコンパイル条件は Xojo で用意されていますが、 自分でコンパイルの条件を設定したい場合もあります。例えば、いくつものプロジェクトで共通の外部ファイルを参照している場合で、特定のプロジェクトからの呼び出された時のみコンパイルをしたい、などの場合です。

自分の条件での条件付きコンパイルには、自分で Boolean 型の定数を定義して、その変数名をそのまま条件付きコンパイルのための条件として使用することで、その機能を実現できます。

Const MyCondition = True

#if MyCondition then
// ここはコンパイルします ( True なので )
#else
// ここはコンパイルしません
#endif

定数はメソッド内、Window 内、App 内のどこにでも設定することができます。詳しくはサンプルプロジェクトをご参考になさってください。

参考

デベロッパーセンター:条件付きコンパイルの解説
サンプルプロジェクト:ダウンロード

Sound の鳴動処理と管理

Sound オブジェクト

Sound オブジェクトは Xojo でサウンドを再生するときに使用します。プロジェクトフォルダにサウンドファイルをドラッグドロップするだけで簡単に利用できます。プロジェクト内でファイル名に即した名前が自動的につけられますので、必要に応じて変更します。

Sound の再生

サウンドを再生するメソッドには以下のものが用意されています。
Play …… 1回サウンドファイルが再生されます。
PlayLooping …… サウンドファイルが繰り返し再生されます。

サウンドの停止

サウンドを停止するメソッドには以下のものが用意されています。
Stop …… サウンドファイルの再生を停止します。

サウンドの管理

一度サウンドを再生したあと、もう一度同じサウンドを再生するには必ず Stop メソッドでサウンドの停止処理を行う必要があります。あるサウンドを1回再生したあと、音がなっていないからと言ってすぐに Play することはできません。もう一度再生するには明確に Stop でサウンドを停止させる必要があります。

そのため通常は Stop コマンドを実行したかどうかを管理する変数が必要になります。また、プロジェクトにドラッグドロップしてサウンドファイルを追加すると、アプリケーションの実行時にすべてメモリ上にロードされるため、メモリを不用意に圧迫することもなります。簡単に数音のサウンドを使用する場合はこれでも良いかもしれませんが、数多くのサウンドを再生したい場合にはサウンドの管理を行うクラスを作成することになるでしょう。

サウンドファイルはプロジェクトファイルに入れずに別にフォルダを用意してその中に入れておき、必要に応じてメモリ上に読み込み、不要になったら破棄する、という繰り返しで管理する方法も考えてみましょう。ただし、ファイルから毎回読み込んでいると(特にゲームなどの場合は)読み込みに時間がかかり過ぎて再生のタイミングを逸することもありますので、用途に応じてハンドリングしましょう。

DatabaseRecord の日付の値に Nil を設定した場合、そのメンバーに登録されない

以下のコードのように DatabaseRecord のメンバーとして日付の値を設定する場合、その日付の値が Nil だった場合、メンバーの値として Nil になるのではなく、メンバーそのものが登録されないため、注意をしてください。

Dim row As DatabaseRecord
Dim theBD, theWrongBD As Date

theBD = New Date( 2010, 4, 1 )
theWrongBD = Nil

// row にはメンバー「誕生日」が追加される
row.DateColumn( “誕生日” ) = theBD

// row にはメンバー「誕生日」が追加されない
row.DateColumn( “誕生日” ) = theWrongBD   // theBirthday

d = New date( 0, 0, 0 ) とした場合、年月日としては 1961/1/1 が設定されるので、これで判断する方法も選択肢にしないといけない場合があると思われます。

date 型を継承した myDate 型を作成し、計算型プロパティを1つ作成して、1961/1/1 の場合には Nil を返すなど、自分なりに使いやすいクラスを作るほうが幸せかもしれません。

コンテナコントロールの中にコンテナコントロールを入れる場合

コンテナコントロール

コンテナコントロールは一緒に使いたいコントロールを組み合わせてセットにできる機能です。コードと名称の入力欄を組み合わせて、コード入力終了時にエンターを入力すると名称を表示させる、というよく利用される組み合わせで再利用を何度も行いたい場合に特に有効です。また、コンテナコントロールはクラスとして定義されますので、コンテナコントロールにメソッドやプロパティを追加することもできます。

コンテナコントロールの中にコンテナコントロール

あるコンテナコントロールの中にさらに別のコンテナコントロールを入れて組み合わせを複雑にしていくこともできます。伝票行の1行分をコンテナコントロールで作成しておき、それを取りまとめるコンテナコントロールの中に、伝票行のコンテナコントロールを明細行の数だけ縦に並べることもできます。

コンテナコントロールの入れ子

コンテナコントロールを入れ子にするときの注意

コンテナコントロールの名前を(日本語を含む)非ANK文字にしてしまった場合、コンテナコントロールの入れ子ができません。そのためコンテナコントロールの入れ子にすることを考える場合には、そのコントロール名には日本語を使わないようにします。