Using Events between class

Hello all,

I am building a test app in which i am using an SVG file to grab shapes.
I am using the core class ofApp and a custom class Shape which defines each shape as an ofPath and its control points.

What i need to do is to be able to grab those control points and move them.

I was thinking the way to achieve this is by using ofEvents to link actions between ofApp and Shape class… but practically i have some difficulties in putting this together and see how to achieve it.

  1. Do i have to sync mouse events ?

  2. or is it better to throw an event when ofApp mouse is clicked and catch that event into Shape class and do something ?

  3. should i move the grabbed point coordinate from ofApp or in the Shape class ?

  4. i had a look at the event examples but it keeps confusing me ! I don’t find them really helpful… but maybe it i because i don’t get the way i works… can someone help me understand how to use events between two calss ? ofEvent, ofListener, ofNotifyEvent… ?

here is my code so far :

ofApp.cpp

void ofApp::setup(){

    svg.load("1.svg");
    
    for(int i = 0; i < svg.getNumPath(); i++) {
        Shape s;
        s.setup(svg.getPathAt(i));
        shapes.push_back(s);
     }
}

//--------------------------------------------------------------
void ofApp::draw(){

    ofBackground(0);
    ofSetColor(200);
    
    for(int i = 0; i < shapes.size(); i++) {
        shapes.at(i).draw();
    }
}

//--------------------------------------------------------------
void ofApp::keyPressed(int key){
    if (key == OF_KEY_TAB) {
        for(int i = 0; i < shapes.size(); i++) {
            shapes.at(i).bCommands = !shapes.at(i).bCommands;
        }
    }
}

ofApp.h

ofxSVG svg;
vector<Shape> shapes;

Shape.h

class Shape {
public:
    Shape();
    void setup(ofPath p);
    void update();
    void draw();
    
    ofPath path;
    vector<ofPath::Command> controls;
    bool bCommands;    
};

Shape.cpp

void Shape::setup(ofPath p){
    path = p;
    controls = p.getCommands();
    bCommands = false;
}

void Shape::draw(){
    ofNoFill();
    ofSetColor(200);
    ofPushMatrix();
    
    path.draw();
    
    if(bCommands) {
        for (int i = 0; i < controls.size(); i++) {
            ofCircle(controls[i].to, 5);
        }
    }
    ofPopMatrix();
}

I don’t know the “best” aproach, but I did once some similar but with warped rectangles.

I had a pointer to an empty warped rentangle and in the main ofApp::MousePressed there was a loop that cheched if the mouse position was inside the rectangle, if it was the I pointed the pointer to that warped rectangle, if no I leaved it empty. Doing the contains method was easy with a rectangle, a polygon its more trouble if you want to be precise or you can always use the bounding box.

WarpedRect *  mTempRect;
mTempRect = NULL;

