「オープンソース – Curl Sonntag」カテゴリーアーカイブ

Sonntag の EventBus について

Sonntag の VEventBus について

Sonntag は設計上、画面のレイアウトを担当するGraphic、処理の内容を担当するCommand、そしてその間を取り持つScreenの3つの役割がそれぞれ分担されることによって、疎結合を実現しています。

この方式を採用することで、各業務(画面)ごとに開発が行えるだけでなく、デザインとロジックを切り分けることで、依存性を低く保ち、たとえば画面デザイナーと処理記述のプログラマーが分散して開発を進めることができるようになっています。

 

 

各業務画面(Graphic)をユーザーが操作し、発生する処理は、Commandの中に記述し、bind-screen-command や Screen.do-command などを利用して間接的に呼び出すようになっています。

また、ある画面から、別の画面の処理を呼び出したい、という場合には、「イベントバス」という概念を利用します。

このイベントバスには、インスタンス化されたすべてのScreenが関連付けられており、あるScreenがこのイベントバスにメッセージを送ると、関連付けられているすべてのScreenにそのメッセージがブロードキャストされます。

 

 

具体的には、メッセージを送るScreen の send-message メソッドを呼び出してメッセージを送信します。
そのメッセージは、各Screen の handle-message メソッドに送られるので、受け側のScreenはそのメソッドをオーバーライドすることで、メッセージを受け取ることができます。

 

 

メッセージとして送信できるのは、 any型の任意の値(またはオブジェクト)です。
他のScreenにデータを渡す必要がある場合は、そのデータをもつオブジェクトをメッセージとして送信することができます。

また、このsend-message のイベントは非同期で実行されますが、send-message-syncを利用することで、メッセージを送った後のイベントが実行し終えるまで同期して待つことが可能です。

この同期方式を利用し、受け側のScreenの処理の中で送られてきたメッセージの中にデータを格納し、処理が終了した後、同期された送り側のScreenがそのデータを参照することで、間接的に戻り値のようにしてデータを受け取ることも可能です。

 

■EventBus を利用したサンプル

イベントバスを利用するサンプルを作成しましたので参考にして下さい。
   Sonntag EventBusサンプル http://developers.curlap.com/curl/Sontag-event-bus.zip

サンプルでは、あるScreen (親画面)の中に「クリア」ボタンと、タブコンテナが含まれ、そのタブコンテナの2つのペインが、それぞれ別のScreenを含んでいます。
コンテナペインの中のScreenは、テキストフィールドをひとつずつ含んでいます。

親画面の「クリア」ボタンを押すと、各コンテナの中のテキストフィールドの値がクリアされるという処理を、イベントバスを利用して実装しています。

親画面の「クリア」ボタンを押すと、send-message メソッドが呼ばれ、”clear” というメッセージがイベントバスに送られます。
イベントバスは、タブコンテナの中のScreenにメッセージをブロードキャストして届け、そのhandle-event の中でテキストフィールドをクリアするCommandを呼び出しています。

 

Curl Sonntag とは

 

Curl Sonntag とは

Curl Sonntag (カールゾンターク)とは、画面単位でプログラムを整理するためのCurlによるMVCフレームワークで、オープンソースとして提供しております。(ライセンス:Apache2.0)ソースコード及びバイナリファイルは、SourceForgeからダウンロードすることができます。

SourceForge.net(ダウンロード)

Curl Sonntagを使った開発では、1画面毎に以下の3種類のクラスを作ります。

  • ScreenGraphic (グラフィック)
  • Screen (スクリーン)
  • ScreenCommand (コマンド)

 

son1.jpg

アプリケーションのUIに応じて、1つのウィンドウを複数の画面として開発することもできます。

例えば、以下のようなヘッダー・メニュー・ボディに分かれたレイアウト構成の場合は、

son2.jpg

タブペインの場合は、

son3.jpg

のように画面を分割定義することができます。 

グラフィックとは(ScreenGraphic)

デザインをする人が、画面レイアウトを定義するクラスとなります。

  • ロジックは基本的に持たない
  • 動的なレイアウトはスクリーン(Screen)で実装

グラフィックを作成するには、VLE(ビジュアル・レイアウト・エディタ)で作成したクラスか、グラフィカルオブジェクト(FrameやCanvasを継承したもの)のようなPure Curlクラスで定義します。具体的には、以下のようなクラス定義となります。

|| グラフィカルオブジェクト
{define-class public LoginGraphic {inherits Frame}
  ・・・
}

|| VLEレイアウト
{define-layout-class public OrderEntryLayout
  ・・・
}

 

スクリーンとは(Screen)

画面毎のアプリケーションロジックを持つクラスとなります。このスクリーンクラス内で、画面の初期化やグラフィックへのアクセスなどを記述し、グラフィックの操作をカプセル化します。

