We will discuss how to connect to signals, we will also explore event signals.
Any Gtk+ program emits signals. To make your program useful, you have to connect these signals to signal handlers. Signal handlers are pieces of code that gets executed as soon as the specified signal is emitted.
There are special signals, called events. These event signals originate from the graphical system and are generated by Gtk2::Gdk
Signals give an indication what is happening to the program, it is the programmer's decision to respond or not. We will receive signals when the user clicks on a button, or when the window is minimized, or when text is entered into an entry, the list goes on. Signals gets emitted all the time. Not every Gtk2::Object
emits the same signals.
In the "Hello Gtk2-Perl
" program, we were already exposed to signal handling. We used Glib::Object
's signal_connect
method and connected the clicked
and delete_event
signals to pieces of code which executes when those signals are emitted.
Events are part of the Gtk2::Gdk
subsystem. In a Gtk+ program, you will never receive Gtk2::Gdk::Event
s directly. Instead, all Gtk2::Gdk::Event
s are passed to a Gtk2::Widget
, which emits a corresponding signal. You handle Gtk2::Gdk::Event
s by connecting signal handlers to Gtk2::Widget
's event signals.
Here we assume a typical Linux environment. The X server sends a stream of events to each X client. Events are sent and received in the order of their occurrence. Gtk2::Gdk
converts each event from X it receives into a Gtk2::Gdk::Event
, then places events in a queue. Gtk+ monitors Gtk2::Gdk
's event queue and for each event received, it decides which Gtk2::Widget
(if any) should receive the Gtk2::Gdk::Event
. The Gtk2::Widget
base class defines signals for most event types (such as button_press_event
); it also defines a generic event
signal. The Gtk+ main loop calls Gtk2::Widget
's event
method to deliver an event to a widget.
It is important to remember than not all signals are |
If you want to know the "event" type of signals that may be available to you, you can look at the signals on Gtk2::Widget
's man page. All the signals which ends in "event" are Gtk2::Gdk::Event
signals.
The factor that decides which Gtk2::Gdk::Event
signals will be emitted by the Gtk2::Widget
is the event mask. Widgets all have pre-defined event masks, but you are also allowed to add some to them. You use Gtk2::Widget
's add_event
method.
You can also find available event masks on the |
Lets say you want to follow the mouse movement on a Gtk2::EventBox
, then you first have to ask it to allow these events through
$ebox->add_events (['pointer-motion-mask', 'pointer-motion-hint-mask']);as by default, the
Gtk2::EventBox
does not allow mouse movement to end up as event signals. Now you can connect to the motion_notify_event
.
$ebox->signal_connect (motion_notify_event => sub { },$userdata);to catch any mouse movement.
The default signals that gets emitted by widgets, will in 95% + cases, be enough for you to work with, and adding extra event signals to them are only for more specialized requirements. |
The callback that you connect to a signal can be a sub, or an anonymous sub (closure). Connecting it to a sub limits you to only one argument for user data. The data that you pass to the sub, is a static "snapshot" of a the value when the program executed the code to connect the event to the sub. To reflect changes in a variable, we have to use a reference.
unsigned = $instance->signal_connect ($detailed_signal, $callback, $data=undef) * $detailed_signal (string) * $callback (subroutine) * $data (scalar) arbitrary data to be passed to each invocation of callback
Closures overcome the "one argument" userdata limitation. This will allow you to do the following:
$button->signal_connect('clicked' => sub { my ($button,$userdata) = @_; &change_labels($lbl_name,$lbl_surname,$lbl_birthdate....); },$userdata);You can now pass variables within the same scope as the closure as arguments to a sub within the closure.
When the signal that was emitted, is an "event" type of signal, the callback receives the following, and in this order:
An object instance of the widget who called this sub.
A blessed reference to the Gtk2::Gdk::Event
which caused the signal.
Userdata, if it is defined.
If it is NOT an "event" type of signal, the callback receives the following, and in this order:
An object instance of the widget who called this sub.
Userdata, if it is defined.
A reference to the Gtk2::Gdk::Event
is included because we may want to get extra info from it. We may want to check which button was pressed, or which key on the keyboard. In the two sample programs we will show how to get extra info from the Gtk2::Gdk::Event
.
The return value of the signal handler for event signals is very important, and determines if the signal will propagate through, or not.
Lets say you want to offer the user the option, when he/she closes a window, to save their work before the window closes, and the program exits. You can then incorporate a return value in the delete-event
signal handler. If the return value is boolean TRUE, the event signal will be stopped from propagating, but if it is boolean FALSE, it will continue with the propagation of the signal, and the window will close.
It is good practice to exclusively return boolean FALSE from the signal handler. This will ensure that other widgets may also receive the event, if they need to. #good practice to let the event propagate, should we need it somewhere else return FALSE; |
We will look at a program that connects the button_release_event
signal generated from a Gtk2::Button
to a sub. This sub will then use Gtk2::Gdk::Event::Button
to determine which mouse button was released. This will typically be used to pop up a menu when a user "right-click" on a widget.
The program can be found here: 'Event Demo (Mouse)'
1 #! /usr/bin/perl -w 2 3 use strict; 4 use Glib qw/TRUE FALSE/; 5 use Gtk2 -init; 6 7 my $count =0; 8 9 my $window = Gtk2::Window->new ('toplevel'); 10 $window->signal_connect (delete_event => sub { Gtk2->main_quit }); 11 my $button = Gtk2::Button->new ('Action'); 12 13 $window->add ($button); 14 $window->set_position ('center-always'); 15 $window->show_all; 16 17 #beware that the argument is a static snapshot of the varialble! 18 #To get a dynamic one you have to specify a refrence! 19 $button->signal_connect('button-release-event' => sub { 20 21 my ($widget,$event) = @_; 22 #count is in the same namespace as the closure 23 &test_button_release($widget,$event,\$count); 24 }); 25 26 Gtk2->main; 27 28 sub test_button_release { 29 30 my ($widget,$event,$parameter)= @_; 31 32 my $button_nr = $event->button(); 33 34 #to get the value of a reference, we ad a '$' to the reference's variable 35 #from the camel book: "If what's inside is a reference, 36 # you can look inside that (dereferencing $foo) by 37 # prepending another funny character" -> $$parameter 38 $widget->set_label("You Clicked Mouse button NR: $button_nr"."\nCounter data:". $$parameter); 39 #we increment the value by 1 40 ${ $parameter }++; 41 print $count. "\n"; 42 #good practice to let the event propagate, should we need it somewhere else 43 return FALSE; 44 }
Remember that event signals pass the Gtk2::Gdk::Event
as a second argument to the sub or closure. See the following line:
my ($widget,$event) = @_;
Certain events can be further inspected. In the program above, we inspect which mouse button was released. For this we use the Gtk2::Gdk::Event::Button
(check its man page for more detail) 's button()
method. This will return an integer, presenting which button was released.
my $button_nr = $event->button();
The data that you pass to the sub, is a static "snapshot" of a the value when the program executed the code to connect the signal to the sub. To reflect changes in a variable, we have to use a reference.
#count is in the same namespace as the closure &test_button_release($widget,$event,\$count);Then we can "dereference" the reference to get is value.
#to get the value of a reference, we ad a '$' to the reference's variable #from the camel book: "If what's inside is a reference, # you can look inside that (dereferencing $foo) by # prepending another funny character" -> $$parameter $widget->set_label("You Clicked Mouse button NR: $button_nr"."\nCounter data:". $$parameter);
You may want to bind certain keys that will cause the program to execute some code as soon as that key or combination of keys is pressed.
In the program, we will do the following:
Connect the window's key-press-event
signal to a sub that will show us which key was pressed.
The sub will use methods from Gtk2::Gdk::Keysyms
and Gtk2::Gdk::Event::Key
to discover the name of the key that was pressed.
To discover the keyvalue (integer) that was pressed, we use
my $key_nr = Gtk2::Gdk::Event::Key
->keyval()
We then go throug the Gtk2::Gdk::Keysyms
hash's values, and get the key of the integer value.(A bit of a reverse way - we assume each value is also unique. )
foreach my $key (keys %Gtk2::Gdk::Keysyms){ my $key_compare = $Gtk2::Gdk::Keysyms{$key}; if($key_compare == $key_nr){ #print ("You typed $key \n"); last; } }
The program can be found here: 'Event Demo (Keyboard)'
1 #! /usr/bin/perl -w 2 use strict; 3 use Gtk2::Gdk::Keysyms; 4 use Glib qw/TRUE FALSE/; 5 use Gtk2 -init; 6 7 my $window = Gtk2::Window->new ('toplevel'); 8 $window->signal_connect (delete_event => sub { Gtk2->main_quit }); 9 $window->signal_connect('key-press-event' => \&show_key); 10 11 my $label = Gtk2::Label->new(); 12 $label->set_markup("<span foreground=\"blue\" size=\"x-large\">Type something on the keyboard!</span>"); 13 14 $window->add ($label); 15 $window->show_all; 16 $window->set_position ('center-always'); 17 18 Gtk2->main; 19 20 sub show_key { 21 22 my ($widget,$event,$parameter)= @_; 23 24 my $key_nr = $event->keyval(); 25 26 #run trough the available key names, and get the values of each, 27 #compare this with $event->keyval(), when you get a match exit the loop 28 foreach my $key (keys %Gtk2::Gdk::Keysyms){ 29 30 my $key_compare = $Gtk2::Gdk::Keysyms{$key}; 31 32 if($key_compare == $key_nr){ 33 #print ("You typed $key \n"); 34 $label->set_markup("<span foreground=\"blue\" size=\"x-large\">You typed a</span><span foreground=\"red\" size=\"30000\"><i><tt> $key</tt></i></span><span foreground=\"blue\" size=\"x-large\"> which has a numeric value of </span><span foreground=\"red\" size=\"30000\"><i><tt> $key_nr!</tt></i></span>"); 35 $widget->set_title("key pressed: $key -> numeric value: $key_nr"); 36 last; 37 } 38 } 39 #good practice to let the event propagate, should we need it somewhere else 40 return FALSE; 41 }
From the documentation: "As the list of keycodes is quite large and rather rarely used in application code, we've put it in a separately-loaded module to save space." To do this, you have to specify: use Gtk2::Gdk::Keysyms;at the start of your program. |