controlP5 - get value when pressing a button

PerPer
edited December 2015 in Library Questions

I want to get the value from the button when I press the button (but without using a function). Got two alternatives but none of them are good (most be a better way then using the if (mousePressed). How can I succed with this probably simple task. Guess i want to use some of these but I cant figure out how to implement them:

controlP5.Button : boolean isPressed()
controlP5.Controller : boolean isMousePressed()
controlP5.Controller : Button onPress(CallbackListener)

Anyone can guide me getting value from button without using function?

import controlP5.*;

ControlP5 cp5;

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

  cp5 = new ControlP5(this);

  cp5.addButton("Button")
    .setValue(1)
    .setPosition(100, 100)
    .setSize(200, 19)
    ;
}

void draw() {
  background(255);

  if (mousePressed) {
    // alternative 1
    println("\nalternative 1: " + cp5.getController("Button").getValue());

    // alternative 2
    println("alternative 2: " + cp5.isMouseOver(cp5.getController("Button")));
  }
}

Answers

  • Anyone?

  • Hi, the easiest would be to add a function to your sketch that uses the same name as your button, see the example below. You can also browse through the examples that come with controlP5 to learn more about how controlP5 works and can be used.

    import controlP5.*;
    
    ControlP5 cp5;
    
    void setup() {
      size(400, 600);
    
      cp5 = new ControlP5(this);
    
      cp5.addButton("Button")
        .setValue(1)
        .setPosition(100, 100)
        .setSize(200, 19)
        ;
    }
    
    void draw() {
      background(255);
    }
    
    void Button(float theValue) {
      println("got a button press",theValue);
    }
    
  • PerPer
    edited December 2015

    Thanks for answer. For various reasons the function option doesnt work for me. (Got my reasons :-) ) I need to create something in draw ()

    What about these different options:

    controlP5.Button : boolean isPressed() 
    controlP5.Controller : boolean isMousePressed() 
    controlP5.Controller : Button onPress(CallbackListener)

    Im a bit confused how to implement them though. Been looking for example but cant find any with these.

    Not possible to create something in draw that says:

    If button1 is pressed get theValue

  • PerPer
    edited December 2015

    So here is the reason and the problem with a function in my case. Im adding and removing buttons. So as soon as I have added more buttons than I have functions the new button of course doesnt work. I need to loop through the buttons somehow!

    How can I solve this? I guess it have to be without using a function:

    import controlP5.*;
    
    int count = 0;
    int nrOfButtons = 0;
    
    ControlP5 cp5;
    
    void setup() {
      size(400, 600);
    
      cp5 = new ControlP5(this);
    
      for (int i = 0; i < 2; i++) {
        cp5.addButton("Button" + i)
          .setValue(i)
          .setPosition(100, 100+i*30)
          .setSize(200, 19)
          ;
        count = i+1;
        nrOfButtons = i+1;
      }
    }
    
    void draw() {
      background(255);
    }
    
    // ADD A NEW BUTTON with +
    void keyReleased() {
      if (key == '+') {
        for (int i = count; i < count+1; i++) {
          if (nrOfButtons <= count) {
            cp5.addButton("Button" + i)
              .setValue(i)
              .setPosition(100, 100+i*30)
              .setSize(200, 19)
              ;
    
            nrOfButtons++;
            println("ADD BUTTON");
          } else if (nrOfButtons > count) {
            cp5.getController("Button" + i).setVisible(true);
            println("ACTIVATE BUTTON");
          }
        }
        count++;
      }
    
    // REMOVE A BUTTON with -     
      if (key == '-') {
        for (int i = count-1; i < count; i++) {
          cp5.getController("Button" + i).setVisible(false);
          println("DEACTIVATE BUTTON");
        }
        count--;
      }
    }
    
    void Button0(float theValue) {
      println("Button0: " + theValue);
    }
    
    void Button1(float theValue) {
      println("Button1: " + theValue);
    }
    
  • ok, this example makes more sense of what you are looking for. Adding a callback function onPress for each controller will prevent you from manually adding functions for each button that is dynamically created. Below are 2 sketches that show the transition from your sketch to a more dynamic approach. The first sketch adds a callback listener to each button for onPress events. The second example uses an ArrayList to keep track of dynamically added and removed Buttons, the structure and logic of your sketch changes a bit, I have added comments into the code, please have a look.

    sketch 1

    import controlP5.*;
    
    int count = 0;
    int nrOfButtons = 0;
    
    ControlP5 cp5;
    
    void setup() {
      size(400, 600);
    
      cp5 = new ControlP5(this);
    
      for (int i = 0; i < 2; i++) {
        createButton("Button"+i ,i ,100 ,100 + i*30 );
        count = i+1;
        nrOfButtons = i+1;
      }
    }
    
    void draw() {
      background(255);
    }
    
    /* use a function to create a new Button */
    void createButton(String theName, int theValue, int theX, int theY) {
      cp5.addButton(theName)
        .setValue(theValue)
        .setPosition(theX, theY)
        .setSize(200, 19)
        .onPress(new CallbackListener() { // a callback function that will be called onPress
          public void controlEvent(CallbackEvent theEvent) {
            String name = theEvent.getController().getName();
            float value = theEvent.getController().getValue();
            println("got a press from a " + name + ", the value is " + value);
          }
        });
      ;
    }
    
    // ADD A NEW BUTTON with +
    void keyReleased() {
      if (key == '+') {
        for (int i = count; i < count+1; i++) {
          if (nrOfButtons <= count) {
            createButton("Button"+i ,i ,100 ,100 + i*30 );
            nrOfButtons++;
            println("ADD BUTTON");
          } else if (nrOfButtons > count) {
            cp5.getController("Button" + i).setVisible(true);
            println("ACTIVATE BUTTON");
          }
        }
        count++;
      }
    
      // REMOVE A BUTTON with -     
      if (key == '-') {
        for (int i = count-1; i < count; i++) {
          cp5.getController("Button" + i).setVisible(false);
          println("DEACTIVATE BUTTON");
        }
        count--;
      }
    }
    

    sketch 2

    import controlP5.*;
    
    // use an arraylist to store Buttons
    ArrayList buttons = new ArrayList();
    ControlP5 cp5;
    
    void setup() {
      size(400, 600);
      cp5 = new ControlP5(this);
      buttons.add(createButton("Button", 0, 0));
      buttons.add(createButton("Button", 1, 1));
    }
    
    void draw() {
      background(255);
    }
    
    /* use a function to create a new Button */
    Button createButton(String thePrefix, int theIndex, int theValue) {
      return cp5.addButton(thePrefix+theValue)
        .setValue(theValue)
        .setPosition(100, 100 + theIndex * 30)
        .setSize(200, 19)
        // add a callback function that will be called onPress
        .onPress(new CallbackListener() { 
        public void controlEvent(CallbackEvent theEvent) {
          String name = theEvent.getController().getName();
          float value = theEvent.getController().getValue();
          println("got a press from a " + name + ", the value is " + value);
        }
      }
      );
    }
    
    
    
    void keyReleased() {
      if (key == '+') { // ADD A NEW BUTTON with +
        int i = buttons.size(); // use the size of the buttons list as id for the next Button
        buttons.add(createButton("Button", i, i)); // now create and add a button to the buttons List
      }
    
      if (key == '-') { // REMOVE A BUTTON with -
        // first, check if there are any elements inside buttons (to avoid a NullPointerException)
        if (!buttons.isEmpty()) { 
          int last = buttons.size()-1; // get the index of the last element inside the buttons List
          ((Button)buttons.get(last)).remove(); // remove the button instance from cp5
          buttons.remove(last); // finally, remove the Button from the buttons List
        }
      }
    }
    
  • PerPer
    edited January 2016

    Thanks! This solves my problem! Thankyou. I got som questions though, some things i dont understand. See my questions in CAPITALS in the sketch below. I put them here as well:

    1) WHEN I CREATE FUNCTION I HAVE ALWAYS USING RETURNTYPE VOID. HERE THE RETURNTYPE IS BUTTON. WHY?

    2) I DONT REALLY GET THE CALLBACKLISTENER. HOW DOES THIS ONPRESS(NEW CALLBACKLISTENER) WORK?

    3) WHY BUTTON? I UNDERSTAND YOU ARE CALLING THE BUTTONS IN CP5 BUT WHAT DOES THIS (BUTTON)BUTTONS REALLY MEAN?

    import controlP5.*;
    
    // use an arraylist to store Buttons
    ArrayList buttons = new ArrayList();
    ControlP5 cp5;
    
    void setup() {
      size(400, 600);
      cp5 = new ControlP5(this);
      buttons.add(createButton("Button", 0, 0));
      buttons.add(createButton("Button", 1, 1));
    }
    
    void draw() {
      background(255);
    }
    
    /* use a function to create a new Button */
    // WHEN I CREATE FUNCTION I HAVE ALWAYS USING RETURNTYPE VOID. HERE THE RETURNTYPE IS BUTTON. 
    // WHY?
    Button createButton(String thePrefix, int theIndex, int theValue) {
      return cp5.addButton(thePrefix+theValue)
        .setValue(theValue)
        .setPosition(100, 100 + theIndex * 30)
        .setSize(200, 19)
        // add a callback function that will be called onPress
        // I DONT REALLY GET THE CALLBACKLISTENER. HOW DOES THIS ONPRESS(NEW CALLBACKLISTENER) WORK?
        .onPress(new CallbackListener() { 
        public void controlEvent(CallbackEvent theEvent) {
          String name = theEvent.getController().getName();
          float value = theEvent.getController().getValue();
          println("got a press from a " + name + ", the value is " + value);
        }
      }
      );
    }
    
    
    
    void keyReleased() {
      if (key == '+') { // ADD A NEW BUTTON with +
        int i = buttons.size(); // use the size of the buttons list as id for the next Button
        buttons.add(createButton("Button", i, i)); // now create and add a button to the buttons List
      }
    
      if (key == '-') { // REMOVE A BUTTON with -
        // first, check if there are any elements inside buttons (to avoid a NullPointerException)
        if (!buttons.isEmpty()) { 
          int last = buttons.size()-1; // get the index of the last element inside the buttons List
          // THE SAME HERE. WHY BUTTON? I UNDERSTAND YOU ARE CALLING THE BUTTONS IN CP5 BUT WHAT DOES 
          // THIS (BUTTON)BUTTONS REALLY MEAN?
          ((Button)buttons.get(last)).remove();
          buttons.remove(last); // finally, remove the Button from the buttons List
        }
      }
    }
    
  • edited January 2016

    1) WHEN I CREATE FUNCTIONS I HAVE ALWAYS BEEN USING RETURN TYPE void.
    HERE THE RETURN TYPE IS Button. WHY?

    The returning object of function createButton() is being used as argument for List's add() method:
    buttons.add( createButton("Button", 0, 0) );

    Obviously, void is outta question there. We can't put void in a List or anything else! ;;)

    Now some very important observations about how @sojamo had declared variable buttons: 8-X
    ArrayList buttons = new ArrayList();

    The way it is right now, we are free to add() anything ( but void or primitive types ;-) ) into the ArrayList referred to by buttons. ~:>

    It's almost anathema within Java community! >:) Proper way is to specify the generic type for the List:

    import java.util.List;
    final List<Button> buttons = new ArrayList<Button>();
    

    Besides guaranteeing that only compatible Button object references goes into the List now, there's a nice boon of completely getting rid of (cast) operators when using get() methods and such: :-bd

    No more ((Button)buttons.get(last)).remove();
    Just buttons.get(last).remove(); is enough! :bz

  • edited January 2016

    2) I DON'T REALLY GET THE CallbackListener.
    HOW DOES THIS onPress( new CallbackListener() {} ) WORK?

    Dunno much about controlP5 library, but I bet onPress() method is a way to tell some Button instance which function to callback when it gets pressed.

    More precisely that function's name is controlEvent(). Whatever is inside it is executed every time a CallbackEvent of such type is triggered over that Button.

    At least before Java 8, we can't pass functions/methods/lambdas around directly.
    We gotta 1st instantiate an interface/class that includes the function requested by the higher function.

    So the high-order function onPress() requests a callback function parameter called controlEvent().
    But instead we need to instantiate its CallbackListener interface and only then @Override controlEvent().
    And finally pass the resultant instance to method onPress() as its argument:

    .onPress( new CallbackListener() { 
      @ Override public void controlEvent(CallbackEvent theEvent) {
        // blah, blah, blah...
      }
    } );
    

    Such a hassle boilerplate! But that's the reality when using Java before version 8. :-& Or not using JS! :P

  • edited January 2016

    As an alternative, the CallbackListener along w/ its overridable controlEvent() method can be instantiated apart once, stored in some variable, and reused as onPress()'s argument over & over: :ar!

    // forum.Processing.org/two/discussion/14176/
    // controlp5-get-value-when-pressing-a-button
    
    // GoToLoop (2016-Jan-01)
    
    import controlP5.*;
    
    final CallbackListener btnAction = new CallbackListener() {
      @ Override void controlEvent(CallbackEvent evt) {
        Button btn = (Button) evt.getController();
        String name = btn.getName();
        println(name, frameCount);
      }
    };
    
    ControlP5 cp5;
    Button myBtn1;
    
    void setup() {
      size(150, 100);
      smooth(4);
      frameRate(2);
    
      cp5 = new ControlP5(this);
      myBtn1 = cp5.addButton("Button1").onPress(btnAction);
      println(myBtn1);
    }
    
    void draw() {
      background((color) random(#000000));
    }
    

    P.S.: Gonna leave question #3 to @sojamo! :-h

  • edited January 2016

    Thanks @GoToLoop for this in detail feedback to @Per's questions. Regarding questions 3, casting can indeed be avoid if the List would be generic and only accept Buttons as valid elements. sketch 2 does not use a generic ArrayList, hence accepts any element of type Object and even primitive types. As a consequence when requesting an element from this List you need to specify it's type if you want to run type specific operations on that element (which we want, we want to call a Button's remove function). So what happens here in sketch 2:

    ((Button)buttons.get(last)).remove();

    first we get an element from ArrayList buttons using get(index) which returns the Object at that particular index, or position, in the List.

    buttons.get(last)

    the element we receive is of type Object, but we need a Button and since we know that we are dealing with an element of type Button (we only added Buttons previously) we need to cast the element from an Object to a Button.

    ((Button)buttons.get(last))

    and finally we can call the remove() function on the Button which will remove that particular Button from cp5.

    ((Button)buttons.get(last)).remove()

    Using a generic List (ArrayList) would make the code more explicit and would prevent casting. Maybe something for @Per to enhance the sketch 2 code?

  • edited January 2016

    ... hence accepts any element of type Object and even primitive types.

    Although it seems to accept any of those 8 Java primitive types, some wrapper object needs to be created so it can be stored in the List. That's called auto-boxing: :-B
    http://docs.Oracle.com/javase/tutorial/java/data/autoboxing.html

    Notice that add()'s signature is boolean add(E e):
    http://docs.Oracle.com/javase/8/docs/api/java/util/List.html#add-E-

    The type E represents the chosen generic datatype when a List is created.
    It can't be a primitive type. Even the most permissive Object isn't a primitive type either!

    Therefore buttons.add(true) would be auto-boxed as buttons.add(Boolean.valueOf(true)).
    buttons.add('+') as buttons.add(Character.valueOf('+')).
    buttons.add(100) as buttons.add(Integer.valueOf(100)).
    buttons.add((short) 100) as buttons.add(Short.valueOf(100))

  • PerPer
    edited January 2016

    Thanks a lot for your answers!

    Got a last question. I thought that would be easy to solve but stuck again :-)

    Now first thing I now want to remove one of the buttons by clicking on them.

    Im using Collections.swap(buttons, (int)value, last);from the java.util package to first swap place and put the button I pressed last in line in arraylist. And after that removing the button. But I probably need to get the indexnumber rather than the valuenumber for the button because in my example the index is getting OutOfBounds. How do I get the indexnr?

    And second I also want to change the position so the buttons move up when ever some is removed and are filling the gap.

    Im really thankful for your help!

    import controlP5.*;
    import java.util.Collections;
    
    ArrayList<Button> buttons = new ArrayList<Button>();
    String paths[];
    
    ControlP5 cp5;
    
    void setup() {
      size(800, 600);
      cp5 = new ControlP5(this);
    
      buttons.add(createButton("Button0", 0, 0));
      buttons.add(createButton("Button1", 1, 1));
      buttons.add(createButton("Button2", 2, 2));
      buttons.add(createButton("Button3", 3, 3));
    }
    
    void draw() {
      background(255);
    }
    
    /* use a function to create a new Button */
    Button createButton(String theLink, int theIndex, int theValue) {
      return tower  = cp5.addButton(theLink)
        .setValue(theValue)
        .setPosition(10, 100 + theIndex * 30)
        .setSize(600, 19)
        // add a callback function that will be called onPress
        .onPress(new CallbackListener() { 
        public void controlEvent(CallbackEvent theEvent) {
          String name = theEvent.getController().getName();
          float value = theEvent.getController().getValue();
          println("got a press from a " + name + ", the value is " + value);
    
          int last = buttons.size()-1; // get the index of the last element inside the buttons List
    
          // putting the value of the button last in line
          // MY PROBLEM is that I need to get the index nr rather than value nr
          Collections.swap(buttons, (int)value, last);
    
          // I know want to make the button to fill the gap
          // maybe I can use in some way:
          //theEvent.getController().setPosition(posx, posy);
    
          buttons.get((int)last).remove(); 
    
          buttons.remove((int)last);
        }
      }
      )
      ;
    }
    
    
    void keyReleased() {
      if (key == '+') { // ADD A NEW BUTTON with +
        int i = buttons.size(); // use the size of the buttons list as id for the next Button
        buttons.add(createButton("Button"+i, i, i)); // now create and add a button to the buttons List
      }
    
      if (key == '-') { // REMOVE A BUTTON with -
        // first, check if there are any elements inside buttons (to avoid a NullPointerException)
        if (!buttons.isEmpty()) { 
          int last = buttons.size()-1; // get the index of the last element inside the buttons List
          //((Button)buttons.get(last)).remove(); 
          buttons.get(last).remove(); 
          buttons.remove(last); // finally, remove the Button from the buttons List
        }
      }
    }
    
  • edited January 2016

    Now first thing I now want to remove one of the buttons by clicking on them.

    I was successful in removing the only Button I had in my own example by modifying callback instance btnAction a little in order to include Controller's remove() method: *-:)

    import controlP5.*;
    ControlP5 cp5;
    
    final CallbackListener btnAction = new CallbackListener() {
      @ Override void controlEvent(CallbackEvent evt) {
        final Button btn = (Button) evt.getController();
    
        String name = btn.getName();
        int value = (int) btn.getValue();
        println(name, value, frameCount);
    
        btn.remove();
      }
    };
    

    But since you've got a List of Button instances too, you need to remove() them from that as well! :|

    Lucky for us, the List's remove() method, besides being capable of removing any element by its index:
    http://docs.Oracle.com/javase/8/docs/api/java/util/List.html#remove-int-

    It is also overloaded to do the same by specifying the target Object itself: \m/
    http://docs.Oracle.com/javase/8/docs/api/java/util/List.html#remove-java.lang.Object-

    Although performance wise it is O(n) now rather than O(1). But who cares? :P

    // forum.Processing.org/two/discussion/14176/
    // controlp5-get-value-when-pressing-a-button
    
    // GoToLoop (2016-Jan-03)
    
    import controlP5.*;
    ControlP5 cp5;
    
    final CallbackListener btnAction = new CallbackListener() {
      @ Override void controlEvent(CallbackEvent evt) {
        final Controller<?> btn = evt.getController();
    
        String name = btn.getName();
        int value = (int) btn.getValue();
        println(name, value, frameCount);
    
        btn.remove();         // Controller's remove() method.
        buttons.remove(btn);  // List's remove() method.
    
        println("Size:", buttons.size(), ENTER);
      }
    };
    
    import java.util.List;
    final List<Button> buttons = new ArrayList<Button>();
    
    void setup() {
      size(200, 100);
      smooth(4);
      frameRate(3);
    
      cp5 = new ControlP5(this);
      buttons.add( cp5.addButton("Button0").setValue(10).onPress(btnAction) );
      buttons.add( cp5.addButton("Button1").setValue(20).onPress(btnAction) );
    
      println();
      println(buttons, ENTER);
    }
    
    void draw() {
      background( (color) random(#000000) );
    }
    
  • edited January 2016

    And second I also want to change the position so the buttons move up whenever some is removed

    So you're gonna need some function to setPosition() all Button objects every time onPress() is triggered.

    That is, besides issuing remove() for both the Button and List, you're gonna need to invoke some repositionButtons() code: *-:)

    /**
     * Click-Remove Button (v2.1)
     * GoToLoop (2016-Jan-03)
     *
     * forum.Processing.org/two/discussion/14176/
     * controlp5-get-value-when-pressing-a-button
     */
    
    import controlP5.*;
    ControlP5 cp5;
    
    final CallbackListener btnAction = new CallbackListener() {
      @ Override void controlEvent(final CallbackEvent evt) {
        final ControllerInterface<?> btn = evt.getController();
    
        final String name = btn.getName();
        final int value = (int) btn.getValue();
        println(name, value, frameCount);
    
        btn.remove();         // Controller's remove() method.
        buttons.remove(btn);  // List's remove() method.
        println("Size:", buttons.size(), ENTER);
    
        repositionButtons(GAP);
      }
    };
    
    import java.util.List;
    final List<Button> buttons = new ArrayList<Button>();
    
    static final int QTY = 28, GAP = 20;
    
    void setup() {
      size(380, 290);
      smooth(4);
      frameRate(4);
    
      cp5 = new ControlP5(this);
    
      for (int i = 0; i != QTY; ++i)  buttons.add( cp5
        .addButton("Button" + i)
        .setValue(i*10)
        .onPress(btnAction) );
    
      repositionButtons(GAP);
    
      println();
      println(buttons, ENTER);
    }
    
    void draw() {
      background( (color) random(#000000) );
      frame.setTitle( str(frameCount) );
    }
    
    void repositionButtons(final int gap) {
      final int len = buttons.size(), last = len - 1;
      if (len == 0)  return;
    
      final int w = buttons.get(last).getWidth();
      final int h = buttons.get(last).getHeight();
    
      int x = gap, y = gap;
    
      for (final ControllerInterface<Button> btn : buttons) {
        btn.setPosition(x, y);
        if ((x += w + gap) >= width - w)  y += h + (x = gap);
      }
    }
    
  • PerPer
    edited January 2016

    Thank you! I implemented your code as below

    I used another solution for reposition the buttons with this code:

              for (int i = 0; i < buttons.size(); i++) {
                buttons.get(i).setPosition(10, 100 + i * 30);
              }
    

    I also added buttons for adding buttons. I remove button by clicking on the button.

    Heres the full sketch:

    import controlP5.*;
    
    ArrayList<Button> buttons = new ArrayList<Button>();
    
    ControlP5 cp5;
    
    void setup() {
      size(800, 600);
      cp5 = new ControlP5(this);
    
      buttons.add(createButton("Button0", 0, 0));
      buttons.add(createButton("Button1", 1, 1));
      buttons.add(createButton("Button2", 2, 2));
      buttons.add(createButton("Button3", 3, 3));
    
      cp5.addButton("addButton")
        .setValue(1)
        .setPosition(650, 100)
        .setSize(130, 19)
        ;
    }
    
    void draw() {
      background(255);
    }
    
    /* use a function to create a new Button */
    Button createButton(String theLink, int theIndex, int theValue) {
      return cp5.addButton(theLink)
        .setValue(theValue)
        .setPosition(10, 100 + theIndex * 30)
        .setSize(600, 19)
        // add a callback function that will be called onPress
        .onPress(new CallbackListener() { 
        public void controlEvent(CallbackEvent theEvent) {
          Button btn = (Button) theEvent.getController();
          String name = btn.getName();
          float value = (int) btn.getValue();
          println("got a press from a " + name + ", the value is " + value);
    
          btn.remove();
          buttons.remove(btn);
          println(btn + " btn");
    
          for (int i = 0; i < buttons.size(); i++) {
            buttons.get(i).setPosition(10, 100 + i * 30);
          }
        }
      }
      )
      ;
    }
    
    void addButton() { // ADD A NEW BUTTON with +
      for (int j = 0; j < buttons.size(); j++) {
        buttons.get(j).setPosition(10, 100 + j * 30);
      }
      int i = buttons.size(); // use the size of the buttons list as id for the next Button
      buttons.add(createButton("Button"+i, i, i)); // now create and add a button to the buttons List
    }
    
Sign In or Register to comment.