Changing the Basic Window
In the last lesson, we learned the basic structure of a GTK Window. We also learned how to set the text that appears in the title bar. This time, we’ll go a small step further; we’ll change the window size as well as its position, set an icon for our program, create a basic menu, and finally destroy the program properly,
Center and Set Window Size
The code to do this is:
gtk_window_set_default_size(GTK_WINDOW(window), 400, 300);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
You may have already guessed, 400 is the width and 300 is the height, which we will set as the default size of our window. For the initial position, you may have noticed that one of the parameters is the constant GTK_WIN_POS_CENTER, which, you guessed it, centers the window.
Set Window Icon
The following is the function used for setting the window icon, using 2 parameters: the window, and a Gdk Pixelbuffer object.
gtk_window_set_icon(GTK_WINDOW(window), pixbuf);
The problem is converting your desired graphic into something useable as an icon. Here we borrow a function from Zetcode’s First Programs tutorial (see References) which accepts a filename as a parameter and returns a GdkPixbuf object:
GdkPixbuf *create_pixbuf(const gchar * filename)
{
GdkPixbuf *pixbuf;
GError *error = NULL;
pixbuf = gdk_pixbuf_new_from_file(filename, &error);
if(!pixbuf) {
fprintf(stderr, "%s\n", error->message);
g_error_free(error);
}
return pixbuf;
}
The gdk_pixbuf_new_from_file() functions takes two parameters–the filename of the image, and a GError object where it’ll put an error message if something goes wrong. It automatically detects the filetype of the file and convert it into a GdkPixbuf object.
We make a call to the create_pixbuf() function as follows:
gtk_window_set_icon(GTK_WINDOW(window), create_pixbuf("mylogo.gif"));
You can replace mylogo.gif with your own GIF, PNG, BMP, or JPG file.
Create a Menu
As mentioned in the first post, GTK uses hierarchies and containers quite extensively, and menus and submenus are no different. A menu is basically a container where you can put in items or even other containers i.e. submenus. The basic logic behind putting a menu into your GTK application is that the window itself is a container, and you attach a box container that will hold your menubar (itself a container), which will hold your menu (another container), which holds your menu items or even submenus (yet another container).
I did not grasp this relationship immediately when I studied it, so here I present the sample code relating to that hierarchical relationship. It may not be the best coding standard (personally, I prefer to put all my variable declarations together at the beginning), but for the sake of easier understanding, we’ll put relevant objects as they are needed and being built.
First, we create the menu items like so by creating new GtkWidgets and initializing them like so:
//Initialize the File menu items
GtkWidget *file;
GtkWidget *open;
GtkWidget *quit;
file = gtk_menu_item_new_with_label("File");
open = gtk_menu_item_new_with_label("Open...");
quit = gtk_menu_item_new_with_label("Quit");
Next, we create the File menu to hold our menu items:
//Initialize File menu and put in the menu items
GtkWidget *filemenu;
filemenu = gtk_menu_new();
gtk_menu_item_set_submenu(GTK_MENU_ITEM(file), filemenu);
gtk_menu_shell_append(GTK_MENU_SHELL(filemenu), open);
gtk_menu_shell_append(GTK_MENU_SHELL(filemenu), quit);
Take note that we are actually setting first item (file) as a submenu. The open and quit menu items are then added to the filemenu using the gtk_menu_shell_append() function.
Next, we put our File menu into the menu bar:
//Initialize the menu bar and put in the File menu we created
GtkWidget *menubar;
menubar = gtk_menu_bar_new();
gtk_menu_shell_append(GTK_MENU_SHELL(menubar), file);
Now that everything’s almost set, we have to put our menubar on to the window itself:
//Put the menu bar into the window
GtkWidget *vbox;
vbox = gtk_vbox_new(FALSE, 0);
gtk_container_add(GTK_CONTAINER(window), vbox);
gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 3);
vbox is initialized into a GtkVBox object, which is a vertical container box. No, I don’t know either why they called a horizontal-looking box a vertical container box. I guess we’ll learn that soon enough. Anyway, gtk_container_add() adds the vbox container to the window (our big, main container) and gtk_box_pack_start() packs our menu bar into our box.
Closing the Window Properly
In our last lesson, although clicking on the close button makes the window disappear, it doesn’t actually destroy the program. We have to connect the destroy signal to the gtk_main_quit function().
g_signal_connect_swapped(G_OBJECT(window), "destroy", G_CALLBACK(gtk_main_quit), NULL);
If you noticed, the Quit button doesn’t close our program, even if we have just set the callback for the destroy signal event. That’s because the Quit menu item is just a menu item like Open. Until we tell it what to do, it does nothing. The signal we actually have to listen for is not the destroy signal, but the activate signal, which is sent whenever you click on a menu item. So we connect the quit object’s “activate” signal to the gtk_main_quit() function, which we already know will terminate our program nicely.
g_signal_connect(G_OBJECT(quit), "activate", G_CALLBACK(gtk_main_quit), NULL);
Summing Up
Our program which contains the previous Hello Wolrd lesson, as well as the new window size and position, icon, menu, and proper destruction on exit should look something similar to this:
#include <gtk/gtk.h>
GdkPixbuf *create_pixbuf(const gchar * filename)
{
GdkPixbuf *pixbuf;
GError *error = NULL;
pixbuf = gdk_pixbuf_new_from_file(filename, &error);
if(!pixbuf) {
fprintf(stderr, "%s\n", error->message);
g_error_free(error);
}
return pixbuf;
}
int main (int argc, char **argv) {
gtk_init(&argc, &argv);
//Basic Window Setup
GtkWidget *window;
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_window_set_title (GTK_WINDOW (window), "Hello, World");
gtk_window_set_default_size(GTK_WINDOW(window), 400, 300);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_window_set_icon(GTK_WINDOW(window), create_pixbuf("mylogo.gif"));
//Initialize the File menu items
GtkWidget *file;
GtkWidget *open;
GtkWidget *quit;
file = gtk_menu_item_new_with_label("File");
open = gtk_menu_item_new_with_label("Open...");
quit = gtk_menu_item_new_with_label("Quit");
//Initialize File menu and put in the menu items
GtkWidget *filemenu;
filemenu = gtk_menu_new();
gtk_menu_item_set_submenu(GTK_MENU_ITEM(file), filemenu);
gtk_menu_shell_append(GTK_MENU_SHELL(filemenu), open);
gtk_menu_shell_append(GTK_MENU_SHELL(filemenu), quit);
//Initialize the menu bar and put in the File menu we created
GtkWidget *menubar;
menubar = gtk_menu_bar_new();
gtk_menu_shell_append(GTK_MENU_SHELL(menubar), file);
//Put the menu bar into the window
GtkWidget *vbox;
vbox = gtk_vbox_new(FALSE, 0);
gtk_container_add(GTK_CONTAINER(window), vbox);
gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 3);
//All set. Display all.
gtk_widget_show_all (window);
//Connect the destroy signal to the gtk_main_quit() function
g_signal_connect_swapped(G_OBJECT(window), "destroy", G_CALLBACK(gtk_main_quit), NULL);
//Connect the "quit" object's "activate" signal to the gtk_main_quit() function
g_signal_connect(G_OBJECT(quit), "activate", G_CALLBACK(gtk_main_quit), NULL);
gtk_main();
return 0;
}
References:
