2012-08-17 59 views
10

我開始學習Clojure,並且我想嘗試用於GUI的JavaFX。我發現這篇文章:http://nailthatbug.net/2011/06/clojure-javafx-2-0-simple-app/ ,但我想通過repl啓動它,以進行快速測試和方便。來自Clojure的JavaFX repl

所以,舉例來說,我可以在這個REPL寫,看看新的窗口:

(defn main-start [] 
    (doto (JFrame. "Window!") 
    (.setSize (java.awt.Dimension. 400 300)) 
    (.setVisible true))) 

有沒有辦法做這樣的事情與javafx.application.Application - 看到新的JavaFX窗口?

Thx。安德魯。

+0

你如何啓動repl?由於JavaFX運行時依賴關係,jfxrt.jar必須位於類路徑中。你使用Leiningen創建了一個項目嗎? – 2012-08-18 13:39:37

回答

8

雖然它還處於起步階段,但我可以使用Upshot從REPL使用JavaFx。主要技巧是完全忽略Application並直接創建場景。要做到這一點,你只需要強制運行時初始化,其中的例子可以在core.clj:69看到。另一個訣竅是幾乎所有你所做的事都必須封裝在run-now塊中以確保它在JavaFX線程上運行。 JavaFX比Swing更挑剔線程。

9

如果您閱讀JavaFX Application class documentation,您將看到Application類是一個抽象類,不能直接實例化。這意味着,你至少必須創建一個javafx.application.Application的子類。

生命週期

的入口點JavaFX應用程序是應用程序類。所述 JavaFX運行將執行以下操作,以便,每當應用 啓動:

  1. 構造指定的應用程序類
  2. 的實例調用init()方法
  3. 呼叫開始(JavaFX的。 stage.Stage)方法
  4. 等待應用程序來完成,當發生以下任一 的這恰好:應用程序調用Platform.exit()的最後一個窗口已關閉 和平臺的implicitExit屬性爲true調用 stop()方法請注意,啓動方法是抽象的,必須重寫 。

因此,你需要生成一個類 - 使用GEN-類的宏,因爲它可以在博客文章中可以看出 - 一個start方法可以啓動應用程序。

編輯:使用gen-class方法添加鏈接到示例應用程序
我創建了一個Github repository with a simple example JavaFX application in Clojure。這裏是繼根類方法Clojure的文件:

(ns jfx.app 
    (:import (javafx.beans.value ChangeListener ObservableValue) 
      (javafx.concurrent Worker$State) 
      (javafx.event ActionEvent EventHandler) 
      (javafx.scene Scene) 
      (javafx.scene.control Button) 
      (javafx.scene.layout StackPane)   
      (javafx.stage Stage) 
      (javafx.scene.web WebView))) 

(gen-class 
:name clj.jfx.App 
:extends javafx.application.Application 
:prefix "app-") 

(defn app-start [app ^Stage stage] 
    (let [root (StackPane.) 
     btn (Button.) 
     web-view (WebView.) 
     state-prop (.stateProperty (.getLoadWorker (.getEngine web-view))) 
     url "http://clojure.org"] 

    ;; Add a WebView (headless browser) 
    (.add (.getChildren root) web-view) 
    ;; Register listener for WebView state changes 
    (.addListener state-prop 
        (proxy [ChangeListener] [] 
        (changed [^ObservableValue ov 
           ^Worker$State old-state 
           ^Worker$State new-state] 
         (println (str "Current state:" (.name new-state))) 
         (if (= new-state Worker$State/SUCCEEDED) 
         (println (str "URL '" url "' load completed!")))))) 
    ;; Load a URL 
    (.load (.getEngine web-view) url) 

    ;; add a Button with a click handler class floating on top of the WebView 
    (.setTitle stage "JavaFX app with Clojure") 
    (.setText btn "Just a button") 
    (.setOnAction btn 
        (proxy [EventHandler] [] 
        (handle [^ActionEvent event] 
         (println "The button was clicked")))) 
    (.add (.getChildren root) btn) 

    ;; Set scene and show stage 
    (.setScene stage (Scene. root 800 600)) 
    (.show stage))) 

(defn app-stop 
    "Stop method is called when the application exits." 
    [app] 
    (println "Exiting application!") 
) 

(defn launch 
    "Launch a JavaFX Application using class clj.jfx.App" 
    [] 
    (javafx.application.Application/launch clj.jfx.App (into-array String []))) 

jfx.app命名空間有,如果你在REPL直接運行的代碼被編譯到啓動應用程序,這是不行的。如果您想嘗試這些代碼,請按照說明在project's README.md file中使用Maven和Leiningen設置JavaFX。

+0

所以,這意味着無法從repl啓動JavaFX應用程序,對吧? – Andrew 2012-08-18 10:16:28

+0

我想你可以使用'proxy'來創建一個從應用程序繼承並使用該對象的類 – Ankur 2012-08-18 10:22:15

+1

我嘗試了一個代理類,並沒有工作。通過我做的一個小測試證實:當在類構造函數完成之前調用抽象方法時,Clojure不會提供足夠的代理方法。這意味着,gen-class是唯一的選擇。 – 2012-08-18 21:01:44

2

Thx很多戴夫。 我也找到了javafx.embed.swing的解決方案。JFXPanel:

(ns to-dell3 
    (:import 
    (javafx.application Application Platform) 
    (java.util Date) 
    (javafx.scene Group Scene) 
    (javafx.scene.text Font Text) 
    (javax.swing JFrame SwingUtilities) 
    ChartApp1 
    javafx.scene.paint.Color 
    javafx.embed.swing.JFXPanel)) 



(defn launch-javafx [] (SwingUtilities/invokeLater 
    (proxy [Runnable] [] (run [] 
       (let [frame2 (JFrame. "JFrame") 
         fxPanel2 (JFXPanel.) 
         ] 
        (do 
        (.setSize frame2 500 200) 
        (.setVisible frame2 true) 
        (.setDefaultCloseOperation frame2 JFrame/DISPOSE_ON_CLOSE) 
        (.add frame2 fxPanel2) 
        (Platform/runLater (proxy [Runnable] [] (run [] (let [root2 (Group.) 
               scene2 (Scene. root2 Color/ALICEBLUE) 
               text2 (Text.)] 
              (do 
              (.setX text2 40) 
              (.setY text2 100) 
              (.setFont text2 (Font. 25)) 
              (.setText text2 "Welcome to Clojure + REPL + JavaFX!") 
              (.add (.getChildren root2) text2) 
              (.setScene fxPanel2 scene2) 
              ))))))))))) 

需要JavaFX 2.2。並且爲此,在REPL中:(Platform/setImplicitExit false) 這是此代碼的直接端口:Integrating JavaFX into Swing Applications 所以它看起來非常必要且天真,因爲我在Clojure世界中是Im noob,並且可能是更有經驗的人,將其重寫爲更多Clojureish的方式。無論如何,它現在適用於我,我想到了兩個啓動器的概念:一個用於通過(launch-javafx)進行基於repl的開發,另一個用於發佈:通過普通的javafx.application.Application啓動器。我仍然不知道它們是否相當於對方(我的意思是在Javafx.embed.swing.JFXPanel的情況下提供完整的JavaFX API),如果是這樣,這適合我的目標(通過REPL進行開發)。而且我仍在調查Upshot代碼 - 可能會更溫柔,我們將會發現。