2012-08-17 18 views
10

Inizio a imparare Clojure e voglio provare JavaFX per la GUI. Ho trovato questo articolo: http://nailthatbug.net/2011/06/clojure-javafx-2-0-simple-app/ , ma voglio avviarlo tramite repl per test rapidi e convenienza.JavaFX da Clojure repl

Così, per esempio, posso scrivere in questo repl e vedere una nuova finestra:

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

Esiste un modo per fare una cosa con javafx.application.Application - per vedere una nuova finestra JavaFX?

Thx. Andrea.

+0

Come si avvia il repl? A causa della dipendenza del runtime JavaFX, jfxrt.jar deve trovarsi sul classpath. Hai creato un progetto usando Leiningen? –

risposta

8

Anche se è ancora nella sua infanzia, sono stato in grado di utilizzare JavaFx dal REPL utilizzando Upshot. Il trucco principale è semplicemente ignorare completamente Application e creare direttamente la scena. Per fare questo è necessario solo forzare il runtime per inizializzare e un esempio del quale può essere visto a core.clj:69. L'altro trucco è che quasi tutto ciò che fai deve essere racchiuso in un blocco run-now per garantire che venga eseguito sul thread JavaFX. JavaFX è molto più schizzinoso sul thread rispetto a Swing.

9

Se si legge JavaFX Application class documentation, si noterà che la classe Application è una classe astratta, che non può essere istanziata direttamente. Ciò significa che devi almeno creare una sottoclasse di javafx.application.Application.

del ciclo di vita

Il punto di ingresso per applicazioni JavaFX è la classe Application. Il JavaFX runtime fa seguito, in ordine, ogni volta che un'applicazione è lanciato:

  1. Costruisce un'istanza della classe Application specificato
  2. Chiama il metodo init()
  3. Chiamate l'inizio (JavaFX. stage.Stage)
  4. Attende il completamento dell'applicazione, che si verifica quando si verifica uno dei seguenti numeri : l'applicazione chiama Platform.exit() l'ultima finestra è stata chiusa e l'attributo implicitExit su Platform è true Chiama il 012.metodo stop() Si noti che il metodo di avvio è astratto e deve essere sottoposto a override .

Pertanto è necessario generare una classe - utilizzando la macro Gen-classe come si può vedere nel post del blog - con un metodo di avvio per poter avviare l'applicazione.

Modifica: collegamento a un'applicazione di esempio utilizzando l'approccio classe gen aggiunto
Ho creato un Github repository with a simple example JavaFX application in Clojure. qui è il file Clojure seguendo l'approccio gen-classe:

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

Il jfx.app namespace deve essere compilato per lanciare l'applicazione, questo non funzionerà se si esegue il codice nella REPL direttamente. Se si desidera provare il codice, seguire le istruzioni per l'impostazione di JavaFX con Maven e Leiningen nello project's README.md file.

+0

Quindi, questo significa che non c'è modo di avviare l'app JavaFX da repl, giusto? – Andrew

+0

Immagino che tu possa usare 'proxy' per creare una classe che erediti dall'applicazione e usare quell'oggetto – Ankur

+1

Ho provato con una classe proxy, e questo non ha funzionato. Confermato che con un piccolo test ho fatto: quando viene effettuata una chiamata al metodo abstract prima che il costruttore della classe abbia terminato, il metodo proxy non verrà fornito abbastanza presto da Clojure. Ciò significa che la gen-class è l'unica opzione. –

2

Grazie mille Dave. Ho anche trovato una soluzione 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) 
              ))))))))))) 

JavaFX 2.2 necessario. Prima di questo, in REPL: (Platform/setImplicitExit false) Questa è la porta diretta di questo codice: Integrating JavaFX into Swing Applications quindi sembra molto imperativo e ingenuo, coz Im noob nel mondo Clojure, e potrebbe essere qualcuno più esperto, riscrivi questo in più Modo clandestino. Ad ogni modo, ora funziona per me, e penso al concetto di due launcher: uno per lo sviluppo basato su repl attraverso (launch-javafx) e uno per la pubblicazione: attraverso il normale launcher javafx.application.Application. Non so ancora che siano equivalenti l'uno all'altro (intendo l'API JavaFX completa disponibile in caso di javafx.embed.swing.JFXPanel), e se è così, questo è adatto al mio obiettivo (sviluppo tramite REPL) ora. E sto ancora indagando sul codice Upshot - potrebbe essere più gentile da trovare.

Problemi correlati