Beginner stuck with timers and JavaFX/Fn-Fx


#1

I’m kinda new to Clojure and I’ve been trying to maker a simple little timelapse video GUI. I’ve sorta learned that Clojure is kinda poorly suited for this b/c there is no “native” way to make GUIs or access cameras… but I’m trying to push through and not fall back on my usual C++. The GUI is using fn-fx and I really like the way you end up describing your GUI in a datastructure. It’s a very nice reoccuring pattern I’m seeing in Clojure (also saw this in geom-viz ). Next I managed to hook up origami to get camera frames through OpenCV (the author was very helpful working through some problem there… b/c it’s a bit of a messy tie-in to some native precompiled binaries)

But the next part is fetching frames at a regularly interval and I just can’t figure out :(. The JavaFX Timer event in fn-fx itself is broken… maybe? and I don’t really know enough Java to figure out how to “inject” the right code for it to work

I’ve spent a few weeks on this on and off, but I’m not really sure where to find help at this point. The library seems dead so my issue on its github has no activity :frowning:

I tried to add an external event using at-at but that doesn’t trigger the event properly (or I’m doing it wrong). In the fn-fx example it seems like it should go some place here they initialize the event handler - but the code is very hard for me to parse:

(defn -main []
  (let [;; Data State holds the business logic of our app
        data-state (atom {:authed? false})

        ;; handler-fn handles events from the ui and updates the data state
        handler-fn (fn [{:keys [event] :as all-data}]
                     (println "UI Event" event all-data)
                     (case event
                       :auth (swap! data-state assoc :authed? true)
                       (println "Unknown UI event" event all-data)))

        ;; ui-state holds the most recent state of the ui
        ui-state (agent (dom/app (stage @data-state) handler-fn))]
   
        ;; BROKEN: Something like this??
        (at-at/every 1000 #(swap! data-state handle-event :add-item) my-pool)


    ;; Every time the data-state changes, queue up an update of the UI
    (add-watch data-state :ui (fn [_ _ _ _]
                                (send ui-state
                                      (fn [old-ui]
                                        (dom/update-app old-ui (stage @data-state))))))))

If anyone has any good suggestions please let me know (or maybe I should just ditch this and rewrite it in C++ or something with C-FFI like Chicken Scheme)


#2

I would start with something naive like

(future ;; or (.start (Thread. (fn [] ...)))
  (loop []
    ,,,do something,,,
    (Thread/sleep 1000)
    (recur)))

Something to watch out for though is that GUI toolkits like JavaFX are quite particular about which thread GUI events/updates come from, so you should wrap them in a Platform/runLater. Not sure if fn-fx does that for you or not.


#3

Thanks for trying to help :slight_smile:
After playing around with it a bit I’m pretty confident that the future loop is pretty much the same as my (at-at/every ..)

ie.

(at-at/every 4000 #(swap! data-state handle-event {:event :my-camera-event}) my-pool)

is the same as

(future ;; or (.start (Thread. (fn [] ...)))
  (loop []
    (swap! data-state handle-event {:event :my-camera-event})
    (Thread/sleep 4000)
    (recur)))

I’m injecting this in the main (same as above). I had a small typo, so now I’ve manage to have it spit some errors at me finally - so that’s progress :stuck_out_tongue: However b/c I don’t really understand the fn-fx event handler dispatch setup I’m sorta poking around in the dark. The way I’m triggering these events “manually” seems to not produce the same behavior as whatever mechanism is internal to fn-fx (I think that that’s the last line in main - which is black magic to me haha)

The error stack is also not very meaningful b/c I don’t understand fn-fx internals

#error {
 :cause No implementation of method: :create-component! of protocol: #'fn-fx.diff/IDom found for class: nil
 :via
 [{:type java.lang.IllegalArgumentException
   :message No implementation of method: :create-component! of protocol: #'fn-fx.diff/IDom found for class: nil
   :at [clojure.core$_cache_protocol_fn invokeStatic core_deftype.clj 568]}]
 :trace
 [[clojure.core$_cache_protocol_fn invokeStatic core_deftype.clj 568]
  [clojure.core$_cache_protocol_fn invoke core_deftype.clj 560]
  [fn_fx.diff$eval13133$fn__13206$G__13114__13213 invoke diff.clj 7]
  [fn_fx.diff$diff$new_node__13410 invoke diff.clj 95]
  [fn_fx.diff$diff invokeStatic diff.clj 99]
  ...
  ...
  [... etc...]

Hopefully I’m just missing something obvious :slight_smile:
I thoght maybe hitting this JavaFX thread quirk you mentioned - but the error doesn’t quite look like that (or maybe I’m wrong?)
Thanks again @plexus . Let me know if you have any more thoughts!


#4

I’ve had a generally good experience interactively building GUIs in clojure using Seesaw. Sadly, the JavaFX wrappers are all fairly fragile and generally report errors poorly, which is frustrating in combination with the JavaFX model itself, which is also a bit fragile and “ahead of time” in comparison to Swing.

If you have a good interative development setup, maybe work through the Seesaw tutorial and see if it works for you?


#5

Yeah, thanks. I’ve seen Seesaw. It’s definitely more mature, but it felt very “Java”. Not to mention no one has been using Swing in the past 5 years :slight_smile:

I actually started off by using it a bit and I had it kinda working but I can’t quite remember the exact reason I dropped it. They had some limitation that I didn’t have in JFX. I think something related to updating camera frames in place to create a live camera feed… It also didn’t feel as snappy and nice as JFX

Anyways, it took me like a week to get the OpenCV camera matrices to work with JavaFX Groups… don’t really want to redo that all for the Swing Image equivalents. I feel like I spend 80% of my coding time dealing with interop issues :disappointed_relieved:

I don’t really know the precise terminology but it seemed like JavaFX really complemented Clojure with the way the layout data structure triggered events in a very functional decoupled way; so it’s a shame no framework has reached maturity. And at the end of the day it doesn’t looks like this is getting much traction here. Spending some time on Clojure forums it definitely feels like everything is about web and JS with little activity elsewhere at this point

Anyways, thanks for the helps guy - love the language, but might need to look for another tool for the job here.


#6

Just use Java FX directly. Unless you’re willing to extend and maintain FxFx yourself.

I think you’ll find it easier that way.

Now, you’re fighting too many battle. Trying to interop with Java and shoehorn Java FX into a different model as FxFx does.


#7

Huh… not a bad idea.

Do you have any example you think is worth looking at? (something simple to see the layout and workflow?)
I’m kinda new to Clojure and I’ve done almost no Java/Java-interop