Get the current instance of PApplet from a library

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:

import java.util.*;
import java.lang.reflect.*;
ClassLoader orgcl=Thread.currentThread().getContextClassLoader();
try {
  Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
  Field f=ClassLoader.class.getDeclaredField("classes");
  f.setAccessible(true);
  Class<?> searched=null;
  Vector<Class> classes=(Vector<Class>)f.get(getClass().getClassLoader());
  for (Class c:classes) {
    println(c);
    if (c.getSuperclass()==PApplet.class) searched=c;
  }
  println(searched);
}catch(Exception e){}
finally {
  Thread.currentThread().setContextClassLoader(orgcl);
}

Is there a way to get the PApplet object?

You can pass the current instance when you create library class objects like this

LibraryClass lc;

void setup(){
  lc = new LibraryClass(this); // Pass current PApplet instance
}

class LibraryClass {
  PApplet pa;
  
  LibraryClass(PApplet pa){
    this.pa = pa;
  }
 
}

There are other ways of doing the same thing but it depends on the design of your library.

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.

Here is another approach

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();
}
1 Like

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.

If he’s coding an addon library for Processing I believe he’s expecting it to be used by Processing’s IDE (PDE) coders.

When the 1st statement of our sketch is run the PApplet object has already been created.

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.

import java.util.*;
import java.lang.reflect.*;
static volatile boolean newThread=false;
void setup() {
  size(600,700);
  newRunningPapplet();
}
void draw(){
background(frameCount);
}
Class<?> getSketchClass() {
  ClassLoader orgcl=Thread.currentThread().getContextClassLoader();
  Class<?> searched=null;
  try {
    Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
    Field f=ClassLoader.class.getDeclaredField("classes");
    f.setAccessible(true);
    Vector<Class> classes=(Vector<Class>)f.get(getClass().getClassLoader());
    for (Class c:classes) {
      println(c);
      if (c.getSuperclass()==PApplet.class) searched=c;
    }
    println(searched);
  }
  catch(Exception e) {
  }
  finally {
    Thread.currentThread().setContextClassLoader(orgcl);
  }
  return searched;
}

PApplet newRunningPapplet() {
  if (!newThread) {
    PApplet newSketch=null;
    try {
      Class<?> c=getSketchClass();
      newSketch=(PApplet) c.newInstance();
      java.awt.Window win[]=java.awt.Window.getWindows();
      for(int i=0;i<win.length;i++) win[i].dispose();
      
      PApplet.runSketch(new String[]{newSketch.toString()}, newSketch);
      newThread=true;
    }
    catch(Exception e) {
      e.printStackTrace();
    }
    return newSketch;
  }else return null;
}

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

void setup() {
  javax.swing.JFrame  mainFrame=(javax.swing.JFrame) ((processing.awt.PSurfaceAWT.SmoothCanvas)this.getSurface().getNative()).getFrame();
  println(GetPApplet.get(mainFrame.getContentPane().getComponents()[0]));
}
void draw() {
}

GetPApplet.java

package processing.core;
import java.awt.*;
import processing.awt.*;
import javax.swing.*;
import processing.core.*;
public class GetPApplet{

  public static PApplet get(java.awt.Component c) {
    processing.awt.PSurfaceAWT.SmoothCanvas sc=(processing.awt.PSurfaceAWT.SmoothCanvas) c;
    PApplet f=sc.sketch;
    return f;
  }
}

However trying it gives me: sketch cannot be resolved or is not a field

So I’m looking for a salution to one of the following problems:

  • Get the PSurfaceAWT from the window
  • Get the PSurfaceAWT from the SmoothCanvas
  • Access a field of the class from within a instance of a nested class

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:

PAppletFinderTest.pde

int test=5;
void setup() {
  PApplet newpapplet=PAppletFinder.force();
  println(newpapplet,this);
  ((PAppletFinderTest)newpapplet).test=1;
  println(test);
}

PAppletFinder.java

