11

我开始学习 Clojure,我想尝试 JavaFX for GUI。我找到了这篇文章:http ://nailthatbug.ne​​t/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 窗口?

谢谢。安德鲁。

4

3 回答 3

10

如果您阅读JavaFX Application 类文档,您会看到 Application 类是一个抽象类,不能直接实例化。这意味着,您至少必须创建 javafx.application.Application 的子类。

生命周期

JavaFX 应用程序的入口点是 Application 类。每当启动应用程序时,JavaFX 运行时都会按顺序执行以下操作:

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

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

编辑:使用添加的 gen-class 方法链接到示例应用程序
我创建了一个Github 存储库,其中包含 Clojure 中的一个简单示例 JavaFX 应用程序。这是遵循 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 中运行代码,这将不起作用。如果您想尝试代码,请按照项目的 README.md 文件中使用 Maven 和 Leiningen 设置 JavaFX 的说明进行操作。

于 2012-08-17T17:22:16.260 回答
8

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

于 2012-08-22T04:14:47.630 回答
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)这是此代码的直接端口:将 JavaFX 集成到 Swing 应用程序 中,因此它看起来非常必要和幼稚,因为我在 Clojure 世界中是菜鸟,可能是更有经验的人,重写这个以更 Clojureish 的方式。无论如何,它现在对我有用,我想到了两个启动器的概念:一个用于通过 (launch-javafx) 进行基于 repl 的开发,另一个用于发布:通过普通的 javafx.application.Application 启动器。我仍然不知道它们是否彼此等价(我的意思是在 javafx.embed.swing.JFXPanel 的情况下可以使用完整的 JavaFX API),如果是这样,这适合我现在的目标(通过 REPL 开发)。而且我仍在调查 Upshot 代码 - 可能会发现更温和。

于 2012-08-24T01:20:17.803 回答