Your first JavaFX app

By | November 24, 2013

Complete JavaFX installation

Before making a barebones JavaFX application under Clojure, you need to make sure the Lein program can find the appropriate jar files.  For this, we need to…

Install Maven

Maven is a java-based build system.  I really don’t know anything about it, except it uses a bunch of xml files.  Fortunately we won’t have to get into that here.

Download and install Maven from here.  Scroll down to the Windows installation instructions.  Basically you unzip the file, then set the M2_HOME and M2 environment variables to the directory where you installed it, and the bin/ directories, respectively, and add %M2% to your PATH.  Verify it works at the command line with
mvn --version.

mvnversion

Add javafxrt.jar to Maven repository

It appears Maven and therefore Lein operate on the idea of a repository, which can be remote or local.  In most circumstances when you set up your Clojure project you will point to some remote repository for Lein to get the latest .jars from.  However for JavaFX you need Lein to know that javafxrt.jar is on the local machine so it will automatically include that .jar file in your Clojure JavaFX projects.  Otherwise you’d probably have to manually monkey around with the CLASSPATH to get it to work.  You only have to run this Maven thing once.  After the initial set up, your Clojure JavaFX projects will just work.

Go to the command prompt and type this (example taken from here):

mvn install:install-file -Dfile="C:\Program Files\Java\jdk1.7.0_45\jre\lib\jfxrt.jar" -DgroupId=com.oracle.local -DartifactId=jfxrt -Dversion=1.7.0_45 -Dpackaging=jar

Of course this makes some assumptions about where your java installation is; modify the above command as necessary for your system.  After a bunch of messages and downloads it should give you some successful message:

mvnsuccess

Create Minimal JavaFX Clojure program

Go to your code directory (eg c:\users\Admin\code\clojure) and create a new lein project with:

lein new myproject

This creates a bunch of folders, an initial project.clj, and an initial src/myproject/core.clj which contains the barebones minimal clojure app.  Actually it’s not even sufficient because you can’t run it directly from the command line.  For that you have to add the :aot and :main options at the bottom of the project.clj file.  The :main tells Lein the namespace to use, and then within that namespace it looks for a function called -main.    So in other words you also need to modify the core.clj file so it has the -main function, which we’ll do next.  The :aot tells Lein to do ahead-of-time compiling.  This is necessary so you have the .class files already available at runtime, instead of compiling on the fly as Clojure would normally do.  This also enables you to compile the source from within an Emacs nrepl session.

Edit the project.clj file so it looks like this:

(defproject myproject "0.1.0-SNAPSHOT"
  :description "FIXME: write description"
  :url "http://example.com/FIXME"
  :license {:name "Eclipse Public License"
            :url "http://www.eclipse.org/legal/epl-v10.html"}
  :dependencies [[org.clojure/clojure "1.5.1"]
                 [com.oracle.local/jfxrt "1.7.0_45"]]
  :aot :all
  :main myproject.core)

Then run lein deps. This somehow does some magic behind the scenes to pull in the dependencies you specify in project.clj.  You can find that the required jfxrt.jar file has been copied to c:\Users\Admin\.m2\repository\com\oracle\local\jfxrt\1.7.0_45\ .  I’m not sure if Maven put it there or if Lein put it there, and I’m not sure how Lein encodes the fact that myproject needs to use that .jar file.  In older versions of Lein it would copy the .jar files to a subdirectory of myproject, but I’m not sure what it does in the version I’m running now (lein version gives me 2.3.3) .

You can dive right into the repl based on this with lein repl .  This starts the application and gives you this:

LeinRepl

You can test the default foo function with (foo "you there!").  This yields

LeinReplFoo

You can also verify the jfxrt.jar file has been included in the classpath with this line I got from here.

(println (seq (.getURLs (java.lang.ClassLoader/getSystemClassLoader))))

Here it yields the following. Highlight shows the .jar file we care to include.

classpathcheck

Type (quit) <enter> then edit the src/myproject/core.clj file by adding the highlighted lines.

(ns myproject.core)

(defn foo
  "I don't do a whole lot."
  [x]
  (println x "Hello, World!"))

(defn -main []
  (foo "You there"))

Then go back and type lein run from the the myproject directory.  Actually it’ll work from anywhere within the myproject folder structure.

LeinRun2

So basically we’ve recreated what an earlier post already had you do — just run a basic clojure program with Lein.  Except now we have the required jxfrt.jar file included in the classpath.

Let’s make the barebones app do something.  For that we need to edit the main file core.clj as follows.

(ns myproject.core
  (:gen-class
   :extends javafx.application.Application)
  (:import (javafx.application Application)
           (javafx.event ActionEvent EventHandler)
           (javafx.scene Scene)
           (javafx.scene.layout VBox)
           (javafx.scene.control Button)
           (javafx.stage Stage)))

;; This is required to start running from lein command line 'lein run'
(defn -main []
  ;; into-array function creates an empty String array which is passed to Application/launch
  (javafx.application.Application/launch myproject.core (into-array String [])))

(defn -start [this primaryStage]
  (let [btn (Button. "Hello World from JavaFX Clojure!")
        vbox (VBox.)
        scene (Scene. vbox)]
    (-> vbox (.getChildren) (.add btn))
    (.setOnAction btn
                  (proxy [EventHandler] []
                    (handle [ event]
                      (println "Button pressed!"))))
    (.setScene primaryStage scene)
    (.setTitle primaryStage "Hello JavaFX from Clojure")
    (.show primaryStage)))

(defn -stop [app]
  (println "Exiting now"))

Type lein run again at the command line and you’ll see the JavaFX window! Press the button a few times and the text appears on the console.
HelloJavaFX
Congratulations on your first JavaFX app for Clojure :)

In the next post I’ll show a cool example from the Pro JavaFX 2 book translated to Clojure, and some macros to help reduce the repeated typing required in Java.

Leave a Reply

Your email address will not be published. Required fields are marked *