tgt Event Handling Example
Overview
Event Listener
tgt::EventListener is the base class for all objects that should respond to mouse or keyboard interaction of the user or to certain window manager actions, or should do anything triggerd by a timer. The user derives its own event listener classes from EventListener and overrides either the onEvent() method, or at least one of the methods matching the specific events the listener should react to.
The overriding method executes the routines that should occur on the given event and for normal afterwards calls the event’s accept() method.
Event Handler
An EventHandler object manages a set of event listeners and informs them about occuring events.
E.g.: Imagine a tgt Qt application. The user clicks somewhere on our canvas. Qt calls a event handling method of tgt::QtCanvas object the user clicked. This method generates a tgt::Event and passes it to the according EventHandler (using EventHandler::broadcast() method). The EventHandler now informs all EventListeners known to him about this event, one by one, until one of these signals to have handled the event as it calls event’s accept() method.
Note: Event listeners are stored in the event handler within a queue to which one can append or prepend listeners. Listeners can be registered at multiple handlers. A Listener can also occure multiple times within a certain handler (though this will not make much sense in most cases).
Timers
A timer object can be used to execute some methods with a certain frequency, or with a certain delay.
To start a timer, it is given a time interval and a limit l. After the length of one time interval, the timer creates a TimeEvent and passes it to an event handler. Then the timer waits for another interval until it generates the next TimeEvent. If l is not zero, l time events are generated until the timer stops. If l equals zero, the timer will keep going generating time events until stopped explizitly.
Example
MyPainter
We just paint a sphere. The size of shere can be changed using the size variable.
class MyPainter : public Painter {
public:
MyPainter(GLCanvas* canvas) : Painter(canvas) {};
virtual void paint();
virtual void init();
virtual void sizeChanged(const ivec2& size);
};
void MyPainter::paint() {
glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
getCamera()->look();
Sphere sphere(size, 64, 32);
sphere.render();
}
void MyPainter::init() {
glEnable(GL_DEPTH_TEST);
glShadeModel(GL_SMOOTH);
glEnable(GL_COLOR_MATERIAL);
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
getCamera()->setPosition(vec3(0.f,0.f,3.f));
glColor4f(1.f, 0.f, 0.f, 1.f);
}
void MyPainter::sizeChanged(const ivec2& size) {
glViewport(0,0,size.x,size.y);
getCamera()->setRatio((float) size.x/size.y);
}
MyEventListener
We derive a class MyEventListener form tgt::EventListener and override the method keyEvent() which is run at keyboard keypresses and -releases as well as the method timerEvent() which is run at timer events.
class MyEventListener : public EventListener {
void keyEvent(KeyEvent *e);
void timerEvent(TimeEvent *e);
};
keyEvent()
A tgt::KeyEvent contains information which key changed, wheather it was pressed or released, and if any modifier keys were pressed. See doxygen (TODO: link to doxygen) for details.
At first, we check wheather our key event was a key press or release. We only want to handle key presses (= key-down events).
If it is a key press, we switch according to the keycode. We do enlage or shrink the sphere’s size or the timer interval. When changing the interval, timer must be restartet to use the new interval. If we would not use the key that was pressed, we just change gl color somehow depending on the keycode.
canvas->update() ensures that the sphere gets rendered again, using the new size or color.
void MyEventListener::keyEvent(KeyEvent *e) {
// only handle key-down events
if (e->pressed()) {
switch (e->keyCode()) {
case '+':
size *= 2;
break;
case '-':
size /= 2;
break;
case KeyEvent::K_LEFT:
timerMSecs *= 2;
if (timerMSecs > 1000)
timerMSecs = 1000;
std::cout << "Timer is set to " << timerMSecs << " milliseconds.\n";
timer->stop();
timer->start(timerMSecs, 0);
break;
case KeyEvent::K_RIGHT:
timerMSecs /= 2;
if (timerMSecs == 0)
timerMSecs = 1;
std::cout << "Timer is set to " << timerMSecs << " milliseconds.\n";
timer->stop();
timer->start(timerMSecs, 0);
break;
default:
// generate a color of the keycode:
glColor4f( (float)e->keyCode()/KeyEvent::K_LAST,
1.f - (float)e->keyCode()/KeyEvent::K_LAST,
(float)((e->keyCode()+KeyEvent::K_LAST/2)%KeyEvent::K_LAST)/KeyEvent::K_LAST,
1.f);
break;
}
glCanvas->update();
e->accept();
}
}
timerEvent
A tgt::TimeEvent contains a pointer to a tgt::Timer. As the value of this pointer must be unique for each single timer, one can use it to identify which timer send the event, if multiple timers are in use.
We have only one timer in our program, we may not check if it is the right timer that send us a time event as we do in lines XXXX. We do the check though, just for demonstration purposes
In our example, we simply enlarge or shrink the spheres size so that it gets closer to 1.
canvas->update() ensures that the sphere gets rendered again, using the size.
void MyEventListener::timerEvent(TimeEvent *e) {
if (e->getTimer() == timer) {
if (fabs(size - 1.0f) > 2 * epsilon) {
if (size < 1.0f)
size += epsilon;
else
size -= epsilon;
glCanvas->update();
}
e->accept();
}
}
main-Method
int main(int argc, char** argv) {
tgtApp = ToolkitFactory::createApplication(argc, argv);
glCanvas = ToolkitFactory::createCanvas("tgt Example: Event Handling");
tgtApp->addCanvas(glCanvas);
tgtApp->init();
Camera camera;
glCanvas->setCamera(&camera);
MyPainter painter(glCanvas);
glCanvas->setPainter(&painter);
MyEventListener eventListener;
glCanvas->getEventHandler()->addListenerToFront(&eventListener);
timer = ToolkitFactory::createTimer( glCanvas->getEventHandler() );
timer->start(timerMSecs, 0);
tgtApp->run();
delete glCanvas;
delete tgtApp;
return 0;
}
We create and initialize GuiApplication, GLCanvas, Camera and Painter as usual. We create a MyEventListener object and register it at the EventHandler of our GLCanvas. All events occuring on this canvas will now be broadcasted to our event listener.
When creating a timer, we must supply an event handler the timer will give its timeEvents to. We can simply use the canvas’ event handler as it already is accessible and our event lister is already registered.
Afterwards, we start the timer and let it run an endless loop (limit = 0).
We enter the applications main loop. Whenever another timer interval is over or a event occurs on our canvas, the event handler of our canvas broadcasts it to all registered event listeners, which in our exapmle is only our instance of MyEventListener. It handles all keyEvents and timeEvents and calls the according methods.
Complete source code
samples/events.cpp
#include "tgt/quadric.h"
#include "tgt/memorymanager.h"
#include "tgt/guiapplication.h"
#include "tgt/glcanvas.h"
#include "tgt/toolkitfactory.h"
#include "tgt/painter.h"
using namespace tgt;
GuiApplication* tgtApp;
GLCanvas* glCanvas;
Timer* timer;
float size = 1.f;
float epsilon = 0.01f;
int timerMSecs = 40;
class MyPainter : public Painter {
public:
MyPainter(GLCanvas* canvas) : Painter(canvas) {};
virtual void paint();
virtual void init();
virtual void sizeChanged(const ivec2& size);
};
void MyPainter::paint() {
glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
// set Camera
getCamera()->look();
// render sphere
Sphere sphere(size, 64, 32);
sphere.render();
}
void MyPainter::init() {
glEnable(GL_DEPTH_TEST);
glShadeModel(GL_SMOOTH);
glEnable(GL_COLOR_MATERIAL);
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
getCamera()->setPosition(vec3(0.f,0.f,3.f));
glColor4f(1.f, 0.f, 0.f, 1.f);
}
void MyPainter::sizeChanged(const ivec2& size) {
glViewport(0,0,size.x,size.y);
getCamera()->setRatio((float) size.x/size.y);
}
class MyEventListener : public EventListener {
void keyEvent(KeyEvent *e);
void timerEvent(TimeEvent *e);
};
void MyEventListener::keyEvent(KeyEvent *e) {
// only handle key-down events
if (e->pressed()) {
switch (e->keyCode()) {
case '+':
size *= 2;
break;
case '-':
size /= 2;
break;
case KeyEvent::K_LEFT:
timerMSecs *= 2;
if (timerMSecs > 1000)
timerMSecs = 1000;
std::cout << "Timer is set to " << timerMSecs << " milliseconds.\n";
timer->stop();
timer->start(timerMSecs, 0);
break;
case KeyEvent::K_RIGHT:
timerMSecs /= 2;
if (timerMSecs == 0)
timerMSecs = 1;
std::cout << "Timer is set to " << timerMSecs << " milliseconds.\n";
timer->stop();
timer->start(timerMSecs, 0);
break;
default:
// generate a color of the keycode:
glColor4f( (float)e->keyCode()/KeyEvent::K_LAST,
1.f - (float)e->keyCode()/KeyEvent::K_LAST,
(float)((e->keyCode()+KeyEvent::K_LAST/2)%KeyEvent::K_LAST)/KeyEvent::K_LAST,
1.f);
break;
}
glCanvas->update();
e->accept();
}
}
void MyEventListener::timerEvent(TimeEvent *e) {
if (e->getTimer() == timer) {
if (fabs(size - 1.0f) > 2 * epsilon) {
if (size < 1.0f)
size += epsilon;
else
size -= epsilon;
glCanvas->update();
}
e->accept();
}
}
int main(int argc, char** argv) {
std::cout
<< "tgt Sample Program: events" << std::endl
<< std::endl
<< "Shows tgt's event system and how events can be caught to make things happen." << std::endl
<< std::endl
<< "Usage:" << std::endl
<< "+/-: Change sphere size" << std::endl
<< "Left/Right arrow key: shrink/enlage timer frequency" << std::endl
<< "any other key: Change sphere color based on keycode" << std::endl
<< std::endl;
tgtApp = ToolkitFactory::createApplication(argc, argv);
glCanvas = ToolkitFactory::createCanvas("tgt Example: Event Handling");
tgtApp->addCanvas(glCanvas);
tgtApp->init();
Camera camera;
glCanvas->setCamera(&camera);
MyPainter painter(glCanvas);
glCanvas->setPainter(&painter);
MyEventListener eventListener;
glCanvas->getEventHandler()->addListenerToFront(&eventListener);
timer = ToolkitFactory::createTimer( glCanvas->getEventHandler() );
timer->start(timerMSecs, 0);
tgtApp->run();
delete glCanvas;
delete tgtApp;
return 0;
} 