スクリーンを作成するには、フレームワークのクラスを継承したクラスを定義します。例えば、VLEで作成したグラフィックの場合は「inherits {VScreen-of XxxxLayout}」のように、VLEで作成せずスクラッチでコーディングしたグラフィックの場合は「inherits {Screen-of XxxxGraphic}」のようにVScreen-ofまたはScreen-ofを継承します。(Xxxxはグラフィッククラス名です)

|| グラフィッククラスがグラフィカルオブジェクト
{define-class public LoginScreen
  {inherits {Screen-of LoginGraphic}}
  ・・・
}

|| グラフィッククラスがVLEレイアウト
{define-class public OrderEntryScreen
  {inherits {VScreen-of OrderEntryLayout}}
  ・・・
}

 

コマンドとは(ScreenCommand)

画面上の操作に応じた処理ごと(ボタンを押下したときの処理、イベントハンドラなど)にひとつずつ作成するもので、ロジックを記述します。

コマンドを作成するには、フレームワークのマクロ(define-screen-command)で定義します。

{define-screen-command
    login of screen:LoginScreen
 do
    ・・・|| ここから処理を記述
}

{define-screen-command
    login of screen:LoginScreen
    {enabled? ・・・}
 do
    ・・・|| ここから処理を記述
}

 

Curl Sonntag チュートリアル

Curl Sonntag チュートリアル

ここでは簡単なサンプルプログラムをもとに Curl Sonntag の基本的な使い方を説明します。サンプルプログラムは以下のリンクからダウンロードできます。(サンプルには pcurl 版の Curl Sonntag 0.6 が含まれています)

サンプルプログラム

サンプルの実行

ダウンロードした zip ファイルを特権のある任意のフォルダに解凍します。中に含まれる start.dcurl を実行(ダブルクリック)すると以下のようなログイン画面(を模したウィンドウ)が起動します。

son-ex1-1.png

 

 

 

 

 

ユーザIDに「sonntag」、パスワードに「curl」と入力してログインボタンを押すと画面遷移します。

son-ex1-2.png

 

 

 

 

 

以下からこのサンプルプログラムのソースコードを説明していきます。

はじめに

Sonntag では画面毎に以下の3種類のクラスを作成します。

  • スクリーングラフィック
  • スクリーン
  • スクリーンコマンド

スクリーングラフィックは、静的な画面レイアウトを表すクラスです。アプリケーションロジックは記述しません。アプリケーションロジックはスクリーンとスクリーンコマンドに持たせます。スクリーンはスクリーングラフィックと1対1で定義するクラスです。このクラスには、スクリーングラフィック上のコントロールにアクセス(値の取得、設定など)する処理や、動的に変わるレイアウトの処理などを記述します。画面上のボタンが押された場合の処理など、アプリケーションにおける主要なロジックについてはそれぞれをスクリーンコマンドとして定義します。

フレームワークパッケージのインポート

以下はサンプルプログラムに含まれる screens/load.scurl の内容です。SONNTAG.EX1.SCREENS というパッケージを定義しています。このパッケージ中に上述した3種類のクラスが含まれています。Sonntag を利用するために COM.CURLAP.SONNTAG.STANDARD パッケージをインポートしています。

{curl 6.0 package}
{curl-file-attributes character-encoding = “shift-jis”}
{package SONNTAG.EX1.SCREENS,
    {compiler-directives careful? = true}
}

{import * from COM.CURLAP.SONNTAG.STANDARD}

{include ・・・(略)}

スクリーングラフィックの定義

画面の作成する場合は、最初にスクリーングラフィックを定義します。スクリーングラフィックは一般的な Curl プログラムと同様、以下のいずれかの方法で定義できます。(Sonntag 固有のスーパークラスなどを継承する必要はありません)

  • Frame や Dialog などを継承して Curl のグラフィッククラスとして定義する
  • VLE を使ってレイアウトを作成する

screens/login/graphic.scurl ファイルにログイン画面のスクリーングラフィックが定義されています。Ex1LoginGraphic という名前の Frame のサブクラスです。

{define-class public Ex1LoginGraphic {inherits Frame}
  {constructor public {default}
    ・・・(略)
  }
}

このクラスのコンストラクタでは、ユーザIDやパスワードの入力フィールドおよびログインボタン、クリアボタンを生成・配置しています。

{define-class public Ex1LoginGraphic {inherits Frame}

  {constructor public {default}
    def vbox =
        {spaced-vbox
            margin = 5pt,
            {Table
                columns = 2,
                “ユーザID”, {TextField},
                “パスワード”, {PasswordField}
            },
            {spaced-hbox
                {CommandButton
                    width = 3cm,
                    label = “ログイン”
                },
                {CommandButton
                    width = 3cm,
                    label = “クリア”
                }
            }
        }
    {self.add vbox}
  }
 
}

