GTK2-Perl Tutorial [ALPHA]

Emmanuele Bassi

James Curbo

& the GTK2-Perl team

Abstract

This is a tutorial on how to use GTK (the GIMP Toolkit) through its Perl interface. It is based on the original GTK Tutorial by Tony Gale and Ian Main.


Table of Contents

1. Tutorial Availability
2. Introduction
3. Using GTK+ in Perl and This Tutorial
4. Getting Started
1. Hello World in GTK2-Perl
2. Theory of Signals and Callbacks
3. Events
4. Stepping Through Hello World
5. Moving On
1. More on Signal Handlers
2. An Upgraded Hello World
6. Packing Widgets
1. Theory of Packing Boxes
2. Details of Boxes
3. Packing Demonstration Program
4. Packing Using Tables
5. Table Packing Example
7. Widget Overview
1. Widget Hierarchy
2. Widgets Without Windows
8. The Button Widget
1. Normal Buttons
2. Toggle Buttons
3. Check Buttons
4. Radio Buttons
9. Adjustments
1. Creating an Adjustment
2. Using Adjustments the Easy Way
3. Adjustment Internals
10. Range Widgets
1. Scrollbar Widgets
2. Scale Widgets
2.1. Creating a Scale Widget
2.2. Functions and Signals (well, functions, at least)
3. Common Range Functions
3.1. Setting the Update Policy
3.2. Getting and Setting Adjustments
4. Key and Mouse bindings
5. Example
11. Miscellaneous Widgets
1. Labels
2. Arrows
3. The Tooltips Object
4. Progress Bars
5. Dialogs
6. Rulers
7. Statusbars
8. Text Entries
9. Spin Buttons
10. Combo Box
11. Calendar
12. Color Selection
13. File Selections
12. Container Widgets
1. The EventBox
2. The Alignment widget
3. Fixed Container
4. Layout Container
5. Frames
6. Aspect Frames
7. Paned Window Widgets
8. Viewports
9. Scrolled Windows
10. Button Boxes
11. Toolbar
12. Notebooks
13. Menu Widget
1. Manual Menu Creation
2. Manual Menu Example
3. Using ItemFactory
3.1. ItemFactory entries
3.2. Creating an ItemFactory
3.3. Making use of the menu and its menu items
4. Item Factory Example
14. Undocumented Widgets
1. Accel Label
2. Option Menu
3. Menu Items
3.1. Check Menu Item
3.2. Radio Menu Item
3.3. Separator Menu Item
3.4. Tearoff Menu Item
4. Curves
5. Drawing Area
6. Font Selection Dialog
7. Message Dialog
8. Gamma Curve
9. Image
10. Plugs and Sockets
11. Tree View
12. Text View
15. Setting Widget Attributes
16. Timeouts, IO and Idle Functions
1. Timeouts
17. Advanced Events And Signals
18. Managing Selections
1. Overview
2. Retrieving the selection
3. Supplying the selection
19. Drag-and-drop (DND)
1. Overview
2. Properties
3. Functions
3.1. Signals on the source widget:
3.2. Signals on the destination widget:
20. GLib
21. GTK's rc Files
1. Functions For rc Files
2. GTK's rc File Format
3. Example rc file
22. Writing Your Own Widgets
1. Overview
23. Scribble, A Simple Example Drawing Program
1. Overview
24. Tips For Writing GTK Applications
25. Contributing
26. Credits of the GTK2-Perl Tutorial
27. Original GTK C Tutorial Credits
28. Tutorial Copyright and Permissions Notice
A. GTK Signals
B. GDK Event Types
C. Code Examples

Chapter 1. Tutorial Availability

A copy of this tutorial is available online for reference at http://gtk2-perl.sourceforge.net/tutorial.

The original version of this tutorial for the C interface comes with this advice:

A copy of this tutorial in SGML and HTML is distributed with each source code release of GTK+. For binary distributions, please check with your vendor.

A packaged verion of this tutorial is available from ftp://ftp.gtk.org/pub/gtk/tutorial which contains the tutorial in various different formats. This package is primary for those people wanting to have the tutorial available for offline reference and for printing.

Chapter 2. Introduction

GTK (GIMP Toolkit) is a library for creating graphical user interfaces. It is licensed using the LGPL license, so you can develop open software, free software, or even commercial non-free software using GTK without having to spend anything for licenses or royalties.

It's called the GIMP toolkit because it was originally written for developing the GNU Image Manipulation Program (GIMP), but GTK has now been used in a large number of software projects, including the GNU Network Object Model Environment (GNOME) project. GTK is built on top of GDK (GIMP Drawing Kit) which is basically a wrapper around the low-level functions for accessing the underlying windowing functions (Xlib in the case of the X windows system), and gdk-pixbuf, a library for client-side image manipulation. The Perl modules for these libraries are bound within the Gtk2 namespace, and are called Gtk2::Gdk and Gtk2::Gdk::Pixbuf.

The primary authors of GTK are:

GTK is currently maintained by:

GTK is essentially an object oriented application programmers interface (API). Although written completely in C, it is implemented using the idea of classes and callback functions (pointers to functions). The Perl module which binds this library is called Gtk2.

There is also a third component called GLib which contains a few replacements for some standard calls, as well as some additional functions for handling linked lists, etc. The replacement functions are used to increase GTK's portability, as some of the functions implemented here are not available or are nonstandard on other Unixes such as g_strerror(). Some also contain enhancements to the libc versions, such as g_malloc() that has enhanced debugging utilities.

In version 2.0, GLib has picked up the type system which forms the foundation for GTK's class hierarchy, the signal system which is used throughout GTK, a thread API which abstracts the different native thread APIs of the various platforms and a facility for loading modules. The Perl module that binds this library is called Glib.

As the last component, GTK uses the Pango library for internationalized text output. This library is bound within the Gtk2 namespace, inside the Gtk2::Pango module.

GTK2-Perl is the collective name for a set of perl bindings for Gtk+ 2.x and various related libraries. These modules make it easy to write Gtk and Gnome applications using a natural, perlish, object-oriented syntax.

The authors and maintainers of GTK2-Perl are:

Chapter 3. Using GTK+ in Perl and This Tutorial

This tutorial is for people new to using the gtk2-perl bindings, but not necessarily those new to Perl itself. We will assume you already know such things as how Perl's object oriented system works, how Perl does subroutines, hashes as argument lists, etc. and such things are described better elsewhere.

There are some things to keep in mind, especially if you are familiar with using GTK+ in C. The Gtk2::api POD page that is distributed with gtk2-perl outlines what you will need to know, but some of it will be repeated here for clarity.

The biggest thing to keep in mind is that gtk2-perl fully utilizes the ideas of packages in Perl. The namespaces in C have been mapped to package names in Perl and exploit Perl's OO syntax for many things. For example:

  • g_ => Glib

  • gtk_ => Gtk2

  • gdk_ => Gdk

  • gdk_pixbuf_ => Gtk2::Gdk::Pixbuf

  • pango_ => Gtk2::Pango

This list is nowhere near exhaustive, however common sense dictates in most areas. A good idea of the full breakdown of namespace-to-package conversion can be seen by looking at the POD documentation index on the gtk2-perl website.

This namespace mapping is also used for the various widgets and elements in GTK+. For example:

  • GtkButton => Gtk2::Button

  • GdkPixbuf => Gtk2::Gdk::Pixbuf

  • GtkScrolledWindow => Gtk2::ScrolledWindow

  • PangoFontDescription => Gtk2::Pango::FontDescription

These mappings are also pretty common sense most of the time. See the aformentioned POD index for a full list.

gtk2-perl has an automatic POD generation system for documentation as well. perldoc Gtk2::Button, for example, will give you a nice manpage-like page:

      
      Gtk2::Button(3)       User Contributed Perl Documentation      Gtk2::Button(3)

NAME
       Gtk2::Button

HIERARCHY
         Glib::Object
         +----Gtk2::Object
               +----Gtk2::Widget
                     +----Gtk2::Container
                           +----Gtk2::Bin
                                 +----Gtk2::Button

INTERFACES
         Gtk2::Atk::ImplementorIface

MNEMONICS
       Mnemonics are "memory aids"; in GTK+, a mnemonic is an underlined char-
       acter which corresponds to a keyboard accelerator.  For a button, that
       means pressing Alt and that key activates the button.

       For convenience, Gtk2-Perl uses mnemonics by default on widgets that
       support them.  If characters in label string are preceded by an under-
       score, they are underlined.  If you need a literal underscore character
       in a label, use '__' (two underscores).  If you don't want to use
       mnemonics at all, use the non-mnemonic version explicitly (e.g.
       "Gtk2::Button::new_with_label").

METHODS
       widget = Gtk2::Button->new


       widget = Gtk2::Button->new ($mnemonic)


           * $mnemonic (string) used to label the widget, see "MNEMONICS"

[...]

    

Here you can see the widget hierarchy as well as a list of methods. Each pod page also includes available signals, properties, and a list of references to other pages.

Due to the Perl object system, casting is unnecessary, thus making your programs shorter and easier to understand. In addition, the constructors for most widgets that support mnemonics will take the mnemonic as an argument, avoiding the use of the 'new_with_mnemonic' call. The function is still bound in the bindings, however, for those who wish to stay strict to the C API.

Constants such as GTK_WINDOW_TOPLEVEL are handled as strings in Perl. In the same manner as the functions and namespaces, constant names are transformed into their Perl counterparts by a simple method: strip off the namespace prefix, lowercase the constant, and replace _ with -. For example:

  • GTK_WINDOW_TOPLEVEL => 'toplevel'

  • GTK_BUTTONS_OK_CANCEL => 'ok-cancel' (or 'ok_cancel')

Note that the bindings will still accept underscores. The bindings will also accept the full C constant name as a string, for clarity.

For flags that need to be OR'd together, use an array of strings instead, such as qw/foo bar baz/. For times when you need to check if a field is set, the & operator works like you would expect it to in C due to some Perl overloading magic. [more to come]

Chapter 4. Getting Started

To begin our introduction to GTK2-Perl, we'll start with the simplest program possible. This program will create a 200x200 pixel window and has no way of exiting except to be killed by using the shell.



use Gtk2;

Gtk2->init;

$window = Gtk2::Window->new('toplevel');
$window->show;

Gtk2->main;

0;


You can run the above program using:


perl base.pl

All programs will of course use the Gtk2 module.

The next line:

Gtk2->init;

