Vector of ofxGui & vector listener callbacks

I am trying to make an expandable ofxGui. I think I may have done this all wrong. I dont know how many cameras I will have but I want to control them all.

I used this in ofApp.h

ofxPanel gui;
vector<ofxToggle*>  drawcams;

and this in my setup in ofApp.cpp

    for (int i = 0; i < deviceList.size(); i++) {
    
    
    ofxToggle *  drawcam = new ofxToggle();
    drawcams.push_back(drawcam);
    gui.add(drawcams[i]->setup("Draw Cam " + ofToString(i+1), true));

}

This seems to be problematic as my settings are going on and off sometimes.

I would also like to use listener callbacks like this

drawcam[i].addListener(this, &ofApp::camDrawToggled);

But I guess I am on the wrong track- I would have to make a method like this for every element in my vector right?

void ofApp::camDrawToggled(){
	//Do cool stuff here when the GUI is changed
}

I think I am out of my league and I am guessing there is a good way to solve this but I searched for a while and cannot find anything.

Fred

I don’t know if that helps you but I have a pretty similar example within my addon ofxSortableList called example-togglelist-parameters. It adds random shapes and creates a toggle for each one. The forms are shown or hidden depending on the toggle status. You can just ignore the feature of sorting the elements.

It does not use listener callbacks but I still thought you might want to have a look at the code.

I just saw that ofParameter also has an addListener function. So you should be able to do something like this:

vector<ofPtr<ofParameter<bool>>> params;

for (int i = 0; i < deviceList.size(); i++) {
    ofPtr<ofParameter<bool>> drawcam(new ofParameter<bool>());
    drawcam->set("Draw Cam " + ofToString(i+1),true);
    drawcam->addListener(this, &ofApp::camDrawToggled);
    params.push_back(drawcam);
    gui.add(drawcam.get());
 }

You would still need to figure out which cam toggled in the camDrawToggled function. Or you just do this in your draw function:

for (int i = 0; i < deviceList.size(); i++) {
    if(params.at(i)->get()) {
        //draw cam i
    }
}

@frauzufall Thanks for the tips, I will integrate this method into my project. Nice addon by the way, I could have used it quite a few times in the past.

take a look at gui/parameterGroupExample and in general the examples in the gui folder they shows how to use ofParameter and ofParameterGroup properly. among other things you don’t need to use pointers to use ofParameter

Ok, I have switched to using parameter group for all my work. I am stuck on something in the same chain. I have a parameter group that I initialise as a vector as I don’t know how many of these I need later. I would really like to have callbacks attached to the parameter changes but I am not sure how to move forward.

Any clues on where to look for info on this?

Cheers

Hi,
Not sure this is a best practice, but here’s what I do to create n ofxToggle (not ofParameter, sorry :slight_smile: )

for ( int i = 0; i < n; i++ )
{
    Toggle * toggle = new Toggle();
    toggle->setup( "toggle " + ofToString(i), false );
    gui.add( toggle );
    toggle->addListener( this, &ofApp::onToggle );
}

And the listener function:

void ofApp::onToggle( const void * sender, bool & value )
{
    // Warning: The sender is not actualy the ofxToggle, but the ofParameter<bool> used by
    // the toggle to carry its value
    ofParameter<bool> * p = ( ofParameter<bool> * ) sender;
    
    // Fortunately, the ofParameter<bool> name is the same as the toggle name,
    // so we can identify which toggle was used
    string toggleName = p->getName();
    ofLogNotice() << "The toggle " << toggleName << " is " << ( value ? "selected":"unselected") ;
    
    // If you want the toggle itself
    ofxToggle & toggle = gui.getToggle( name );
    
    // Do something...
}

@lilive thanks, that works well for this case but I am still missing a link. I followed @arturo 's advice and used parameter groups, the structure is great and much more manageable for what I am doing - also for the workflow.

As my parameters are now in a separate class I cannot use this method. I run into an inheritance problem I cannot solve-

I have a class called PlotParameters

in my .h file I have

#include "ofParameterGroup.h"
#include "ofParameter.h"
#include "ofMain.h"

class PlotParameters {
public:
    PlotParameters();
    void setup(int plotterId, string plotTitles);
    ofParameterGroup parameters;
    ofParameter<int> plotXPos;
    void changedplotXPos(int param);

};

in my .cpp I have

#include "PlotParameters.h"

PlotParameters::PlotParameters() {
    // TODO Auto-generated constructor stub 
}
void PlotParameters::setup(int plotterId, string plotTitles){
    parameters.setName(plotTitles);
    parameters.add(plotXPos.set("X Position " +ofToString(plotterId), 0, 0, 3000));
    plotXPos.addListener(this, &PlotParameters::changedplotXPos);
}

void PlotParameters::changedplotXPos(int param){
    cout<<"got it"<<endl;
}

When I compile this I get an error no matching function call to “ofAddlistener”

I dont understand why.

I would also like to have the method based in my ofApp class, not the PlotParameters class, in my limited understanding I would move the

void PlotParameters::changedplotXPos(int param){
        cout<<"got it"<<endl;
    }

to ofApp.cpp

and call

    plotXPos.addListener(this, &ofApp::changedplotXPos);

and for that I would have to include ofApp in my PlotParameters class but this gives me other errors.

Sorry for my ignorance, I am out of my depth but this comes up for me over a few projects and I would like to fix it.

Cheers

void PlotParameters::changedplotXPos(int param){
    cout<<"got it"<<endl;
}

should be:

void PlotParameters::changedplotXPos(int & param){
    cout<<"got it"<<endl;
}

note the reference in the parameter

1 Like

For this point, you can do, because plotXPos is public, in ofApp:

class ofApp : public ofBaseApp{
public:
    PlotParameters p;
    void setup(){
        p.plotXPos.addListener(this, &ofApp::changedplotXPos);
    }
    void changedplotXPos(int & param){
        cout<<"got it"<<endl;
    };
};

You can also create an event dispatched by the PlotParameters, listened by ofApp:

class PlotParameters{
public:
    ofEvent<void> changed;
    void setup(){
        plotXPos.addListener(this, &PlotParameters::changedplotXPos);
    };
    void changedplotXPos(int & param){
        ofNotifyEvent( changed );
    } ;
};

class ofApp : public ofBaseApp{
public:
    PlotParameters p;
    void setup(){
        ofAddListener( p.changed, this, & ofApp::onPlotParametersChange);
    };
    void ofApp::onPlotParametersChange(){
        cout<<"got it"<<endl;
    }
};

I think this is better than the previous solution because you can now change the PlotParameters code, without touching ofApp, as long as PlotParameters has an ofEvent called “changed”.

And if you want to pass some values to the listener function, it is also possible. For example if you want to know which PlotParameters has dispatched the event:

class PlotParameters{
public:
    ofEvent< PlotParameters > changed;
    string name;
    void setup(){
        plotXPos.addListener(this, &PlotParameters::changedplotXPos);
    };
    void changedplotXPos(int & param){
        ofNotifyEvent( changed, *this );
    } ;
};

class ofApp : public ofBaseApp{
public:
    PlotParameters p1;
    PlotParameters p2;
    void setup(){
        p1.name = "PlotParameters 1";
        p2.name = "PlotParameters 2";
        ofAddListener( p1.changed, this, & ofApp::onPlotParametersChange );
        ofAddListener( p2.changed, this, & ofApp::onPlotParametersChange );
    };
    void ofApp::onPlotParametersChange(PlotParameters & p){
        cout << p.name << endl;
    }
};
1 Like

@lilive Hey thanks a lot for the time you gave to this. It is super helpful.

Cheers