Button type G4P not show in save canvas to PNG

Win10 // Processing 3.5.3
i am using the example
/ File / Examples / Contributed Libraries / G4P / G4P_ ImageButtons /

and added acc https://processing.org/reference/save_.html

//KLL test

String outfile = "data/snap.png";

void keyPressed() {
  if ( key == 'p' ) {
    save(outfile);
    println("save to "+outfile);
  }
}

but instead this screen shot
processing_G4P_button_NO_snap
i find only
snap

do i need to save it from inside draw? somewhere

2 Likes

I know this is not efficient way to do that
but redrawing the components solved the problem :flushed:
btnInfo.draw();

3 Likes

yes, thanks again, while that works, i still want to ask if the

save(outfile);

can be repaired to print ALL canvas elements?
? what/where is the save.xml thing ?
or must i use saveFrame()…?


or, should i ask @quark to add a own
?canvas print to PNG…incl. G4P elements?
function to his library?


obviously i not understand the background / incl. why @humayung 's trick works

To fully understand what is happening we need to discuss Processing’s event loop.

Each processing loop executes a number of methods

  1. pre()
    ++ all G4P controls containing pre() methods and registered with Processing
    ------------------------------- beginDraw()
  2. draw()
    ++ all G4P controls containing draw() methods and registered with Processing
  3. post()
    ++ all G4P controls containing post() methods and registered with Processing
    ------------------------------- endDraw()
  4. mouseEvent(MouseEvent event)
    ++ all G4P controls containing mouseEvent() methods and registered with Processing
  5. keyEvent(KeyEvent event)
    ++ all G4P controls containing keyEvent() methods and registered with Processing

Note the ++ stages execute after the previous one has finished

Everything that appears in the window occurs between beginDraw() and endDraw() so you would think saveFrame(…) {have you tried saveFrame, I haven’t yet} and save() would also save the G4P controls but they don’t. I suspect that Processing saves the display image after (2) but before G4P draw its controls.

Now you tried it inside the keyPressed() method and you might think this should have worked but Processing queues all drawing commands inside the key??? and mouse??? methods so they are executed inside (2) which again would explain why they don’t get saved.

Now when Processing went to version 3 the changes were so significant that I had to completely rework G4P and one of the improvements to increase speed was for each visual control to have a graphic buffer for its display and have lazy updating of the buffer. This buffer is present in all visual controls (see below for exceptions) and two new methods were added (inside GAbstractControl class) to save access the buffer, they are

PGraphics getSnapshot()  // return a copy of the buffer
void saveSnapshot(String filename) // save the buffer as a file

The only controls that don’t have this capability are those which use images directly for its UI because a buffer would not improve performance. These controls include GImageButton, GCustomSlider and off the top of my head I can’t remember the others.

It would be nice if Processing saved the frame after endDraw so that everything is saved but I have no control over that.

The reason that the solution provided by humaung works is now obvious, the button image is now being draw inside the draw() method so gets saved off. It is not an efficient method because the button will get drawn twice for each frame.

I might have a better solution to this but I can’t test it until I get home to my computer.

I hope that you find the inner workings of Processing as interesting as I do :wink:

4 Likes

thanks for taking the time,
so basically i understand:

it is a problem
WHEN the save ( is called ( where in sketch ) but mostly executed by processing )

because actually i worried like there are 2 different
?places? buffers where your controls and the processing draw things end up.

but as your controls are always ON TOP
i think that is happening directly after the processing draw() ??
and my keyboard action activated save() should happen later?

would it make a difference if i use a G4P button OR a G4P key event to trigger the save()
instead like in my example?

True, G4P controls are drawn immediately after the Processing sketch completes its draw() method

Thats is a good idea because G4P mouse and button events are both called outside the beginDraw - endDraw at stage (4) and (5) on my previous timeline.

now i tried to test from the other side,
got little bit confused by the drawing order,
but basically all is printed to PNG file.

the idea was that the ?precompiler? for a registered class draw
or better processing in the execution sequence of it
could make that mistake, but i was not able to reproduce it.

