Programming with Gtk2-Perl/Getting Started/Quick Note
What does this do?
editBefore running this script or reading the explanation below, read through though the program and see if you can tell what it does. After you think you figured it out, try running it. What happens when you press Alt+s or Alt+x? What do you notice when you change the size of the window?
use warnings;
use strict;
use Glib qw(TRUE FALSE);
use Gtk2 '-init';
my $window = Gtk2::Window->new;
$window->set_title('Quick Note');
$window->signal_connect('delete-event' => sub { $_[0]->destroy });
$window->signal_connect('destroy' => sub { Gtk2->main_quit });
my $hbox = Gtk2::HBox->new;
my $label = Gtk2::Label->new('Note');
my $entry = Gtk2::Entry->new();
my $save_btn = Gtk2::Button->new_with_mnemonic('_Save');
$save_btn->signal_connect('clicked' => \&save_note, $entry);
my $exit_btn = Gtk2::Button->new_with_mnemonic('E_xit');
$exit_btn->signal_connect('clicked' => sub { $window->destroy });
$hbox->pack_start($label , FALSE, FALSE, 0);
$hbox->pack_start($entry , TRUE , TRUE , 0);
$hbox->pack_start($save_btn, FALSE, FALSE, 0);
$hbox->pack_start($exit_btn, FALSE, FALSE, 0);
$window->add($hbox);
$window->show_all;
Gtk2->main;
sub save_note {
my $save = shift;
my $entry = shift;
my $text = $entry->get_text;
$entry->set_text('');
open my $OUTFILE, '>>notes.txt'
or die "could not open notes.txt for appending";
flock $OUTFILE, 2;
print $OUTFILE join '|', time, $text . "\n";
close $OUTFILE;
}
What's new
editIn this script, we introduced a few new widgets. The HBox is a container widget which we used to control the layout of the other widgets in the window. The two others are the Entry and Button widgets, which allow the user to interact with the program.
Breaking it Down
edituse Glib qw(TRUE FALSE);
Glib is a dependency of Gtk. We include this line in our program to gain access to the constants TRUE and FALSE. You may simply use the Boolean values 1 and 0, but we will use TRUE and FALSE for clarity.
$window->signal_connect('delete-event' => sub { $_[0]->destroy });
$window->signal_connect('destroy' => sub { Gtk2->main_quit });
You can see we took a slightly different approach when binding to our signals/events. Here we call the destroy method (which emits the 'destroy' signal) on the window when the 'delete-event' occurs. Connected to the 'destroy' signal, we have instructions to quit running in Gtk's main loop. This approach was used because we want the program to exit when the application window is destroyed in any manner, not just when the user presses the close button in the window title bar. Later on we connect to the 'clicked' signal of $exit_btn to destroy the window, thereby exiting the application.
Despite using the same method to connect, there is a distinction between an event and a signal (these signals are not the same as Unix system signals, nor implemented using them, but the terminology is very similar). There will be more on this later.
my $save_btn = Gtk2::Button->new_with_mnemonic('_Save');
This creates a new Button widget that has the label 'Save'. Notice we used the method 'new_with_mnemonic' here. This sets up the keyboard shortcut Alt+s to fire the 'clicked' event on the button. The 'S' in 'Save' also becomes underlined to alert the end user to the existence of they keyboard shortcut.
Because we are using the Perl bindings, we could have gotten away with saying:
my $save_btn = Gtk2::Button->new('_Save');
This is because the Perl bindings realize that we really want to be using the 'new_with_mnemonic' constructor that is supplied in the underlying C library. Other widgets that also have 'new_with_mnemonic' or 'new_with_label' constructors usually allow you to do this as well. Many people still prefer to use the 'new_with_label' or 'new_with_mnemonic' constructors regardless.
$save_btn->signal_connect('clicked' => \&save_note, $entry)
Here we use a reference to a subroutine instead of an anonymous one. The $entry will be passed in as an argument when the callback is executed.
my $exit_btn = Gtk2::Button->new_with_mnemonic('E_xit');
$exit_btn->signal_connect('clicked' => sub { $window->destroy });
It should now be clear to you what these two lines do.
$hbox->pack_start($label , FALSE, FALSE, 0);
$hbox->pack_start($entry , TRUE , TRUE , 0);
$hbox->pack_start($save_btn, FALSE, FALSE, 0);
$hbox->pack_start($exit_btn, FALSE, FALSE, 0);
These lines pack our interface widgets into the HBox widget. The syntax is:
$container->pack_start($widget, $expand, $fill, $padding)
If the $expand argument is FALSE, the container will be shrunk tight around the widget. If the $expand argument is TRUE, the container will expand around the widget to fill the space it's parent has allotted. When the $fill argument is TRUE, the extra space within the container is allocated to the child widget. When set to FALSE, the extra space is left as padding in the container widget. The $fill argument only has any effect when the $expand argument is TRUE as well.
In our example, the $entry object is packed with the $expand and $fill parameters set to TRUE. When you change the size of the window, the box around the $entry expands, allowing the $entry to fill the remaining area. The other objects, which were packed with FALSE $expand and $fill values, will remain their natural sizes.
sub save_note {
my $save = shift;
my $entry = shift;
my $text = $entry->get_text;
$entry->set_text('');
open my $OUTFILE, '>>notes.txt'
or die "could not open notes.txt for appending";
flock $OUTFILE, 2;
print $OUTFILE join '|', time, $text . "\n";
close $OUTFILE;
}
If it is not clear to you what is happening here, it is suggested you prowl around Perl Programming.