JavaFX из Clojure repl

Я начинаю изучать Clojure, и я хочу попробовать JavaFX для графического интерфейса. Я нашел эту статью: http://nailthatbug.net/2011/06/clojure-javafx-2-0-simple-app/ , но я хочу начать его с помощью repl для быстрого тестирования и удобства.

Так, например, я могу написать в repl this и увидеть новое окно:

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

Есть ли способ сделать это с помощью javafx.application.Application - увидеть новое окно JavaFX?

спасибо. Эндрю.

Ответ 1

Хотя он все еще находится в зачаточном состоянии, я смог использовать JavaFx из REPL, используя Upshot. Основной трюк - просто полностью игнорировать Application и создать свою сцену напрямую. Для этого вам нужно только принудительно запустить среду выполнения, и пример которой можно увидеть на core.clj: 69. Другой трюк заключается в том, что почти все, что вы делаете, должно быть завернуто в блок run-now, чтобы гарантировать, что он работает в потоке JavaFX. JavaFX гораздо более придирчив к потоковому использованию, чем Swing.

Ответ 2

Если вы прочитали документацию JavaFX Application class, вы увидите, что класс Application является абстрактным классом, который нельзя создать напрямую. Это значит, что вам необходимо создать подкласс javafx.application.Application.

Жизненный цикл

Точкой входа для приложений JavaFX является класс Application. Среда исполнения JavaFX делает следующее, в порядке, когда приложение запускается:

  • Создает экземпляр указанного класса Application
  • Вызывает метод init()
  • Вызывает метод start (javafx.stage.Stage)
  • Ожидает завершения приложения, которое происходит, когда выполняется одно из следующих действий:: приложение вызывает Platform.exit(), последнее окно было закрыто, а атрибут implicitExit на платформе - true. Вызывает stop() Обратите внимание, что метод start является абстрактным и должен быть переопределяется.

Поэтому вам нужно сгенерировать класс - используя макрос gen-класса, как это видно в сообщении в блоге, - с помощью метода запуска, чтобы можно было запустить приложение.

Изменить: ссылка на пример приложения с использованием подхода gen-class добавлена ​​
Я создал репозиторий Github с помощью простого примера приложения JavaFX в Clojure. вот файл Clojure, следующий за подходом gen-class:

(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. Если вы хотите попробовать код, следуйте инструкциям по настройке JavaFX с Maven и Leiningen в файле README.md.

Ответ 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 Applications так что он выглядит очень повелительным и наивным, потому что в мире Clojure, и может быть кто-то более опытным, переписывайте это более жестоким образом. Во всяком случае, он работает для меня сейчас, и я думаю о концепции двух пусковых установок: один для репликации на основе через (launch-javafx), а другой для публикации: через обычный javafx.application.Application launcher. Я все еще не знаю, эквивалентны ли они друг другу (я имею в виду полный API JavaFX, доступный в случае javafx.embed.swing.JFXPanel), и если это так, это подходит для моей цели (разработка через REPL). И Im, все еще исследующий код Upshot - может быть более мягким, но его можно найти.