Moving and closing PApplets (Processing 4.2)

I have a program I am trying to make that requires opening multiple additional windows on the screen and using them, setting them up in specific locations, and closing them…
The issue is that, although I’ve been trying to read though the documentation and looking at the debug values (I found them buried but very inaccessible), I unfortunately can’t figure out how to do that.
Messing with the surface seems to only affect the main window, and I can’t figure out how to set the location for the new PApplet. On top of that, closing one of these pop ups only seems to be tricky. I’m toying with having them in a list so I can drop it from the list, noLoop() it, and then hide it, but just closing it would be so much cleaner.

I am using Processing 4.2, so some of the older frame stuff I used in the past is not longer working (all gone for 4+ Remove `frame` from `PApplet` · Issue #54 · benfry/processing4 · GitHub), as well as calling surface or this.surface within the PApplet.

I found this: Closing only PApplet with exit() code
However it’s a bit old and seems to no longer work with the newer Processing version as far as I can tell.

I also found this: Getting the window position in P2D
I am looking for a way to just set it with a preexisting functionality if possible though.

If someone understands how to deal with PApplets and can answer how to set/move them (and also get their position would be a bonus), and close them without closing the whole program, I would be super appreciative!

Hi @Edenstudent,

In Processing 4, surface methods were replaced by windowResizable(), windowResize(), windowTitle(), windowMove(), and windowRatio(), and two new event handlers were added: windowResized() and windowMoved().

I don’t know about closing the window however.

Hope this still helps!

Raphaël

3 Likes

Processing does not natively support sketches with multiple windows and I don’t believe that the developers ever anticipated making that a feature.

The G4P library supports multiple windows and includes an example called ‘Mandelbrot’ which creates new windows on demand.

When using multiple windows in a single sketch there are two things to be aware of

  1. each window is effectively a distinct sketch with its own animation thread. So sharing data between the windows can cause concurrent data access exceptions although these can be avoided with appropriate code.
  2. when using G4P it is recommended to avoid closing windows unless they are actually finished with, instead simply hide/show them as and when required.
3 Likes

Thanks, but I wasn’t able to get this to work with the additional PApplets, sorry.

Awesome, thanks!
I’m looking at the documentation and the example (which is a really cool program!), and it looks like it does what I need (on the surface; still need to test and play with it).

Ok, so I adapted the code to try and simplify it for my purposes, and have this:

import g4p_controls.*;



OtherWindowData baseWindow;


void setup() {
  size(400, 400);

  println(width, height, displayWidth, displayHeight);

  surface.setTitle("test!");
  //  surface.setResizable(true);
  surface.setLocation(300, 100);
  //  surface.hideCursor();
  surface.setCursor(0);
  //println(surface.getLocation());
  println(surface.getNative());

  int a = 0;

  a = 6;

  /*
  GWindow window;
   
   // Then create the window(480x320 pixels) and show it at position [40,20]
   window = GWindow.getWindow(this, 40, 20, 480, 320, JAVA2D);
   */

  //baseWindow = new OtherWindow(-2d, -1.25d, 0.5d, 1.25d, 400, 400);
  baseWindow = new OtherWindowData(new PVector(100, 100), new PVector(400, 400));
  registerMethod("mouseEvent", this);

  //  makeNewWindow(new PVector(displayWidth - 200, 100), new PVector(300, 100));
}

void makeNewWindow(PVector pos, PVector size) {
  GWindow window = null;
  String title = "new window";
  /*
  float winPosX = pos.x;
   float winPosY = pos.y;
   */

  //  OtherWindow w = new OtherWindow(nsx, nsy, nex, ney, w, h);  //  data
  OtherWindowData wData = new OtherWindowData(pos, size);  //  data
  window = GWindow.getWindow(this, title, (int)pos.x, (int)pos.y, (int)size.x, (int)size.y, JAVA2D);  //  this creates the window http://www.lagers.org.uk/g4p/ref/classg4p__controls_1_1_g_window.html#ae31d24b803de163948350db3d885b831


  window.addData(wData);
  window.addDrawHandler(this, "renderPlot");
  window.addMouseHandler(this, "processMouseEvent");
  window.setActionOnClose(G4P.CLOSE_WINDOW);
  //  new Thread(wData).start();
}




void draw() {
  background(200, 200, 255);
}

void mouseEvent(MouseEvent event) {
  processMouseEvent(this, baseWindow, event);
}

void processMouseEvent(PApplet appc, GWinData data, MouseEvent event) {
  OtherWindowData md = (OtherWindowData)data;


  switch(event.getAction()) {
  case MouseEvent.PRESS:
    break;
  case MouseEvent.DRAG:
    break;
  case MouseEvent.RELEASE:
    makeNewWindow(new PVector(displayWidth - 200, 100), new PVector(300, 100));
    break;
  }
}



class OtherWindowData extends GWinData {

  //  public int msx, msy, mex, mey;
  //  public final double sx, sy, ex, ey;
  public final int w, h;
  public PGraphics pg;
  public boolean working = true;
  // used to count iteration range used.
  public int minC = 999, maxC = -999;

  /**
   * Create the Mandelbrot plot based on the complex plane
   * coordinates provided
   * @param sx minimum real value
   * @param sy maximum real value
   * @param ex minimum imaginary value
   * @param ey maximum imaginary vale
   * @param w pixel width for the plot
   * @param h pixel height for the plot
   */
  //  public OtherWindowData(double sx, double sy, double ex, double ey, int w, int h) {
  public OtherWindowData(PVector pos, PVector size) {
    super();
    /*
    this.sx = sx;
     this.sy = sy;
     this.ex = ex;
     this.ey = ey;
     */
    this.w = (int)size.x;
    this.h = (int)size.y;
    pg = createGraphics(this.w, this.h, JAVA2D);
    pg.beginDraw();
    pg.background(60);
    pg.textSize(24);
    pg.textAlign(CENTER, CENTER);
    pg.fill(0, 255, 0);
    pg.text("Working ...", 0, 0, this.w, this.h);
    pg.endDraw();
  }

  // This is a separate thread to calculate the Mandelbrot plot
  public void draw() {
    println("test");
  }

  public String toString() {
    //  overrides the name for the window
    return super.toString();
  }
}

So, it runs and I can click to open new ones. Going to test with more stuff soon (changing size and calling global functions from them and vice versa, but that seems simple enough); any glaring issues though?

So, follow up here; I want to have functions in the GWindow that I can call from my root/global draw(). So I set up a subclass here:

class JWindow extends GWindow {

  JWindow() {
    //super();
  }

  //  getWindow(this, title, (int)pos.x, (int)pos.y, (int)size.x, (int)size.y, JAVA2D)
  @Override
    void getWindow(PApplet pa, String t, int px, int py, int sx, int sy, Renderer r) {
    return super.getWindow(pa, t, px, py, sx, sy, r);
  }

  void fireCloseWindowEvent() {
    super.fireCloseWindowEvent();
  }
  void initListeners() {
    super.initListeners();
  }

  PVector getPosition(PVector p) {  //  p will be populated with the position
    return super.getPosition(p);
  }
  String getTitle() {
    return super.getTitle();
  }
  boolean isVisible() {
    return super.isVisible();
  }

  void setActionOnClose(int a) {
    super.setActionOnClose(a);
  }
  void setAlwaysOnTop(boolean a) {
    super.setAlwaysOnTop(a);
  }
  void setLocation(int x, int y) {
    super.setPosition(x, y);
  }

  void setTitle(String s) {
    super.setTitle(s);
  }
  void setVisible(boolean v) {
    super.setVisible(v);
  }
  boolean usesGL() {
    return super.usesGL();
  }
}

As you can see, I clarified all of the abstract functions by calling them above. However, the issue is the constructor. It says it needs a constructor, but:
-if there isn’t one: “Implicit super constructor GWindow() is undefined for default constructor. Must define an explicit constructor
-if there is but no super: “Implicit super constructor GWindow() is undefined. Must explicitly invoke another constructor
-and if it tries call super(): “The constructor GWindow() is undefined

So, as an abstract class, is there no constructor, in which case, what do I do here? Or is there one but I’m missing the right input?

…actually, I think I misunderstood and what I need is to change the PApplet on the GWindow to my functional subclass. Is that right?

Haven’t had a chance to go through all your posts but couple of comments

I wouldn’t recommend this unless you want to rebuild the library.

There are two classes that inherit from the abstract GWindow class

  1. GWindowAWT for JAVA2D sketches and
  2. GWindowNEWT for P2D/P3D sketches

If you want to create your own window class e.g. JWindow I suggest it inherits from one of these classes depending on the renderer.

The JWindow class would need its own constructor: The following code examples assume you are inheriting from GWindowAWT and needs to be modified if using GWindowNEWT

class JWindow extends GWindowAWT {
    public JWindow(String title, int w, int h) {
        super(title, w, h);
        is3D = false; 
        renderer_type = JAVA2D; 
    }
    // rest of class...

Then I suggest that you copy the GWindow getWindow method and modify it to create JWindow objects e.g.

/**
 * @param app      the main sketch window PApplet instance ('this')
 * @param title    text to appear in frame title bar
 * @param px       horizontal position of top-left corner
 * @param py       vertical position of top-left corner
 * @param w        width of drawing surface
 * @param h        height of surface
 * @return the window created (use this to get a handle on your window)
 */
public JWindow getJWindow(PApplet app, String title, int px, int py, int w, int h) {
	G4P.registerSketch(app);
	JWindow g3w = new JWindow(title, w, h);
	if (g3w != null) {
		String spath = "--sketch-path=" + G4P.sketchWindow.sketchPath();
		String loc = "--location=" + px + "," + py;
		String className = g3w.getClass().getName();
		String[] args = { spath, loc, className };
		G4P.registerWindow(g3w);
		PApplet.runSketch(args, g3w);
	}
	return g3w;
}

You can then modify the class to suit but keeps all the capabilities built into G4P.

Hope this helps :grinning:

2 Likes