Signals
Signals are used for communication between objects. This is a
central feature of gtk-- and probably differs most from
other toolkits.
Most other C++ toolkits have messy pointers or pointers to member
functions to implement callbacks. Other problems you're likely to find
are decreased typesafety or limitations in use of callbacks
with static functions, member functions or function objects. Some even
break the C++ language and add incompatible extensions and code
generation, which decrease the ability of tools designed to parse the
C++ language to handle your code.
Gtk-- provides signal framework which solves your problems with communication
between objects. Signal framework makes your objects
reusable components which are independent of other objects it communicates
with. This means reducing coupling between objects and resulting less
dependencies and thus more reusable code.
How does the communication work?
In the callback mechanism there's 3 separate entities involved.
- sender
- receiver
- someone making connection between sender and receiver
In actual code, the sender specifies an interface which it can call when
it wants to tell other objects something. This interface is specified as
a function object and in gtk terms it is called "Signal". Calling that
interface is called "emitting a signal".
The receiver of the signal can be almost anything. In gtk-- the following
objects can receive messages:
- member function, where the object receiving the message is derived from Gtk_Signal_Base
- static, global or friend function
- function object
For each these types of receivers, there's different function for connecting
the signal to the receiver:
- connect_to_method(signal, objectptr, &method)
- connect_to_function(signal, &function)
- connect_to_signal(signal, functionobject)
Making a connection connects sender to the receiver. After that, if the sender
emits a signal, all methods, functions and function objects that have
been connected to that signal are called with the arguments given at
signal emission.
Signature of both sender interface and receiver method must match
exactly to be able to make connection between them. If there's type
mismatches in the signatures, C++ compiler will give compile time
type error.
Implementation of new signals
Signals are C++'s function objects in gtk--. Because signals are
normal C++-objects, you can use them in file scope, in function local
scope - but they're most used inside class scope. A signal definition
is of form:
Signal2<int, float> buttonPressed;
,where
2 = number of arguments
int = type of the first parameter of the signal
float = type of the 2nd parameter of the signal
This way application programmers can specify interface for a signal.
A connection from a signal to a (member) function matching signal's interface
can be made:
void my_function(int param1, float param2);
connect_to_function(buttonPressed, &my_function);
If the function is a member function, you'll need to specify the object too.
Note that this object's class needs to be derived from Gtk_Signal_Base (Gtk_Object is):
myclass *my_object_ptr;
connect_to_method(buttonPressed, my_object_ptr, &my_class::my_function);
If the signal is inside an object, you'll need to specify it too:
connect_to_method(obj.buttonPressed, my_object_ptr, &my_class::my_function);
(The last format is most useful and most used format of connect_to_*() functions)
When connection between a signal and a function has been made, calling the
signal will make the system call all the connected functions with given
parameters. Of course many connections can be made to same signal and
the system will call all of them when the signal is called.
Calling a signal looks exactly like calling normal C++ function:
buttonPressed(10, 20.0);
or in case where you have the signal inside an object, call is in format:
obj.buttonPressed(10, 20.0);
Adding extra arguments at connect_to_*() function call to the callback
For your application use, you can add extra argument at connection time, which
is added to the called function's last parameter. To the connect_to_*()-call, add
one extra parameter and add same
parameter to a function, and you can pass information from connect-point to
the destination function.
Example:
foo *fptr;
connect_to_method(o.buttonPressed,obj,&objtype::function,fptr);
buttonpressed(10,20.0); // will call obj.function(10,20.0,fptr);
Signals with return types
There's a set of Signal's and connect_to_*() functions to handle signals
with return types:
Signal1_r<gint,int> foosignal;
That signal can be connected to the methods with the following signature:
gint my_callback(int);
Reading gtk--'s interface
*.gen_h files has definitions of the form(example is from Gtk_Widget's button_pressed_event -signal):
SIGNAL_SPEC(gint button_pressed_event(GdkEventButton*));
That definition specifies two things:
Signal_proxy1<gint,Gtk_Widget,GdkEventButton*> button_pressed_event;
virtual gint button_pressed_event_impl(GdkEventButton *e);
The first one is a function object specifying a signal, just like
object of type Signal1_r<gint,GdkEventButton*> would do. Thus
you can use that in connect_to_*() function calls to a function with
signature gint func(GdkEventButton *e) and it can be called to emit a
signal.
The second one is the default implementation of the signal, and it can
be overriden in derived class.
Here's example:
class mywindow : public Gtk_Window {
public:
// the next one catches delete_event and exits program when the window's
// close button is pressed (delete_event is called at that point).
gint delete_event_impl(GdkEventAny *e) { Gtk_Main::instance()->quit(); }
};
Connecting to a function objects
Because gtk--'s signals use heavily function objects, there needs to
be way to connect a signal to a function object. Lets connect a
button's clicked()-signal to another button's clicked signal:
Gtk_Button b1,b2;
connect_to_signal(b1.clicked, b2.clicked);
Disconnecting signals
Every connect_to_*()-function returns a Connection object, which can be
stored -- but not copied -- and it can be used to disconnect the
connection by calling function disconnect().
Connection c;
c=connect_to_function(o.buttonPressed,&myfunction);
...
c.disconnect();
You cannot do anything else with the connection object and its
perfectly legal to just ignore the return value of connect_to_*()
functions - all bookeeping information used by signal system
is released properly.
Copying Connection object does make the original Connection-object to
lose its ability to disconnect the connection. (In this sense it works
like auto_ptr in the C++ draft standard.) So, Connection objects can
be stored in a class, passed in function arguments and so on, but
there exists only one object in the system at a time that can be used
to disconnect one connection.
Common errors in use of the signals
- Signature wrong in connect_to_*() method
- Return type?
- arguments have correct type? (especially with *_event -signals this seems to be problem as they return gint instead of void)
- the signal has correct types?
- Forgot to take address of the member function pointer?
- connect_to_method(button.clicked, this, &fooclass::method); // ok
- connect_to_method(button.clicked, this, fooclass::method); // wrong
Where do I find what signals are provided by gtk--'s widgets?
- gtk--gen.h (the main header file) Comes with the package -- configure does not install this anywhere with 'make install', but you can copy it somewhere and read it.
- Read gtk's documentation and convert signals to gtk-- version
How do I convert the signatures from gtk's documentation to gtk--'s
signals?
If there's a signature like this in the documentation(the gimp.org doc):
Signal: void GtkCheckMenuItem::toggled (GtkCheckMenuItem *check_menu_item)
Then you can do the following things for it:
- Connect the signal
void mycallback(); // no args, because 1st arg from doc needs
// to be removed (its in this ptr)
Gtk_CheckMenuItem m;
connect_to_function(m.toggled, &mycallback); // if mycallback is static function
connect_to_member(m.toggled, this, &someclass::mycallback); // if its member function
- Override member function
class mycheckmenuitem : public Gtk_CheckMenuItem {
public:
void toggled_impl() { ... }
};
- emit a signal
Gtk_CheckMenuItem m;
m.toggled(); // emit a signal