I just started making a GUI app using clojure and seesaw. It does little more that create a JFrame and a couple components. Here's the code. The main function does nothing but call start-gui and exit as soon as it returns.
(ns pause.gui
(:use seesaw.core))
(native!)
; (javax.swing.UIManager/setLookAndFeel
; "org.pushingpixels.substance.api.skin.SubstanceGraphiteLookAndFeel")
(def main-window
(frame :title "Pause"
:on-close :exit))
(def sidebar (listbox :model []))
(def main-area (text :multi-line? true
:font "MONOSPACED-PLAIN-14"
:text "test"))
(def main-split
(left-right-split (scrollable sidebar)
(scrollable main-area)
:divider-location 1/5))
(defn setup-main-window
"Fills the main window with its content"
[main-window]
(config! main-window
:content main-split)
main-window)
(defn start-gui
"Create the main window"
[]
(-> main-window
setup-main-window
pack!
show!))
I compiled this using lein uberjar and timed it with time java -jar. It reported 14.5 seconds. Is there something I'm doing wrong? I'm okay with 3 seconds startup but this is completely unacceptable.
Clojure still has quite a bit of startup time, sadly. This is mainly because of the amount of compilation / code loading that happens when Clojure loads in all the required namespaces.
For the Swing-based GUI apps that I have written, I have often written the main entry point in Java, so that you can display the initial GUI or a splash screen quickly to the user - while the rest of the app / Clojure code loads in the background.
Related
I am trying to use AWS Amplify Authentication lib in a re-frame app.
The lib provides a higher order component withAuthenticator which is supposed to wrap the main view of your app. I am trying to use reactify-component and adapt-react-class but unfortunately I get the following error:
Uncaught TypeError: Failed to construct ‘HTMLElement’: Please use the ‘new’ operator, this DOM object constructor cannot be called as a function.
(defn main-panel []
[:div
[:h1 "Hello" ]])
(def root-view
(reagent/adapt-react-class
(withAuthenticator
(reagent/reactify-component main-panel))))
(defn ^:dev/after-load mount-root []
(re-frame/clear-subscription-cache!)
(aws-config/configure)
(re-frame/dispatch-sync [::events/initialize-db])
(reagent/render [root-view]
(.getElementById js/document "app")))
Any help is appreciated
I had this issue with reagent + amplify.
Solved it with 2 changes, but I'm unsure if both are needed
#1 Change output of the google closure compiler to es6 (or higher). Amplify seems to use es6 features that Cannot be polyfilled.
This is for shadow-cljs (shadow-cljs.edn), but this should be possible for other build systems as well.
{:builds {:app {:compiler-options {:output-feature-set :es6}}}}
Disclaimer: I switched to shadow-cljs from lein-cljsbuild since I could not get lein-cljsbuild to respect the configuration for es6 output.
#2 Use functional components.
In reagent 1.0.0-alpha1 and up you can change the compiler to produce functional components by default.
(ns core
(:require
[reagent.core :as r]))
(def functional-compiler (r/create-compiler {:function-components true}))
(r/set-default-compiler! functional-compiler)
There are other ways to make functional components. Check the documentation if you don't like, or can't use, this approach.
Recently started playing with Clojurescript in Spacemacs(emacs 26.1). I am using figwheel, I usually make a change in the *.cljs file and then test the results in the function in the REPL. I recently watched a video where someone evaluated the function in the file without having to go to the REPL.
I tried to do the same using cider in spacemacs, The problem is it evaluates but does not display the results.
Below snippet is from config in my project.clj
:profiles {:dev {:source-paths ["src" "env/dev/clj"]
:dependencies [[binaryage/devtools "0.9.10"]
[figwheel-sidecar "0.5.16"]
[nrepl "0.4.4"]
[cider/piggieback "0.3.9"]]}})
Snippet from cljs file.
(defn tes []
(inc 1234))
(prn (tes))
(tes)
I tried evaluating (tes) and (prn (tes)), it evaluates but does not print anything in the echo area.
I have cider setup in spacemacs and cider REPL running during the execution. Executing the function in cider REPL by navigating to the namespace works.
Is there anything I am missing here in order to get it working in terms of setup.
You probaly need cider-nrepl.Put the following in your ~/.lein/profiles.clj file.
{:repl {:plugins [[cider/cider-nrepl "0.18.0"]]}}
I Javascript's version of react I can use
this.props
but what can I use to gave props in
:reagent-render
callback?
I am trying to do as done here in Chart.js line 14.
To answer your question, you can access the component and props in reagent-render by doing something like this
(ns chartly.core
(:require
[reagent.ratom :as ra]
[reagent.core :as r]))
(defn data-fn [implicit-this]
;; use implicit-this as needed, which is equivalent to "this"
;; From create-class docs -
;; :component-did-mount (fn [this])
)
(defn chart-render []
(let [comp (r/current-component) ;; Reagent method
r-props (r/props comp) ;; Reagent method
js-props (.-props (clj->js comp))]
(js/console.log "PROPS" r-props) ;;-- etc...
[:div {:style {:width 100}}]))
(def data (ra/atom {})) ;;--> see more info on reagent-atom
(defn chart-component []
(r/create-class
{:component-did-mount data-fn
:display-name "chart-component"
:reagent-render chart-render}))
To use -
[chart-component]
However, this is anti-pattern and will be quite difficult to manage, since you are trying to update data prop internally with component-did-mount, which, on completion, would need to manually signal the React component to update itself.
One of the features of Reagent is that is offers to detect changes and updating the component, usually using atom. See Managing State in Reagent for more info.
What you are looking to do is exactly what the Re-frame Framework is helping to manage. You set-up a data-store, and when the store changes, it signals to subscribers (React elements) to update accordingly, and you don't have to handle the signal changes yourself.
There are some edge cases where tapping into the lifecyle events are necessary, especially with charts and other drawing libraries, but I might recommend re-visiting if you find reagent atoms and/or the re-frame library insufficient for your needs. Hope this helps.
As far as I see, you accept some Hiccup data from a user as a string, right? And then try to evaluate it into user namespace, where only reagent library is loaded?
First, the more you build your further code to evaluate, the more difficult to understand it becomes. You could use something like this:
(binding [*ns* user-ns] (eval (read-string user-data)))
Also, to prevent wrong input, it would be better to validate user's input either with Schema or clojure.spac libraries. Since read-string returns a data structure, it might be checked with those two as well. So you would see an error before starting to evaluate something.
Perhaps this is bug in Clojure... my question is...
Why when I create a new JLabel in the code below do I get a ClassNotFoundException while creating the JavaFX Stage but when I don't create the JLabel then the JavaFX Stage is created (and the class is found) just fine?
To reproduce this create a new leiningen project with lein new app class-path-fail and replace core.clj with this code:
(ns class-path-fail.core
(:gen-class)
(:import (javafx.stage Stage)
(javafx.application Platform)
(javax.swing JLabel SwingUtilities)
(javafx.embed.swing JFXPanel)))
(JFXPanel.)
(SwingUtilities/invokeAndWait #(new JLabel "ha"))
(defn simple-fn-will-fail []
(let [form `(fn [] (new Stage))]
(eval form)))
(Platform/runLater #(simple-fn-will-fail))
(defn -main
"I don't do a whole lot ... yet."
[& args]
(println "Hello, World!"))
You can verify this doesn't work by running it... but then... comment out the creation of the JLabel and observe it works!!
I'm using Clojure 1.8.0.
Here's my project.clj:
(defproject class-path-fail "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.8.0"]]
:main ^:skip-aot class-path-fail.core
:target-path "target/%s"
:profiles {:uberjar {:aot :all}})
I discovered that if the first two lines of code are reversed then Stage is found on the classpath and loaded. The new first two lines would look like:
(SwingUtilities/invokeAndWait #(new JLabel "ha"))
(JFXPanel.)
I have run into similar issues (albeit in a different context), and I suspect it is due to the class loader configuration of the JavaFX application thread, not necessarily anything specific to Clojure. See for example this similar question: API works with Java application but not JavaFX and: https://community.oracle.com/thread/2564617?start=0&tstart=0
That said, why are you using so many levels of indirection, and so many different mechanisms for scheduling things to happen in the future? The eval function is rarely needed in normal Clojure code.
The line (Platform/runLater #(simple-fn-will-fail)) can be simplified: there is no need for the anonymous function, because simple-fn-will-fail is already a function taking no arguments. So this line is completely equivalent (and fails in exactly the same way): (Platform/runLater simple-fn-will-fail)
Also, simple-fn-will-fail does not actually try to create a new Stage, it just returns a function that will do that (and that function is discarded, never invoked), so if you were not invoking the compiler by calling eval, this problem would not occur.
If we can step back and get a better understanding of what you are actually trying to accomplish, there is probably a way to work around the issues with the class loader on the JavaFX application thread.
Anyway, those are some ideas. Perhaps we will hear more from someone with a deeper understanding of JavaFX and Clojure compilation.
I tinkered a bit more, after making my comment below, and found that even if I pulled the side-effecting forms inside -main, the same exception would be thrown trying to eval the form inside simple-fn-will-fail. So the issue was pointed all the more strongly at the JavaFX class loader.
I was able to get everything to run fine, however, by making sure that compilation occurs on the normal Clojure thread, and then running the resulting function (which, I think, is probably what you wanted to do anyway) using Platform/runLater. Here is my tweaked version:
(ns class-path-fail.core
(:gen-class)
(:import (javafx.stage Stage)
(javafx.application Platform)
(javax.swing JLabel SwingUtilities)
(javafx.embed.swing JFXPanel)))
(defn simple-fn-will-fail []
(let [form `(fn [] (new Stage))]
(eval form)))
(defn -main
"I don't do a whole lot ... yet."
[& args]
(JFXPanel.)
(SwingUtilities/invokeAndWait #(new JLabel "ha"))
(let [stage-creator (simple-fn-will-fail)]
(Platform/runLater stage-creator))
(println "Hello, World!"))
With this version, I can invoke (-main) with no problem.
how implement $(document).ready(function(){}) in clojurescript.
I tried this:
(. ready js/document ());;but i am trying to achieve the callback function
But doesn't seem right to me. Any ideas?
new to clojurescript so i am bit confused as to how to do this.
This should work:
(.addEventListener
js/window
"DOMContentLoaded"
(fn [] (.log js/console "DOMContentLoaded callback")))
For simply a clojurescript entry point, you may implement a main function in e.g. the core namespace:
(ns app.core)
(defn main []
(activate-app))
Then call the entry point at the end of the module:
(main)
The idea is to have the entry point main function called after all code has been loaded. Hence the module with the entry point call should not itself be required by any other modules.
A variation sets up an entry point explicitly called from javascript after the compiled clojurescript has been loaded:
(defn ^:export main []
(activate-app))
(set! js/cljs-entry-point main)
This entry point can now be called from a script element at the bottom of the body of the associated html document:
<script>cljs_entry_point()</script>
A benefit with the latter approach is that other modules still can require the module containing the entry point.
Consider a function main which calls a number of other functions.
(defn main []
(app-instruction-1)
(app-instruction-2))
(set! (.-onload js/window) main)