2012-08-17 22 views
10

Empiezo a aprender Clojure, y quiero probar JavaFX para GUI. Encontré este artículo: http://nailthatbug.net/2011/06/clojure-javafx-2-0-simple-app/ , pero quiero iniciarlo a través de repl para pruebas rápidas y conveniencia.JavaFX de Clojure repl

Así, por ejemplo, puedo escribir en repl esto y ver una nueva ventana:

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

¿Hay alguna manera de hacer tal cosa con javafx.application.Application - para ver una nueva ventana JavaFX?

Thx. Andrés.

+0

¿Cómo se lanza el repl? Debido a la dependencia del tiempo de ejecución de JavaFX, el jfxrt.jar debe estar en el classpath. ¿Creó un proyecto usando Leiningen? –

Respuesta

8

Aunque todavía está en su infancia, he podido usar JavaFx desde REPL usando Upshot. El truco principal es ignorar por completo Application y crear su escena directamente. Para hacer esto, solo necesita forzar el tiempo de ejecución para inicializar y un ejemplo de lo cual se puede ver en core.clj:69. El otro truco es que casi todo lo que tienes que hacer tiene que estar envuelto en un bloque run-now para garantizar que se ejecuta en el subproceso JavaFX. JavaFX es mucho más exigente con el enhebrado que Swing.

9

Si lee JavaFX Application class documentation, verá que la clase de aplicación es una clase abstracta, que no puede crearse una instancia directamente. Eso significa que, al menos, tiene que crear una subclase de javafx.application.Application.

del ciclo de vida

El punto de entrada para aplicaciones JavaFX es la clase de aplicación. El JavaFX tiempo de ejecución hace lo siguiente, en orden, cada vez que se inicia una aplicación :

  1. construye una instancia de la clase de la aplicación especificada
  2. Llama al método init()
  3. Llama al inicio (javafx. stage.Stage) método
  4. Espera a que finalice la aplicación, que ocurre cuando ocurre cualquiera de las siguientes : la aplicación llama a Platform.exit() la última ventana ha sido cerrada y el atributo implicitExit en la plataforma es verdadero. Llama al método stop() Tenga en cuenta que el método de inicio es abstracto y debe ser anulado.

Por lo tanto es necesario generar una clase - con ayuda de la macro-Gen clase, ya que se puede ver en la entrada del blog - con un método de inicio para poder iniciar la aplicación.

Editar: Enlace a la aplicación de ejemplo utilizando el enfoque gen-class agregado
He creado un Github repository with a simple example JavaFX application in Clojure. aquí es el archivo de Clojure siguiendo el enfoque de clase GEN:

(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 []))) 

El jfx.app espacio de nombres tiene que ser compilado para iniciar la aplicación, esto no funcionará si se ejecuta el código en el REPL directamente. Si desea probar el código, siga las instrucciones para configurar JavaFX con Maven y Leiningen en el project's README.md file.

+0

Entonces, esto significa que no hay forma de iniciar la aplicación JavaFX desde la réplica, ¿verdad? – Andrew

+0

Supongo que puede usar 'proxy' para crear una clase que herede de la aplicación y usar ese objeto – Ankur

+1

Lo intenté con una clase proxy, y eso no funcionó. Confirmé que con una pequeña prueba lo hice: cuando se hace una llamada al método abstracto antes de que el constructor de la clase haya terminado, Clojure no proporcionará el método por proxy lo suficientemente temprano. Eso significa que gen-class es la única opción. –

2

Muchas gracias Dave. También encontré una solución con 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) 
              ))))))))))) 

Requiere JavaFX 2.2. Y antes de esto, en REPL: (Platform/setImplicitExit false) Este es el puerto directo de este código: Integrating JavaFX into Swing Applications por lo que se ve muy imperativo e ingenuo, porque soy novato en el mundo Clojure, y puede ser alguien con más experiencia, reescribir esto en más Clojureish manera. De todos modos, ahora funciona para mí y pienso en el concepto de dos lanzadores: uno para el desarrollo basado en repl a través de (launch-javafx) y otro para publish: a través de la aplicación javafx.application normal. Lanzador de aplicaciones. Todavía no sé si los hace equivalentes entre sí (me refiero a la API completa de JavaFX disponible en el caso de javafx.embed.swing.JFXPanel), y si es así, esto es adecuado para mi objetivo (desarrollo a través de REPL) ahora. Y todavía estoy investigando el código de Upshot, puede ser más suave y lo encontraremos.

Cuestiones relacionadas