倭マン's BLOG

くだらない日々の日記書いてます。 たまにプログラミング関連の記事書いてます。 書いてます。

はじめての幻獣 Griffon 研 (5) : Controller

今回は関数描画アプリケーションの Controller の部分を作成します(一覧)。

コントローラの分析


コントローラに定義する処理は前々回の「アクションの分析」で列挙してました。

名前 説明 実装
Paint グラフを描画する controller.paintGraph
About 説明を表示する controller.showAbout

FunctionPlotterController 概要


次は実装。 ソースファイルは

  • FunctionPlotter/griffon-app/controllers/functionplotter/FunctionPlotterController.groovy

です。 生成されたソースコードは以下のようになっています:

package functionplotter

class FunctionPlotterController {

    def model
    def view
}

model, view フィールドにはそれぞれ FunctionPlotterModel, FunctionPlotterView のインスタンスが自動的に設定されます。

showAbout の処理


まずは簡単そうな About アクションから。 処理はメソッドとしても実装できますが、Closure 型のプロパティとして定義する方が普通なようです。

package functionplotter

import javax.swing.JOptionPane

class FunctionPlotterController {

    def model
    def view

    def showAbout = { evt = null ->
        JOptionPane.showMessageDialog(app.windowManager.windows[0],
            '''A Function Plotter
that serves as a SwingBuilder example for
Groovy in Action''')
    }
}
  • controller から view を構築するビルダーをどうやって参照するのか分からなかったので(たぶんそんなに難しくないかと思うけど)、JOptionPane を直接使ってメッセージダイアログを表示しています。
  • view では javax.swing パッケージのクラスは自動インポートされますが、controller ではそうではないので、JOptionPane のインポートもしています。
  • メッセージダイアログを表示させるためには親ウインドウが必要ですが、Griffon アプリケーションのルートウインドウは app プロパティから辿って取得できるようです:

app.windowManager.windows[0]

paintGraph の処理


次は paintGraph の処理。 まずは関数式を表す文字列を Script オブジェクトに変換して、式評価も行う Dynamo クラスを定義。 これは FunctionPlotterController.groovy に直接書いていいでしょう*1

class Dynamo{

    static final GroovyShell SHELL = new GroovyShell()

    Script functionScript

    Dynamo(String function){
        this.functionScript = SHELL.parse('import static java.lang.Math.*;'+function)
    }

    Object f(x){
        functionScript.x = x
        return functionScript.run()
    }
}
  • java.lang.Math クラスに定義されている static メソッド(で表される関数)を簡単に使えるように、ビューのテキストフィールドに入力された式(文字列)に 'import static java.lang.Math.*;' を付け加えて評価するようにしています(特に必要な処理ではありませんが)。

これを踏まえて paintGraph 処理の実装を。

package functionplotter

import java.awt.Color

class FunctionPlotterController {

    def model
    def view

    def paintGraph = { evt = null ->
        def calc = new Dynamo(model.function)
        def canvas = view.canvas
        def g = canvas.graphics
        int w = canvas.size.width
        int h = canvas.size.height
        
        g.color = new Color(255, 255, 150)
        g.fillRect(0, 0, w, h)
        g.color = Color.BLUE
        
        def dx = (model.to - model.from) / w
        def dy = h / (model.max - model.min)
        int ceiling = h + model.min * dy
        int lastY = calc.f(model.from) * dy
        for(x in (1..w)){
            int y = calc.f(model.from + x * dx) * dy
            g.drawLine(x-1, ceiling - lastY, x, ceiling-y)
            lastY = y
        }
    }

    def showAbout = { evt = null ->
        // ...
    }
}
  • 処理内容は『Groovyイン・アクション』 Chapter 8 の関数描画アプリケーションに載ってるのと基本的に同じです。 ローカル変数はちょっと名前変えてますが(必ずしも適切な名前ではありませんが、短くして見やすくするため)。
  • モデルに定義した変数(function, from など)を使用する際は、model のプロパティとして参照します(model.function, model.from など)。
  • ビューに定義したコンポーネントは、ビューに id を指定して(panel(id:'canvas') など)、view のプロパティとして参照します(view.canvas など)。
  • 時間のかかりそうな処理は doLater{} や doOutside{} などを使って別スレッドで実行した方がよいようですけど、まぁここでは気にしないことに。

これでとりあえず MVC は実装完了。

Groovyイン・アクション

Groovyイン・アクション

*1:MVC に直接関係ないクラスは FunctionPlotter/src/main あたりに定義した方がいいのかな?