calls the init method which will be called in all GTK applications. This sets up a few things for us such as the default visual and color map. This method initializes the library for use, sets up default signal handlers, and checks the arguments passed to your application on the command line, looking for one of the following:

  • --gtk-module
  • --g-fatal-warnings
  • --gtk-debug
  • --gtk-no-debug
  • --gdk-debug
  • --gdk-no-debug
  • --display
  • --sync
  • --name
  • --class

It removes these from the argument list, leaving anything it does not recognize for your application to parse or ignore. This creates a set of standard arguments accepted by all GTK applications.

The init method could be implicitly being run by using the '-init' paramater when loading the Gtk2 module, like this:

use Gtk2 '-init';

The next two lines of code create and display a window.

$window = Gtk2::Window->new('toplevel');
$window->show;

The toplevel argument specifies that we want the window to undergo window manager decoration and placement. Rather than create a window of 0x0 size, a window without childer is set to 200x200 by default so you can still manipulate it.

The show method lets GTK know that we are done setting the attributes of this widget, and that it can display it.

The last line enters the GTK main processing loop.

  Gtk2->main;

main is another call you will see in every GTK application. When control reaches this point, GTK will sleep waiting for X events (such as a button or key presses), timeouts, or gile IO notifications to occur. In our simple example, however, events are ignored.

1. Hello World in GTK2-Perl

Now for a program with a widget (a button). It's the classic hello world a la GTK.



# Use the TRUE and FALSE constants exported by the Glib module.
use Glib qw/TRUE FALSE/;
use Gtk2 '-init';

# This is a callback function. We simply say hello to the world, and destroy
# the window object in order to close the program.
sub hello
{
	my ($widget, $window) = @_;
	print "Hello, World\n";

	$window->destroy;
}

sub delete_event
{
	# If you return FALSE in the "delete_event" signal handler,
	# GTK will emit the "destroy" signal. Returning TRUE means
	# you don't want the window to be destroyed.
	# This is useful for popping up 'are you sure you want to quit?'
	# type dialogs.
	print "delete event occurred\n";

	# Change TRUE to FALSE and the main window will be destroyed with
	# a "delete_event".
	return TRUE;
}

# create a new window
$window = Gtk2::Window->new('toplevel');

# When the window is given the "delete_event" signal (this is given
# by the window manager, usually by the "close" option, or on the
# titlebar), we ask it to call the delete_event () functio
# as defined above. No data is passed to the callback function.
$window->signal_connect(delete_event => \&delete_event);

# Here we connect the "destroy" event to a signal handler.
# This event occurs when we call Gtk2::Widget::destroy on the window,
# or if we return FALSE in the "delete_event" callback. Perl supports
# anonymous subs, so we can use one of them for one line callbacks.
$window->signal_connect(destroy => sub { Gtk2->main_quit; });

# Sets the border width of the window.
$window->set_border_width(10);

# Creates a new button with a label "Hello World".
$button = Gtk2::Button->new("Hello World");

# When the button receives the "clicked" signal, it will call the function
# hello() with the window reference passed to it.The hello() function is
# defined above.
$button->signal_connect(clicked => \&hello, $window);

# This packs the button into the window (a gtk container).
$window->add($button);

# The final step is to display this newly created widget.
$button->show;

# and the window
$window->show;

# All GTK applications must have a call to the main() method. Control ends here
# and waits for an event to occur (like a key press or a mouse event).
Gtk2->main;

0;


2. Theory of Signals and Callbacks

Before we look in detail at helloworld, we'll discuss signals and callbacks. GTK is an event driven toolkit, which means it will sleep in Gtk2::main() until an event occurs and control is passed to the appropriate function.

This passing of control is done using the idea of "signals". (Note that these signals are not the same as the Unix system signals, and are not implemented using them, although the terminology is almost identical.) When an event occurs, such as the press of a mouse button, the appropriate signal will be "emitted" by the widget that was pressed. This is how GTK does most of its useful work. There are signals that all widgets inherit, such as "destroy", and there are signals that are widget specific, such as "toggled" on a toggle button.

To make a button perform an action, we set up a signal handler to catch these signals and call the appropriate function. This is done by using a function such as:

unsigned Glib::Object->signal_connect($instance,
                                      $name,
			              $func,
				      $data=undef);

where the first argument is the widget instance which will be emitting the signal, and the second the name of the signal you wish catch. The third is the function you wish to be called when it is caught, and the (optional) fourth, the data you wish to have passed to this function.

The function specified in the third argument is called a "callback function", and should generally be of the form:

sub callback_func
{
	my ($widget, $data) = @_;
	
	#...
}

where the first argument will be a blessed reference to the widget that emitted the signal, and the second a reference to the data given as the last argument to the signal_connect() method as shown above.

Note that the above form for a signal callback function declaration is only a general guide, as some widget specific signals generate different calling parameters.

Perl supports anonymous subroutines, so it is possible to use them when defining callbacks, in this way:

$instance->signal_connect($signal_name => sub {
		# ...
	});

As a coding style rule of thumb, this form should be avoided for complex and or long callbacks, since it reduces code clarity. For short or simple callbacks, though, anonymous subs keep the code compact.

Remember that, when using anonymous subs, all variables in the scope of the block where the sub is defined are accessible within the anonymous sub itself. Thus, you could write:

$some_object->some_method();
$other_object->signal_connect($signal_name => sub {
		# ...
		$some_object->some_other_method();
	});

And avoid the need to pass $some_object to the callback as a parameter.

3. Events

In addition to the signal mechanism described above, there is a set of events that reflect the X event mechanism. Callbacks may also be attached to these events. These events are:

  • event
  • button_press_event
  • button_release_event
  • scroll_event
  • motion_notify_event
  • delete_event
  • destroy_event
  • expose_event
  • key_press_event
  • key_release_event
  • enter_notify_event
  • leave_notify_event
  • configure_event
  • focus_in_event
  • focus_out_event
  • map_event
  • unmap_event
  • property_notify_event
  • selection_clear_event
  • selection_request_event
  • selection_notify_event
  • proximity_in_event
  • proximity_out_event
  • visibility_notify_event
  • client_event
  • no_expose_event
  • window_state_event

In order to connect a callback function to one of these events you use the function g_signal_connect(), as described above, using one of the above event names as the name parameter. The callback function for events has a slightly different form than that for signals:

sub callback_func
{
	my ($widget, $event, $data) = @_;
	
	#...

	return $ret;
}

$event is a blessed reference which inherits from Gtk2::Gdk::Event and whose package name type will depend upon which of the above events has occurred. In order for us to tell which event has been issued each of the possible alternatives has a type method that reflects the event being issued. The other components of the event object will depend ipon the type of the event. Possible values for the type are:

'nothing' / 'GDK_NOTHING'
'delete' / 'GDK_DELETE'
'destroy' / 'GDK_DESTROY'
'expose' / 'GDK_EXPOSE'
'motion-notify' / 'GDK_MOTION_NOTIFY'
'button-press' / 'GDK_BUTTON_PRESS'
'2button-press' / 'GDK_2BUTTON_PRESS'
'3button-press' / 'GDK_3BUTTON_PRESS'
'button-release' / 'GDK_BUTTON_RELEASE'
'key-press' / 'GDK_KEY_PRESS'
'key-release' / 'GDK_KEY_RELEASE'
'enter-notify' / 'GDK_ENTER_NOTIFY'
'leave-notify' / 'GDK_LEAVE_NOTIFY'
'focus-change' / 'GDK_FOCUS_CHANGE'
'configure' / 'GDK_CONFIGURE'
'map' / 'GDK_MAP'
'unmap' / 'GDK_UNMAP'
'property-notify' / 'GDK_PROPERTY_NOTIFY'
'selection-clear' / 'GDK_SELECTION_CLEAR'
'selection-request' / 'GDK_SELECTION_REQUEST'
'selection-notify' / 'GDK_SELECTION_NOTIFY'
'proximity-in' / 'GDK_PROXIMITY_IN'
'proximity-out' / 'GDK_PROXIMITY_OUT'
'drag-enter' / 'GDK_DRAG_ENTER'
'drag-leave' / 'GDK_DRAG_LEAVE'
'drag-motion' / 'GDK_DRAG_MOTION'
'drag-status' / 'GDK_DRAG_STATUS'
'drop-start' / 'GDK_DROP_START'
'drop-finished' / 'GDK_DROP_FINISHED'
'client-event' / 'GDK_CLIENT_EVENT'
'visibility-notify' / 'GDK_VISIBILITY_NOTIFY'
'no-expose' / 'GDK_NO_EXPOSE'
'scroll' / 'GDK_SCROLL'
'window-state' / 'GDK_WINDOW_STATE'
'setting' / 'GDK_SETTING'

So, to connect a callback function to one of these events we would use something like:

$button->signal_connect(button_press_event => \&button_press_callback);

This assumes that button is a Gtk2::Button widget. Now, when the mouse is over the button and a mouse button is pressed, the sub button_press_callback() will be called.

The value $ret returned from this function indicates whether the event should be propagated further by the GTK event handling mechanism. Returning TRUE indicates that the event has been handled, and that it should not propagate further. Returning FALSE continues the normal event handling. See the section on Advanced Event and Signal Handling for more details on this propagation process.

For details on the GdkEvent data types, see the appendix entitled GDK Event Types.

The GDK selection and drag-and-drop APIs also emit a number of events which are reflected in GTK by the signals. See Signals on the source widget and Signals on the destination widget for details on the signatures of the callback functions for these signals:

  • selection_received
  • selection_get
  • drag_begin_event
  • drag_end_event
  • drag_data_delete
  • drag_motion
  • drag_drop
  • drag_data_get
  • drag_data_received

4. Stepping Through Hello World

Now that we know the theory behind this, let's clarify by walking through the example helloworld program.

First of all, if we want to use the symbolic names of TRUE and/or FALSE inside our Perl programs, instead of the generic integers 1 or 0, we should include the Glib module:

use Glib qw/TRUE FALSE/;

The next use pragma should be used in every Perl program which accesses the GTK2-Perl bindings. We require the Gtk2 module, and we tell this module to automatically call the init() method upon inclusion, using the -init parameter.

use Gtk2 '-init';

Here is the callback function that will be called when the button is "clicked". We use the second argument of the callback to pass the window object inside the hello() function, where we call the destroy() method upon it, in order to close our program.

sub hello {
	my ($widget, $window) = @_;
	
	print "Hello, World\n";

	$window->destroy;
}

The next callback is a bit special. The "delete_event" occurs when the window manager sends this event to the application. We have a choice here as to what to do about these events. We can ignore them, make some sort of response, or simply quit the application.

The value you return in this callback lets GTK know what action to take. By returning TRUE, we let it know that we don't want to have the "destroy" signal emitted, keeping our application running. By returning FALSE, we ask that "destroy" be emitted, which in turn will call our "destroy" signal handler.

sub delete_event
{
    print "delete event occurred\n";

    return TRUE; 
}

Create a new window. This is fairly straightforward. It sets up a new window, but it is not displayed until we call the show() method near the end of our program.

Notice the toplevel argument. In GTK2-Perl, the standard C enumerators are automatically translated into string literals and vice-versa. We could have used the constant GTK_WINDOW_TOPLEVEL, instead.

$window = Gtk2::Window->new('toplevel');

Here are two examples of connecting a signal handler to an object, in this case, the window. Here, the "delete_event" and "destroy" signals are caught. The first is emitted when we use the window manager to kill the window, or when we use the Gtk2::Widget::destroy() method passing in the window widget as the object to destroy. The second is emitted when, in the "delete_event" handler, we return FALSE.

In the second call of the signal_connect() method, we use one of the facilities offered by Perl: anonymous subs. You may have as many callback functions as you need, and all will be executed in the order you connected them.

$window->signal_connect(delete_event => \&delete_event);
$window->signal_connect(destroy => sub { Gtk2->main_quit; });

This next function is used to set an attribute of a container object. This just sets the window so it has a blank area along the inside of it 10 pixels wide where no widgets will go. There are other similar functions which we will look at in the section on Setting Widget Attributes

$window is an object which inherits from the Gtk2::Container class, so we can call any method of this class upon instances of the Gtk2::Window class.

$window->set_border_width(10);

This call creates a new button. It will have the label "Hello World" on it when displayed.

$button = Gtk2::Button->new("Hello World");

Here, we take this button, and make it do something useful. We attach a signal handler to it so when it emits the "clicked" signal, our hello() function is called. We pass the the callback the window reference. Obviously, the "clicked" signal is emitted when we click the button with our mouse pointer.

$button->signal_connect(clicked => \&hello, $window);

We want the button to close our program. This will illustrate how the "destroy" signal may come from either the window manager, or our program. When the button is "clicked", same as above, it calls the first hello() callback function, and that will call the destroy() method upon the window object.

This is a packing call, which will be explained in depth later on in Packing Widgets. But it is fairly easy to understand. It simply tells GTK that the button is to be placed in the window where it will be displayed. Note that a Gtk2::Container can only contain one widget. There are other widgets, that are described later, which are designed to layout multiple widgets in various ways.

$window->add($button);

Now we have everything set up the way we want it to be. With all the signal handlers in place, and the button placed in the window where it should be, we ask GTK to "show" the widgets on the screen. The window widget is shown last so the whole window will pop up at once rather than seeing the window pop up, and then the button form inside of it. Although with such a simple example, you'd never notice.

$button->show;

$window->show;

And of course, we call main() which waits for events to come from the X server and will call on the widgets to emit signals when these events come.

Gtk2->main;

And the final return. Control returns here after gtk_quit() is called.

0;

Now, when we click the mouse button on a GTK button, the widget emits a "clicked" signal. In order for us to use this information, our program sets up a signal handler to catch that signal, which dispatches the function of our choice. In our example, when the button we created is "clicked", the hello() function is called with a NULL argument, and then the next handler for this signal is called. This calls the gtk_widget_destroy() function, passing it the window widget as its argument, destroying the window widget. This causes the window to emit the "destroy" signal, which is caught, and calls our destroy() callback function, which simply exits GTK.

Another course of events is to use the window manager to kill the window, which will cause the "delete_event" to be emitted. This will call our "delete_event" handler. If we return TRUE here, the window will be left as is and nothing will happen. Returning FALSE will cause GTK to emit the "destroy" signal which of course calls the "destroy" callback, exiting GTK.

Chapter 5. Moving On

1. More on Signal Handlers

Let's take another look at the Glib::Object::signal_connect declaration:

unsigned Glib::Object->signal_connect($instance,
                                      $name,
			              $func,
				      $data=undef);

Notice the unsigned integer return value? This is a tag that identifies your callback function. As stated above, you may have as many callbacks per signal and per object as you need, and each will be executed in turn, in the order they were attached.

This tag allows you to remove this callback from the list by using:

Glib::Object->signal_handler_disconnect($instance, $id);

So, by passing in the widget you wish to remove the handler from, and the tag returned by one of the signal_connect functions, you can disconnect a signal handler.

You can also temporarily disable signal handlers with the Glib::Object::signal_handler_block() and Glib::Object::signal_handler_unblock() family of methods.

Glib::Object->signal_handler_block($instance, $id);

Glib::Object->signal_handlers_block_by_func($instance, $func, $data);

Glib::Object->signal_handler_unblock($instance, $id);

Glib::Object->signal_handlers_unblock_by_func($instance, $func, $data);

2. An Upgraded Hello World

Let's take a look at a slightly improved helloworld with better examples of callbacks. This will also introduce us to our next topic, packing widgets.



use Glib qw/TRUE FALSE/;
use Gtk2 '-init';

# Our new improved callback.  The data passed to this function
# is printed to stdout.
sub callback
{
	my ($button, $data) = @_;
	
	print "Hello again - $data was pressed\n";
}

# another callback
sub delete_event
{
	Gtk2->main_quit;
	return FALSE;
}

# Create a new window
$window = Gtk2::Window->new('toplevel');

# This is a new call, which just set the title for our
# new window to "Hello Buttons!"
$window->set_title("Hello Buttons!");

# Here we just set a handler for the delete_event that immediately
# exits GTK.
$window->signal_connect(delete_event => \&delete_event);

# Sets the border width of the window.
$window->set_border_width(10);

# We create a box to pack widgets into.  This is described in detail
# in the "packing" section. The box is not really visible, it
# is just used as a tool to arrange widgets.
$box1 = Gtk2::HBox->new(FALSE, 0);

# Put the box into the main window.
$window->add($box1);

# Creates a new button with the label "Button 1".
$button = Gtk2::Button->new("Button 1");

# Now when the button is clicked, we call the "callback" function
# with the string "button 1" as its argument.
$button->signal_connect(clicked => \&callback, 'button 1');

# Instead of Gtk2::Container::add, we pack this button into the invisible
# box, which has been packed into the window.
$box1->pack_start($button, TRUE, TRUE, 0);

# Always remember this step, this tells GTK that our preparation for this
# button is complete, and it can now be displayed.
$button->show;

# Do the same steps again to create a second button.
$button = Gtk2::Button->new("Button 2");

# Call the same callback function with a different argument, passing the string
# "button 2" instead.
$button->signal_connect(clicked => \&callback, 'button 2');

$box1->pack_start($button, TRUE, TRUE, 0);

# The order in which we show the buttons is not really important, but I
# recommend showing the window last, so it all pops up at once.

$button->show;

$box1->show;

$window->show;

# Rest in main and wait for the fun to begin!
Gtk2->main;

0;


You'll notice this time there is no easy way to exit the program, you have to use your window manager or command line to kill it. A good exercise for the reader would be to insert a third "Quit" button that will exit the program. You may also wish to play with the options to Gtk2::Box::pack_start() while reading the next section. Try resizing the window, and observe the behavior.

Chapter 6. Packing Widgets

When creating an application, you'll want to put more than one widget inside a window. Our first helloworld example only used one widget so we could simply use a gtk_container_add() call to "pack" the widget into the window. But when you want to put more than one widget into a window, how do you control where that widget is positioned? This is where packing comes in.

1. Theory of Packing Boxes

Most packing is done by creating boxes. These are invisible widget containers that we can pack our widgets into which come in two forms, a horizontal box, and a vertical box. When packing widgets into a horizontal box, the objects are inserted horizontally from left to right or right to left depending on the call used. In a vertical box, widgets are packed from top to bottom or vice versa. You may use any combination of boxes inside or beside other boxes to create the desired effect.

To create a new horizontal box, we use a call to Gtk2::HBox::new(), and for vertical boxes, Gtk2::VBox::new(). The Gtk2::Box::pack_start() and Gtk2::Box::pack_end() functions are used to place objects inside of these containers. The Gtk2::Box::pack_start() function will start at the top and work its way down in a vbox, and pack left to right in an hbox. Gtk2::Box::pack_end() will do the opposite, packing from bottom to top in a vbox, and right to left in an hbox. Using these functions allows us to right justify or left justify our widgets and may be mixed in any way to achieve the desired effect. We will use Gtk2::Box::pack_start() in most of our examples. An object may be another container or a widget. In fact, many widgets are actually containers themselves, including the button, but we usually only use a label inside a button.

By using these calls, GTK knows where you want to place your widgets so it can do automatic resizing and other nifty things. There are also a number of options as to how your widgets should be packed. As you can imagine, this method gives us a quite a bit of flexibility when placing and creating widgets.

2. Details of Boxes

Because of this flexibility, packing boxes in GTK can be confusing at first. There are a lot of options, and it's not immediately obvious how they all fit together. In the end, however, there are basically five different styles.

Each line contains one horizontal box (hbox) with several buttons. The call to Gtk2::Box::pack is shorthand for the call to pack each of the buttons into the hbox. Each of the buttons is packed into the hbox the same way (i.e., same arguments to the Gtk2::Box::pack_start() function).

This is the declaration of the gtk_box_pack_start() function:

Gtk2::Box->pack_start ($instance, $child, $expand, $fill, $padding);

The first argument is the box you are packing the object into, the second is the object. The objects will all be buttons for now, so we'll be packing buttons into boxes.

The $expand argumento to Gtk2::Box::pack_start() and Gtk2::Box::pack_end() controls whether the widgets are laid out in the box to fill in all the extra space in the box so the box is expanded to fill the aread allotted to it (TRUE); or the box is shrunk to just fit the widgets (FALSE). Setting $expand to FALSE will allow you to do right and left justification of your widgets. Otherwise, they will all expand to fit into the box, and the same effect could be achieved by using only one of Gtk2::Box::pack_start() or Gtk2::Box::pack_end().

The $fill argument to the Gtk2::Box::pack functions control whether the extra space is allocated to the objects themselves (TRUE), or as extra padding in the box around these objects (FALSE). It only has an effect if the $expand argument is also TRUE.

When creating a new box, the function looks like this:

$box = Gtk2->HBox->new($homogenous, $spacing);

The $homogeneous argument to Gtk2::HBox::new() (and the same for Gtk2::VBox::new()) controls whether each object in the box has the same size (i.e., the same width in an hbox, or the same height in a vbox). If it is set, the Gtk2::Box::pack() routines function essentially as if the $expand argument was always turned on.

What's the difference between spacing (set when the box is created) and padding (set when elements are packed)? Spacing is added between objects, and padding is added on either side of an object. The following figure should make it clearer:

Here is the code used to create the above images. I've commented it fairly heavily so I hope you won't have any problems following it. Run it yourself and play with it.

3. Packing Demonstration Program



use Glib qw/TRUE FALSE/;
use Gtk2 '-init';

sub delete_event
{
	Gtk2->main_quit;
	return FALSE;
}

# Make a new hbox filled with button-labels. Arguments for the 
# variables we're interested are passed in to this function. 
# We do not show the box, but do show everything inside.
sub make_box
{
	my ($homogeneous, $spacing, $expand, $fill, $padding) = @_;
	
	# Create a new hbox with the appropriate homogeneous
	# and spacing settings
	$box = Gtk2::HBox->new($homogenous, $spacing);

	# Create a series of buttons with the appropriate settings
	$button = Gtk2::Button->new("Gtk2::HBox::pack");
	$box->pack_start($button, $expand, $fill, $padding);
	$button->show;

	$button = Gtk2::Button->new("(box,");
	$box->pack_start($button, $expand, $fill, $padding);
	$button->show;

	$button = Gtk2::Button->new("button,");
	$box->pack_start($button, $expand, $fill, $padding);
	$button->show;
	
	# Create a button with the label depending on the value of $expand.
	if ($expand == TRUE)
	{
		$button = Gtk2::Button->new("TRUE,");
	}
	else
	{
		$button = Gtk2::Button->new("FALSE,");
	}

	$box->pack_start($button, $expand, $fill, $padding);
	$button->show;
	
	# This is the same as the button creation for "expand"
	# above, but uses the shorthand form.
	$button = Gtk2::Button->new($fill ? "TRUE," : "FALSE,");
	$box->pack_start($button, $expand, $fill, $padding);
	$button->show;

	$button = Gtk2::Button->new("$padding");
	$box->pack_start($button, $expand, $fill, $padding);
	$button->show;

	return $box;
}

die "usage: packbox.pl num, where num is 1, 2, or 3.\n"
	unless defined $ARGV[0];

# Create our window
$window = Gtk2::Window->new('toplevel');

# You should always remember to connect the delete_event signal
# to the main window. This is very important for proper intuitive
# behavior
$window->signal_connect(delete_event => \&delete_event);
$window->set_border_width(10);

# We create a vertical box (vbox) to pack the horizontal boxes into.
# This allows us to stack the horizontal boxes filled with buttons one
# on top of the other in this vbox.
$box1 = Gtk2::VBox->new(FALSE, 0);
$which = $ARGV[0];

# which example to show. These correspond to the pictures above.
if (1 == $which)
{
	# create a new label.
	$label = Gtk2::Label->new("Gtk2::HBox->new (FALSE, 0);");

	# Align the label to the left side.  We'll discuss this function and
	# others in the section on Widget Attributes.
	$label->set_alignment(0.0, 0.0);

	# Pack the label into the vertical box (vbox box1).  Remember that 
	# widgets added to a vbox will be packed one on top of the other in
	# order.
	$box1->pack_start($label, FALSE, FALSE, 0);

	# Show the label
	$label->show;

	# Call our make box function - homogeneous = FALSE, spacing = 0,
	# expand = FALSE, fill = FALSE, padding = 0
	$box2 = make_box(FALSE, 0, FALSE, FALSE, 0);
	$box1->pack_start($box2, FALSE, FALSE, 0);
	$box2->show;

	# Call our make box function - homogeneous = FALSE, spacing = 0,
	# expand = TRUE, fill = FALSE, padding = 0
	$box2 = make_box(FALSE, 0, TRUE, FALSE, 0);
	$box1->pack_start($box2, FALSE, FALSE, 0);
	$box2->show;

	# Args are: homogeneous, spacing, expand, fill, padding
	$box2 = make_box(FALSE, 0, TRUE, TRUE, 0);
	$box1->pack_start($box2, FALSE, FALSE, 0);
	$box2->show;

	# Creates a separator, we'll learn more about these later,
	# but they are quite simple.
	$separator = Gtk2::HSeparator->new;

	# Pack the separator into the vbox. Remember each of these
	# widgets is being packed into a vbox, so they'll be stacked
	# vertically.
	$box1->pack_start($separator, FALSE, TRUE, 5);
	$separator->show;

	# Create another new label, and show it.
	$label = Gtk2::Label->new("Gtk2::HBox->new (TRUE, 0);");
	$label->set_alignment(0.0, 0.0);
	$box1->pack_start($label, FALSE, FALSE, 0);
	$label->show;
	
	# Args are: homogeneous, spacing, expand, fill, padding
	$box2 = make_box(TRUE, 0, TRUE, FALSE, 0);
	$box1->pack_start($box2, FALSE, FALSE, 0);
	$box2->show;
	
	# Args are: homogeneous, spacing, expand, fill, padding
	$box2 = make_box(TRUE, 0, TRUE, TRUE, 0);
	$box1->pack_start($box2, FALSE, FALSE, 0);
	$box2->show;

	# Another new separator.
	$separator = Gtk2::HSeparator->new;
	# The last 3 arguments to gtk_box_pack_start are:
	# expand, fill, padding.
	$box1->pack_start($separator, FALSE, TRUE, 5);
	$separator->show;
}
elsif (2 == $which)
{
	# Create a new label, remember box1 is a vbox as created
	# near the beginning
	$label = Gtk2::Label->new("Gtk2::HBox->new (FALSE, 10);");
	$label->set_alignment(0.0, 0.0);
	$box1->pack_start($label, FALSE, FALSE, 0);
	$label->show;

	# Args are: homogeneous, spacing, expand, fill, padding
	$box2 = make_box(FALSE, 10, TRUE, FALSE, 0);
	$box1->pack_start($box2, FALSE, FALSE, 0);
	$box2->show;

	# Args are: homogeneous, spacing, expand, fill, padding
	$box2 = make_box(FALSE, 10, TRUE, TRUE, 0);
	$box1->pack_start($box2, FALSE, FALSE, 0);
	$box2->show;

	$separator = Gtk2::HSeparator->new;
	# The last 3 arguments to gtk_box_pack_start are:
	# expand, fill, padding.
	$box1->pack_start($separator, FALSE, TRUE, 5);
	$separator->show;

	$label = Gtk2::Label->new("Gtk2::HBox->new (FALSE, 0);");
	$label->set_alignment(0.0, 0.0);
	$box1->pack_start($label, FALSE, FALSE, 0);
	$label->show;

	# Args are: homogeneous, spacing, expand, fill, padding
	$box2 = make_box(FALSE, 0, TRUE, FALSE, 10);
	$box1->pack_start($box2, FALSE, FALSE, 0);
	$box2->show;

	# Args are: homogeneous, spacing, expand, fill, padding
	$box2 = make_box(FALSE, 0, TRUE, TRUE, 10);
	$box1->pack_start($box2, FALSE, FALSE, 0);
	$box2->show;

	$separator = Gtk2::HSeparator->new;
	# The last 3 arguments to gtk_box_pack_start are:
	# expand, fill, padding.
	$box1->pack_start($separator, FALSE, TRUE, 5);
	$separator->show;
}
elsif (3 == $which)
{
	# This demonstrates the ability to use gtk_box_pack_end() to
	# right justify widgets. First, we create a new box as before.
	$box2 = make_box(FALSE, 0, FALSE, FALSE, 0);

	# Create the label that will be put at the end.
	$label = Gtk2::Label->new("end");
	# Pack it using Gtk2::Box::pack_end(), so it is put on the right
	# side of the hbox created in the make_box() call.
	$box2->pack_end($label, FALSE, FALSE, 0);
	# Show the label.
	$label->show;

	# Pack box2 into box1 (the vbox remember ? :)
	$box1->pack_start($box2, FALSE, FALSE, 0);
	$box2->show;

	# A separator for the bottom.
	$separator = Gtk2::HSeparator->new;
	# This explicitly sets the separator to 400 pixels wide by 5 pixels
	# high. This is so the hbox we created will also be 400 pixels wide,
	# and the "end" label will be separated from the other labels in the
	# hbox. Otherwise, all the widgets in the hbox would be packed as
	# close together as possible.
	$separator->set_size_request(400, 5);
	# pack the separator into the vbox (box1) created near the start
	$box1->pack_start($separator, FALSE, TRUE, 5);
	$separator->show;
}

# Create another new hbox.. remember we can use as many as we need!
$quitbox = Gtk2::HBox->new(FALSE, 0);

# Our quit button.
$button = Gtk2::Button->new("Quit");

# Setup the signal to terminate the program when the button is clicked
$button->signal_connect(clicked => sub { Gtk2->main_quit; });

# Pack the button into the quitbox.
# The last 3 arguments to gtk_box_pack_start are:
# expand, fill, padding.
$quitbox->pack_start($button, TRUE, FALSE, 0);
# pack the quitbox into the vbox (box1)
$box1->pack_start($quitbox, FALSE, FALSE, 0);

# Pack the vbox (box1) which now contains all our widgets, into the
# main window.
$window->add($box1);

# And show everything left
$button->show;
$quitbox->show;

$box1->show;

# Showing the window last so everything pops up at once.
$window->show;

# And of course, our main function.
Gtk2->main;

# Control returns here when gtk_main_quit() is called, but not when
# exit() is used.
0;


4. Packing Using Tables

Let's take a look at another way of packing - tables. These can be extremely useful in certain situations.

Using tables, we create a grid that we can place widgets in. The widgets may take up as many spaces as we specify.

The first thing to look at, of course, is the Gtk2::Table::new() function:

$widget = Gtk2::Table->new ($rows, $columns, $homogeneous=FALSE)

The first argument is the number of rows to make in the table, while the second, obviously, is the number of columns.

The homogeneous argument has to do with how the table's boxes are sized. If homogeneous is TRUE, the table boxes are resized to the size of the largest widget in the table. If homogeneous is FALSE, the size of a table boxes is dictated by the tallest widget in its same row, and the widest widget in its column.

The rows and columns are laid out from 0 to n, where n was the number specified in the call to Gtk2::Table::new(). So, if you specify rows = 2 and columns = 2, the layout would look something like this:

 0          1          2
0+----------+----------+
 |          |          |
1+----------+----------+
 |          |          |
2+----------+----------+

Note that the coordinate system starts in the upper left hand corner. To place a widget into a box, use the following function:

Gtk2::Table->attach($table,
                    $child,
	            $left_attach, $right_attach,
		    $top_attach, $bottom_attach,
		    $xoptions, $yoptions,
		    $xpadding, $ypadding);

The first argument ("table") is the table you've created and the second ("child") the widget you wish to place in the table.

The left and right attach arguments specify where to place the widget, and how many boxes to use. If you want a button in the lower right table entry of our 2x2 table, and want it to fill that entry only, left_attach would be = 1, right_attach = 2, top_attach = 1, bottom_attach = 2.

Now, if you wanted a widget to take up the whole top row of our 2x2 table, you'd use left_attach = 0, right_attach = 2, top_attach = 0, bottom_attach = 1.

The xoptions and yoptions are used to specify packing options; they are flags, so they may be packed inside arrays in order to allow multiple options.

These options are:

'fill'

If the table box is larger than the widget, and 'fill' is specified, the widget will expand to use all the room available.

'shrink'

If the table widget was allocated less space then was requested (usually by the user resizing the window), then the widgets would normally just be pushed off the bottom of the window and disappear. If 'shrink' is specified, the widgets will shrink with the table.

'expand'

This will cause the table to expand to use up any remaining space in the window.

Padding is just like in boxes, creating a clear area around the widget specified in pixels.

Gtk2::Table::attach() has a lot of options. So, there's a shortcut:

Gtk2::Table->attach_defaults($table,
                             $widget,
			     $left_attach, $right_attach,
			     $top_attach, $bottom_attach);

The X and Y options default to qw/'fill' 'expand'/, and X and Y padding are set to 0. The rest of the arguments are identical to the previous function.

We also have Gtk2::Table::set_row_spacing() and Gtk2::Table::set_col_spacing(). These places spacing between the rows at the specified row or column.

Gtk2::Table->set_row_spacing($table, $row, $spacing);

and

Gtk2::Table->set_col_spacing($table, $column, $spacing);

Note that for columns, the space goes to the right of the column, and for rows, the space goes below the row.

You can also set a consistent spacing of all rows and/or columns with:

Gtk2::Table->set_row_spacings($table, $spacing);

And,

Gtk2::Table->set_col_spacings($table, $spacing);

Note that with these calls, the last row and last column do not get any spacing.

5. Table Packing Example

Here we make a window with three buttons in a 2x2 table. The first two buttons will be placed in the upper row. A third, quit button, is placed in the lower row, spanning both columns. Which means it should look something like this:

Here's the source code:



use Glib qw/TRUE FALSE/;
use Gtk2 '-init';

# Our callback.
# The data passed to this function is printed to stdout
sub callback
{
	my ($widget, $data) = @_;
	print "Hello again - $data was pressed\n";
}

# This callback quits the program
sub delete_event
{
	Gtk2->main_quit;
	return FALSE;
}

# Create a new window
$window = Gtk2::Window->new('toplevel');

# Set the window title
$window->set_title("Table");

# Set a handler for delete_event that immediately exits GTK.
$window->signal_connect(delete_event => \&delete_event);

# Sets the border width of the window.
$window->set_border_width(20);

# Create a 2x2 table
$table = Gtk2::Table->new(2, 2, TRUE);

# Put the table in the main window
$window->add($table);

# Create first button
$button = Gtk2::Button->new("button 1");

# When the button is clicked, we call the "callback" function
# with the string "button 1" as its argument
$button->signal_connect(clicked => \&callback, 'button 1');

# Insert button 1 into the upper left quadrant of the table
$table->attach_defaults($button, 0, 1, 0, 1);

$button->show;

# Create second button
$button = Gtk2::Button->new("button 2");

# When the button is clicked, we call the "callback" function
# with the string "button 2" as its argument
$button->signal_connect(clicked => \&callback, 'button 2');

# Insert button 2 into the upper right quadrant of the table
$table->attach_defaults($button, 1, 2, 0, 1);

$button->show;

# Create "Quit" button
$button = Gtk2::Button->new("Quit");

# When the button is clicked, we call the "delete_event" function
# and the program exits
$button->signal_connect(clicked => \&delete_event);

# Insert the quit button into the both lower quadrants of the table
$table->attach_defaults($button, 0, 2, 1, 2);

$button->show;

$table->show;
$window->show;

Gtk2->main;

0;


Chapter 7. Widget Overview

The general steps to creating a widget in GTK are:

  1. Gtk2::*::new() - one of various functions to create a new widget. These are all detailed in this section.
  2. Connect all signals and events we wish to use to the appropriate handlers.
  3. Set the attributes of the widget.
  4. Pack the widget into a container using the appropriate call such as Gtk2::Container::add() or Gtk2::Box::pack_start().
  5. Gtk2::Widget::show() the widget.

Gtk2::Widget::show() lets GTK know that we are done setting the attributes of the widget, and it is ready to be displayed. You may also use Gtk2::Widget::hide() to make it disappear again. The order in which you show the widgets is not important, but I suggest showing the window last so the whole window pops up at once rather than seeing the individual widgets come up on the screen as they're formed. The children of a widget (a window is a widget too) will not be displayed until the window itself is shown using the Gtk2::Widget::show() function.

1. Widget Hierarchy

For your reference, here is the class hierarchy tree used to implement widgets. (Deprecated widgets and auxiliary classes have been omitted.)

Glib::Object
 |  
 Gtk2::Object
  +Gtk2::Widget
  | +Gtk2::Misc
  | | +Gtk2::Label
  | | | `Gtk2::AccelLabel
  | | +Gtk2::Arrow
  | | `Gtk2::Image
  | +Gtk2::Container
  | | +Gtk2::Bin
  | | | +Gtk2::Alignment
  | | | +Gtk2::Frame
  | | | | `Gtk2::AspectFrame
  | | | +Gtk2::Button
  | | | | +Gtk2::ToggleButton
  | | | | | `Gtk2::CheckButton
  | | | | |   `Gtk2::RadioButton
  | | | | `Gtk2::OptionMenu
  | | | +Gtk2::Item
  | | | | +Gtk2::MenuItem
  | | | |   +Gtk2::CheckMenuItem
  | | | |   | `Gtk2::RadioMenuItem
  | | | |   +Gtk2::ImageMenuItem
  | | | |   +Gtk2::SeparatorMenuItem
  | | | |   `Gtk2::TearoffMenuItem
  | | | +Gtk2::Window
  | | | | +Gtk2::Dialog
  | | | | | +Gtk2::ColorSelectionDialog
  | | | | | +Gtk2::FileSelection
  | | | | | +Gtk2::FontSelectionDialog
  | | | | | +Gtk2::InputDialog
  | | | | | `Gtk2::MessageDialog
  | | | | `Gtk2::Plug
  | | | +Gtk2::EventBox
  | | | +Gtk2::HandleBox
  | | | +Gtk2::ScrolledWindow
  | | | `Gtk2::Viewport
  | | +Gtk2::Box
  | | | +Gtk2::ButtonBox
  | | | | +Gtk2::HButtonBox
  | | | | `Gtk2::VButtonBox
  | | | +Gtk2::VBox
  | | | | +Gtk2::ColorSelection
  | | | | +Gtk2::FontSelection
  | | | | `Gtk2::GammaCurve
  | | | `Gtk2::HBox
  | | |   +Gtk2::Combo
  | | |   `Gtk2::Statusbar
  | | +Gtk2::Fixed
  | | +Gtk2::Paned
  | | | +Gtk2::HPaned
  | | | `Gtk2::VPaned
  | | +Gtk2::Layout
  | | +Gtk2::MenuShell
  | | | +Gtk2::MenuBar
  | | | `Gtk2::Menu
  | | +Gtk2::Notebook
  | | +Gtk2::Socket
  | | +Gtk2::Table
  | | +Gtk2::TextView
  | | +Gtk2::Toolbar
  | | `Gtk2::TreeView
  | +Gtk2::Calendar
  | +Gtk2::DrawingArea
  | | `Gtk2::Curve
  | +Gtk2::Editable
  | | +Gtk2::Entry
  | |   `Gtk2::SpinButton
  | +Gtk2::Ruler
  | | +Gtk2::HRuler
  | | `Gtk2::VRuler
  | +Gtk2::Range
  | | +Gtk2::Scale
  | | | +Gtk2::HScale
  | | | `Gtk2::VScale
  | | `Gtk2::Scrollbar
  | |   +Gtk2::HScrollbar
  | |   `Gtk2::VScrollbar
  | +Gtk2::Separator
  | | +Gtk2::HSeparator
  | | `Gtk2::VSeparator
  | +Gtk2::Invisible
  | +Gtk2::Preview
  | `Gtk2::ProgressBar
  +Gtk2::Adjustment
  +Gtk2::CellRenderer
  | +Gtk2::CellRendererPixbuf
  | +Gtk2::CellRendererText
  | +Gtk2::CellRendererToggle
  +Gtk2::ItemFactory
  +Gtk2::Tooltips
  `Gtk2::TreeViewColumn

Since GTK2-Perl offers an object oriented interface to the underlying C libraries, we can take advantage of this, and use methods of generic classes on instances of classes derived by them. For instance, the Gtk2::Window class inherits its packaging methods from the Gtk2::Bin class, which, in turn, inherits methods from its parent class, Gtk2::Container. All these classes are widgets, so they inherit methods from the Gtk2::Widget class. Above all, each class inherits from Glib::Object, which is used, for instance, when handling signals and events.

2. Widgets Without Windows

The following widgets do not have an associated window. If you want to capture events, you'll have to use the Gtk2::EventBox. See the section on the Gtk2::EventBox widget.

Gtk2::Alignment
Gtk2::Arrow
Gtk2::Bin
Gtk2::Box
Gtk2::Button
Gtk2::CheckButton
Gtk2::Fixed
Gtk2::Image
Gtk2::Label
Gtk2::MenuItem
Gtk2::Notebook
Gtk2::Paned
Gtk2::RadioButton
Gtk2::Range
Gtk2::ScrolledWindow
Gtk2::Separator
Gtk2::Table
Gtk2::Toolbar
Gtk2::AspectFrame
Gtk2::Frame
Gtk2::VBox
Gtk2::HBox
Gtk2::VSeparator
Gtk2::HSeparator

We'll further our exploration of GTK by examining each widget in turn, creating a few simple functions to display them.

Chapter 8. The Button Widget

1. Normal Buttons

We've almost seen all there is to see of the button widget. It's pretty simple. There is however more than one way to create a button. You can use Gtk2::Button::new_with_label() or Gtk2::Button::new_with_mnemonic() (which can be shortened to just Gtk2::Button::new() ) to create a button with a label, use Gtk2::Button::new_from_stock() to create a button containing the image and text from a stock item or use Gtk2::Button::new() with no arguments to create a blank button. It's then up to you to pack a label or pixmap into this new button. To do this, create a new box, and then pack your objects into this box using the usual Gtk2::Box::pack_start() function, and then use the Gtk2::Box::add() method call to pack the box into the button.

Here's an example of using Gtk2::Button::new() to create a button with a image and a label in it. I've broken up the code to create a box from the rest so you can use it in your programs. There are further examples of using images later in the tutorial.



use Glib qw/TRUE FALSE/;
use Gtk2 '-init';

# Create a new hbox with an image and a label packed into it and return
# the box.
sub xpm_label_box {
	my ($xpm_filename, $label_text) = @_;

	# create box for image and label 
	my $box = Gtk2::HBox->new(FALSE, 0);
	$box->set_border_width(2);

	# now on to the image stuff
	my $image = Gtk2::Image->new_from_file($xpm_filename);
	
	# Create a label for the button
	my $label = Gtk2::Label->new($label_text);
	
	# pack the image and label into the box
	$box->pack_start($image, FALSE, FALSE, 3);
	$box->pack_start($label, FALSE, FALSE, 3);

	$image->show;
	$label->show;

	return $box
}

# our usual callback function
sub callback {
	my $widget = shift;
	my $data   = shift;
	printf "Hello again - %s was pressed\n", $data;
}

##################
# main starts here

# create a new window
my $window = Gtk2::Window->new('toplevel');

$window->set_title("Pixmap'd Buttons!");

# it's a good idea to do this for all windows
$window->signal_connect("destroy" => sub { Gtk2->main_quit; });
$window->signal_connect("delete-event" => sub { Gtk2->main_quit; });

# sets the border width of the window
$window->set_border_width(10);

# create a new button
my $button = Gtk2::Button->new();

# connect the 'clicked' signal of the button to our callback
$button->signal_connect("clicked" => \&callback, "cool button");

# this calls our box creating function
my $box = xpm_label_box("info.xpm", "cool button");

# pack and show all our widgets
$box->show();

$button->add($box);

$button->show();

$window->add($button);

$window->show();

# rest in the GTK main loop and wait for the fun to begin!
Gtk2->main;

0;
	

The xpm_label_box() function could be used to pack images and labels into any widget that can be a container.

The Gtk2::Button widget has the following signals:

  • pressed - emitted when pointer button is pressed within Gtk2::Button widget
  • released - emitted when pointer button is released within Gtk2::Button widget
  • clicked - emitted when pointer button is pressed and then released within Gtk2::Button widget
  • enter - emitted when pointer enters Gtk2::Button widget
  • leave - emitted when pointer leaves Gtk2::Button widget

2. Toggle Buttons

Toggle buttons are derived from normal buttons and are very similar, except they will always be in one of two states, alternated by a click. They may be depressed, and when you click again, they will pop back up. Click again, and they will pop back down.

Toggle buttons are the basis for check buttons and radio buttons, as such, many of the calls used for toggle buttons are inherited by radio and check buttons. I will point these out when we come to them.

Creating a new toggle button:

$togglebutton = Gtk2::ToggleButton->new($mnemonic);

$togglebutton = Gtk2::ToggleButton->new_with_label($label);

$togglebutton = Gtk2::ToggleButton->new_with_mnemonic($label);

As you can imagine, these work identically to the normal button widget calls. The first creates a blank toggle button, and the last two, a button with a label widget already packed into it. The plain and _mnemonic() variants additionally parse the label for '_'-prefixed mnemonic characters.

To retrieve the state of the toggle widget, including radio and check buttons, we use a construct as shown in our example below. The signal of interest to us emitted by toggle buttons (the toggle button, check button, and radio button widgets) is the "toggled" signal. To check the state of these buttons, set up a signal handler to catch the toggled signal, and call Gtk2::ToggleButton::get_active() to determine its state. The callback will look something like:

sub toggle_button_callback {
    my $button = shift;
    
    if ($button->get_active) {
        # if control reaches here, the toggle button is down
    } else {
        # if control reaches here, the toggle button is up
    }   
} 

To force the state of a toggle button, and its children, the radio and check buttons, use the Gtk2::ToggleButton::set_active() method. This function can be used to set the state of the toggle button, and its children the radio and check buttons. The function takes only one parameter, a TRUE or FALSE for the state. Default is up, or FALSE.

Note that when you use the Gtk2::ToggleButton::set_active() function, and the state is actually changed, it causes the "clicked" and "toggled" signals to be emitted from the button.

The Gtk2::ToggleButton::get_active() function will return the current state of the button as a boolean TRUE/FALSE value.

3. Check Buttons

Check buttons inherit many properties and functions from the the toggle buttons above, but look a little different. Rather than being buttons with text inside them, they are small squares with the text to the right of them. These are often used for toggling options on and off in applications.

The creation functions are similar to those of the normal button.

$checkbutton = Gtk2::CheckButton->new($mnemonic);

$checkbutton = Gtk2::CheckButton->new_with_label($label);

The Gtk2::CheckButton::new_with_label() function creates a check button with a label beside it.

Checking the state of the check button is identical to that of the toggle button.

4. Radio Buttons

Radio buttons are similar to check buttons except they are grouped so that only one may be selected/depressed at a time. This is good for places in your application where you need to select from a short list of options.

Creating a new radio button is done with one of these calls:

$radiobutton = Gtk2::RadioButton->new($member_or_listref=undef, $label=undef);

$radiobutton = Gtk2::RadioButton->new_from_widget($group, $label=undef);

$radiobutton = Gtk2::RadioButton->new_with_label($member_or_listref=undef, $label=undef);

$radiobutton = Gtk2::RadioButton->new_with_label_from_widget($group, $label=undef);

$radiobutton = Gtk2::RadioButton->new_with_mnemonic($member_or_listref=undef, $label=undef);

$radiobutton = Gtk2::RadioButton->new_with_mnemonic_from_widget($group, $label=undef);

You'll notice the extra argument to these calls. They require a group to perform their duty properly. The first call to Gtk2::RadioButton::new() or Gtk2::RadioButton::new_with_label() should pass undef as the first argument. Then create a group using:

Gtk2::RadioButton->get_group();

The important thing to remember is that Gtk2::RadioButton::get_group() must be called for each new button added to the group, with the previous button passed in as an argument. The result is then passed into the next call to Gtk2::RadioButton::new() or Gtk2::RadioButton::new_with_label(). This allows a chain of buttons to be established. The example below should make this clear.

You can shorten this slightly by using the following syntax, which removes the need for a variable to hold the list of buttons:

$radiobutton = Gtk2::RadioButton->new_with_label(
                 $button1->get_group(),
                 "button2");

The _from_widget() variants of the creation functions allow you to shorten this further, by omitting the Gtk2::RadioButton::get_group() call. This form is used in the example to create the third button:

$radiobutton = Gtk2::RadioButton->new_with_label_from_widget(
	         $button2, 
                 "button3");

It is also a good idea to explicitly set which button should be the default depressed button with:

Gtk2::ToggleButton->set_active($toggle_button, gboolean state);

This is described in the section on toggle buttons, and works in exactly the same way. Once the radio buttons are grouped together, only one of the group may be active at a time. If the user clicks on one radio button, and then on another, the first radio button will first emit a "toggled" signal (to report becoming inactive), and then the second will emit its "toggled" signal (to report becoming active).

The following example creates a radio button group with three buttons.



use Glib qw/TRUE FALSE/;
use Gtk2 '-init';

$window = Gtk2::Window->new('toplevel');
$window->signal_connect(delete_event => sub { Gtk2->main_quit; return FALSE; });
$window->set_title("radio buttons");
$window->set_border_width(0);

$box1 = Gtk2::VBox->new(FALSE, 0);
$window->add($box1);
$box1->show;

$box2 = Gtk2::VBox->new(FALSE, 10);
$box2->set_border_width(10);
$box1->pack_start($box2, TRUE, TRUE, 0);
$box2->show;

$button = Gtk2::RadioButton->new(undef, "button 1");
$box2->pack_start($button, TRUE, TRUE, 0);
$button->show;

@group = $button->get_group;
$button = Gtk2::RadioButton->new_with_label(@group, "button 2");
$button->set_active(TRUE);
$box2->pack_start($button, TRUE, TRUE, 0);
$button->show;

$button = Gtk2::RadioButton->new_with_label_from_widget($button, "button 3");
$box2->pack_start($button, TRUE, TRUE, 0);
$button->show;

$separator = Gtk2::HSeparator->new;
$box1->pack_start($separator, FALSE, TRUE, 0);
$separator->show;

$box2 = Gtk2::VBox->new(FALSE, 10);
$box2->set_border_width(10);
$box1->pack_start($box2, FALSE, TRUE, 0);
$box2->show;

$button = Gtk2::Button->new("close");
$button->signal_connect(clicked => sub { Gtk2->main_quit; });
$box2->pack_start($button, TRUE, TRUE, 0);
$button->can_default(TRUE);
$button->grab_default;
$button->show;
$window->show;

Gtk2->main;

0;


Chapter 9. Adjustments

GTK has various widgets that can be visually adjusted by the user using the mouse or the keyboard, such as the range widgets, described in the Range Widgets section. There are also a few widgets that display some adjustable portion of a larger area of data, such as the text widget and the viewport widget.

Obviously, an application needs to be able to react to changes the user makes in range widgets. One way to do this would be to have each widget emit its own type of signal when its adjustment changes, and either pass the new value to the signal handler, or require it to look inside the widget's data structure in order to ascertain the value. But you may also want to connect the adjustments of several widgets together, so that adjusting one adjusts the others. The most obvious example of this is connecting a scrollbar to a panning viewport or a scrolling text area. If each widget has its own way of setting or getting the adjustment value, then the programmer may have to write their own signal handlers to translate between the output of one widget's signal and the "input" of another's adjustment setting function.

GTK solves this problem using the Adjustment object, which is not a widget but a way for widgets to store and pass adjustment information in an abstract and flexible form. The most obvious use of Adjustment is to store the configuration parameters and values of range widgets, such as scrollbars and scale controls. However, since Adjustments are derived from Object, they have some special powers beyond those of normal data structures. Most importantly, they can emit signals, just like widgets, and these signals can be used not only to allow your program to react to user input on adjustable widgets, but also to propagate adjustment values transparently between adjustable widgets.

You will see how adjustments fit in when you see the other widgets that incorporate them: Progress Bars, Viewports, Scrolled Windows and others.

1. Creating an Adjustment

Many of the widgets which use adjustment objects do so automatically, but some cases will be shown in later examples where you may need to create one yourself. You create an adjustment using:

$adjustment = Gtk2::Adjustment->new($value,
                                    $lower,
				    $upper,
				    $step_increment,
				    $page_increment,
				    $page_size);

The value argument is the initial value you want to give to the adjustment, usually corresponding to the topmost or leftmost position of an adjustable widget. The lower argument specifies the lowest value which the adjustment can hold. The step_increment argument specifies the "smaller" of the two increments by which the user can change the value, while the page_increment is the "larger" one. The page_size argument usually corresponds somehow to the visible area of a panning widget. The upper argument is used to represent the bottom most or right most coordinate in a panning widget's child. Therefore it is not always the largest number that value can take, since the page_size of such widgets is usually non-zero.

2. Using Adjustments the Easy Way

The adjustable widgets can be roughly divided into those which use and require specific units for these values and those which treat them as arbitrary numbers. The group which treats the values as arbitrary numbers includes the range widgets (scrollbars and scales, the progress bar widget, and the spin button widget). These widgets are all the widgets which are typically "adjusted" directly by the user with the mouse or keyboard. They will treat the lower and upper values of an adjustment as a range within which the user can manipulate the adjustment's value. By default, they will only modify the value of an adjustment.

The other group includes the text widget, the viewport widget, the compound list widget, and the scrolled window widget. All of these widgets use pixel values for their adjustments. These are also all widgets which are typically "adjusted" indirectly using scrollbars. While all widgets which use adjustments can either create their own adjustments or use ones you supply, you'll generally want to let this particular category of widgets create its own adjustments. Usually, they will eventually override all the values except the value itself in whatever adjustments you give them, but the results are, in general, undefined (meaning, you'll have to read the source code to find out, and it may be different from widget to widget).

Now, you're probably thinking, since text widgets and viewports insist on setting everything except the value of their adjustments, while scrollbars will only touch the adjustment's value, if you share an adjustment object between a scrollbar and a text widget, manipulating the scrollbar will automagically adjust the viewport widget? Of course it will! Just like this:

  # creates its own adjustment
  $viewport = Gtk2::ViewPort->new(undef, undef);
  # uses the newly-created adjustment for the scrollbar as well
  $vscrollbar = Gtk2::VScrollBar->new($viewport->get_vadjustment());

3. Adjustment Internals

[...]

Chapter 10. Range Widgets

The category of range widgets includes the ubiquitous scrollbar widget and the less common scale widget. Though these two types of widgets are generally used for different purposes, they are quite similar in function and implementation. All range widgets share a set of common graphic elements, each of which has its own X window and receives events. They all contain a "trough" and a "slider" (what is sometimes called a "thumbwheel" in other GUI environments). Dragging the slider with the pointer moves it back and forth within the trough, while clicking in the trough advances the slider towards the location of the click, either completely, or by a designated amount, depending on which mouse button is used.

As mentioned in Adjustments above, all range widgets are associated with an adjustment object, from which they calculate the length of the slider and its position within the trough. When the user manipulates the slider, the range widget will change the value of the adjustment.

1. Scrollbar Widgets

These are your standard, run-of-the-mill scrollbars. These should be used only for scrolling some other widget, such as a list, a text box, or a viewport (and it's generally easier to use the scrolled window widget in most cases). For other purposes, you should use scale widgets, as they are friendlier and more featureful.

There are separate types for horizontal and vertical scrollbars. There really isn't much to say about these. You create them with the following functions:

$scrollbar = Gtk2::HScrollBar->new($adjustment=undef);

$scrollbar = Gtk2::VScrollBar->new($adjustment=undef);

and that's about it (if you don't believe me, look in the header files!). The adjustment argument can either be an existing Adjustment, or undef, in which case one will be created for you. Specifying undef might actually be useful in this case, if you wish to pass the newly-created adjustment to the constructor function of some other widget which will configure it for you, such as a text widget.

2. Scale Widgets

Scale widgets are used to allow the user to visually select and manipulate a value within a specific range. You might want to use a scale widget, for example, to adjust the magnification level on a zoomed preview of a picture, or to control the brightness of a color, or to specify the number of minutes of inactivity before a screensaver takes over the screen.

2.1. Creating a Scale Widget

As with scrollbars, there are separate widget types for horizontal and vertical scale widgets. (Most programmers seem to favour horizontal scale widgets.) Since they work essentially the same way, there's no need to treat them separately here. The following functions create vertical and horizontal scale widgets, respectively:

$scale = Gtk2::VScale->new($adjustment=undef);

$scale = Gtk2::VScale->new_with_range($min, $max, $step);

$scale = Gtk2::HScale->new($adjustment=undef);

$scale = Gtk2::HScale->new_with_range($min, $max, $step);

The adjustment argument can either be an adjustment which has already been created with Gtk2::Adjustment::new(), or undef, in which case, an anonymous Adjustment is created with all of its values set to 0.0 (which isn't very useful in this case). In order to avoid confusing yourself, you probably want to create your adjustment with a page_size of 0.0 so that its upper value actually corresponds to the highest value the user can select. The _new_with_range() variants take care of creating a suitable adjustment. (If you're already thoroughly confused, read the section on Adjustments again for an explanation of what exactly adjustments do and how to create and manipulate them.)

2.2. Functions and Signals (well, functions, at least)

Scale widgets can display their current value as a number beside the trough. The default behaviour is to show the value, but you can change this with this function:

Gtk2::Scale->set_draw_value($scale, $draw_value);

As you might have guessed, draw_value is either TRUE or FALSE, with predictable consequences for either one.

The value displayed by a scale widget is rounded to one decimal point by default, as is the value field in its Adjustment. You can change this with:

Gtk2::Scale->set_digits($scale, $digits);

where digits is the number of decimal places you want. You can set digits to anything you like, but no more than 13 decimal places will actually be drawn on screen.

Finally, the value can be drawn in different positions relative to the trough:

Gtk2::Scale->set_value_pos($scale, $pos);

The argument pos is of type Gtk2::PositionType, which can take one of the following values:

  'left'/'GTK_POS_LEFT'
  'right'/'GTK_POS_RIGHT'
  'top'/'GTK_POS_TOP'
  'bottom'/'GTK_POS_BOTTOM'

If you position the value on the "side" of the trough (e.g., on the top or bottom of a horizontal scale widget), then it will follow the slider up and down the trough.

3. Common Range Functions

The Range widget class is fairly complicated internally, but, like all the "base class" widgets, most of its complexity is only interesting if you want to hack on it. Also, almost all of the functions and signals it defines are only really used in writing derived widgets.

3.1. Setting the Update Policy

The "update policy" of a range widget defines at what points during user interaction it will change the value field of its Adjustment and emit the "value_changed" signal on this Adjustment. The update policies, defined as type Gtk2::UpdateType, are:

'continous'/'GTK_UPDATE_CONTINUOUS'

This is the default. The "value_changed" signal is emitted continuously, i.e., whenever the slider is moved by even the tiniest amount.

'discontinous'/'GTK_UPDATE_DISCONTINUOUS'

The "value_changed" signal is only emitted once the slider has stopped moving and the user has released the mouse button.

'delayed'/'GTK_UPDATE_DELAYED'

The "value_changed" signal is emitted when the user releases the mouse button, or if the slider stops moving for a short period of time.

The update policy of a range widget can be set by this function:

Gtk2::Range->set_update_policy($range, $policy);

3.2. Getting and Setting Adjustments

Getting and setting the adjustment for a range widget "on the fly" is done, predictably, with:

$adjustment = Gtk2::Range->get_adjustment($range);

Gtk2::Range->set_adjustment($range, $adjustment);

Gtk2::Range::get_adjustment() returns the adjustment object to which range is connected.

Gtk2::Range::set_adjustment() does absolutely nothing if you pass it the adjustment that range is already using, regardless of whether you changed any of its fields or not. If you pass it a new Adjustment, it will unreference the old one if it exists (possibly destroying it), connect the appropriate signals to the new one, and call the private function adjustment_changed(), which will (or at least, is supposed to...) recalculate the size and/or position of the slider and redraw if necessary. As mentioned in the section on adjustments, if you wish to reuse the same Adjustment, when you modify its values directly, you should emit the "changed" signal on it, like this:

Glib::Object->signal_emit($adjustment, "changed");

4. Key and Mouse bindings

All of the GTK range widgets react to mouse clicks in more or less the same way. Clicking button-1 in the trough will cause its adjustment's page_increment to be added or subtracted from its value, and the slider to be moved accordingly. Clicking mouse button-2 in the trough will jump the slider to the point at which the button was clicked. Clicking button-3 in the trough of a range or any button on a scrollbar's arrows will cause its adjustment's value to change by step_increment at a time.

Scrollbars are not focusable, thus have no key bindings. The key bindings for the other range widgets (which are, of course, only active when the widget has focus) are do not differentiate between horizontal and vertical range widgets.

All range widgets can be operated with the left, right, up and down arrow keys, as well as with the Page Up and Page Down keys. The arrows move the slider up and down by step_increment, while Page Up and Page Down move it by page_increment.

The user can also move the slider all the way to one end or the other of the trough using the keyboard. This is done with the Home and End keys.

5. Example

This example is a somewhat modified version of the "range controls" test from testgtk.c. It basically puts up a window with three range widgets all connected to the same adjustment, and a couple of controls for adjusting some of the parameters mentioned above and in the section on adjustments, so you can see how they affect the way these widgets work for the user.



use Glib qw/TRUE FALSE/;
use Gtk2 '-init';

my ($hscale, $vscale);

sub cb_pos_menu_select
{
	my ($widget, $pos) = @_;
	$hscale->set_value_pos($pos);
	$vscale->set_value_pos($pos);
}

sub cb_update_menu_select
{
	my ($widget, $policy) = @_;
	$hscale->set_update_policy($policy);
	$vscale->set_update_policy($policy);
}

sub cb_digits_scale
{
	my ($adjustment) = @_;
	# Set the number of decimal places to which adj->value is rounded
	$hscale->set_digits($adjustment->value);
	$vscale->set_digits($adjustment->value);
}

# We do not have a Glib::CLAMP function, so we provide it here.
sub clamp ($val, $low, $high)
{
	return (($val > $high) ? $high : (($val < $low) ? $low : $val));
}

sub cb_page_size
{
	my ($get, $set) = @_;
	
	# Set the page size and page increment size of the sample
	# adjustment to the value specified by the "Page Size" scale
	$set->page_size($get->value);
	$set->page_increment($get->value);

	# This sets the adjustment and make it emit the "changed" signal to
	# reconfigure all the widgets that are attached to this signal.
	$set->set_value(clamp ($set->value,
	                       $set->upper - $set->page_size,
			       $set->lower));
	
	$set->signal_emit("changed");
}

sub cb_draw_value
{
	my $button = shift;
	$hscale->set_draw_value($button->get_active());
	$vscale->set_draw_value($button->get_active());
}

# Convenience functions
sub make_menu_item
{
	my ($name, $func, $data) = @_;
	$item = Gtk2::MenuItem->new_with_label($name);
	$item->signal_connect(activate => $func, $data);
	$item->show;

	return $item;
}

sub scale_set_default_values
{
	my $scale = shift;
	$scale->set_update_policy('continuous');
	$scale->set_digits(1);
	$scale->set_value_pos('top');
	$scale->set_draw_value(TRUE);
}

# makes the sample window
sub create_range_controls
{
	# Standard window-creating stuff
	$window = Gtk2::Window->new('toplevel');
	$window->signal_connect(destroy => sub { Gtk2->main_quit; });
	$window->set_title("range controls");

	$box1 = Gtk2::VBox->new(FALSE, 0);
	$window->add($box1);
	$box1->show;

	$box2 = Gtk2::HBox->new(FALSE, 10);
	$box2->set_border_width(10);
	$box1->pack_start($box2, TRUE, TRUE, 0);
	$box2->show;

	# value, lower, upper, step_increment, page_increment, page_size
	# Note that the page_size value only makes a difference for
	# scrollbar widgets, and the highest value you'll get is actually
	# (upper - page_size).
	$adj1 = Gtk2::Adjustment->new(0.0, 0.0, 101.0, 0.1, 1.0, 1.0);

	$vscale = Gtk2::VScale->new($adj1);
	scale_set_default_values($vscale);
	$box2->pack_start($vscale, TRUE, TRUE, 0);
	$vscale->show;

	$box3 = Gtk2::VBox->new(FALSE, 10);
	$box2->pack_start($box3, TRUE, TRUE, 0);
	$box3->show;

	# Reuse the same adjustment
	$hscale = Gtk2::HScale->new($adj1);
	$hscale->set_size_request(200, -1);
	scale_set_default_values($hscale);
	$box3->pack_start($hscale, TRUE, TRUE, 0);
	$hscale->show;
	
	# Reuse the same adjustment again
	$scrollbar = Gtk2::HScrollBar->new($adj1);
	# Notice how this causes the scales to always be updated
	# continuously when the scrollbar is moved
	$scrollbar->set_update_policy('continuous');
	$box3->pack_start($scrollbar, TRUE, TRUE, 0);
	$scrollbar->show;
	
	$box2 = Gtk2::HBox->new(FALSE, 10);
	$box2->set_border_width(10);
	$box1->pack_start($box2, TRUE, TRUE, 0);
	$box2->show;

	# A checkbutton to control whether the value is displayed or not
	$button = Gtk2::CheckButton->new("Display value on scale widgets");
	$button->set_active(TRUE);
	$button->signal_connect(toggled => \&cb_draw_value);
	$box2->pack_start($button, TRUE, TRUE, 0);
	$button->show;

	$box2 = Gtk2::HBox->new(FALSE, 10);
	$box2->set_border_width(10);

	# An option menu to change the position of the value
	$label = Gtk2::Label->new("Scale Value Position:");
	$box2->pack_start($label, FALSE, FALSE, 0);
	$label->show;

	$opt = Gtk2::OptionMenu->new;
	$menu = Gtk2::Menu->new;

	$item = make_menu_item("Top", \&cb_pos_menu_select, 'top');
	$menu->append($item);
	
	$item = make_menu_item("Bottom", \&cb_pos_menu_select, 'bottom');
	$menu->append($item);

	$item = make_menu_item("Left", \&cb_pos_menu_select, 'left');
	$menu->append($item);

	$item = make_menu_item("Right", \&cb_pos_menu_select, 'right');
	$menu->append($item);

	$opt->set_menu($menu);
	$box2->pack_start($opt, TRUE, TRUE, 0);
	$opt->show;

	$box1->pack_start($box2, TRUE, TRUE, 0);
	$box2->show;

	$box2 = Gtk2::HBox->new(FALSE, 10);
	$box2->set_border_width(10);

	# Yet another option menu, this time for the update policy of the
	# scale widgets
	$label = Gtk2::Label->new("Scale Update Policy:");
	$box2->pack_start($label, FALSE, FALSE, 0);
	$label->show;

	$opt = Gtk2::OptionMenu->new;
	$menu = Gtk2::Menu->new;

	$item = make_menu_item("Continuous", \&cb_update_menu_select, 'continuous');
	$menu->append($item);

	$item = make_menu_item("Discontinuous", \&cb_update_menu_select, 'discontinuous');
	$menu->append($item);

	$item = make_menu_item("Delayed", \&cb_update_menu_select, 'delayed');
	$menu->append($item);

	$opt->set_menu($menu);
	$box2->pack_start($opt, TRUE, TRUE, 0);
	$opt->show;

	$box1->pack_start($box2, TRUE, TRUE, 0);
	$box2->show;

	$box2 = Gtk2::HBox->new(FALSE, 10);
	$box2->set_border_width(10);

	# An HScale widget for adjusting the number of digits on the
	# sample scales.
	$label = Gtk2::Label->new("Scale Digits:");
	$box2->pack_start($label, FALSE, FALSE, 0);
	$label->show;

	$adj2 = Gtk2::Adjustment->new(1.0, 0.0, 5.0, 1.0, 1.0, 0.0);
	$adj2->signal_connect(value_changed => \&cb_digits_scale);
	$scale = Gtk2::HScale->new($adj2);
	$scale->set_digits(0);
	$box2->pack_start($scale, TRUE, TRUE, 0);
	$scale->show;

	$box1->pack_start($box2, TRUE, TRUE, 0);
	$box2->show;

	$box2 = Gtk2::HBox->new(FALSE, 10);
	$box2->set_border_width(10);

	# And, one last HScale widget for adjusting the page size of the
	# scrollbar.
	$label = Gtk2::Label->new("Scrollbar Page Size:");
	$box2->pack_start($label, FALSE, FALSE, 0);
	$label->show;

	$adj2 = Gtk2::Adjustment->new(1.0, 1.0, 101.0, 1.0, 1.0, 0.0);
	$adj2->signal_connect(value_changed => \&cb_page_size, $adj1);
	$scale = Gtk2::HScale->new($adj2);
	$scale->set_digits(0);
	$box2->pack_start($scale, TRUE, TRUE, 0);
	$scale->show;
	
	$box1->pack_start($box2, TRUE, TRUE, 0);
	$box2->show;

	$separator = Gtk2::HSeparator->new;
	$box1->pack_start($separator, FALSE, TRUE, 0);
	$separator->show;

	$box2 = Gtk2::VBox->new(FALSE, 10);
	$box2->set_border_width(10);
	$box1->pack_start($box2, FALSE, TRUE, 0);
	$box2->show;

	$button = Gtk2::Button->new("Quit");
	$button->signal_connect(clicked => sub { Gtk2->main_quit; });
	$box2->pack_start($button, TRUE, TRUE, 0);
	$button->can_default(TRUE);
	$button->grab_default;
	$button->show;

	$window->show;
}

create_range_controls();

Gtk2->main;

0;


You will notice that the program does not call Glib::Object::signal_connect() for the "delete_event", but only for the "destroy" signal. This will still perform the desired function, because an unhandled "delete_event" will result in a "destroy" signal being given to the window.

Chapter 11. Miscellaneous Widgets

1. Labels

Labels are used a lot in GTK, and are relatively simple. Labels emit no signals as they do not have an associated X window. If you need to catch signals, or do clipping, place it inside a EventBox widget or a Button widget.

To create a new label, use:

$label = Gtk2::Label->new($str=undef);

$label = Gtk2::Label->new_with_mnemonic($str);

The sole argument is the string you wish the label to display.

To change the label's text after creation, use the function:

Gtk2::Label->set_text($label, $string);

The first argument is the label widget you created previously, and the second is the new string.

The space needed for the new string will be automatically adjusted if needed. You can produce multi-line labels by putting line breaks in the label string.

To retrieve the current string, use:

$text = Gtk2::Label->get_text($label);                    

The label text can be justified using:

Gtk2::Label->set_justify($label, $jtype);

Values for jtype are:

  left
  right
  center (the default)
  fill

The label widget is also capable of line wrapping the text automatically. This can be activated using:

Gtk2::Label->set_line_wrap($label, $wrap);

The wrap argument takes a TRUE or FALSE value.

If you want your label underlined, then you can set a pattern on the label:

Gtk2::Label->set_pattern($label, $pattern);

The pattern argument indicates how the underlining should look. It consists of a string of underscore and space characters. An underscore indicates that the corresponding character in the label should be underlined. For example, the string "__ __" would underline the first two characters and eight and ninth characters.

Note

If you simply want to have an underlined accelerator ("mnemonic") in your label, you should use Gtk2::Label::new_with_mnemonic() or Gtk2::Label::set_text_with_mnemonic(), not Gtk2::Label::set_pattern().

Below is a short example to illustrate these functions. This example makes use of the Frame widget to better demonstrate the label styles. You can ignore this for now as the Frame widget is explained later on.

In GTK+ 2.0, label texts can contain markup for font and other text attribute changes, and labels may be selectable (for copy-and-paste). These advanced features won't be explained here.



use Glib qw/TRUE FALSE/;
use Gtk2 '-init';

$window = Gtk2::Window->new('toplevel');
$window->signal_connect(destroy => sub { Gtk2->main_quit; });

$window->set_title("Label");
$vbox = Gtk2::VBox->new(FALSE, 5);
$hbox = Gtk2::HBox->new(FALSE, 5);
$window->add($hbox);
$hbox->pack_start($vbox, FALSE, FALSE, 0);
$window->set_border_width(5);

$frame = Gtk2::Frame->new("Normal Label");
$label = Gtk2::Label->new("This is a Normal label");
$frame->add($label);
$vbox->pack_start($frame, FALSE, FALSE, 0);

$frame = Gtk2::Frame->new("Multi-line Label");
$label = Gtk2::Label->new("This is a Multi-line label.\nSecond line\n" .
                          "Third line");
$frame->add($label);
$vbox->pack_sta