このように、なるべくスクリーングラフィックはレイアウトのコードのみとなるように作成します。(上記のコードは、後ほど説明するコードを省略してあるため screens/login/graphic.scurl ファイルの内容とは若干異なります) 

スクリーンの定義

次は screens/login/screen.scurl に定義されたスクリーンクラスです。Sonntag が提供する Screen-of というクラスを継承しています。Screen-of はパラメータ化クラスで、引数にスクリーングラフィッククラス(この例では Ex1LoginGraphic)を指定しています。

{define-class public Ex1LoginScreen
  {inherits {Screen-of Ex1LoginGraphic}}

  ・・・(略)
}

補足: スクリーングラフィックを VLE で作成した場合は Screen-of の代わりに VScreen-of というスーパークラスを使用します。VScreen-of の場合も同様に、パラメータで VLE のレイアウトクラスを指定します。Sonntag で VLE を利用する方法については、今後別の記事で解説する予定です。

スクリーンからスクリーングラフィックへのアクセス

スクリーンはスクリーングラフィックに対する論理的なインターフェースを提供するように作成します。ログイン画面の例では、スクリーングラフィック上のユーザIDとパスワードのフィールドに対する入力値を取得するためのメソッド(Ex1LoginScreen.get-inputs メソッド)を定義しています。

このメソッドを作成するためにまず、Ex1LoginGraphic クラス上の TextField と PasswordField に name を設定しています。 

