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
.
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:
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:
You can test the default foo function with (foo "you there!")
. This yields
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.
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.
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.
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.
Thanks for the tutorial, is there anyway to obfuscate this project? There’s a demo online that works with just the minimal clojure project using proguard but I can’t get it to work when javafx is added
Hi, thanks for the tutorial, do you have any idea how to obfuscate this project using proguard? There’s a demo available online on how to use it with a minimal clojure project but I can’t seem to get it to work when using javafx