addListener to ofParameterGroup from one class to ofApp (ofxGui)

Hi,
I am really struggling to be able to pass events from a ofParameterGroup in some objects of a class to the main ofApp .
I have tested many answers found in this forum but none is working…
I am missing something?

Here is the structure of my code:

Player.h ----

class player {
.....
ofParameterGroup parameterGroup;
ofParameter<void> par_pass, par_or; // will be buttons on the gui.. 
...
void setupGui(){
    parameterGroup.setName("NAME");
    parameterGroup.add(par_pass.set("PASS"));
    parameterGroup.add(par_or.set("OR"));

gamePlay.h ----

class gamePlay { ...
gamePlay(){
     player = Player("playerName");
};
Player  player;

ofApp.h ------

#include Player.h
#include gamePlay.h

...
gamePlay game;
ofxPanel gui;
ofParameterGroup parameters;
...

`

ofApp.cpp --------

ofApp.setup:


void btnChange(ofAbstractParameter &e){
        string widget_name = e.getName();
        cout << "change ! " <<  widget_name << endl;
    };

gamePlay.player.setupGui();
parameters.add(gamePlay.player.parameterGroup);
gui.setup(parameters);

// here I would like to do like this:
ofAddListener(gamePlay.player.parameterGroup.parameterChangedE(), this, &ofApp::btnChange);
// but it is not working... 

Ideally I would like to call from the Player class a function in ofApp that receives which ofParameter has been triggered. Those ofParameters are <void> (buttons in the GUI).

If in player I reference functions in ofApp of gamePlay, I get loops, error, etc.
Using ofEvent and ofNotify I get memory bad access, etc. etc.

I think the solution must be really easy, but I do not see how to do that.
Any help would be great :wink:

Hi @miguelvb , if it helps, maybe try to use the idea of encapsulation. Each class can have its own set of ofEventListeners listening to its own ofParameters, and running class functions on class variables. From what I remember (from 6 years ago), its possible to have a listener in a class run a function in ofApp. But I found it a bit messy, awkward, and inconvenient to have a listener in 1 class call a function in another.

I like to use ofEventListener, as its an object that will get destroyed when it goes out of scope. Also I think you have to .setup() an empty ofxPanel and then add the group(s) to keep the listeners separate.

There is an awesome blog post about listeners in case you haven’t found it yet. And roymacdonald wrote a great chapter for ofBook about ofEvents and listeners that I can’t find the link for just now.

OK so here’s a quick example project that should compile and run. It uses lambda functions with the ofEventListeners.

ofApp.h

#pragma once

#include "ofMain.h"
#include "ofxGui.h"

class MyClass {
public:
    inline void setName(std::string s) {name = s;}
    inline std::string getName() {return name;}
    inline ofParameterGroup& getGroup() {return group;}
    
    inline void listenerFunction(std::string s) {name = s;}
    void setup(std::string s = "hello from openFrameworks!") {
        name = s;
        group.add(button.set("MyClass button"));
        groupListener = group.parameterChangedE().newListener([this](const ofAbstractParameter&) {listenerFunction("hello from MyClass!");});
    };
    
protected:
    std::string name;
    ofParameterGroup group;
    ofParameter<void> button;
    ofEventListener groupListener;
};

class ofApp : public ofBaseApp{
public:
    void setup();
    void update();
    void draw();
            
    MyClass myClass;
    ofxPanel panel;
    ofParameterGroup group;
    ofParameter<void> button;
    ofEventListener groupListener;
    ofEventListener myClassListener;
};

ofApp.cpp

#include "ofApp.h"
//--------------------------------------------------------------
void ofApp::setup(){
    myClass.setup();
    
    group.add(button.set("ofApp button"));
    groupListener = group.parameterChangedE().newListener([this](const ofAbstractParameter&) {
        std::string s = "ofApp time: " + ofToString(ofGetElapsedTimef());
        myClass.setName(s);
    });
    
    myClassListener = myClass.getGroup().parameterChangedE().newListener([this](const ofAbstractParameter& p) {
        ofSetColor(ofFloatColor(1.0, 0.0, ofRandom(1.0)));
    });
    
    // setup an empty panel and then add the groups to keep the listeners separate
    panel.setup();
    panel.add(myClass.getGroup());
    panel.add(group);
}
//--------------------------------------------------------------
void ofApp::update(){
}
//--------------------------------------------------------------
void ofApp::draw(){
    ofDrawBitmapString(myClass.getName(), ofGetWidth() / 2, ofGetHeight() / 2);
    panel.draw();
}