An example that has a preferences window with three tabs and an alternate window that can come up when the user exits is shown below.
The file using the C language bindings to GTK to make this interface layout into a functioning program is below.
/* This program shows a preferences window, which contains
* RadioButtons, a color selection widget, and a font selection
* widget. It does manual signal connection the gtk way (as opposed
* to either the automatic libglade way or the manual libglade way,
* the latter of which is sort of ugly IMO), pops up a (modal) window
* to confirm whether the user really wants to exit, demonstrates how
* to use pango markup, and shows how to manually free the memory
* used by widgets when they're not wanted any more.
*/
#include <gtk/gtk.h>
#include <glade/glade.h>
#include <stdio.h>
/* A bunch of data to be set and used in the callbacks */
typedef struct _QuitData QuitData;
struct _QuitData
{
GtkFontSelection* font_selection;
GtkDialog * annoying_confirmation_window;
GtkLabel * warning_label;
GdkColor color;
gchar location[10];
};
void
new_location_selected (GtkWidget *widget, gpointer data)
{
QuitData * quit_choices = (QuitData*) data;
/* Copy the widget name to quit_choices->location */
snprintf(quit_choices->location, 10, gtk_widget_get_name (widget));
}
void
new_color_selected (GtkWidget *widget, gpointer data)
{
QuitData * quit_choices = (QuitData*) data;
gtk_color_selection_get_current_color (GTK_COLOR_SELECTION (widget),
&quit_choices->color);
}
/* This simple helper function which converts an RGB triplet (which is
* part of a GdkColor) into a "#RRGGBB" string.
*/
void
convert_color_to_string (GdkColor * color, gchar * color_string)
{
color_string[0] = '#';
/* GdkColor stores its red, green, and blue as 0..65536 instead of 0..256 */
sprintf(&color_string[1], "%.2X", color->red /256);
sprintf(&color_string[3], "%.2X", color->green/256);
sprintf(&color_string[5], "%.2X", color->blue /256);
}
/* This makes the confirmation window pop up to ask the user if they really
* want to quit. The message to the user changes slightly depending on the
* preferences they had set.
*/
void
time_to_quit (GtkWidget *widget, gpointer data)
{
QuitData * quit_choices = (QuitData*) data;
/* Create a color string of the form #RRGGBB, where R, G, & B are hex digits */
gchar color_string[8];
convert_color_to_string(&quit_choices->color, color_string);
/* Get the warning message, and find where we want to modify it */
const gchar * message = gtk_label_get_label (quit_choices->warning_label);
gchar * end_span = g_strrstr (message, "span");
gchar * beg_span = g_strrstr_len (message, end_span-message, "span");
/* Now, modify the string--namely the second span markup stuff */
GString * new_message = g_string_new(NULL);
g_string_append_len(new_message, message, beg_span-message-1);
g_string_append_printf(new_message,
"<span font_desc=\"%s\" foreground=\"%s\">%s</span>",
gtk_font_selection_get_font_name (quit_choices->
font_selection),
color_string,
quit_choices->location);
g_string_append_printf(new_message, "%s", end_span+5);
/* Update the label and free our temporary string */
gtk_label_set_label (quit_choices->warning_label, new_message->str);
g_string_free (new_message, TRUE);
/* Make sure the confirmation window is shown */
gtk_widget_show (GTK_WIDGET (quit_choices->annoying_confirmation_window));
}
/* This exists simply because delete_event and clicked signals require different
* numbers of arguments and return types. This just calls time_to_quit.
*/
gboolean
time_to_quit_dumb_events (GtkWidget *widget, GdkEvent *event, gpointer data)
{
time_to_quit (widget, data);
return TRUE;
}
/* clean_up merely frees the data being passed around to all the callbacks */
void
clean_up (GtkWidget *widget, gpointer data)
{
QuitData * quit_choices = (QuitData*) data;
g_free(quit_choices);
}
void
hook_up_callbacks_and_set_defaults (GladeXML * the_widgets)
{
GtkWidget *widget;
/* Get the preferences and confirmation windows */
GtkWidget *preferences = glade_xml_get_widget (the_widgets, "MainWindow");
GtkWidget *are_you_sure = glade_xml_get_widget (the_widgets, "ConfirmClose");
GtkWidget *font_sel = glade_xml_get_widget (the_widgets, "FontSelection");
/* FIRST, allocate some data needed by callbacks and initialize some of it. */
QuitData * the_data = (QuitData*)malloc (sizeof (QuitData));
the_data->font_selection = GTK_FONT_SELECTION (font_sel);
the_data->annoying_confirmation_window = GTK_DIALOG (are_you_sure);
sprintf(the_data->location, "undefined");
/* SECOND, we handle setting up defaults and connect callbacks for
* the confirmation window.
*/
/* Have the delete event (window close) do nothing */
g_signal_connect (G_OBJECT (are_you_sure), "delete_event",
G_CALLBACK (gtk_true), NULL);
/* Have the cancel button hide the confirmation window */
widget = glade_xml_get_widget (the_widgets, "CancelButton");
g_signal_connect_swapped (G_OBJECT (widget), "clicked",
G_CALLBACK (gtk_widget_hide),
G_OBJECT (are_you_sure));
/* Have the "Exit anyway" button close everything */
widget = glade_xml_get_widget (the_widgets, "ExitButton");
g_signal_connect (G_OBJECT (widget), "clicked",
G_CALLBACK (gtk_main_quit), NULL);
/* Get the warning label so we can modify it later. */
GtkWidget *info_label = glade_xml_get_widget (the_widgets, "InfoLabel");
the_data->warning_label = GTK_LABEL (info_label);
/* Make the window start hidden */
gtk_widget_hide (are_you_sure);
/* THIRD, we handle setting up defaults and connect callbacks for
* the preferences window.
*/
/* Keep track of when the location changes */
widget = glade_xml_get_widget (the_widgets, "Here");
g_signal_connect (G_OBJECT (widget), "toggled",
G_CALLBACK (new_location_selected),
(gpointer)the_data);
widget = glade_xml_get_widget (the_widgets, "There");
g_signal_connect (G_OBJECT (widget), "toggled",
G_CALLBACK (new_location_selected),
(gpointer)the_data);
/* Make 'There' be the default; note this also emits the toggled signal */
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), TRUE);
/* Keep track of when the color changes */
widget = glade_xml_get_widget (the_widgets, "ColorSelection");
g_signal_connect (G_OBJECT (widget), "color_changed",
G_CALLBACK (new_color_selected),
(gpointer)the_data);
/* Set the default to whatever the ColorSelection widget defaults to */
gtk_color_selection_get_current_color (GTK_COLOR_SELECTION (widget),
&the_data->color);
/* Turn off the opacity slider */
gtk_color_selection_set_has_opacity_control (GTK_COLOR_SELECTION (widget),
FALSE);
/* Have the quit button show the confirmation window */
widget = glade_xml_get_widget (the_widgets, "QuitButton");
g_signal_connect (G_OBJECT (widget), "clicked",
G_CALLBACK (time_to_quit),
(gpointer)the_data);
/* Have the delete event (window close) also show the confirmation window */
g_signal_connect (G_OBJECT (preferences), "delete_event",
G_CALLBACK (time_to_quit_dumb_events),
(gpointer)the_data);
/* Have the destroy signal call the cleanup callback, to free the_data */
g_signal_connect (G_OBJECT (preferences), "destroy",
G_CALLBACK (clean_up),
(gpointer)the_data);
}
int
main (int argc, char **argv)
{
GladeXML *all_da_widgets;
gtk_init (&argc, &argv);
/* load the interface */
all_da_widgets = glade_xml_new ("example-4.glade", NULL, NULL);
/* Connect all the signals to the appropriate callbacks */
hook_up_callbacks_and_set_defaults (all_da_widgets);
/* start the event loop */
gtk_main ();
/* Free the memory used by the widgets we're no longer using */
gtk_widget_destroy( glade_xml_get_widget (all_da_widgets, "MainWindow"));
gtk_widget_destroy( glade_xml_get_widget (all_da_widgets, "ConfirmClose"));
return 0;
}
The files using the C++ language bindings to GTK to make this interface layout into a functioning program are below. First, the PreferencesWindow.h header file:
#ifndef PREFERENCES_WINDOW_H
#define PREFERENCES_WINDOW_H
#include <libglademm.h>
#include <gtkmm.h>
class PreferencesWindow : public Gtk::Window
{
public:
PreferencesWindow(BaseObjectType* cobject,
const Glib::RefPtr<Gnome::Glade::Xml>& glade_xml);
virtual ~PreferencesWindow();
protected:
virtual bool on_delete_event(GdkEventAny* event);
void cancel_button_pressed();
void new_location_selected(Glib::ustring location);
void new_color_selected();
void time_to_quit();
Glib::ustring d_location;
Gtk::ColorSelection* d_color_sel;
Gtk::FontSelection* d_font_sel;
Gdk::Color d_color;
Gtk::Dialog* d_confirm_close;
Gtk::Label* d_warning_label;
};
#endif // PREFERENCES_WINDOW_H
Next is the PreferencesWindow.cc file:
#include "PreferencesWindow.h"
#include <iostream>
using namespace std;
static bool
do_nothing(GdkEventAny* event)
{
// Return true to signify that this event has been fully handled
return true;
}
PreferencesWindow::PreferencesWindow(
BaseObjectType* base_object,
const Glib::RefPtr<Gnome::Glade::Xml>& glade_xml)
: Gtk::Window(base_object)
{
//
// FIRST, get the widgets I want to track
//
// Get the widgets I'm interested in
glade_xml->get_widget("ColorSelection", d_color_sel);
glade_xml->get_widget("FontSelection", d_font_sel);
glade_xml->get_widget("ConfirmClose", d_confirm_close);
glade_xml->get_widget("InfoLabel", d_warning_label);
// Initialize the color and location to "not-yet-known" values
d_location = "undefined";
d_color.set_rgb(0,0,0);
//
// SECOND, we handle setting up defaults and connect callbacks for
// the confirmation window.
//
// Have the confirm-closing window's delete event (window close) do nothing
d_confirm_close->signal_delete_event().connect(sigc::ptr_fun(&do_nothing));
// Have the cancel button hide the confirmation window
Gtk::Button* cancel_button;
glade_xml->get_widget("CancelButton", cancel_button);
cancel_button->signal_clicked().connect(
sigc::mem_fun(*this, &PreferencesWindow::cancel_button_pressed) );
// Have the "Exit anyway" button hide the Preferences window (which has the
// result of closing everything
Gtk::Button* exit_button;
glade_xml->get_widget("ExitButton", exit_button);
exit_button->signal_clicked().connect(
sigc::mem_fun(*this, &Gtk::Widget::hide));
// Make the confirm-closing window start hidden
d_confirm_close->hide();
//
// THIRD, we handle setting up defaults and connect callbacks for
// the preferences window.
//
// Keep track of when the location changes
Gtk::CheckButton* here =
dynamic_cast<Gtk::CheckButton*>(glade_xml->get_widget("Here"));
here->signal_toggled().connect(
sigc::bind<Glib::ustring>(
sigc::mem_fun(*this, &PreferencesWindow::new_location_selected),
"Here")
);
Gtk::CheckButton* there =
dynamic_cast<Gtk::CheckButton*>(glade_xml->get_widget("There"));
there->signal_toggled().connect(
sigc::bind<Glib::ustring>(
sigc::mem_fun(*this, &PreferencesWindow::new_location_selected),
"There")
);
// Make 'There' be the default; note this also emits the toggled signal
there->set_active();
// Keep track of when the color changes
d_color_sel->signal_color_changed().connect(
sigc::mem_fun(*this, &PreferencesWindow::new_color_selected));
// Set the default to whatever the ColorSelection widget defaults to
d_color = d_color_sel->get_current_color();
// Turn off the opacity slider
d_color_sel->set_has_opacity_control(false);
// Have the quit button show the confirmation window
Gtk::Button* quit_button =
dynamic_cast<Gtk::Button*>(glade_xml->get_widget("QuitButton"));
quit_button->signal_clicked().connect(
sigc::mem_fun(*this, &PreferencesWindow::time_to_quit));
// Delete event taken care of by overriding the on_delete_event function...
// Destroy even taken care of by destructor...
}
PreferencesWindow::~PreferencesWindow()
{
}
void
PreferencesWindow::cancel_button_pressed()
{
d_confirm_close->hide();
}
void
PreferencesWindow::new_location_selected(Glib::ustring location)
{
d_location = location;
}
void
PreferencesWindow::new_color_selected()
{
d_color = d_color_sel->get_current_color();
}
// This simple helper function which converts an RGB triplet (which is
// part of a GdkColor) into a "#RRGGBB" string.
static Glib::ustring
convert_color_to_string (Gdk::Color & color)
{
char color_string[8];
color_string[0] = '#';
// Gdk::Color stores its red, green, and blue as 0..65536 instead of 0..256
sprintf(&color_string[1], "%.2X", color.get_red() /256);
sprintf(&color_string[3], "%.2X", color.get_green()/256);
sprintf(&color_string[5], "%.2X", color.get_blue() /256);
return color_string;
}
void
PreferencesWindow::time_to_quit()
{
// Create a color string of the form #RRGGBB, where R, G, & B are hex digits
Glib::ustring color = convert_color_to_string(d_color);
// Get the warning message, and find where we want to modify it
Glib::ustring message = d_warning_label->get_label();
Glib::ustring::size_type end_span = message.rfind("span");
Glib::ustring::size_type beg_span = message.rfind("span", end_span-1);
// Set up the replacement text. Note that we basically found the two
// places where the letter 's' in span was in a string of the form
// <span bla bla bla...>yadda yadda yadda...</span>
// Now, we are just replacing all the stuff between those two s's.
// (And yeah, I should really check out the String Composition)
// (Library at http://www.cs.auc.dk/~olau/compose/ but I'm )
// (lazy and will use sprintf for now. )
char replacement_text[500];
sprintf(replacement_text, "span font_desc=\"%s\" foreground=\"%s\">%s</",
d_font_sel->get_font_name().c_str(),
color.c_str(),
d_location.c_str());
// Make the new message and update the label
message.replace(beg_span, end_span-beg_span, replacement_text);
d_warning_label->set_label(message);
// Make sure the confirmation window is shown
d_confirm_close->show();
}
bool
PreferencesWindow::on_delete_event(GdkEventAny* event)
{
time_to_quit();
// No other handlers need be called...
return true;
}
And finally, here is the example-4.cc file which glues everything together:
/* This program shows a preferences window, which contains
* RadioButtons, a color selection widget, and a font selection
* widget. It pops up a (modal) window to confirm whether the user
* really wants to exit, demonstrates how to use pango markup, and
* shows how to manually free the memory used by widgets when they're
* not wanted any more.
*/
#include <gtkmm.h>
#include <libglademm/xml.h>
#include "PreferencesWindow.h"
using namespace std;
int
main (int argc, char *argv[])
{
Glib::RefPtr<Gnome::Glade::Xml> all_da_widgets;
Gtk::Main kit(argc, argv);
// load the interface
all_da_widgets = Gnome::Glade::Xml::create("example-4.glade");
// Get the main window
PreferencesWindow* window;
all_da_widgets->get_widget_derived("MainWindow", window);
// start the event loop
Gtk::Main::run( *window );
return 0;
}
The file using the perl language bindings to GTK to make this interface layout into a functioning program is below.
#!/usr/bin/perl -w
# This program shows a preferences window, which contains
# RadioButtons, a color selection widget, and a font selection widget.
# It demonstrates the use of anonymous perl subroutines to serve as
# callbacks including using them as a method of passing extra or
# different parameters to the real callback, pops up a (modal) window
# to confirm whether the user really wants to exit, and demonstrates
# how to use pango markup.
use Glib qw(TRUE FALSE);
use Gtk2 '-init';
use Gtk2::GladeXML;
use POSIX qw(floor);
# Just to be pedantic...
use strict;
use vars qw($all_da_widgets);
sub convert_color_to_string
{
my ($color) = @_;
my ($color_string);
$color_string = "#"
. sprintf("%.2X", floor($color->red /256))
. sprintf("%.2X", floor($color->green/256))
. sprintf("%.2X", floor($color->blue /256));
return $color_string;
}
sub time_to_quit
{
my ($font_selection, $confirm_close, $warning_label, $color, $location) = @_;
my ($color_string, $message, $replacement_text);
# Create a color string of the form #RRGGBB, where R, G, & B are hex digits
$color_string = &convert_color_to_string($color);
# Get the warning message
$message = $warning_label->get_label;
# Set up the replacement text.
$replacement_text = '<span '
. 'font_desc="' . $font_selection->get_font_name . '" '
. 'foreground="' . $color_string . '">'
. $location
. '</span>';
# Make the new message
$message =~ s/to <span.*<\/span>/to $replacement_text/;
# Update the label
$warning_label->set_label($message);
# Make sure the confirmation window is shown
$confirm_close->show;
}
sub hook_up_callbacks_and_set_defaults
{
my ($the_widgets) = @_;
my ($preferences, $are_you_sure, $font_sel, $info_label, $location);
# Get the preferences and confirmation windows
$preferences = $the_widgets->get_widget('MainWindow');
$are_you_sure = $the_widgets->get_widget('ConfirmClose');
$font_sel = $the_widgets->get_widget('FontSelection');
# First, we handle setting up defaults and connect callbacks for
# the confirmation window.
# Have the delete event (window close) do nothing
$are_you_sure->signal_connect( delete_event => sub { return TRUE; } );
# Have the cancel button hide the confirmation window
my $cancel_button = $the_widgets->get_widget('CancelButton');
$cancel_button->signal_connect( clicked => sub {$are_you_sure->hide;} );
# Have the "Exit anyway" button close everything
my $exit_button = $the_widgets->get_widget('ExitButton');
$exit_button->signal_connect( clicked => sub {Gtk2->main_quit;} );
# Get the warning label so we can modify it later.
$info_label = $the_widgets->get_widget('InfoLabel');
# Make the confirm-close window starts hidden
$are_you_sure->hide;
# Second, we handle setting up defaults and connect callbacks for
# the preferences window.
# Keep track of when the location changes
my $here_toggle = $the_widgets->get_widget('Here');
$here_toggle->signal_connect( toggled =>
sub{$location = $here_toggle->get_name;}
);
my $there_toggle = $the_widgets->get_widget('There');
$there_toggle->signal_connect( toggled =>
sub{$location = $there_toggle->get_name;}
);
# Make 'There' be the default; note this also emits the toggled signal
$there_toggle->set_active(TRUE);
# Get the color selection widget
my $color_widget = $the_widgets->get_widget('ColorSelection');
# Set the default to whatever the ColorSelection widget defaults to
my $color = $color_widget->get_current_color;
# Keep track of when the color changes
$color_widget->signal_connect( color_changed =>
sub {$color=$color_widget->get_current_color;}
);
# Turn off the opacity slider
$color_widget->set_has_opacity_control(FALSE);
# Have the quit button show the confirmation window
my $quit_button = $the_widgets->get_widget('QuitButton');
$quit_button->signal_connect( clicked =>
sub {
&time_to_quit($font_sel, $are_you_sure, $info_label, $color, $location);
}
);
# Have the delete event (window close) also show the confirmation window
$preferences->signal_connect( delete_event =>
sub {
&time_to_quit($font_sel, $are_you_sure, $info_label, $color, $location);
return TRUE;
}
);
}
# load the interface
$all_da_widgets = Gtk2::GladeXML->new('example-4.glade');
# Connect all the signals to the appropriate callbacks
&hook_up_callbacks_and_set_defaults($all_da_widgets);
# start the event loop
Gtk2->main;
The file using the python language bindings to GTK to make this interface layout into a functioning program is below.
#!/usr/bin/env python
# This program shows a preferences window, which contains
# RadioButtons, a color selection widget, and a font selection widget.
# It does manual signal connection the pygtk way (as opposed to either
# the autoconnect libglade method), pops up a (modal) window to
# confirm whether the user really wants to exit, and demonstrates how
# to use pango markup.
import gtk
import gtk.glade
import re
class PreferencesWindow:
def __init__(self, glade_file):
# Parse the glade file
self.all_da_widgets = gtk.glade.XML(glade_file)
#
# FIRST, get the widgets I want to track
#
# Get the widgets I'm interested in
self.preferences = self.all_da_widgets.get_widget("MainWindow")
self.color_sel = self.all_da_widgets.get_widget("ColorSelection")
self.font_sel = self.all_da_widgets.get_widget("FontSelection")
self.confirm_close = self.all_da_widgets.get_widget("ConfirmClose")
self.warning_label = self.all_da_widgets.get_widget("InfoLabel")
# Initialize the color and location to "not-yet-known" values
self.location = "undefined"
self.color = gtk.gdk.Color(0,0,0)
#
# SECOND, we handle setting up defaults and connect callbacks for
# the confirmation window.
#
# Have the confirm-closing window's delete event (window close) do nothing
self.confirm_close.connect("delete_event", self.do_nothing);
# Have the cancel button hide the confirmation window
cancel_button = self.all_da_widgets.get_widget("CancelButton")
cancel_button.connect("clicked", self.cancel_button_pressed)
# Have the "Exit anyway" button close everything
exit_button = self.all_da_widgets.get_widget("ExitButton")
exit_button.connect("clicked", gtk.mainquit)
# Make the confirm-closing window start hidden
self.confirm_close.hide()
#
# THIRD, we handle setting up defaults and connect callbacks for
# the preferences window.
#
# Keep track of when the location changes
here = self.all_da_widgets.get_widget("Here")
here.connect("toggled", self.new_location_selected, "Here")
there = self.all_da_widgets.get_widget("There")
there.connect("toggled", self.new_location_selected, "There")
# Make 'There' be the default; note this also emits the toggled signal
there.set_active(gtk.TRUE)
# Keep track of when the color changes
self.color_sel.connect("color_changed", self.new_color_selected)
# Set the default to whatever the ColorSelection widget defaults to
self.color = self.color_sel.get_current_color()
# Turn off the opacity slider
self.color_sel.set_has_opacity_control(gtk.FALSE)
# Have the quit button show the confirmation window
quit_button = self.all_da_widgets.get_widget("QuitButton")
quit_button.connect("clicked", self.time_to_quit)
# Delete event taken care of by overriding the on_delete_event function...
self.preferences.connect("delete_event", self.call_time_to_quit)
def do_nothing(self,confirmation_dialog,event):
# Return true to signify that this event has been fully handled
return gtk.TRUE
def cancel_button_pressed(self, cancel_button):
self.confirm_close.hide()
def new_location_selected(self, radio_button, location):
self.location = location
def new_color_selected(self, color_selection_widget):
self.color = self.color_sel.get_current_color()
def convert_color_to_string(self, color):
"""
This simple helper function which converts an RGB triplet (which is
part of a Gdk.Color) into a "#RRGGBB" string.
"""
color_string = "#%.2X%.2X%.2X" % \
(color.red/256,color.green/256,color.blue/256)
return color_string
def time_to_quit(self, ignored_widget):
color_string = self.convert_color_to_string(self.color);
# Get the warning message
message = self.warning_label.get_label()
# Set up the replacement text.
replacement_text = '<span ' \
+ 'font_desc="' + self.font_sel.get_font_name() + '" ' \
+ 'foreground="' + color_string + '">' \
+ self.location \
+ '</span>'
# Make the new message
old_regex = re.compile(r'to <span.*<\/span>')
message = old_regex.sub("to " + replacement_text, message)
# Update the label
self.warning_label.set_label(message)
# Make sure the confirmation window is shown
self.confirm_close.show()
def call_time_to_quit(self, window, event):
self.time_to_quit(window)
# No other handlers need be called...
return gtk.TRUE
# Create the PreferencesWindow
window = PreferencesWindow("example-4.glade")
# start the event loop
gtk.main()