1
votes

I'm trying to implement the basic JavaFX example shown here: http://docs.oracle.com/javafx/2/get_started/fxml_tutorial.htm . I was able to get the basic stuff working (programmatically creating the gui) and using css, but I'm having trouble with the FXMLLoader.

The java version is this:

@Override
public void start(Stage stage) throws Exception {
   Parent root = FXMLLoader.load(getClass().getResource("fxml_example.fxml"));

    stage.setTitle("FXML Welcome");
    stage.setScene(new Scene(root, 300, 275));
    stage.show();
}

I'm not a Java expert, but I don't think an FXMLLoader object is instantiated ie. there is not a new FXMLLoader(); statement. So where is the load coming from?

When i try the following clojure code:

(ns jfxtwo.core
  (:gen-class
   :extends javafx.application.Application)

  (:import (javafx.application Application)
           (javafx.fxml FXMLLoader)
           (javafx.scene Parent Scene)
           (javafx.stage Stage)))

(defn -main []
  (javafx.application.Application/launch jfxtwo.core (into-array String [])))

(defn -start [this primaryStage]
  (let [loc (clojure.java.io/resource "fxml_example.fxml")
        root (.load FXMLLoader ^java.net.URL loc)
        scene (Scene. root 300 250)]
    (.setScene primaryStage scene)
    (.show primaryStage)))

...i get Caused by: java.lang.IllegalArgumentException: No matching method found: load for class java.lang.Class .

So I put a dot after the FXMLLoader to create an instance: (FXMLLoader.) I get this: ClassCastException java.net.URL cannot be cast to java.io.InputStream

So this tell me I'm onto something since one of the load methods for FXMLLoader supports an InputStream. I tried forcing the compiler to know the resource is a java.net.URL because that's one of the supported overloads for FXMLLoader.load, by placing the call to (clojure.java.io/resource...) directly in the call to (.load...) but it still doesn't like it (I knew it was a long shot). I also tried type-hinting, (.load (FXMLLoader.) ^java.net.URL loc) and (.load (FXMLLoader.) #^java.net.URL loc), but no dice; it still tries to use the java.io.InputStream version of load.

There is also the getClass() call in java which I think is getting the superclass of Application, but I'm not sure what to do with that in clojure-land.

Any ideas on how to load the fxml file?

After that, the java code has @FXML annotation for allowing the FXML into private class members. Is this necessary in clojure (java code breaks when I remove it)? The @Override annotation doesn't seem to be used in clojure.

thanks

2

2 Answers

1
votes

Given the Java syntax, the load method being called here appears to be a static method of the FXMLLoader class. To call static methods in Clojure, you need to use (ClassName/methodName args...) syntax:

(FXMLLoader/load ...)

(Just checked: FXMLLoader has both static and instance load methods with multiple signatures. You'll want to call the same method the Java code does; static methods will be called with the FXMLLoader.load syntax in Java, instance methods -- someFXMLLoaderInstance.load.)

As for the getClass method call, it's target is implicitly this in Java; in Clojure, you'll have to make the target explicit ((.getClass this)).

1
votes

I was able to work around my problem by creating the FXMLLoader separately from setting the location.

(defn -start [this primaryStage]
  (let [loc (clojure.java.io/resource "fxml_example.fxml")
        fxmlloader (FXMLLoader.)]
    (.setLocation fxmlloader loc) 
    (let [root (.load fxmlloader )
          scene (Scene. root 300 250)]
      (.setScene primaryStage scene)
      (.show primaryStage))))