Fork me on GitHub
Ben Sless13:12:26

Hi, I'm trying to get started with cljfx, tried both with lein and cli but I'm unable to load cljfx.api for some reason

(require '[cljfx.api :as fx])
Execution error (FileNotFoundException) at user/eval14091 (REPL:1).
Could not locate cljfx/api__init.class, cljfx/api.clj or cljfx/api.cljc on classpath.
These are the deps I tried with
 {org.clojure/clojure {:mvn/version "1.10.1"}}
 {commons-io/commons-io {:mvn/version "2.8.0"}}
 {cljfx {:git/url "" :sha "LATEST"}}
 :paths ["src"]}
Using java 11 on arm64 Weirdly enough, I am able to find and load other namespaces when using lein:
(require '[cljfx.fx :as fx])
works just fine Please advise?


:sha "LATEST" ?

Ben Sless13:12:18

I also tried with a real sha

Ben Sless13:12:25

{cljfx {:git/url "" :sha "955514a7968677d674ecc79126af428a9f21c91c"}}


there is something wrong with your deps.edn


{commons-io/commons-io {:mvn/version "2.8.0"}} is a key

Ben Sless13:12:09

still had the issue with lein, though

Ben Sless13:12:22

with these deps:

:dependencies [[org.clojure/clojure "1.10.1"]
                 [commons-io/commons-io "2.8.0"]
                 [cljfx "1.7.10"]]


is error the same?

Ben Sless13:12:14

(require '[cljfx.api :as fx])
Syntax error compiling at (*cider-repl Projects/clj-dedupe-fs:localhost:36281(clj)*:1:21).
namespace 'cljfx.api' not found


what’s in *e?

Ben Sless13:12:05

#error {
 :cause "namespace 'cljfx.api' not found"
 [{:type clojure.lang.Compiler$CompilerException
   :message "Syntax error compiling at (*cider-repl Projects/clj-dedupe-fs:localhost:36281(clj)*:1:21)."
   :data #:clojure.error{:phase :compile-syntax-check, :line 1, :column 21, :source "*cider-repl Projects/clj-dedupe-fs:localhost:36281(clj)*"}
   :at [clojure.core$throw_if invokeStatic "core.clj" 5867]}
  {:type java.lang.Exception
   :message "namespace 'cljfx.api' not found"
   :at [clojure.core$apply invokeStatic "core.clj" 667]}]


is this all? no stacktrace?

Ben Sless13:12:48

there is a trace, was wondering if you wanted it

Ben Sless13:12:51

[[clojure.core$apply invokeStatic "core.clj" 667]
  [clojure.core$load_libs invokeStatic "core.clj" 5985]
  [clojure.core$load_libs doInvoke "core.clj" 5969]
  [clojure.lang.RestFn applyTo "" 137]
  [clojure.core$apply invokeStatic "core.clj" 667]
  [clojure.core$require invokeStatic "core.clj" 6007]
  [clojure.core$require doInvoke "core.clj" 6007]
  [clojure.lang.RestFn invoke "" 408]
  [clj_dedupe_fs.core$eval15884 invokeStatic "form-init3277701471284635884.clj" 1]
  [clj_dedupe_fs.core$eval15884 invoke "form-init3277701471284635884.clj" 1]
  [clojure.lang.Compiler eval "" 7177]
  [clojure.lang.Compiler eval "" 7132]
  [clojure.core$eval invokeStatic "core.clj" 3214]
  [clojure.core$eval invoke "core.clj" 3210]
  [nrepl.middleware.interruptible_eval$evaluate$fn__2542$fn__2543 invoke "interruptible_eval.clj" 87]
  [clojure.lang.AFn applyToHelper "" 152]
  [clojure.lang.AFn applyTo "" 144]
  [clojure.core$apply invokeStatic "core.clj" 665]
  [clojure.core$with_bindings_STAR_ invokeStatic "core.clj" 1973]
  [clojure.core$with_bindings_STAR_ doInvoke "core.clj" 1973]
  [clojure.lang.RestFn invoke "" 425]
  [nrepl.middleware.interruptible_eval$evaluate$fn__2542 invoke "interruptible_eval.clj" 87]
  [clojure.main$repl$read_eval_print__9086$fn__9089 invoke "main.clj" 437]
  [clojure.main$repl$read_eval_print__9086 invoke "main.clj" 437]
  [clojure.main$repl$fn__9095 invoke "main.clj" 458]
  [clojure.main$repl invokeStatic "main.clj" 458]
  [clojure.main$repl doInvoke "main.clj" 368]
  [clojure.lang.RestFn applyTo "" 137]
  [clojure.core$apply invokeStatic "core.clj" 665]
  [clojure.core$apply invoke "core.clj" 660]
  [refactor_nrepl.ns.slam.hound.regrow$wrap_clojure_repl$fn__11776 doInvoke "regrow.clj" 20]
  [clojure.lang.RestFn invoke "" 1523]
  [nrepl.middleware.interruptible_eval$evaluate invokeStatic "interruptible_eval.clj" 84]
  [nrepl.middleware.interruptible_eval$evaluate invoke "interruptible_eval.clj" 56]
  [nrepl.middleware.interruptible_eval$interruptible_eval$fn__2573$fn__2577 invoke "interruptible_eval.clj" 152]
  [clojure.lang.AFn run "" 22]
  [nrepl.middleware.session$session_exec$main_loop__2640$fn__2644 invoke "session.clj" 202]
  [nrepl.middleware.session$session_exec$main_loop__2640 invoke "session.clj" 201]
  [clojure.lang.AFn run "" 22]
  [java.lang.Thread run "" 834]]}


Hmm, I assuming you call (require 'cljfx.api) in clj-dedupe-fs.core ns?

Ben Sless13:12:40

(ns clj-dedupe-fs.core
   [cljfx.api :as fx])
   [javafx.stage FileChooser]
   [javafx.event ActionEvent]
   [javafx.scene Node]
   ( File)
   ( FileUtils)))