{define-class public Ex1LoginGraphic {inherits Frame}

  {constructor public {default}
    def vbox =
        {spaced-vbox
            margin = 5pt,
            {Table
                columns = 2,
                “ユーザID”,
                {TextField name = “userid-tf”},
                “パスワード”,
                {PasswordField name = “password-pf”}
            },
             ・・・(略)
  }
 
}

 スクリーンからはこの名前を使ってコントロールにアクセスできます。Ex1LoginScreen クラスのコンストラクタで、スーパークラスで定義された find-graphic-by-name というメソッドを使ってフィールドにユーザIDとパスワードのフィールドをセットしています。 

{define-class public Ex1LoginScreen
  {inherits {Screen-of Ex1LoginGraphic}}

  field userid-tf:TextField
  field password-pf:PasswordField

  {constructor public {default}
    set self.userid-tf = {self.find-graphic-by-name
                             “userid-tf”
                         }
asa TextField
    set self.password-pf = {self.find-graphic-by-name
                               “password-pf”
                           }
asa PasswordField
  }
  ・・・(略) 
}

このフィールドを使用して Ex1LoginScreen.get-inputs メソッドは以下のように定義されます。

  {method {get-inputs}:(userid:String, password:String)
    {return
        self.userid-tf.value,
        self.password-pf.value
    }
  }

スクリーンコマンドの定義

3種類目のスクリーンコマンドは screens/login/commands.scurl ファイルに定義されています。このファイルには、ログイン画面のログインボタンとクリアボタンに対応して、2つのスクリーンコマンドが含まれます。ここでは、先ほど説明した Ex1LoginScreen.get-inputs メソッドを使用するログインボタンのスクリーンコマンドについて見ていきます。

スクリーンコマンドは define-screen-command という Sonntag のマクロを使用して定義します。ログインボタンの処理に対応するスクリーンコマンドの定義は以下のようになっています。 

||| Ex1LoginScreen の “login” スクリーンコマンドの定義
{define-screen-command
    login of screen:Ex1LoginScreen
 do
  ・・・(コマンドの処理)
}

このマクロの構文は {define-screen-command コマンド名 of [変数名:]スクリーンクラス do ・・・} です。(詳細は API リファレンスを参照してください) 上記の例は、「login」という名前の Ex1LoginScreen に関連付けされたスクリーンコマンドの定義になります。do キーワード以降には任意の Curl ステートメントを記述できます。このステートメント中では、指定した変数名(上記の例では「screen」)でスクリーンを参照することができます。「login」コマンドでは以下の赤字部分でスクリーンのメソッドを呼び出しています。 

{define-screen-command
    login of screen:Ex1LoginScreen
 do
    || Ex1LoginScreen.get-inputsメソッドを呼出して
    || ユーザIDとパスワードの入力値を取得する

    def (userid, password) = {screen.get-inputs}
    || ユーザID:sonntag、パスワード:curlでログイン
    {if userid == “sonntag” and
        password == “curl”
     then
        || 次画面に遷移する
        {screen.replace-with Ex1NextScreen}
     else
        {popup-message
            “ユーザID「sonntag」、パスワード「curl」を入力してください。”
        }
    }

}

2番目の赤字部分で呼び出している replace-with というメソッドはスクリーンのスーパークラスで定義されているものです。このメソッドはスクリーンを引数にとり(正確には ScreenFeed を引数にとる。API リファレンス参照)、その画面に表示を切り替えます。

スクリーングラフィックからスクリーンコマンドへのバインド

以上で説明したスクリーンコマンドは、スクリーングラフィック上の CommandButton に以下のような記述を加えることによってボタン押下時に実行されるようになります。

{define-class public Ex1LoginGraphic {inherits Frame}
  {constructor public {default}
    ・・・(略)
            {spaced-hbox
                {CommandButton
                    width = 3cm,
                    label = “ログイン”,
                    {bind-screen-command “login”}
                },
                {CommandButton
                    width = 3cm,
                    label = “クリア”,
                    {bind-screen-command “clear”}
                }
            }
    ・・・(略)
  }
}

bind-screen-command というマクロの呼び出しを CommandButton のコンストラクタ引数に含めます。マクロの引数で、スクリーンコマンドの名前を文字列で指定します。

スクリーンの表示(スクリーンスロット)

最後に、Sonntag を利用して作成した画面を表示する方法について説明します。Sonntag は画面を表示するためのスクリーンスロットというオブジェクトを提供しています。start.dcurl 内では、以下のように独立型アプレット用の View を作成している箇所で ScreenSlot というクラスを使用しています。

{View
    {Dialog
        {ScreenSlot Ex1LoginScreen}
    },
    ・・・(略),
    {on WindowClose do
        {exit}
    }
}

 ScreenSlot はスクリーンを表示するために使用できる BaseFrame です。上記では、コンストラクタ引数で Ex1LoginScreen を指定しており、これによってログイン画面が表示されます。(上述の replace-with メソッドと同様 ScreenSlot も ScreenFeed を受け取ります。API リファレンスを参照してください)

 

API リファレンスのインストール

Curl Sonntag の API リファレンスは以下の手順でインストールできます。

  1. SourceForge のサイトから Sonntag をダウンロードする。
  2. Curl IDE または Curl ドキュメント・ビューワのメニューから「ヘルプ→ドキュメンテーションのインストール」を選択する。
  3. ダイアログの「インストール」ボタンを押して、ダウンロードしたファイルに含まれる document/manifest.mcurl を選択する。

Curl ドキュメント・ビューワの目次に「COM.CURLAP.SONNTAG.0.6」が追加され、API の検索や参照ができるようになります。

Sonntag で VLE を利用する

Sonntag で VLE を利用する

VLE で作成したレイアウトを Sonntag のスクリーングラフィックとして利用することができます。この場合、Sonntag の仕組みによってアプリケーションロジックをスクリーンとスクリーンコマンドに分離でき、VLE で作成するレイアウトクラスにイベントハンドラなどを記述する必要がなくなります。

VLE で画面レイアウトを作成する

VLE で作成した画面レイアウトを用意します。(VLE の利用方法は「レイアウトの作成」などを参考にしてください)

Sonntag で利用するために、まずは作成したレイアウトクラスとそのコンストラクタを public にする必要があります。ソースファイルを開いて、以下の赤字のように public の記述を追加してください。

{define-layout-class public SampleLayout
    || Begin meta-data DO NOT EDIT
    ・・・(略)
    {constructor public {default}
        {self.initialize-components}
    }
}

スクリーンを定義する

VLE で作成したレイアウトクラスをスクリーングラフィックとして利用する場合は、VScreen-of というクラスを継承してスクリーンを定義します。VScreen-of はパラメータ化クラスで、以下のようにスクリーングラフィック(VLE で作成したレイアウトクラス。この例では SampleLayout)をパラメータで指定します。 

{define-class public SampleScreen
  {inherits {VScreen-of SampleLayout}}
  ・・・(略)
}

このように作成したスクリーンは、チュートリアルで説明したスクリーンと同様、スクリーンスロットを利用して以下のように表示することができます。

{ScreenSlot SampleScreen}

VLE レイアウト上のオブジェクトを参照する

スクリーンクラス(VScreen-of のサブクラス)では layout ゲッターを使用して VLE で作成したレイアウトクラスのオブジェクトを参照できます。よって、VLE 上でデザイン名(design-name)を指定したオブジェクトをスクリーンクラス内から参照する構文は以下のようになります。

self.layout.design-name

例えば以下は、デザイン名が foo-tf というコントロールの value に “FOO” という値をセットするコードです。

set self.layout.foo-tf.value = “FOO”

チュートリアルで説明した find-graphic-by-name メソッドを上記で示した方法の代わりに使用することもできます。この場合は VLE 上のオブジェクトに「デザイン名」ではなく「名前」プロパティを設定します。