In the last section of the tutorial it became obvious that you could only put one object into a window. Well, that is not terribly useful. Unless you are happy with programs that only have an exit button, in which case thanks for visiting you have all the tools you need for an exit button.
For the rest of us, hoping to some day do something useful with our Perl GUIs, we are barely getting started. (I had no idea how much this tutorial was going to cut into my drinking time…) The biggest problem with creating a GUI using the tools we have available at this point is that we can only fit one damn object into our window. If only there was an object we could put in the window that could contain other objects.
Containers
Enter Gtk2::Container. Well really, enter Gtk2::HBox, Gtk2::VBox, and Gtk2::Table. There are tons of other classes that implement the Gtk2::Container object that are also useful, but using these three you can do nearly any formatting you want. There might be easier ways to do it, but we will talk about the easy ways later. First, we have to understand the hard way.
Generally, an HBox is an object that allows you to create a line of objects horizontally across the window. A VBox is an object that allows you to create a line of objects vertically down the screen. A Table is a grid that allows you to put objects pretty much wherever you want.
So far the examples have been staying pretty simple. Well, unfortunately, that is about to change. I will try my best to keep things simple, so bare with me. In the end you will either have a window that is formatted all wrong and be cursing the day you chose to use Gtk2, or you will have a beutiful professional looking app that makes you proud. Or you will somehow end up with both and be just a little more compassionate when cursing the names of the people who design bad user interfaces for your favorite programs.
My first word of advice on window layout with Perl-Gtk2 is to get out a piece of paper and sketch out what you want your window to look like. You have to be way too precise in layout to just wing this stuff. There are really a lot of decisions to make here. Do you want your program to have a tabbed interface? Will all of your info fit in one window, or will you need to be able to scroll? Do you have to have total control over size and layout, or are you okay with the API doing autosizing of windows and widgets?
This portion of the tutorial is only going to be really helpful if you want to do a non-tabbed interface, no scrolling, and are okay with GTK2 autosizing everything. There will be more on window formatting in future tutorials including scolled windows and tabbed windows.
Gtk2::VBox
Important Member Functions:new, pack_start_defaults($object), pack_end_defaults($object), reorder_child($object, $int)
Important Signals:None really.
Descriptions:A VBox is a container that allows you to pack objects vertically down your window.
Code:
#!/usr/bin/perl
use strict;
use warnings;
use Gtk2 '-init';
my $vbox = Gtk2::VBox->new;
my $checkbox = Gtk2::CheckButton->new_with_label("Added First (juggle)");
my $checkbox2 = Gtk2::CheckButton->new_with_label("Added Second");
my $checkbox3 = Gtk2::CheckButton->new_with_label("Added Last");
$vbox->pack_start_defaults($checkbox);
$vbox->pack_start_defaults($checkbox2);
$vbox->pack_start_defaults($checkbox3);
# When checkbox is toggled on make it last, when off make it first
$checkbox->signal_connect (toggled => sub {
#If the button is toggled on, make it last
if ($checkbox->get_active) {
$vbox->reorder_child($checkbox,3);
# If the button is toggled off, make it first
} else {
$vbox->reorder_child($checkbox,0);
}
});
# Set up window
my $window = Gtk2::Window->new;
$window->set_title ('VBox example');
$window->signal_connect (delete_event => sub {Gtk2->main_quit});
# Add vbox to window
$window->add($vbox);
$window->show_all;
Gtk2->main;
Gtk2::HBox
Important Member Functions:new, pack_start($object), pack_end($object)
Important Signals:None really.
Descriptions:An Hbox is a container that allows you to pack objects horizontally across your window.
Code:
#!/usr/bin/perl
use strict;
use warnings;
use Gtk2 '-init';
my $hbox = Gtk2::HBox->new;
my $checkbox = Gtk2::CheckButton->new_with_label("Added First (juggle)");
my $checkbox2 = Gtk2::CheckButton->new_with_label("Added Second");
my $checkbox3 = Gtk2::CheckButton->new_with_label("Added Last");
$hbox->pack_start_defaults($checkbox);
$hbox->pack_start_defaults($checkbox2);
$hbox->pack_start_defaults($checkbox3);
# When checkbox is toggled on make it last, when off make it first
$checkbox->signal_connect (toggled => sub {
#If the button is toggled on, make it last
if ($checkbox->get_active) {
$hbox->reorder_child($checkbox,3);
# If the button is toggled off, make it first
} else {
$hbox->reorder_child($checkbox,0);
}
});
# Set up window
my $window = Gtk2::Window->new;
$window->set_title ('HBox example');
$window->signal_connect (delete_event => sub {Gtk2->main_quit});
# Add vbox to window
$window->add($hbox);
$window->show_all;
Gtk2->main;
Gtk2::Table
Important Member Functions:new($rows,$cols), attach_defaults($object, $left, $right, $top, $bottom), remove($object)
Important Signals:None really.
Descriptions:The Table container allows you to create an arbitrarily sized grid, and then attach objects to any part of that grid. This is a really useful container, but it can be hard to get the layout to look exactly as you want.
Code:
#!/usr/bin/perl
use strict;
use warnings;
use Gtk2 '-init';
#Create a table with 10 rows, and 4 cols
my $table = Gtk2::Table->new(9,3);
# Make every row the same height, ever col the same width
$table->set_homogeneous(1);
my $checkbox = Gtk2::CheckButton->new_with_label("Added First (juggle)");
my $checkbox2 = Gtk2::CheckButton->new_with_label("Added Second");
my $checkbox3 = Gtk2::CheckButton->new_with_label("Added Last");
# First checkbox goes top left. Remember it is ($object, $left, $right, $top, $bottom)
$table->attach_defaults($checkbox, 0, 1, 0, 1);
# Second checkbox we will have room to stretch
$table->attach_defaults($checkbox2, 0, 3, 3, 5);
# Last checkboc will skip some space to be on the bottom right
$table->attach_defaults($checkbox3, 2, 3, 8, 9);
# When checkbox is toggled on make it left justified, when off make it right
$checkbox->signal_connect (toggled => sub {
#If the button is toggled on, make it last
if ($checkbox->get_active) {
$table->remove($checkbox);
$table->attach_defaults($checkbox, 2, 3, 0, 1);
# If the button is toggled off, make it first
} else {
$table->remove($checkbox);
$table->attach_defaults($checkbox, 0, 1, 0, 1);
}
});
# Set up window
my $window = Gtk2::Window->new;
$window->set_title ('Table example');
$window->signal_connect (delete_event => sub {Gtk2->main_quit});
# Add vbox to window
$window->add($table);
$window->show_all;
Gtk2->main;
Nesting Containers like little Russian Dolls
The code above shows three common contains and how to use them to add a bunch of objects to one window. We are starting to get close to being able to create a usable UI. Although the table container is nice for organizing layout both vertically and horizontally, there are times that you are going to want to stack a bunch of Hbox on top of each other inside of one Vbox. Nesting these two containers allows the creation of very usable content in windows.
#!/usr/bin/perl
use strict;
use warnings;
use Gtk2 '-init';
# There will be one VBox that contains three HBoxex. The top most
# HBox will also have a VBox nested inside of it.
my $outer_vbox = Gtk2::VBox->new;
my $top_hbox = Gtk2::HBox->new;
my $top_hbox_inner_vbox = Gtk2::VBox->new;
my $main_hbox = Gtk2::HBox->new;
my $bottom_hbox = Gtk2::HBox->new;
# Add a couple of objects to top_hbox
$top_hbox->pack_start_defaults(Gtk2::Label->new("Top HBox First"));
$top_hbox->pack_start_defaults(Gtk2::Label->new("Top Hbox Second"));
# Add a couple of objects to the top_hbox_inner_vbox
$top_hbox_inner_vbox->pack_start_defaults(Gtk2::Label->new("Top Inner First"));
$top_hbox_inner_vbox->pack_start_defaults(Gtk2::Label->new("Top Inner Second"));
$top_hbox_inner_vbox->pack_start_defaults(Gtk2::Label->new("Top Inner Third"));
# Add the top_hbox_inner_vbox to the top_hbox
$top_hbox->pack_start_defaults($top_hbox_inner_vbox);
# Add the top_hbox to the outer_vbox
$outer_vbox->pack_start_defaults($top_hbox);
# Add a couple of objects to the main_hbox
$main_hbox->pack_start_defaults(Gtk2::CheckButton->new_with_label("Main Check 1"));
$main_hbox->pack_start_defaults(Gtk2::CheckButton->new_with_label("Main Check 2"));
$main_hbox->pack_start_defaults(Gtk2::CheckButton->new_with_label("Main Check 3"));
# Add the main_hbox to the outer_vbox
$outer_vbox->pack_start_defaults($main_hbox);
# Add something to the bottom_hbox
$bottom_hbox->pack_start_defaults(Gtk2::Label->new("Bottom Hbox First"));
$bottom_hbox->pack_start_defaults(Gtk2::Label->new("Main is this cluttered and ugly"));
# Add the bottom_hbox to the outer_vbox
$outer_vbox->pack_start_defaults($bottom_hbox);
# Set up window
my $window = Gtk2::Window->new;
$window->set_title ('Nesting example');
$window->signal_connect (delete_event => sub {Gtk2->main_quit});
# Add vbox to window
$window->add($outer_vbox);
$window->show_all;
Gtk2->main;
Man. Both the code and the resulting window is messy. Maybe there is a better way. Right now I don’t know for sure, but hopefully as this tutorial progresses we can figure out some better ways to do page layout. Next up is talking about windows, scrolled windows, and notebooks.