If you can require other nses such as cljfx.fx, it probably means there is some exception during the start of JavaFX runtime


I was hoping you could see it somehow

Ben Sless13:12:00

Maybe I can see it if I clone cljfx and try there


can you try (import 'javafx.application.Platform) ?


and then (Platform/startup #(Platform/setImplicitExit false))


is there an exception at some point?

Ben Sless13:12:21

Import succeeded, startup failed with exception

Execution error (IllegalStateException) at com.sun.javafx.application.PlatformImpl/startup (
Toolkit already initialized


that looks okay

Ben Sless13:12:31

okay, cloned cljfx and tried just evaluating the api file


that means JavaFX works and you should be able to require cljfx.api

Ben Sless13:12:51

Loading library prism_es2 from resource failed: java.lang.UnsatisfiedLinkError: /home/bsless/.openjfx/cache/14/ /home/bsless/.openjfx/cache/14/ cannot open shared object file: No such file or directory (Possible cause: can't load AMD 64-bit .so on a AARCH64-bit platform)
java.lang.UnsatisfiedLinkError: /home/bsless/.openjfx/cache/14/ /home/bsless/.openjfx/cache/14/ cannot open shared object file: No such file or directory (Possible cause: can't load AMD 64-bit .so on a AARCH64-bit platform)

Ben Sless13:12:03

It's because I'm on arm?


I would say yes, there are javafx native libs for various platforms, and I don’t remember seeing arm among them…

Ben Sless13:12:51

Well then, it's 1-0 for java vs. one janky arm laptop 🙂


I just googled it and there seems to be something for arm


you are on your own there I’m afraid :)

Ben Sless13:12:06

It's okay, I'll work on it on another machine, thank you for helping 🙂

👍 3
Ben Sless13:12:57

Also have a preemptive question: uberjars built with javafx work cross platform on windows, linux and mac or do I need to make sure some extra deps are included or have specific builds?


great question 😄


by default JavaFX pulls native libraries for the same platform the build tool is running on and skips others, so uberjar will work only on the platform the uberjar is built on


if you want to create cross-platform uberjar, you’ll need to specify all classifiers explicitly, e.g. like it’s done here


(note: for cljfx project you won’t need javafx-fxml and javafx-swing)


if you want to create cross-platform application, I’d recommend looking at


it’s not too hard to build an installable app

Ben Sless09:12:44

Thanks again 🙂 I find the documentation a bit obtuse so I have resorted to reading the README very slowly and carefully and working through the examples. Any recommendations on getting started or a development guide? I'm not familiar with JavaFX or UI development at all, and I think what I'm missing the most (maybe?) is discoverability. How do I know tables should have cells? How can I know what attributes are there? That kind of stuff.


I thought about making some sort of a repl helper that answers these questions, but time/priorities…


what I do is: - use javafx javadoc to see what options are available in JavaFX - open cljfx.fx.* files to get a reminder of what are props

Ben Sless09:12:26

alright, better than nothing, I'll give it a shot


for example, if I want to configure a table, I open cljfx/fx/table_view.clj to see its props and to see its docs

🙏 3
Ben Sless09:12:06

What do you think about qualifying each view's props with a ns? for example a table would map to fx.table/prop-name?


I thought about it at the start, but decided not to do it because you’ll have to deal with inheritance, e.g. some table props are coming from its superclass, so the use will be something like {:fx/type :fx.type/table :fx.table/items [] :fx.control/tooltip {...} :fx.node/style {...}}


maybe that was a mistake to not do it, what do you think? I didn’t like the idea of having to keep this class hierarchy in the code, but perhaps it’s “essential”?

Ben Sless11:12:30

It looks like in any case I'm forced to know and understand JavaFX and consult its documentation, so perhaps reflecting the class taxonomy might not be too harmful. It would also direct the user to the appropriate class they want to configure when referring to the documentation instead of jumping around. Another option / complement to this idea is using specs to describe the different views. Even adding a simple utility function which can be called on the :fx/type for user convenience will go a very long way, something like (api/props-for :table) which will give a list of keys or a map of key-type.


I experimented a bit with it


If I were to do it properly, I’d do it with spec, but time/priorities…


> It looks like in any case I’m forced to know and understand JavaFX that’s correct, there is no way around that, just like with react where you still have to know DOM. react/cljfx is an abstraction layer with better semantics

Ben Sless11:12:46

That makes me lean more towards preferring namespace qualified keywords

Ben Sless12:12:36

regarding spec, you can always open an issue and label it PRs welcome, maybe share it on twitter/clojurians

👍 3