In the library I’m currently writing I have to give my classes the current instance of the PApplet to e.g. register Methods. If you use the Library you therefore have to call a lot of methods just with this from the sketch. Is there a way to get this info without having the PApplet provide it? (I already use a system that basicly distributes the info across all the classes if the library when a PApplet is used as an argument once.
I however know no way of how to get the info about the instance of PApplet. Currently I only know how to get the class:
This is basicly how I’m already doing it however in the library you often have to use this. Basicly almost every class uses this so it would be nice to catch the object when loading the class.
void setup() {
LibraryClass.init(this);
}
// static required here because it is an inner class
// it is unlikely to be required in your library
static public class LibraryClass {
private static PApplet _pa;
public static PApplet pa() {
return _pa;
}
public static void init(PApplet pa) {
_pa = pa;
}
// In the rest of your library you can get the PApplet instance with
// LibraryClass.pa();
}
I’m already this kind of system. But it seems to be impossible to get an instance from the class alone…
The problem with this aproach is that if you want to set up some code that is executed when the class is loaded you have to give the info to the data storage class before calling the other class wich can be confusing to the user.
Are you working outside the Processing IDE? If so, you can replicate PApplet’s static runSketch() method (which creates an instance of the PApplet from the overridden class) and keep the reference to the PApplet instance that is created for further use.
It might be a dirty solution but I managed to create a method that replaces the original sketch with a copy here is the code to do that. It doesn’t accept a PApplet at a point so I could add this to my library for the case the user doesn’t provide a PApplet willingly.
Sorry for reviving this topic but I might have found an interesting aproach that might work:
I have the following idea:
You can get all active windows from an active thread so you might be able to access the asociated JFrame. From there you could get the SmoothCanvas.
In the creation of the SmoothCanvas the sketch as a field.
Here is the part I didn’t manage:
SmoothCanvas is a nested class of PSurfaceAWT. And the field itself is a part of the superclass of PSurfaceAWT and is protected.
After seeing reflection isn’t very helpful I tried the folowing:
MainSketch.pde
I have written a class that does something simular to finding the instance of the current PApplet. It replaces the PApplet with a copy that has the same properties as the original one disables the first one and returns the new one:
import processing.awt.*;
import processing.core.*;
import javax.swing.*;
import java.awt.*;
import java.lang.reflect.*;
/**This class finds the active PApplet (Processing program)
* @author NumericPrime*/
public class PAppletFinder{
/**This class takes the contents of the window and pieces together the PApplet
* @return PApplet of the current sketch.
* @param c contents of the Processing window (must be a SmoothCanvas)*/
public static PApplet get(Component c) {
PSurfaceAWT.SmoothCanvas sc=(PSurfaceAWT.SmoothCanvas) c;
try {
PSurfaceAWT psa=(PSurfaceAWT) get(sc,"this$0",sc.getClass());
PApplet prg=(PApplet) get((PSurfaceNone)psa,"sketch",PSurfaceNone.class);
return prg;
}
catch(Exception e) {
e.printStackTrace();
}
return null;
}
public static PApplet foundPapplet=null;
/**The main method to be used when using the PAppletFinder*/
public static PApplet find() {
if(foundPapplet==null) foundPapplet=findFromWindow();
return foundPapplet;
}
/**This looks out for windows and gives the contents of the right one to the get method
* @return PApplet of the current sketch.*/
public static PApplet findFromWindow() {
JFrame mainWindow=null;
java.awt.Window win[]=java.awt.Window.getWindows();
for (int i=0; i<win.length&&mainWindow==null; i++) if (win[i] instanceof JFrame) mainWindow=(JFrame) win[i];
Component c=mainWindow.getContentPane().getComponents()[0];
return get(c);
}
/**This is used to get the this$0 field of the SmoothCanvas
* Directly searching for the field didn't work. However for some reason
* looking through all the fields does the trick.
* @return Object value of a field
* @param j the Object from which the value is taken
* @param target name of the field*/
public static Object get(Object j,String target,Class<?> ref) {
Field[] field_=ref.getDeclaredFields();
for(int i=0;i<field_.length;i++) {
field_[i].setAccessible(true);
try {
if(field_[i].getName().equals(target)) return field_[i].get(j);
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
return null;
}
}
(I will expand upon this to make it more suitable for using it in a library) Once I found out in wich catagory to put it I will post the done code.
Your solution works well for sketches using JAVA2D but not in P2D and P3D modes (at least in Processing 3.5.4) I don’t know if this is important to you
I could scout the processing source code to find out more about it. If its just another component that is used I can adjust my code.
Ok I looked in the source code. My code has the following problem:
When using any other other renderer than JAVA2D Processing won’t use JFrames anymore but rather the OpenGL windows it seems.
The reason my code works in the first place is that I can look up all active windows and find the Processing window.
However it seems that the windows generated by P2D and P3D aren’t instances of java.awt.Window so aren’t tracked by getWindows() . This makes it way harder to get a point from wich to try and get the PApplet.
I might get something to work if I can get a custom class to load before the window is created.
Never mind everyone… I did just ignore the most obvious possibility:
PAppletFinder.java
import javax.swing.*;
import java.awt.*;
import java.lang.reflect.*;
/**This class finds the active PApplet (Processing program)
* @author NumericPrime*/
public class PAppletFinder {
public static PApplet findPApplet() {
java.awt.Window win[]=java.awt.Window.getWindows();
return foundPapplet=(PApplet) (get(win[0], "this$0", win[0].getClass()));
}
public static PApplet foundPapplet=null;
/**The main method to be used when using the PAppletFinder*/
public static PApplet find() {
if (foundPapplet==null) findPApplet();
return foundPapplet;
}
/**This is used to get the this$0 field of the SmoothCanvas
* Directly searching for the field didn't work. However for some reason
* looking through all the fields does the trick.
* @return Object value of a field
* @param j the Object from which the value is taken
* @param target name of the field*/
public static Object get(Object j, String target, Class<?> ref) {
Field[] field_=ref.getDeclaredFields();
for (int i=0; i<field_.length; i++) {
field_[i].setAccessible(true);
try {
if (field_[i].getName().equals(target)) return field_[i].get(j);
}
catch (IllegalArgumentException e) {
e.printStackTrace();
}
catch (IllegalAccessException e) {
e.printStackTrace();
}
}
return null;
}
}
Hidden field this$0 points to the most immediate enclosing class.
For a class to have the hidden field this$0 it must be an inner class.
There are 2 types of inner classes:
Non-static nested class
Anonymous created class
A non-static nested class is any class that isn’t declared w/ keyword static & it’s defined inside another class.
In Processing all non-static classes defined inside a “.pde” file is by definition an inner class.
That’s b/c before our sketch is compiled all “.pde” files are concatenated as 1 “.java” file.
Then that whole content is wrapped up inside a PApplet subclass.
Your attempt above failed b/c it wasn’t inside a non-static class.
In my linked example the code is defined inside a non-static class called Inner.
Now for the 2nd type of inner class we have anonymous instantiation.
That special kind of instantiation happens when we instantiate a class or interface at the same time we add more stuff to it on-the-fly using curly braces {}.
This is a regular instantiation: List<Long> vecs = new ArrayList<>();
But even an empty {} at the end: List<Long> vecs = new ArrayList<>() {};
is enough to turn that into an anonymous instantiation, making that particular ArrayList object to have a hidden field this$0 pointing to the class that code is defined!
There’s a particular anonymous instantiation inside PApplet’s source code that creates a Frame object: frame = new Frame() {
A Frame is a subclass of Window, which you’ve smartly found out how to grab it via the static method Window.getWindows().
B/c it has been instantiated using curly braces {}, that Frame inherited a PAppletthis$0 hidden reference!
However you should consider some rare edge cases which a sketch might create other Window objects.
So you should check the whole Window[] array until you find a this$0 that is an instanceofPApplet.
Unfortunately your hackish way of auto-discovering the sketch’s PApplet reference will be short-lived.
Processing 4 has commented out that whole Frame instantiation block!
So your workaround is stuck w/ previous Processing versions only: