ALGOBIT > 離散的な気まぐれ

2011/09/30

Swinging Clojure (3)

Filed under: 離散的な気まぐれ — タグ: , — Kohyama @ 12:57

Clojure から Swing を使います.
今回は, イベントの処理.

テキストフィールド二つ (TF1, TF2 とします) と, ボタン二つ (B1, B2 とします)を配置.
B1 が押されると, TF1 に書かれた文字列を TF2 にコピー.
B2 が押されると, TF1 と TF2 に書かれた文字列を消去します.
ボタンのラベルは “COPY” と “CLEAR” でも良いのですが, ボタンに表示される文字列と,
内部で使うコマンド用の文字列を区別するために少し長い名前にしてあります.

(import
  (java.awt FlowLayout)
  (java.awt.event ActionListener)
  (javax.swing JButton JFrame JTextField))

(let [
  tf1  (JTextField. 32)
  tf2  (JTextField. 32)
  b1   (JButton. "COPY TF1 to TF2")
  b2   (JButton. "CLEAR BOTH")
  f    (proxy [JFrame ActionListener]["Swinging Clojure (3)"]
         (actionPerformed [ae]
           (let [c (.getActionCommand ae)]
             (cond (= c "COPY") (.setText tf2 (.getText tf1))
                   (= c "CLEAR") (dorun (.setText tf1 "")
                                        (.setText tf2 ""))))))
  pane (.getContentPane f)]

  (.setLayout pane (FlowLayout.)) 

  ; commands of buttons
  (dorun (map
    (fn [[button command]]
      (doto button
        (.addActionListener f)
        (.setActionCommand command)))
    [[b1 "COPY"] [b2 "CLEAR"]]))

  ; layout
  (dorun (map #(.add pane %) [tf1 tf2 b1 b2]))

  ; frame
  (doto f
    (.setSize 400 300)
    (.setLocationByPlatform true)
    (.setDefaultCloseOperation JFrame/DISPOSE_ON_CLOSE)
    (.setVisible true)))

10行目 proxy は
(proxy [スーパークラス インターフェイス1 インターフェイス2 ... ] [スーパークラスのコンストラクタへの引数]
(メソッド名 [仮引数] ボディ)
… )
のように書きます.
スーパークラスを extends し, インターフェイスを implements する無名のクラス(内部的には clojure が他と重複しないよう, 適当な名前を付けてくれています.) を作成し, そのインスタンスを new して返してくれます.
インターフェイスに必要な全てのメソッドを定義しなくても構いません.
ここでは, JFrame を extends し, ActionListener を implements するクラス(
actionPerformed で ActionEvent に対する処理を記述します)を無名で作成し, そのインスタンスを f に束縛しています.
23行目で, 二つのボタンにこの f を addActionLisner でリスナーとして登録していますので, ボタンが押されたときは, proxy に書いておいた actionPerformed が(ActionEvent のインスタンスを引数として)呼ばれます.
その際, setActionCommand でボタンに登録しておいた文字列を, 12行目 getActionCommand で ActionEvent から取り出して, 処理を分岐しています.

proxy では,「スーパークラス」を省略することもできます(デフォルトで Object を継承します)ので, JFrame に ActionListener をさせずに, 別途 ActionListener の機能だけを持ったクラスのインスタンスを作り, ボタンのリスナーとして登録する場合は以下のようになります.

(import
  (java.awt FlowLayout)
  (java.awt.event ActionListener)
  (javax.swing JButton JFrame JTextField))

(let [
  f    (JFrame. "Swinging Clojure (3)")
  pane (.getContentPane f)
  tf1  (JTextField. 32)
  tf2  (JTextField. 32)
  b1   (JButton. "COPY TF1 to TF2")
  b2   (JButton. "CLEAR BOTH")
  al   (proxy [ActionListener][]
         (actionPerformed [ae]
           (let [c (.getActionCommand ae)]
             (cond (= c "COPY") (.setText tf2 (.getText tf1))
                   (= c "CLEAR") (dorun (.setText tf1 "")
                                        (.setText tf2 ""))))))]

  (.setLayout pane (FlowLayout.))

  ; commands of buttons
  (dorun (map
    (fn [[button command]]
      (doto button
        (.addActionListener al)
        (.setActionCommand command)))
    [[b1 "COPY"] [b2 "CLEAR"]]))

  ; layout
  (dorun (map #(.add pane %) [tf1 tf2 b1 b2]))

  ; frame
  (doto f
    (.setSize 400 300)
    (.setLocationByPlatform true)
    (.setDefaultCloseOperation JFrame/DISPOSE_ON_CLOSE)
    (.setVisible true)))

Related Posts:

コメントをどうぞ

*

Copyright © 2010 Yoshinori Kohyama All Rights Reserved.