来自Clojure repl的JavaFX

时间:2012-08-17 16:33:40

标签: clojure javafx-2

我开始学习Clojure,我想尝试使用JavaFX for GUI。我找到了这篇文章:http://nailthatbug.net/2011/06/clojure-javafx-2-0-simple-app/ ,但我想通过repl启动它,以便快速测试和方便。

因此,例如,我可以在repl中写这个并看到新窗口:

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

有没有办法用javafx.application.Application做这样的事情 - 看看新的JavaFX窗口?

THX。安德鲁。

3 个答案:

答案 0 :(得分:9)

如果您阅读JavaFX Application class documentation,您将看到Application类是一个抽象类,无法直接实例化。这意味着,您至少必须创建javafx.application.Application的子类。

  

生命周期

     

JavaFX应用程序的入口点是Application类。该   无论何时应用程序,JavaFX运行时都会按顺序执行以下操作   推出:

     
      
  1. 构造指定Application类的实例
  2.   
  3. 调用init()方法
  4.   
  5. 调用start(javafx.stage.Stage)方法
  6.   
  7. 等待应用程序完成,这在以下任何一种情况下都会发生   发生:应用程序调用最后一个窗口的Platform.exit()   关闭并且Platform上的implicitExit属性为true调用   stop()方法请注意,start方法是抽象的,必须是   重写的。
  8.   

因此你需要生成一个类 - 使用gen-class宏,因为它可以在博客文章中看到 - 使用start方法来启动应用程序。

编辑:使用添加的gen-class方法链接到示例应用程序 我创建了一个Github repository with a simple example JavaFX application in Clojure。这是遵循gen-class方法的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的说明进行操作。

答案 1 :(得分:8)

虽然它还处于起步阶段,但我已经能够使用Upshot从REPL中使用JavaFx。主要技巧是完全忽略Application并直接创建场景。要执行此操作,您只需强制运行时进行初始化,并在core.clj:69处查看其示例。另一个技巧是你几乎所做的一切都必须包装在run-now块中,以确保它在JavaFX线程上运行。 JavaFX比Swing更挑剔线程。

答案 2 :(得分:3)

很多戴夫。 我还找到了一个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世界中我很高兴,可能是一个更有经验的人,用更多的Clojureish方式重写它。无论如何,它现在适用于我,我考虑两个启动器的概念:一个用于基于repl的开发(launch-javafx),另一个用于发布:通过普通的javafx.application.Application启动器。我仍然不知道它们是否相当于彼此(我的意思是在javafx.embed.swing.JFXPanel的情况下可以使用完整的JavaFX API),如果是这样的话,这适合我的目标(通过REPL开发)。而且我还在调查Upshot代码 - 我们可能会更加温和。