I'm trying to use Swing from Clojure, and I'm getting confused by gen-class and I can't tell from the documentation if this is supposed to work - paintComponent is a protected method on JPanel, and I'm able to override it, but when I try to call the exposed superclass's method, I get java.lang.IllegalArgumentException: No matching method found: parentPaintComponent for class project.PicturePanel . Can anyone clarify why I don't seem to have access to this method?
(ns project.PicturePanel
(:gen-class
:extends javax.swing.JPanel
:name project.PicturePanel
:exposes-methods {paintComponent parentPaintComponent}))
(defn -paintComponent [this g]
(println this)
(println g)
(.parentPaintComponent this g))
Yes! The code works correctly if you make sure that your compiled .class files are up to date. Try recompiling them!
Related
I'm trying to implement an interface that has properties but can't quite seem to get it to work and I also have not found any relevant examples via Google (yet). I'm sure I'm doing something completely wrong here but have no idea how to fix it.
(System.Reflection.Assembly/LoadWithPartialName "System.Web")
; naive, just trying to figure out how to implement the IHttpHandler interface in Clojure
(defn foo-handler []
(reify System.Web.IHttpHandler
(IsReusable [] false)
(ProcessRequest [context] ())))
IsReusable is a property and I don't know how to tell reify that it is not a traditional function.
CompilerException clojure.lang.CljCompiler.Ast.ParseException: Must supply at least one argument for 'this' in: IsReusable
Okay, I supply 'this' for IsReusable
CompilerException clojure.lang.CljCompiler.Ast.ParseException: Can't define method not in interfaces: IsReusable
I've also tried proxy but I get similar results.
I've also tried naming IsReusable to get_IsReusable which doesn't actually make a difference and I get the same compiler errors as above.
I've also tried deftype but I get a completely different error:
(deftype foo-handler []
System.Web.IHttpHandler
(get_IsReusable [this] false)
(ProcessRequest [this context] ()))
Compiler error:
InvalidCastException Unable to cast object of type 'clojure.lang.Var' to type 'System.Type'. clojure.lang.Namespace.ReferenceClass
Update:
The code posted for deftype works, I cannot reproduce the error that I posted above. I have no idea now what I was doing wrong at the time.
This took me a few hours of research and trial and error but I finally have success!
user=> (def foo-handler
(reify System.Web.IHttpHandler
(get_IsReusable [this] false)
(ProcessRequest [this context] ())))
#'user/foo-handler
user=>
Success!
user=> (instance? System.Web.IHttpHandler foo-handler)
true
This way is better and works fine from an ASP.NET application:
(deftype foo-handler []
System.Web.IHttpHandler
(get_IsReusable [this] false)
(ProcessRequest [this context]
(.Write (.Response context) "Hello, From Clojure CLR!")))
the :injections keyword is really useful. However, I am hoping to dynamically install a couple of functions in core for debugging purposes. How can this be done?
:injections [(require 'spyscope.core)
(use '[cemerick.pomegranate :only (add-dependencies)])
(use '[clojure.tools.namespace.repl :only (refresh)])]
Ideally, I would want refresh to stay around to that I can use it everywhere
You could use intern for this purpose, although I suspect there might be a better way to have debugging functions available all the time. I've used intern with clojure.core the times that I wanted to mess around with existing functions to learn stuff, but injecting functions in other namespaces feels too hackish.
(intern 'clojure.core 'refresh (fn [] (println "refreshed!"))
(ns another-ns)
(refresh)
;=> refreshed!
And in your project.clj you can use the :repl-options key, specifically :init. This, though, depends on the workflow you have in mind, since the function will not be available in all the namespaces that already exist when the REPL fires up, because they all have already refered the public vars in clojure.core.
You could however, call (clojure.tools.namespace.repl/refresh) once, when the REPL starts, to get all namespaces reloaded and then the function should be available from then on. I just tried the following and it seems to work:
:repl-options {:init (do (require 'clojure.tools.namespace.repl)
(intern 'clojure.core 'refresh clojure.tools.namespace.repl/refresh)
(clojure.tools.namespace.repl/refresh))}
I'm trying to implement the basic JavaFX example shown here: http://docs.oracle.com/javafx/2/get_started/fxml_tutorial.htm . I was able to get the basic stuff working (programmatically creating the gui) and using css, but I'm having trouble with the FXMLLoader.
The java version is this:
#Override
public void start(Stage stage) throws Exception {
Parent root = FXMLLoader.load(getClass().getResource("fxml_example.fxml"));
stage.setTitle("FXML Welcome");
stage.setScene(new Scene(root, 300, 275));
stage.show();
}
I'm not a Java expert, but I don't think an FXMLLoader object is instantiated ie. there is not a new FXMLLoader(); statement. So where is the load coming from?
When i try the following clojure code:
(ns jfxtwo.core
(:gen-class
:extends javafx.application.Application)
(:import (javafx.application Application)
(javafx.fxml FXMLLoader)
(javafx.scene Parent Scene)
(javafx.stage Stage)))
(defn -main []
(javafx.application.Application/launch jfxtwo.core (into-array String [])))
(defn -start [this primaryStage]
(let [loc (clojure.java.io/resource "fxml_example.fxml")
root (.load FXMLLoader ^java.net.URL loc)
scene (Scene. root 300 250)]
(.setScene primaryStage scene)
(.show primaryStage)))
...i get Caused by: java.lang.IllegalArgumentException: No matching method found: load for class java.lang.Class .
So I put a dot after the FXMLLoader to create an instance: (FXMLLoader.) I get this: ClassCastException java.net.URL cannot be cast to java.io.InputStream
So this tell me I'm onto something since one of the load methods for FXMLLoader supports an InputStream. I tried forcing the compiler to know the resource is a java.net.URL because that's one of the supported overloads for FXMLLoader.load, by placing the call to (clojure.java.io/resource...) directly in the call to (.load...) but it still doesn't like it (I knew it was a long shot). I also tried type-hinting, (.load (FXMLLoader.) ^java.net.URL loc) and (.load (FXMLLoader.) #^java.net.URL loc), but no dice; it still tries to use the java.io.InputStream version of load.
There is also the getClass() call in java which I think is getting the superclass of Application, but I'm not sure what to do with that in clojure-land.
Any ideas on how to load the fxml file?
After that, the java code has #FXML annotation for allowing the FXML into private class members. Is this necessary in clojure (java code breaks when I remove it)? The #Override annotation doesn't seem to be used in clojure.
thanks
Given the Java syntax, the load method being called here appears to be a static method of the FXMLLoader class. To call static methods in Clojure, you need to use (ClassName/methodName args...) syntax:
(FXMLLoader/load ...)
(Just checked: FXMLLoader has both static and instance load methods with multiple signatures. You'll want to call the same method the Java code does; static methods will be called with the FXMLLoader.load syntax in Java, instance methods -- someFXMLLoaderInstance.load.)
As for the getClass method call, it's target is implicitly this in Java; in Clojure, you'll have to make the target explicit ((.getClass this)).
I was able to work around my problem by creating the FXMLLoader separately from setting the location.
(defn -start [this primaryStage]
(let [loc (clojure.java.io/resource "fxml_example.fxml")
fxmlloader (FXMLLoader.)]
(.setLocation fxmlloader loc)
(let [root (.load fxmlloader )
scene (Scene. root 300 250)]
(.setScene primaryStage scene)
(.show primaryStage))))
I'm placing Clojure into an existing Java project which heavily uses Jersey and Annotations. I'd like to be able to leverage the existing custom annotations, filters, etc of the previous work. So far I've been roughly using the deftype approach with javax.ws.rs annotations found in Chapter 9 of Clojure Programming.
(ns my.namespace.TestResource
(:use [clojure.data.json :only (json-str)])
(:import [javax.ws.rs DefaultValue QueryParam Path Produces GET]
[javax.ws.rs.core Response]))
;;My function that I'd like to call from the resource.
(defn get-response [to]
(.build
(Response/ok
(json-str {:hello to}))))
(definterface Test
(getTest [^String to]))
(deftype ^{Path "/test"} TestResource [] Test
(^{GET true
Produces ["application/json"]}
getTest
[this ^{DefaultValue "" QueryParam "to"} to]
;Drop out of "interop" code as soon as possible
(get-response to)))
As you can see from the comments, I'd like to call functions outside the deftype, but within the same namespace. At least in my mind, this allows me to have the deftype focus on interop and wiring up to Jersey, and the application logic to be separate (and more like the Clojure I want to write).
However when I do this I get the following exception:
java.lang.IllegalStateException: Attempting to call unbound fn: #'my.namespace.TestResource/get-response
Is there something unique about a deftype and namespaces?
... funny my hours on this problem did not yield an answer until after I asked here :)
It looks like namespace loading and deftypes was addressed in this post. As I suspected the deftype does not automatically load the namespace. As found in the post, I was able to fix this by adding a require like this:
(deftype ^{Path "/test"} TestResource [] Test
(^{GET true
Produces ["application/json"]}
getTest
[this ^{DefaultValue "" QueryParam "to"} to]
;Drop out of "interop" code as soon as possible
(require 'my.namespace.TestResource)
(get-response to)))
I have a Clojure proxy statement that was getting large and messy, so I decided to try factoring the code of the beginDrag method redefinition out of the proxy statement, like this:
(defn enhanced-start-drag
""
[pie]
(let [pobj (. pie getPickedNode)
pobj-coll (seq (.. pie getInputManager
getKeyboardFocus getSelection))]
(println pobj)
(println pobj-coll)
(println "----------")
(proxy-super startDrag pie))) ; THIS IS LINE 94 (SEE ERROR MSG)
(defn custom-selection-event-handler [marqueeParent selectableParent]
(proxy [PSelectionEventHandler] [marqueeParent selectableParent]
(decorateSelectedNode [node]
(let [stroke-color (Color/red)]
(.setStrokePaint node stroke-color)))
(undecorateSelectedNode [node]
(let [stroke-color (Color/black)]
(.setStrokePaint node stroke-color)))
(startDrag [pie] ; pie is a PInputEvent
(enhanced-start-drag pie))
(endStandardSelection [pie] ; pie is a PInputEvent
(let [pobj (.getPickedNode pie)
slip (. pobj getAttribute "slip")
]
(swap! *last-slip-clicked*
(fn [x] slip))))))
I get the following compile error:
cd /Users/gw/tech/clojurestuff/cljprojects/infwb/src/infwb/
1 compiler notes:
Unknown location:
error: java.lang.Exception: Unable to resolve symbol: this in this context
core.clj:94:5:
error: java.lang.Exception: Unable to resolve symbol: this in this context
(core.clj:94)
Compilation failed.
As soon as I restore the body of enhanced-start-drag into the body of the proxy statement, everything works.
My question: Is there a way to move the messy details out to a separate function to improve the readability of my code?
Thanks for all your ideas and solutions.
UPDATE, 10/27/11: See the comments below. Arthur Ulfeldt was sharp in pointing out that the issue is captured references, and Dave Ray is also correct in saying that all you have to do is add this as a parameter to enhanced-start-drag and then proxy-super will work correctly. When I made the following two changes (without any changes to the body of enhanced-start-drag), my code was working again:
(defn enhanced-start-drag
""
[pie this]
and
(startDrag [pie] ; IN THE PROXY STMT IN custom-selection-event-handler
(enhanced-start-drag pie this))
BTW, my project uses Dave Ray's seesaw project to get a Java Swing UI. seesaw is awesome, as are its docstrings and sample code (which are much better than most commercial software). I highly recommend it! And thank you, Dave!
You have been bitten by symbol capture. In this case it is intentaional though you need to stay aware of it. From the doc for proxy-super
Use to call a superclass method in the body of a proxy method.
Note, expansion captures 'this`
proxy is creating a class that calls a function, when the call gets into enhanced-start-drag the value of this is not where proxy-super expects
you may needs to pass this as another argument into enhanced-start-drag and then call (. saved-this ...) instead of using proxy-super.