// canvas_save_to_PNG_2
// try to reproduce this problem without G4P
// old: G4P_ImageButtons_save_PNG

// tests on WIN 10 but show all in PNG image, only a small hicks about -c- rectangle and line over it

Textclass textclass;
RegTextclass regtextclass;
String outfile = "data/snap.png";

void setup() {
  size(400, 200);
//  size(400, 200,P2D);
//  size(400, 200,P3D);
//  size(400, 200,FX2D);  //?? [p] not work
  textclass    = new Textclass();
  regtextclass = new RegTextclass(this);
  fill(0, 0, 200);
  textSize(20);
  println("use: key [p] to save canvas to "+outfile);
}

void draw() {
  text("-a- this is a text from draw", 30, 20); 
  rect(5, 0, 20, 20);
  textclass.draw();
  line(15, 0, 15, height);    // this vertical line should be over rect -a- and -b- 
                              // but why is it NOT over -c- rectangle, and ONLY in CANVAS, in PNG it is.
  line(0, 0, width, height);  // this diagonal line should be over text -a- and -b- 
                              // but why is it also over -c-
}

public class Textclass {
  public void draw() {
    rect(5, 30, 20, 20);
    text("-b- this is a class with text from draw", 30, 50);
  }
}

public class RegTextclass {
  PApplet pa;
  public RegTextclass(PApplet pa) {
    this.pa = pa;
    pa.registerMethod("draw", this);
  }
  public void draw() {
    rect(5, 60, 20, 20);
    pa.text("-c- this is a registered class with text", 30, 80);
  }
}

void keyPressed() {
  if ( key == 'p' ) { 
    save(outfile);
    println("save to "+outfile);
  }
}

1 Like

i did not give up,
esp as i noticed a potential mistake using
rect and pa.text in the registered class.
but learned that with or without pa.
there is no effect on CANVAS or PNG.


and tried a other way, just use a function from “main”
in all 3 draw situations. ( draw / class draw / reg class draw )

and had the same but noticed a other mistake:
i forget to use a background()
and now i have a code what reproduces that error i see with G4P
that save("data/snap.png")
can not catch the content drawn by a registered class.

the meaning why it works without background?
does it indicate a wrong “timing” / execution sequence of save()
is above my level.

but clear is now, that it is NOT a error produced by the library G4P !

( tested Win 7 / Win 10 / Raspbian )


// canvas_save_to_PNG_3
// reproduce this problem without G4P
// ( difference to last test (2) is that now use BACKGROUND !)
//

Textclass textclass;
RegTextclass regtextclass;
String outfile = "data/snap.png";

void setup() {
  size(400, 200);
  //  size(400, 200,P2D);
  //  size(400, 200,P3D);
  //  size(400, 200,FX2D);  //?? [p] not work
  textclass    = new Textclass();
  regtextclass = new RegTextclass(this);
  println("use: key [p] to save canvas to "+outfile);
}

void draw() {
  background(0, 200, 0);
  noStroke();
  draw_something("-a- this is from draw", 5, 20);
  textclass.draw();
  fill(200, 200, 0);
  rect(15, 0, 25, height);              // check draw order: shows reg class ON TOP ? runs after draw() ?
}

public class Textclass {
  public void draw() {
    draw_something("-b- this is from class", 5, 50);
  }
}

public class RegTextclass {                 // "registered draw class" aka "self drawing class"
  PApplet pa;
  public RegTextclass(PApplet pa) {
    this.pa = pa;
    pa.registerMethod("draw", this);
  }

  public void draw() {
    draw_something("-c- this is a registered class", 5, 80);
  }
}

void draw_something(String txt, int x, int y ) {
  fill(0, 0, 200);
  rect(x, y, 20, 20);
  fill(0);
  textSize(20);
  text(txt, x+30, y+15);
}

void keyPressed() {
  if ( key == 'p' ) { 
    save(outfile);
    println("save to "+outfile);
  }
}

1 Like