LoginSignup
8
9

More than 5 years have passed since last update.

Clojureから非同期的にJavaFXを触る(ための下準備)

Posted at

Clojureで何かGUIを書きたいなと思うことがあり、今からやるならJavaFXだろうと思い立ったのが発端です。
とても初歩的なことばかりになると思いますが、もしおかしな点などありましたらツッコミ頂けると助かります。

ClojureでJavaFXを使う (基本形)

一番シンプルな書き方だと、次のようなものになると思います。

(ns hellofx.core
  (:import (javafx.application Application)
           (javafx.stage Stage))
  (:gen-class
   :main true))

(defn -start
  [this stage]
  (doto stage
    (.setTitle "Hello, JavaFX")
    .show))

(defn -main
  []
  (Application/launch hellofx.core (into-array String [])))

これはこれで良いんですが、Stageに対して色々操作をしようと考えた場合はApplicationクラスを継承したナニカに実装を付け加えていく必要があり、個人的にあまりしっくりときませんでした。

とりあえずApplicationは起動しちゃうけど後から色々手を加えたい

(Application/launch)が何か管理用のオブジェクトでも返してくれれば話は早いのですがそんなこともなく、Windowを閉じるまでそもそも帰ってきてくれません。
なので、別スレッドで起動します。

(defn start-fx
  []
  (doto (Thread.
           #(Application/launch
             hellofx.core
             (into-array String [])))
    .start))

これでGUIを立ち上げた後でも処理を書き続けることができます。
とは言えこれだけだと外から何の操作も出来ないので、どうにかStageをJavaFXの外に持ち出します。

;; Stageオブジェクトが将来的に入る箱
(def window (promise))
;; start-fxはStageオブジェクトを返すように変更します
(defn start-fx
  []
  (when (not (realized? window))
    (doto (Thread.
             #(Application/launch
               hellofx.core
               (into-array String [])))
      .start))
  @window)
;; startメソッド内でstageをdeliver
(defn -start
  [this stage]
  (deliver window stage)
  (.show stage))

こうすることで、起動時はstart-fxを呼びさえすればStageをJavaFXの外に持ち出すことができるようになります。
手元のコードは次のようになりました。

;; Applicationクラスの継承 (application.clj)
(ns hellofx.application
  (:import (javafx.application Application)
           (javafx.stage Stage))
  (:gen-class
    :extends javafx.application.Application
    :name hellofx.application))

;; 将来的にStageが入る箱
(def window (promise))

;; Applicationの起動をラップし、起動後のStageオブジェクトを持ってくる
(defn start-fx
  []
  (when (not (realized? window))
    (doto (Thread.
           #(Application/launch
             hellofx.application
             (into-array String [])))
      .start))
  @window )

;; 起動時にStageをdeliverする
(defn -start
  [this stage]
  (deliver window stage )
  (.show stage))

;; 実行部分 (core.clj)
(ns hellofx.core
  (:require [hellofx.application :as app])
  (:gen-class
   :main true))

(defn -main
  [& args]
  (let [stage (app/start-fx)]
    (doto stage
      ;; Applicationクラスの外でStageの操作ができた!!
      (.setTitle "Hello, JavaFX")

これでApplicationクラスから解き放たれて、Stageを操作することができます。

まとめ

本当はもう少し深掘りしたいと考えていたのですが、予定外に忙しくなってしまったためここまでになってしまいました。が、ひとまずClojureらしくJavaFXを触るための下準備は出来たのではないかと思います。
REPL上で操作できるようになったのも個人的には嬉しいところです。

もう少し色々できるようになったら、また改めて纏めたいと思います。

8
9
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
9