import processing.awt.PSurfaceAWT;
import processing.core.PApplet;


import java.util.*;
import javax.swing.JFrame;
import java.lang.reflect.*;
public class PAppletFinder {
  public static boolean forceAllowed=true;
  public static volatile boolean newThread=false;
  public static volatile PApplet genPApplet=null;
  public static void deleteAllWindows() {
    java.awt.Window win[]=java.awt.Window.getWindows();
        for(int i=0;i<win.length;i++) win[i].dispose();
  }
  public static PApplet retrieve() {
    return genPApplet;
  }
  public static Class<?> getSketchClass() {
      ClassLoader orgcl=Thread.currentThread().getContextClassLoader();
      Class<?> searched=null;
      try {
        Thread.currentThread().setContextClassLoader(PAppletFinder.class.getClassLoader());
        Field f=ClassLoader.class.getDeclaredField("classes");
        f.setAccessible(true);
        Vector<Class> classes=(Vector<Class>)f.get(PAppletFinder.class.getClassLoader());
        for (Class c:classes) {
          //println(c);
          if (c.getSuperclass()==PApplet.class) searched=c;
        }
        //println(searched);
      }
      catch(Exception e) {
        e.printStackTrace();
      }
      finally {
        Thread.currentThread().setContextClassLoader(orgcl);
      }
      return searched;
    }
  public static PApplet force() {
      if (!newThread) {
      if(!forceAllowed) throw new ForceNotAllowedException("Restarting the sketch isn't allowed");
        PApplet newSketch=null;
        try {
        java.awt.Window win[]=java.awt.Window.getWindows();
        int xwin = 0,ywin=0;
        if(win.length>0) {
          JFrame frame_win=(JFrame)win[win.length-1];
          xwin=frame_win.getLocation().x;
          ywin=frame_win.getLocation().y;
        }
        for(int i=0;i<win.length;i++) win[i].dispose();
          Class<?> c=getSketchClass();
          newSketch=(PApplet) c.newInstance();
          newThread=true;
          PApplet.runSketch(new String[]{newSketch.toString()}, newSketch);
          genPApplet=newSketch;
          if(genPApplet.sketchRenderer().matches(PConstants.JAVA2D))((PSurfaceAWT.SmoothCanvas)(genPApplet.getSurface().getNative())).getFrame().setLocation(xwin, ywin);
          while(newThread){}
        }
        catch(Exception e) {
          e.printStackTrace();
        }
        return newSketch;
      }else return genPApplet;
    }
  static class ForceNotAllowedException extends RuntimeException{
    public ForceNotAllowedException() {
      super();
    }
    public ForceNotAllowedException(String message) {
      super(message);
    }
  }
static{find();}
}

I’ve done it!

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.

Well done on finding a way to do this. :grinning:

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 :question:

3 Likes

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.

This seems an awful lot of work to avoid the sketch providing the PApplet object in the first place.

4 Likes

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;
  }
}


mainSketch

void setup() {
  size(300,300,P3D);
  println(PAppletFinder.find());
  println(this);
}
void draw() {
}

Edit: Made the code PAppletFinder in a .java file one can use in a library

Link for old forum topics mentioning hidden field “this$0”

1 Like

Do you mean this one?
Problem with method shadowing - Processing 2.x and 3.x Forum

1 Like

Any of those 5 topics under tag “this$0”. :wink:

1 Like

Problem is for some reason this code doesn’t work:

import java.awt.*;
import java.lang.reflect.*;
void setup() {
  try {
    java.awt.Window w=java.awt.Window.getWindows()[0];
    Field f=w.getClass().getDeclaredField("this$0");
    f.setAccessible(true);
    PApplet found=(PApplet) f.get(w);
    println(found);
    println(this);
  }
  catch(Exception e) {
    e.printStackTrace();
  }
}

Edit: Never mind it works

1 Like

  • 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:
    1. Non-static nested class
    2. 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 PApplet this$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 instanceof PApplet.
  • 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:

1 Like