ofParameter listeners

#1

Hi All,

Is there any way to assign a listener to an ofParameter that doesn’t care about the ofParameter’s type?

For example, let’s imagine that I have multiple params:

ofParameter myParam;
ofParameter myOtherParam;

And I just want to be notified whenever either changes:

myParam.addListener(MyClass, &MyClass::theSameListener);
myOtherParam.addListener(MyClass, &MyClass::theSameListener);

Would this be possible at all?

Thanks,

c.

#2

No, I don’t think that is possible. ofParameter event listeners are bound to the parameter type.

#3

Ah, too bad. Thanks for the quick answer!

It would be a nice feature to have though. Sometimes you just have to be informed that something changed, and having to write multiple listeners when you want to respond the same way to all of them is not very convenient. That, and having to write listeners because of a type instead of an action is just semantically strange.

#4

if you are using OF from master or nighly builds you can use lambdas to add listeners which will easily allow call the same function from all the listeners like:

listener = myParam.newListener([&](ParamType&){this->theSameListener(...);})
otherListener = .newListener([&](OtherType&){this->theSameListener(...);})

listener and otherListener are of ofEventLstener type and have to be kept around until you want to remove the listener, usually you just declare them in your class and they’ll get auto deleted when the object is deleted itself

5 Likes
How to get the selected parameter in ofxGui
#5

Hey @arturo realy nice tip!

I was about to suggest to use an ofParameterGroup, add the ofParameters to it and then add a listener to the group.

in the class definition ( .h file) I added:

    ofxPanel gui;
    
    ofParameter<float> floatParam;
    ofParameter<int> intParam;
    
    ofParameterGroup group;
    
    void listenerFunction(ofAbstractParameter& e);

And In setup in the .cpp:

    gui.setup();
    gui.add(floatParam.set("float param", 0,0,1));
    gui.add(intParam.set("int param", 0,0,255));
    group.setName("group");
    
    group.add(floatParam);
    group.add(intParam);
    
    ofAddListener(group.parameterChangedE(), this, &ofApp::listenerFunction);

//--------------------------------------------------------------
void ofApp::listenerFunction(ofAbstractParameter& e){
//do something
}

Although, when using the lambdas. do I need to remove the listener somehow (like when you call ofRemoveListener(…)? Or does it get removed automatically when the ofEventListener object gets destroyed?

Thanks!

Dynamic buttons
#6

yes when ofEventListener gets destroyed it’ll remove the listener automatically, you can newListener or ofNewListener with normal functions as usual too.

#7

So @arturo is there a way to use lambda listeners with ofParameters but without having to instance an ofEventListener?
you posted this some time ago but it’s not working

And on a broader sense, which is the correct way for using lambdas as an ofEvent listener?
Thanks!

#8

no you can’t, there’s no way to have an id of a lambda function as a ptr as with member or plain function so we need the listeners. but also using ofEventListener is the recomended way now to setup listeners since it avoids the need for a destructor in most cases

#9

I see.
Thanks

#10

This is not working for me:

Grid::Grid(){

    std::cout << "class initialize" << std::endl;
    params.setName("settings");
    params.add(padding.set("padding", 5, 0, 10));
    params.add(cellSize.set("cellSize", 5, 0, 10));
    params.add(rowOffset.set("rowOffset", 5, 0, 10));
    params.add(columnOffset.set("columnOffset", 5, 0, 10));
    padding.newListener([&](float&){
        std::cout << "bla" << std::endl;
    });

}

The class it is initialized and its parameter are showed in the main ofApp.cpp class that it is using it, but the listener it is not working.

#11

you need to keep the return of calling new listener, destroying it removes the listener.

You can store it in your class as an ofEventListener or if you have more than one use ofEventListeners which can store a collection of listeners.

When your class is destroyed it auto removes all the listeners it’s subscribed to

1 Like
#12

Is the last example at this page http://blog.openframeworks.cc/post/173223240829/events ?

#13

I did like this:

#include "ofxGui.h"

class Grid: public ofBaseApp {
public:
    Grid();
    ofParameterGroup params;
    ofParameter<float> padding;
    ofParameter<float> cellSize;
    ofParameter<float> rowOffset;
    ofParameter<float> columnOffset;

    void rebuildGrid();


private:
    ofEventListener updateListener;
};

And this is the cpp file:

#include "Grid.h"

Grid::Grid(){
    params.setName("grid settings");
    params.add(padding.set("padding", 5, 0, 10));
    params.add(cellSize.set("cellSize", 5, 0, 10));
    params.add(rowOffset.set("rowOffset", 5, 0, 10));
    params.add(columnOffset.set("columnOffset", 5, 0, 10));

    updateListener = padding.newListener([&](float&){this->rebuildGrid();});
}

void Grid::rebuildGrid(){
    ofLog() << "bla";
};

I Add the parameter of the grid class to my main application using gui.add(grid.params);, the parameters show up as expected but still no messages in the console.

1 Like
#14

Now i see what you mean for “keep the return”. Changing it to

updateListener = padding.newListener([&](float&){return this->rebuildGrid();});

solved the issue. Thanks!

#15

i was refering to keeping the return of newListener in updateListener. doing a return of a void function like you are doing there has no effect

#16

With return this->rebuildGrid(); I am seeing the messages in the console. Without the return the rebuildGrid() method is not logging.

#17

can you try using:

updateListener = padding.newListener([this](float&){this->rebuildGrid();});

instead? capturing by reference for closures that last more than the scope in which they are created might be problematic if the object moves in memory so perhaps what you are seeing is just a problem with that

1 Like
#18

Yes, this is working paddingListener = padding.newListener([this](float&){this->rebuildGrid();});. Thanks.

By the way, there is no way to trig the event just when the mouse leaves the slider, right? Because at the moment I am calling “rebuildGrid” every time i move the slider but i would like to trig the event just “onMouseReleased” over the slider. I remember that this was not possible, but so many things has happened with events that maybe now there is a solution.

#19
gui.getFloatSlider("slidername").setUpdateOnReleaseOnly(true)
1 Like
#20

:star_struck: