[Previous] [Contents] [Next]
6 The Button Widget
We've almost seen all there is to see of the button widget. It's pretty simple. There are however two ways to create
a button. You can use the gtk_button_new_with_label() function to create a button with a label,
or use gtk_button_new() function 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
gtk_box_pack_start(), and then use gtk_container_add() to
pack the box into the button.
Here's an example of using gtk_button_new() to create a button with a picture 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 pixmaps later in the tutorial. If you'd like to compile and run the following code you'll need to
copy the the pixmap info.xpm from /usr/doc/fpc-1.0.6/examples/gtk/tutorial (assuming you've installed the
fpc documentation) to the directory that the program is situated in.
{ Converted from C to Pascal by Thomas E. Payne }
PROGRAM Buttons;
USES gtk, gdk, glib;
{ ----------------------------------xpm_label_box------------------------------ }
FUNCTION xpm_label_box( parent : pGtkWidget;
xpm_filename : pgchar ; label_text :
pgchar ) : pGtkWidget;
{ Create a new hbox with an image and a label packed into it and return the box }
VAR
box1, the_label, pixmapwid : pGtkWidget;
pixmap : pGdkPixmap;
mask : pGdkBitmap;
style : pGtkStyle;
BEGIN
box1 := gtk_hbox_new(FALSE,0);
{ Create box for xpm and label }
gtk_container_set_border_width(GTK_CONTAINER(box1), 2);
{ Get the style of the button to get the background colour. }
style := gtk_widget_get_style(parent);
{ Now on to the xpm stuff. }
pixmap := gdk_pixmap_create_from_xpm(parent^.window, @mask,
@style^.bg[GTK_STATE_NORMAL], xpm_filename);
pixmapwid := gtk_pixmap_new(pixmap, mask);
the_label := gtk_label_new(label_text);
{ Create a label for the button. }
{ Pack the pixmap and label into the box. }
gtk_box_pack_start(GTK_BOX(box1), pixmapwid, FALSE,
FALSE, 3);
gtk_box_pack_start(GTK_BOX (Box1), the_label, FALSE,
FALSE, 3);
{ Now show them }
gtk_widget_show(pixmapwid);
gtk_widget_show(the_label);
xpm_label_box := box1; { Return the newly created box }
END;
{------------------------------xpm_label_box--------------------------}
{----------------------------------callback------------------------------}
PROCEDURE callback( widget : pGtkWidget;
data : pgpointer ); cdecl;
{ Our usual callback function. }
BEGIN
writeln('Hello again, ', pchar(data),
' was pressed');
END;
{ ----------------------------------callback------------------------------ }
{ ----------------------------------Global Variables---------------------------- }
VAR
window, button, box1 : pGtkWidget;
{ GtkWidget is the storage type for widgets. }
{ ----------------------------------Main Program------------------------------ }
BEGIN
gtk_init(@argc, @argv);
window := gtk_window_new(GTK_WINDOW_TOPLEVEL);
{ Create a new window. }
gtk_window_set_title(GTK_WINDOW(window), 'Pixmap''d Buttons!');
{ It's a good idea to do this for all windows: }
gtk_signal_connect(GTK_OBJECT(window), 'destroy',
GTK_SIGNAL_FUNC(@gtk_exit), NIL);
gtk_signal_connect(GTK_OBJECT(window), 'delete_event',
GTK_SIGNAL_FUNC(@gtk_exit), NIL);
gtk_container_set_border_width(GTK_CONTAINER(window), 10);
gtk_widget_realize(window);
button := gtk_button_new(); { Create a new button }
{ Connect the --clicked-- signal of the button to our callback: }
gtk_signal_connect(GTK_OBJECT(button), 'clicked',
GTK_SIGNAL_FUNC(@callback),
pchar('cool button'));
{ This calls our box creating function: }
box1 := xpm_label_box(window, 'info.xpm', 'cool button');
{ Pack and show all our widgets: }
gtk_widget_show(box1);
gtk_container_add(GTK_CONTAINER(button), box1);
gtk_widget_show(button);
gtk_container_add(GTK_CONTAINER(window), button);
gtk_widget_show(window);
{ Rest in gtk_main and wait for the fun to begin! }
gtk_main();
END.
{ --------------------------------Main Program----------------------------- }
The xpm_label_box function could be used to pack xpm's and labels into any widget that can be a container.
Notice in xpm_label_box how there is a call to gtk_widget_get_style(). Every widget
has a style, consisting of foreground and background colours for a variety of situations, font selection,
and other graphics data relevant to a widget. These style values are defaulted in each widget, and are required by
many GDK function calls, such as gdk_pixmap_create_from_xpm(), which here is given the
normal background colour. The style data of widgets may be customized, using GTK's rc files.
Also notice the call to gtk_widget_realize() after setting the window's border width. This
function uses GDK to create the X windows related to the widget. The function is automatically called when you invoke
gtk_widget_show() for a widget, and so has not been shown in earlier examples. But the call to
gdk_pixmap_create_from_xpm() requires that its window argument refer to a real X window,
so it is necessary to realize the widget before this GDK call.
The button widget has the following signals:
pressed - emitted when pointer button is pressed within button widget
released - emitted when pointer button is released within button widget
clicked - emitted when pointer button is pressed and then released within button widget
enter - emitted when pointer enters button widget
leave - emitted when pointer leaves button widget
[Previous] [Contents] [Next]
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:
FUNCTION gtk_toggle_button_new() :
pGtkWidget;
FUNCTION gtk_toggle_button_new_with_label( a_label :
pchar ) : pGtkWidget;
As you can imagine, these work identically to the normal button widget calls. The first creates a blank toggle button,
and the second, a button with a label widget already packed into it.
To retrieve the state of the toggle widget, including radio and check buttons, we use a construct as shown in our example
below. This tests the state of the toggle, by accessing the active field of the toggle widget's structure, after first
using the GTK_TOGGLE_BUTTON macro to cast the widget pointer into a toggle widget pointer. 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 access the structure to
determine its state. The callback will look something like:
PROCEDURE toggle_button_callback( a_widget : pGtkWidget;
data : pgpointer );
BEGIN
IF active(GTK_TOGGLE_BUTTON(a_widget)^) <> 0 THEN
do_something ... { if control reaches here, the toggle button is down }
ELSE
do_something_else ...{ if control reaches here, the toggle button is up }
END;
To force the state of a toggle button, and its children, the radio and check buttons, use this function:
PROCEDURE gtk_toggle_button_set_active( toggle_button :
pGtkToggleButton ;
state : boolean);
The above call can be used to set the state of the toggle button, and its children the radio and check buttons. Passing
in your created button as the first argument, and a TRUE or FALSE
for the second state argument to specify whether it should be down (depressed) or up (released). Default is up, or
FALSE.
Note that when you use the gtk_toggle_button_set_active() function, and the state is actually
changed, it causes the clicked signal to be emitted from the button.
PROCEDURE gtk_toggle_button_toggled( toggle_button :
pGtkToggleButton );
This simply toggles the button, and emits the toggled signal.
[Previous] [Contents] [Next]
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 two creation functions are similar to those of the normal button.
FUNCTION gtk_check_button_new() :
pGtkWidget;
FUNCTION gtk_check_button_new_with_label( a_label :
pchar ) : pGtkWidget;
The *_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.
423 92
Here's an example to illustrate (you don't get this is the C tutorial!):
{ Converted from C to Pascal by Thomas E. Payne }
PROGRAM check_buttons;
{$mode objfpc}
USES glib, gdk, gtk, sysutils;
{ ---------------------------------checkbox_callback--------------------------- }
PROCEDURE checkbox_callback( widget : pGtkWidget; data :
pgpointer ); cdecl;
{ Our usual callback }
BEGIN
writeln('Hello again - ' + pchar(data)
+ ' was pressed');
IF active(GTK_CHECK_BUTTON(widget)^.toggle_button) <> 0
THEN
{ If control reaches here, the check button is down }
writeln('Check button is down')
ELSE
{ If control reaches here, the check button is up }
writeln('Check button is up');
END;
{ ------------------------------checkbox_callback------------------------------ }
{ ----------------------------Global Variables--------------------------- }
VAR
{ GtkWidget is the storage type for widgets }
window, button, box1, the_label : pGtkWidget;
{ -----------------------------Main Program------------------------------ }
BEGIN
gtk_init(@argc, @argv);
window := gtk_window_new(GTK_WINDOW_TOPLEVEL);
{ Create a new window }
gtk_window_set_title(GTK_WINDOW(window), 'Check Buttons!');
{ It's a good idea to do this for all windows: }
gtk_signal_connect(GTK_OBJECT(window), 'destroy',
GTK_SIGNAL_FUNC(@gtk_exit), NIL);
gtk_signal_connect(GTK_OBJECT(window), 'delete_event',
GTK_SIGNAL_FUNC(@gtk_exit), NIL);
{ Sets the border width of the window: }
gtk_container_set_border_width(GTK_CONTAINER(window), 10);
gtk_widget_realize(window);
button := gtk_check_button_new(); { Create a new button }
{ Connect the --clicked-- signal of the button to our callback: }
gtk_signal_connect(GTK_OBJECT(button), 'clicked',
GTK_SIGNAL_FUNC(@checkbox_callback),
pchar('check button'));
box1 := gtk_hbox_new(FALSE, 0);
gtk_container_set_border_width(GTK_CONTAINER(box1), 2);
{ Create a label for the button: }
the_label := gtk_label_new('check button');
gtk_box_pack_start(GTK_BOX(box1), the_label, FALSE,
FALSE, 3);
{ Pack and show all our widgets }
gtk_widget_show(the_label);
gtk_widget_show(box1);
gtk_container_add(GTK_CONTAINER(button), box1);
gtk_widget_show(button);
gtk_container_add(GTK_CONTAINER(window), button);
gtk_widget_show(window);
{ Rest in gtk_main and wait for the fun to begin! }
gtk_main();
END.
{ -----------------------------------Main Program---------------------------------- }
[Previous] [Contents] [Next]
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 procedures:
FUNCTION gtk_radio_button_new( group :
pGSList ): pGtkWidget;
FUNCTION gtk_radio_button_new_with_label( group :
pGSList ;
a_label : gpointer ):
pGtkWidget;
You'll notice the extra argument to these calls. They require a group to perform their duty properly. The first call to
gtk_radio_button_new_with_label() or gtk_radio_button_new_with_label()
should pass NIL as the first argument. Then create a group using:
FUNCTION gtk_radio_button_group( radio_button :
pGtkRadioButton ): pGSList;
The important thing to remember is that gtk_radio_button_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 gtk_radio_button_new() or gtk_radio_button_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. This form is used in the example to create the third button:
button2 := gtk_radio_button_new_with_label(
gtk_radio_button_group(GTK_RADIO_BUTTON(button1)), 'button2' );
It is also a good idea to explicitly set which button should be the default depressed button with:
PROCEDURE
gtk_toggle_button_set_active( toggle_button :
pGtkToggleButton ;
state : gint );
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 and looks like this:
{ Converted from C to Pascal by Thomas E. Payne }
PROGRAM radio_buttons;
USES gtk, gdk, glib;
{ -------------------------------------close_application---------------------------- }
PROCEDURE close_application( widget : pGtkWidget;
event : pGdkEvent;
data : gpointer);
BEGIN
gtk_main_quit();
END;
{ -------------------------------------close_application---------------------------- }
{ -------------------------------------Global Variables:---------------------------- }
VAR
window, box1, box2, button, separator : pGtkWidget;
group : pGSList;
{ ------------------------------------Main Program----------------------------- }
BEGIN
gtk_init(@argc,@argv);
window := gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_signal_connect(GTK_OBJECT(window), 'delete_event',
GTK_SIGNAL_FUNC(@close_application), NIL);
gtk_window_set_title(GTK_WINDOW(window), 'radio buttons');
gtk_container_set_border_width(GTK_CONTAINER(window), 0);
box1 := gtk_vbox_new(FALSE, 0);
gtk_container_add(GTK_CONTAINER(window), box1);
gtk_widget_show(box1);
box2 := gtk_vbox_new(FALSE, 10);
gtk_container_set_border_width(GTK_CONTAINER(box2), 10);
gtk_box_pack_start(GTK_BOX(box1), box2, TRUE,
TRUE, 0);
gtk_widget_show(box2);
button := gtk_radio_button_new_with_label(NIL,
'Button1');
gtk_box_pack_start(GTK_BOX(box2), button, TRUE,
TRUE, 0);
gtk_widget_show(button);
group := gtk_radio_button_group(GTK_RADIO_BUTTON(button));
button := gtk_radio_button_new_with_label(group, 'Button2');
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button),
TRUE);
gtk_box_pack_start(GTK_BOX(box2), button, TRUE,
TRUE, 0);
gtk_widget_show(button);
button := gtk_radio_button_new_with_label(
gtk_radio_button_group(GTK_RADIO_BUTTON(button)),
'Button3');
gtk_box_pack_start(GTK_BOX(box2), button, TRUE,
TRUE, 0);
gtk_widget_show(button);
separator := gtk_hseparator_new;
gtk_box_pack_start(GTK_BOX(box1), separator, FALSE,
TRUE, 0);
gtk_widget_show(separator);
box2 := gtk_vbox_new(FALSE, 10);
gtk_container_set_border_width(GTK_CONTAINER(box2), 10);
gtk_box_pack_start(GTK_BOX(box1), box2, FALSE,
TRUE, 0);
gtk_widget_show(box2);
button := gtk_button_new_with_label('Close');
gtk_signal_connect_object(GTK_OBJECT(button), 'clicked',
GTK_SIGNAL_FUNC(@close_application), GTK_OBJECT(window));
gtk_box_pack_start(GTK_BOX(Box2), button, TRUE,
TRUE, 0);
{ Pre-select radio button Button2 }
GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
gtk_widget_grab_default(button);
gtk_widget_show(button);
gtk_widget_show(window);
gtk_main();
END.
{ --------------------------------Main Program------------------------------- }
[Previous] [Contents] [Next]