void ofApp::mousePressed(int x, int y, int button){
   for( auto& rect: warpedRects){
      if(rect.contains(x,y){
          mTempRect  = rect;
          break;
      }
   }
}

The on mouse drag the warped reactangle had an moused drag method, so if the pointer wasn’t null I just called it’s mouse drag method.

void ofApp::mouseDragged(int x, int y, int button){
	if( mTempRect  != NULL)
        mTempRect.mouseDragged(x,y);
}

And clear it on mouse release

void ofApp::mouseReleased(int x, int y, int button){
	mTempRect  = NULL:
}

In this way you has a selection order which is the order of the vector containing the shapes, just as layers. If the shapes overlaps, you’ll get to move only one shape at one time.

Hope it helps.

ok… so this is becoming official : i really don’t get nothing about events in oF !

Well i think above all and don’t know the logic this app should have

Thanks @xumo for the feedback though, i am going to check this out.

hello @Gallo, try to have a look at the examples folder, specifically in events/customEventExample. In that videogame the ofApp class uses a custom event in a pretty clear way

thanks @edapx

yes i had a look at every examples.

but i don’t know, i have made 2 or 3 apps using Qt and i understood the Signal / Slot system quite easily but i don’t get the logic behind events system in oF.

I think for non coders, this can be really confusing. Documentation about events is really poor. For instance, everything i found about ofEventArgs is : “ofEventArgs is the base class for ofKeyEventArgs, ofMouseEventArgs, ofAudioEventArgs and ofResizeEventArgs classes” …

I keep looking for information and examples.

thanks

The example is not useful and this is not the right way to do it, but for the sake of learning, let’s say i have an ofApp and a custom class called “target” which purpose is to draw a circle.

I would like to draw a “target” circle at a maximum distance maxDist from where i clicked.

How this would be done using events ?

ofEvent follows the observer pattern which is the same as qt’s signal slot the syntax is slightly different but the idea is pretty much the same. https://en.wikipedia.org/wiki/Observer_pattern

and ofEvent would be the subject in the observer pattern while the listeners would be the observers.

you can register, add, listeners to an ofEvent and all of them will get notified when the ofEvent is notified.

ofEventArgs isn’t really used, it was a base class that we used originally because then you coulnd’t create an empty, void, ofEvent but will get deprecated at some point.

all you need to use is an ofEvent in the object that will notify of changes and listener methods in the objects that will listen to those changes:

//object that changes
ofEvent<int> intEvent

int value = 5;
intEvent.notify(value);


// object that listens:
void MyClass::setup(){
    ofAddListener(objectThatChanges.intEvent,this,&MyClass::intChanged);
}

void MyClass::intChanged(int & value){
    cout << value << endl;
}

thanks @arturo.

so there is some core built-in events like mouseMoved, mousePressed etc… right ? i think there is a way to “connect” to that kind of core events ?

For other events, one can create custom ones ?

In my example above with ofApp and custom target class i would do something like :

in ofApp.h

ofEvent<ofPoint> mouseEvent;

in ofApp.cpp

//--------------------------------------------------------------
void ofApp::mousePressed(int x, int y, int button){
    ofNotifyEvent(mouseEvent, ofPoint(x, y));
}

in target.h

void mouseClic(ofPoint & p);

in target.cpp

void target::setup() {
    ofAddListener(the_event, this, &target::mouseClic);
}

but how to “reach” the_event in ofApp ?
i tried ofApp.mouseEvent or ofApp::ofEvents.mouseEvent and other things but it doesn’t seem to work

then also in target.cpp

void target::mouseClic(ofPoint &p) {
    ofSetColor(255);
    ofCircle(pt.x, pt.y, 30);
}

This should do the trick:

thanks, this is helpful

If an event is created in a class, let’s say “emitter” then in ofApp.cpp you can add a listener like

 ofAddListener(emitter.theEvent, this, &ofApp::theMethod);

But how do you create a Listener in the “emitter” class waiting for an event managed in ofApp ?

you need a reference to the app when you create the object:

//ofApp::setup
obj.setup(this);

// where obj::setup
void MyClass::setup(ofApp * app){
    ofAddListener(app->event,this,&MyClass::theMethod);
}

having an event in ofApp that other classes listen too is not very useful though since you can just call from ofApp to any objects it holds. so instead of adding theMethod as listener to an event contained in ofApp and then notifying that event in ofApp you just call the methods whenever you would notify the event like:

void ofApp::update(){
    obj.theMethod(value);
}

Thanks for the tip @arturo , very useful !

Hum… fair enough !
Seems like besides mouse events, there is no need (except maybe particular situations) ofApp events…

thanks a lot.

for mouse events you can subscribe to the core events:

ofAddListener(ofEvents().mousePressed, this, &MyClass::mousePressed);

where MyClass::mousePressed:

void MyClass::mousePressed(ofMouseEventArgs & mouse){
...
}

no need to create an extra event in ofApp

I hope it is okay to add my questions.

  • Is it possible to have events with no arguments? Like bangs in PD.
  • And how would event and listener function look like if I want to pass a pointer to the emitter as argument? Is this possible?

I am working on a simple state machine https://github.com/thomasgeissl/ofxStateMachine/blob/master/src/ofxState.h and I would like to have something like this

    void enter()
    {
        //void ofNotifyEvent(EventType &event, const ArgumentsType &args, SenderType *sender)
        ofNotifyEvent(_enteredEvent, this, this);
    }

In case that someone is having the same troubles:

#pragma once
#include "ofMain.h"

class ofApp :⋅
    public ofBaseApp
{
public:
    void setup()
    {
        ofAddListener(_voidEvent, this, &ofApp::onVoidEvent);
        ofAddListener(_pointerEvent, this, &ofApp::onPointerEvent);
    }
    void exit()
    {
        ofRemoveListener(_voidEvent, this, &ofApp::onVoidEvent);
        ofRemoveListener(_pointerEvent, this, &ofApp::onPointerEvent);
    }
    void mouseReleased(int x, int y, int button)
    {
        ofNotifyEvent(_pointerEvent, this);
    }
    void keyReleased(int key)
    {
        ofNotifyEvent(_voidEvent);
    }
private:
    void onVoidEvent()
    {
        ofLogNotice("ofApp")<<"void event";
    }
    void onPointerEvent(ofApp* const &app)
    {
        ofLogNotice("ofApp")<<"pointer event";
    }
private:
    ofEvent<void> _voidEvent;
    ofEvent<ofApp* const> _pointerEvent;
};