简体   繁体   English

Clojure repl的JavaFX

[英]JavaFX from Clojure repl

I start to learn Clojure, and I want to try JavaFX for GUI. 我开始学习Clojure,并且想尝试JavaFX for GUI。 I found this article: http://nailthatbug.net/2011/06/clojure-javafx-2-0-simple-app/ , but I want to start it via repl for fast testing and convenience. 我找到了这篇文章: http : //nailthatbug.ne​​t/2011/06/clojure-javafx-2-0-simple-app/ ,但是我想通过repl来启动它,以进行快速测试和方便。

So, for instance, I can write in repl this and see new window: 因此,例如,我可以这样编写并查看新窗口:

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

Is there any way to do such thing with javafx.application.Application - to see new JavaFX window? 有什么办法用javafx.application.Application做这样的事情-看到新的JavaFX窗口吗?

Thx. 谢谢。 Andrew. 安德鲁。

If you read the JavaFX Application class documentation , you will see that the Application class is an abstract class, which cannot be instantiated directly. 如果阅读JavaFX Application类文档 ,您将看到Application类是一个抽象类,不能直接实例化。 That means, you at least have to create a subclass of javafx.application.Application. 这意味着,您至少必须创建javafx.application.Application的子类。

Life-cycle 生命周期

The entry point for JavaFX applications is the Application class. JavaFX应用程序的入口点是Application类。 The JavaFX runtime does the following, in order, whenever an application is launched: 每当启动应用程序时,JavaFX运行时都会依次执行以下操作:

  1. Constructs an instance of the specified Application class 构造指定的Application类的实例
  2. Calls the init() method 调用init()方法
  3. Calls the start(javafx.stage.Stage) method 调用start(javafx.stage.Stage)方法
  4. Waits for the application to finish, which happens when either of the following occur: the application calls Platform.exit() the last window has been closed and the implicitExit attribute on Platform is true Calls the stop() method Note that the start method is abstract and must be overridden. 等待应用程序完成,这发生在以下情况之一:应用程序调用Platform.exit()时,最后一个窗口已关闭并且Platform上的implicitExit属性为true调用stop()方法注意,start方法为抽象,必须重写。

Therefore you need to generate a class - using the gen-class macro as it can be seen in the blog post - with a start method to be able to start the application. 因此,您需要使用gen-class宏(在博客文章中可以看到)生成一个类,该类具有一个start方法才能启动该应用程序。

Edit: Link to example application using the gen-class approach added 编辑:使用添加的gen-class方法链接到示例应用程序
I've created a Github repository with a simple example JavaFX application in Clojure . 在Clojure中创建了一个带有简单示例JavaFX应用程序Github存储库 here is the Clojure file following the gen-class approach: 这是遵循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 [])))

The jfx.app namespace has to be compiled to launch the application, this will not work if you run the code in the REPL directly. 必须编译jfx.app命名空间才能启动应用程序,如果直接在REPL中运行代码,则该命名空间将无法工作。 If you want to try the code, follow the instructions for setting up JavaFX with Maven and Leiningen in the project's README.md file . 如果要尝试使用代码,请按照说明在项目的README.md文件中使用Maven和Leiningen设置JavaFX。

Though it's still in its infancy, I've been able to use JavaFx from the REPL using Upshot . 尽管它仍处于起步阶段,但我已经能够使用Upshot从REPL中使用JavaFx。 The main trick is just to completely ignore Application and create your scene directly. 主要技巧是完全忽略Application并直接创建场景。 To do this you need only force the runtime to initialize and example of which can be seen at core.clj:69 . 为此,您只需要强制运行时进行初始化即可,其示例可以在core.clj:69上看到。 The other trick is that almost everything you do has to be wrapped in a run-now block to ensure it runs on the JavaFX thread. 另一个技巧是,几乎所有操作都必须包装在“立即run-now块中,以确保其在JavaFX线程上运行。 JavaFX is much more picky about threading than Swing. 与Swing相比,JavaFX对线程的选择更为挑剔。

Thx a lot Dave. 非常感谢戴夫。 I've also found a solution with javafx.embed.swing.JFXPanel: 我还找到了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 needed. 需要JavaFX 2.2。 And befor this, in REPL: (Platform/setImplicitExit false) This is direct port of this code: Integrating JavaFX into Swing Applications so it looks very imperative and naive, coz Im noob in Clojure world, and may be someone more experienced, rewrite this in more Clojureish way. 而且在REPL中,这是这样的:(Platform / setImplicitExit false)这是此代码的直接端口: 将JavaFX集成到Swing应用程序中,因此它看起来非常必要和幼稚,因为在Clojure世界中,我可能是个经验丰富的人,请重写此代码以更多Clojureish的方式。 Anyway, it works for me now, and I think about concept of two launchers: one for repl-based development through (launch-javafx), and one for publish: through normal javafx.application.Application launcher. 无论如何,它现在对我有用,我考虑了两个启动器的概念:一个通过(launch-javafx)用于基于repl的开发,一个用于发布:通过普通的javafx.application.Application启动器。 I still don't know does them equivalent to each other (I mean full JavaFX API availible in case of javafx.embed.swing.JFXPanel), and if so, this is fit for my goal(development through REPL) now. 我仍然不知道它们是否彼此等效(我的意思是在javafx.embed.swing.JFXPanel情况下可以使用完整的JavaFX API),如果可以的话,这很适合我的目标(通过REPL开发)。 And Im still investigating Upshot code - may be more gentle wey wil be found. 而且我仍在调查Upshot代码-也许会发现它更温和。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM