GTK 2.0 Tutorial
From Lapis Wiki
Bu makale bir Taslak tır. İçeriğini geliştirerek Lapis-Wiki'ye yardımcı olabilirsiniz.
| Not: Bu sayfadaki bilgiler http://www.gtk.org/tutorial/index.html sayfasından çevrilmektedir. Dilediğiniz kısmı çevirerek siz de yardımcı olabilirsiniz. |
Eğitmen Temini
Bu eğitmenin aynısını(ing) http://www.gtk.org/tutorial adresinde bulabilirsiniz. Eğitmenin farklı formatlarda paketlenmiş halini ise ftp://ftp.gtk.org/pub/gtk/tutorial adresinden temin edilebilirsiniz. Bu paket, birincil olarak internete bağlı olmadan da belgeye erişimek ve yazdırma niyetinde olanlar için hazırlanmıştır.
GİRİŞ
GTK (GIMP Toolkit/Araç Seti), grafiksel kullanıcı arayüzü oluşturmak için bir kütüphanedir. LGPL lisansı ile lisanslanmıştır; dolayısıyla da açık, özgür ve hatta özgür olmayan ticari yazılımları dahi hiçbir lisans veya telif hakkı ücreti ödemeden GTK+ kullanarak geliştirebilirsiniz.
GIMP toolkit denmesinin sebebi, esasında GNU Görüntü İşleme Programının(GIMP) geliştirilmesi için yazılmış olmasıdır. Bununla birlikte GTK, GNU Network Object Model Environment (GNOME) projesi de dahil olmak üzere şu ana kadar pek çok sayıda yazılım projesinde kullanılmıştır. GTK, temel olarak önemli pencereleme fonksiyonlarına(X windows sistemlerindeki Xlib) ve gdk-pixbuf gibi düşük seviyeli sarmalama fonksiyonlarına erişim sağlayana ve kullanıcı taraflı görüntü işleme kütüphanesi olan GDK(GIMP Drawing Kit) üzerine bina edilmiştir.
Önde gelen GTK yazarları:
- Peter Mattis petm@xcf.berkeley.edu
- Spencer Kimball spencer@xcf.berkeley.edu
- Josh MacDonald jmacd@xcf.berkeley.edu
GTK'nın şu an için yönetimini üstlenenler:
- Owen Taylor otaylor@redhat.com
- Tim Janik timj@gtk.org
Aslında GTK bir nesne yönelimli uygulama programcıları arayüzüdür. C programlama dili ile yazılmış olmasına rağmen sınıflar ve geri çağırma fonksiyonları mantığı(fonksiyon göstericileri) da uygulamasında kullanılır.
Ayrıca üçüncü bir bileşen olarak, bazı standart çağrıların değiştirilmesini ve bağlı listelerin yönetilmesinde kullanılan bazı fonksiyonları içeren Glib'den bahsedebiliriz. Bazı unix sistemlerde geçerli olmasına rağmen bazılarında geçerli olmayan veya da standart olmayan fonksiyonların(örn: g_strerror() gibi) değişimi işlemini üstlenen Glib sayesinde GTK nın taşınabilirlik özelliği artmıştır. Bazı değiştirme fonksiyonları da libc versiyonun arttırılmasını sağlar(hata ayıklama yeteneğini arttıran g_malloc() fonksiyonu gibi).
GTK nın son bileşeni olarak Pango kütüphanesi mevcuttur, bu kütüphane sayesinde yazı çıktıları uluslararası bir duruma getirilmiştir.
Bu eğitmen, GTK için C kullanımını açıklayacaktır. GTK'nın C++, Guile, Perl, Python, TOM, Ada95, Objective C, Free Pascal, Eiffel, Java ve C# gibi C dışında da pek çok programlama dili ile bağlantısı vardır. Eğer GTK'nın bağlı olduğu bu dillerden birini kullanmak istiyorsanız öncelikle o dildeki dökümanlarına bakınız. Zira bazı durumlarda böyle dökümanlarda daha iyi açıklamalar bulabilirsiniz, ardından da bu dökümana dönebilirsiniz. Yine, bir hedef platformları da GTK olan bazı geçiş-platform API'leri mevcuttur, onların dökümanlarına da bakınız.
If you're developing your GTK application in C++, a few extra notes are in order. There's a C++ binding to GTK called GTK--, which provides a more C++-like interface to GTK; you should probably look into this instead. If you don't like that approach for whatever reason, there are two alternatives for using GTK. First, you can use only the C subset of C++ when interfacing with GTK and then use the C interface as described in this tutorial. Second, you can use GTK and C++ together by declaring all callbacks as static functions in C++ classes, and again calling GTK using its C interface. If you choose this last approach, you can include as the callback's data value a pointer to the object to be manipulated (the so-called "this" value). Selecting between these options is simply a matter of preference, since in all three approaches you get C++ and GTK. None of these approaches requires the use of a specialized preprocessor, so no matter what you choose you can use standard C++ with GTK.
This tutorial is an attempt to document as much as possible of GTK, but it is by no means complete. This tutorial assumes a good understanding of C, and how to create C programs. It would be a great benefit for the reader to have previous X programming experience, but it shouldn't be necessary. If you are learning GTK as your first widget set, please comment on how you found this tutorial, and what you had trouble with. There are also C++, Objective C, ADA, Guile and other language bindings available, but I don't follow these.
This document is a "work in progress". Please look for updates on http://www.gtk.org/.
I would very much like to hear of any problems you have learning GTK from this document, and would appreciate input as to how it may be improved. Please see the section on Contributing for further information.
Getting Started
Tabi ki ilk yapılacak şey GTK kurmaktır. GTK'nın en son sürümlerini daima ftp.gtk.org den indirebilirsiniz. Aynı zamanda GTK'nın diğer kaynaklarına http://www.gtk.org/ adresinden ulaşabilirsiniz. GTK, yapılandırma için GNU otomatik konfigürasyonunu kullanır. İndirdiğiniz tar dosyasını açtıktan hemen sonra konsolda "./configure --help" yazarsanız seçenekleri görebilirsiniz. The GTK source distribution also contains the complete source to all of the examples used in this tutorial, along with Makefiles to aid compilation.
GTK ile tanışmamızın başında, olabilecek en kolay proğram ile başlayacağız. Bu program 200x200 piksellik bir pencere oluşturacaktır. Bu programı kapatmanın şu an için tek çıkış yolu kabuk ile öldürmektir.
#include <gtk/gtk.h>
int main( int argc,
char *argv[] )
{
GtkWidget *window;
gtk_init (&argc, &argv);
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_widget_show (window);
gtk_main ();
return 0;
}
Yukardaki programi gcc kullanarak, asagidaki komut ile derleyebilirsiniz:
gcc base.c -o base `pkg-config --cflags --libs gtk+-2.0`
Derleme esnasında kullanılan alışık olmadığımız `` arasındaki seçeneklerin açıklamasını "Hello World" programımızı derlerken anlatacağız.
Şunu unutmamak gerekir ki her programımız muhakkak ki değişkenler, fonksiyonlar ve yapıların tanımlandığı gtk/gtk.h başlık dosyasını içermelidir.
Diğer satır:
gtk_init (&argc, &argv);
GTK uygulamaların tamamında kullanılacak olan gtk_init(gint *argc, gchar ***argv) fonksiyonunu çağırır. Bu fonksiyon bizler için bazı şeyleri hazırlar, varsayılan görüntü ve renk haritası gibi, ve sonrasında da gdk_init(gint *argc, gchar ***argv) fonksiyonunu çağırır. Bu fonksiyon da kullanılmak üzere kütüphaneleri programa bildirir, varsayılan sinyal işleyiciyi kurar ve komut satırınızda varsa kullandığınız argumanları kontrol edip şunlardan birinin var olup olmadığına bakar:
* --gtk-module * --g-fatal-warnings * --gtk-debug * --gtk-no-debug * --gdk-debug * --gdk-no-debug * --display * --sync * --name * --class
Bu iki satır da pencereyi oluşturur ve ekranda görünmesini sağlar.
window = gtk_window_new (GTK_WINDOW_TOPLEVEL); gtk_widget_show (window);
GTK_WINDOW_TOPLEVEL argümanı ise istediğimiz pencerenin nasıl olacağını ve yerini belirler. Bu seçenek ile normal bir pencere elde edeceğiz. Daha alt ayarları(çocuk) yapmadıysak varsayılan pencere boyutu olarak 200x200 piksel boyutunda pencere oluşturacaktır.
gtk_widget_show() fonksiyonu ile GTK artık bizim widget/elemanımızı tanımlayıp ayarlarını yaptığımızı bidirir ve ekranda gösterebileceğini söyleriz.
En son satır da GTK'nın ana işlem döngüsüne girer.
gtk_main ();
Bütün GTK uygulamalarında göreceğiniz bir diğer fonksiyon da gtk_main() dir. Program bu noktaya geldiğinde GTK artık X olaylarının(buton basımı veya karakter girme) gerçekleşmesi, sürenin dolması veya da giriş çıkış bildirimlerinin gerçekleşmesi için bekleyecektir. Bizim basit örneğimizde ise events/olaylar ihmal edildi.
GTK ile "Hello World"
Şimdi de bir eleman(buton) ile klasik Hello World programına bakalım.
#include <gtk/gtk.h>
/* Bu tekrar çağrılabilir bir fonksiyondur. Veri argümanları bu örnekte
* ihmal edilmiştir.*/
static void hello( GtkWidget *widget,
gpointer data )
{
g_print ("Hello World\n");
}
static gboolean delete_event( GtkWidget *widget,
GdkEvent *event,
gpointer data )
{
/* Eğer "delete_event" sinyal işleyiciden FALSE dönerse GTK "destroy"
* sinyalini gönderecektir. Eğer TRUE dönerse, pencerenin yok edilmesini
* istemiyorsunuz demektir. "Çıkmak istediğinizden emin misiniz?"
* tarzındaki açılır pencerelerde kullanmak için iyi bir yoldur. */
g_print ("delete event occurred\n");
/* TRUE değerini FALSE ile değiştirdiğinizde bir "delete_event" ile ana pencere yok
* edilecektir. */
return TRUE;
}
/* Diğer bir geri çağrılabilir fonksiyon */
static void destroy( GtkWidget *widget,
gpointer data )
{
gtk_main_quit ();
}
int main( int argc,
char *argv[] )
{
/* GtkWidget, widget eklemek için kullanılacak türdür*/
GtkWidget *window;
GtkWidget *button;
/* Bu fonksiyon bütün GTK programlarında çağrılır. Argümanların
* komut satırından alınıp uygulamaya dahil edilmesini sağlar. */
gtk_init (&argc, &argv);
/* yeni bir pencere oluşturur. */
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
/* Ne zaman ki pencereye "delete_event" sinyali verilir(bu sinyal
* pencere yöneticisi tarafından genellikle de "kapat" seçeneği ile
* veya pencerenin sağ üstteki "X" ile verilir). Böylece delete_event()
* fonksiyonunu çağırmış oluruz. Geri çağrılan fonksiyonlarda parametre
* olarak NULL girilir ve bu da zaten geri çağrılan fonksiyonlarda dikkate alınmaz*/
g_signal_connect (G_OBJECT (window), "delete_event",
G_CALLBACK (delete_event), NULL);
/* Burada da "destroy" event'ini bir sinyal işeyicisine bağladık
* gtk_widget_destroy() fonksiyonun pencereden çağırdığımızda veya
* "delete_event" geriçağrısına FALSE döndürdüğümüzde bu event gerçekleşecektir */
g_signal_connect (G_OBJECT (window), "destroy",
G_CALLBACK (destroy), NULL);
/* Pencerenin kenar genişliğini ayarlar. */
gtk_container_set_border_width (GTK_CONTAINER (window), 10);
/* "Hello World" etkiketli bir pencere oluşturur */
button = gtk_button_new_with_label ("Hello World");
/* Buton "clicked" sinyalini aldığında hello() fonksiyonuuna NULL parametresini
* gereçek çağıracaktır. */
g_signal_connect (G_OBJECT (button), "clicked",
G_CALLBACK (hello), NULL);
/* "clicked" sinyali ile pencerenin kapatılmasını sağlayan gtk_widget_destroy(window)
* fonksiyonu çağrılacaktır. */
g_signal_connect_swapped (G_OBJECT (button), "clicked",
G_CALLBACK (gtk_widget_destroy),
G_OBJECT (window));
/* Bu durumda bir gtk container halini almış olan pencerenin üzerine butonu ekler. */
gtk_container_add (GTK_CONTAINER (window), button);
/* En son adımda da oluşturukan yeni elemanların gösterilmesi gerekiyor. */
gtk_widget_show (button);
/* ve pencerenin gösterimi. */
gtk_widget_show (window);
/* Bütün GTK uygulamalarında gtk_main() bulunması gerekir. Program kontrolü burda sona
* erer gerçekleşecek olaylar için beklenir. */
gtk_main ();
return 0;
}
Hello World Derlemesi
Derleme için;
gcc -Wall -g helloworld.c -o helloworld `pkg-config --cflags gtk+-2.0` \
`pkg-config --libs gtk+-2.0`
kullanılır.
Burda kullanılan pkg-config programını http://www.freedesktop.org adresinden elde edebilirsiniz. Bu program derleyici için gerekli olan GTK bilgilerini tutan .pc dosyalarını okur. pkg-config --cflags gtk+-2.0 bize derleyici için gerekli bütün dizinleri, pkg-config --libs gtk+-2.0 ise derleyici için gerekli olan kütüphaneleri listeler. Bütün parametreleri kullanacağınız bir örnekte pkg-config --cflags --libs ile bütün kütüphane ve dizinleri kullanabilirsiniz.
Derleme komutundaki tek tırnak karakteri fazlasıyla önemlidir, aksi halde derleme hatası alırsınız.
Derleme komutundan programlar ile sıkça ilişkilendirilen kütüphaneler:
* The GTK library (-lgtk), the widget library, based on top of GDK.
* The GDK library (-lgdk), the Xlib wrapper.
* The gdk-pixbuf library (-lgdk_pixbuf), the image manipulation library.
* The Pango library (-lpango) for internationalized text.
* The gobject library (-lgobject), containing the type system on which GTK is based.
* The gmodule library (-lgmodule), which is used to load run time extensions.
* The GLib library (-lglib), containing miscellaneous functions; only g_print() is used in this particular example. GTK is built on top of GLib so you will always require this library. See the section on GLib for details.
* The Xlib library (-lX11) which is used by GDK.
* The Xext library (-lXext). This contains code for shared memory pixmaps and other X extensions.
* The math library (-lm). This is used by GTK for various purposes.
Theory of Signals and Callbacks
NOT: In version 2.0, the signal system has been moved from GTK to GLib, therefore the functions and types explained in this section have a "g_" prefix rather than a "gtk_" prefix. We won't go into details about the extensions which the GLib 2.0 signal system has relative to the GTK 1.2 signal system.
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 gtk_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:
gulong g_signal_connect( gpointer *object,
const gchar *name,
GCallback func,
gpointer func_data );
where the first argument is the widget which will be emitting the signal, and the second the name of the signal you wish to catch. The third is the function you wish to be called when it is caught, and the 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
void callback_func( GtkWidget *widget,
... /* other signal arguments */
gpointer callback_data );
where the first argument will be a pointer to the widget that emitted the signal, and the last a pointer to the data given as the last argument to the g_signal_connect() function 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.
Another call used in the helloworld example, is:
gulong g_signal_connect_swapped( gpointer *object,
const gchar *name,
GCallback func,
gpointer *callback_data );
g_signal_connect_swapped() is the same as g_signal_connect() except that the instance on which the signal is emitted and data will be swapped when calling the handler. So when using this function to connect signals, the callback should be of the form
void callback_func( gpointer callback_data,
... /* other signal arguments */
GtkWidget *widget);
where the object is usually a widget. We usually don't setup callbacks for g_signal_connect_swapped() however. They are usually used to call a GTK function that accepts a single widget or object as an argument, when a signal is emitted on some other object. In the helloworld example, we connect to the "clicked" signal on the button, but call gtk_widget_destroy() on the window.
If your callbacks need additional data, use g_signal_connect() instead of g_signal_connect_swapped().
Olaylar
Aşağıdaki tanımlanmış olan olaylar, bunlar X olay mekanizmasının olaylar setidir. Çağrımlar bu olaylara eklenmiş olmalıdır. Bu olaylar:
* 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:
gint callback_func( GtkWidget *widget,
GdkEvent *event,
gpointer callback_data );
GdkEvent is a C union structure whose 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 member that reflects the event being issued. The other components of the event structure will depend upon the type of the event. Possible values for the type are:
GDK_NOTHING GDK_DELETE GDK_DESTROY GDK_EXPOSE GDK_MOTION_NOTIFY GDK_BUTTON_PRESS GDK_2BUTTON_PRESS GDK_3BUTTON_PRESS GDK_BUTTON_RELEASE GDK_KEY_PRESS GDK_KEY_RELEASE GDK_ENTER_NOTIFY GDK_LEAVE_NOTIFY GDK_FOCUS_CHANGE GDK_CONFIGURE GDK_MAP GDK_UNMAP GDK_PROPERTY_NOTIFY GDK_SELECTION_CLEAR GDK_SELECTION_REQUEST GDK_SELECTION_NOTIFY GDK_PROXIMITY_IN GDK_PROXIMITY_OUT GDK_DRAG_ENTER GDK_DRAG_LEAVE GDK_DRAG_MOTION GDK_DRAG_STATUS GDK_DROP_START GDK_DROP_FINISHED GDK_CLIENT_EVENT GDK_VISIBILITY_NOTIFY GDK_NO_EXPOSE GDK_SCROLL GDK_WINDOW_STATE GDK_SETTING
So, to connect a callback function to one of these events we would use something like:
g_signal_connect (G_OBJECT (button), "button_press_event",
G_CALLBACK (button_press_callback), NULL);
This assumes that button is a Button widget. Now, when the mouse is over the button and a mouse button is pressed, the function button_press_callback() will be called. This function may be declared as:
static gboolean button_press_callback( GtkWidget *widget,
GdkEventButton *event,
gpointer data );
Note that we can declare the second argument as type GdkEventButton as we know what type of event will occur for this function to be called.
The value 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
Stepping Through Hello World
Now that we know the theory behind this, let's clarify by walking through the example helloworld program.
Here is the callback function that will be called when the button is "clicked". We ignore both the widget and the data in this example, but it is not hard to do things with them. The next example will use the data argument to tell us which button was pressed.
static void hello( GtkWidget *widget,
gpointer data )
{
g_print ("Hello World\n");
}
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.
static gboolean delete_event( GtkWidget *widget,
GdkEvent *event,
gpointer data )
{
g_print ("delete event occurred\n");
return TRUE;
}
Here is another callback function which causes the program to quit by calling gtk_main_quit(). This function tells GTK that it is to exit from gtk_main when control is returned to it.
static void destroy( GtkWidget *widget,
gpointer data )
{
gtk_main_quit ();
}
I assume you know about the main() function... yes, as with other applications, all GTK applications will also have one of these.
int main( int argc,
char *argv[] )
{
This next part declares pointers to a structure of type GtkWidget. These are used below to create a window and a button.
GtkWidget *window;
GtkWidget *button;
Here is our gtk_init() again. As before, this initializes the toolkit, and parses the arguments found on the command line. Any argument it recognizes from the command line, it removes from the list, and modifies argc and argv to make it look like they never existed, allowing your application to parse the remaining arguments.
gtk_init (&argc, &argv);
Create a new window. This is fairly straightforward. Memory is allocated for the GtkWidget *window structure so it now points to a valid structure. It sets up a new window, but it is not displayed until we call gtk_widget_show(window) near the end of our program.
window = gtk_window_new (GTK_WINDOW_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 gtk_widget_destroy() call passing in the window widget as the object to destroy. The second is emitted when, in the "delete_event" handler, we return FALSE. The G_OBJECT and G_CALLBACK are macros that perform type casting and checking for us, as well as aid the readability of the code.
g_signal_connect (G_OBJECT (window), "delete_event",
G_CALLBACK (delete_event), NULL);
g_signal_connect (G_OBJECT (window), "destroy",
G_CALLBACK (destroy), NULL);
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
And again, GTK_CONTAINER is a macro to perform type casting.
gtk_container_set_border_width (GTK_CONTAINER (window), 10);
This call creates a new button. It allocates space for a new GtkWidget structure in memory, initializes it, and makes the button pointer point to it. It will have the label "Hello World" on it when displayed.
button = gtk_button_new_with_label ("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. The data is ignored, so we simply pass in NULL to the hello() callback function. Obviously, the "clicked" signal is emitted when we click the button with our mouse pointer.
g_signal_connect (G_OBJECT (button), "clicked",
G_CALLBACK (hello), NULL);
We are also going to use this button to exit 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 then this one in the order they are set up. You may have as many callback functions as you need, and all will be executed in the order you connected them. Because the gtk_widget_destroy() function accepts only a GtkWidget *widget as an argument, we use the g_signal_connect_swapped() function here instead of straight g_signal_connect().
g_signal_connect_swapped (G_OBJECT (button), "clicked",
G_CALLBACK (gtk_widget_destroy),
G_OBJECT (window));
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 GTK container can only contain one widget. There are other widgets, that are described later, which are designed to layout multiple widgets in various ways.
gtk_container_add (GTK_CONTAINER (window), 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.
gtk_widget_show (button);
gtk_widget_show (window);
And of course, we call gtk_main() which waits for events to come from the X server and will call on the widgets to emit signals when these events come.
gtk_main ();
And the final return. Control returns here after gtk_quit() is called.
return 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.
Moving On
Data Types
There are a few things you probably noticed in the previous examples that need explaining. The gint, gchar, etc. that you see are typedefs to int and char, respectively, that are part of the GLib system. This is done to get around that nasty dependency on the size of simple data types when doing calculations.
A good example is "gint32" which will be typedef'd to a 32 bit integer for any given platform, whether it be the 64 bit alpha, or the 32 bit i386. The typedefs are very straightforward and intuitive. They are all defined in glib/glib.h (which gets included from gtk.h).
You'll also notice GTK's ability to use GtkWidget when the function calls for a GtkObject. GTK is an object oriented design, and a widget is an object.
More on Signal Handlers
Lets take another look at the g_signal_connect() declaration.
gulong g_signal_connect( gpointer object,
const gchar *name,
GCallback func,
gpointer func_data );
Notice the gulong 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:
void g_signal_handler_disconnect( gpointer object,
gulong 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 g_signal_handler_block() and g_signal_handler_unblock() family of functions.
void g_signal_handler_block( gpointer object,
gulong id );
void g_signal_handlers_block_by_func( gpointer object,
GCallback func,
gpointer data );
void g_signal_handler_unblock( gpointer object,
gulong id );
void g_signal_handlers_unblock_by_func( gpointer object,
GCallback func,
gpointer data );
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.
#include <gtk/gtk.h>
/* Our new improved callback. The data passed to this function
* is printed to stdout. */
static void callback( GtkWidget *widget,
gpointer data )
{
g_print ("Hello again - %s was pressed\n", (gchar *) data);
}
/* another callback */
static gboolean delete_event( GtkWidget *widget,
GdkEvent *event,
gpointer data )
{
gtk_main_quit ();
return FALSE;
}
int main( int argc,
char *argv[] )
{
/* GtkWidget is the storage type for widgets */
GtkWidget *window;
GtkWidget *button;
GtkWidget *box1;
/* This is called in all GTK applications. Arguments are parsed
* from the command line and are returned to the application. */
gtk_init (&argc, &argv);
/* Create a new window */
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
/* This is a new call, which just sets the title of our
* new window to "Hello Buttons!" */
gtk_window_set_title (GTK_WINDOW (window), "Hello Buttons!");
/* Here we just set a handler for delete_event that immediately
* exits GTK. */
g_signal_connect (G_OBJECT (window), "delete_event",
G_CALLBACK (delete_event), NULL);
/* Sets the border width of the window. */
gtk_container_set_border_width (GTK_CONTAINER (window), 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 = gtk_hbox_new (FALSE, 0);
/* Put the box into the main window. */
gtk_container_add (GTK_CONTAINER (window), box1);
/* Creates a new button with the label "Button 1". */
button = gtk_button_new_with_label ("Button 1");
/* Now when the button is clicked, we call the "callback" function
* with a pointer to "button 1" as its argument */
g_signal_connect (G_OBJECT (button), "clicked",
G_CALLBACK (callback), (gpointer) "button 1");
/* Instead of gtk_container_add, we pack this button into the invisible
* box, which has been packed into the window. */
gtk_box_pack_start (GTK_BOX(box1), 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. */
gtk_widget_show (button);
/* Do these same steps again to create a second button */
button = gtk_button_new_with_label ("Button 2");
/* Call the same callback function with a different argument,
* passing a pointer to "button 2" instead. */
g_signal_connect (G_OBJECT (button), "clicked",
G_CALLBACK (callback), (gpointer) "button 2");
gtk_box_pack_start(GTK_BOX (box1), 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. */
gtk_widget_show (button);
gtk_widget_show (box1);
gtk_widget_show (window);
/* Rest in gtk_main and wait for the fun to begin! */
gtk_main ();
return 0;
}
Compile this program using the same linking arguments as our first example. 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 gtk_box_pack_start() while reading the next section. Try resizing the window, and observe the behavior.
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.
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 gtk_hbox_new(), and for vertical boxes, gtk_vbox_new(). The gtk_box_pack_start() and gtk_box_pack_end() functions are used to place objects inside of these containers. The gtk_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. gtk_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 gtk_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.
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 gtk_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 gtk_box_pack_start() function).
This is the declaration of the gtk_box_pack_start() function.
void gtk_box_pack_start( GtkBox *box,
GtkWidget *child,
gboolean expand,
gboolean fill,
guint 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 argument to gtk_box_pack_start() and gtk_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 area 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 gtk_box_pack_start() or gtk_box_pack_end().
The fill argument to the gtk_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:
GtkWidget *gtk_hbox_new ( gboolean homogeneous,
gint spacing );
The homogeneous argument to gtk_hbox_new() (and the same for gtk_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 gtk_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. Compile it yourself and play with it.
Packing Demonstration Program
/* example-start packbox packbox.c */
#include <stdio.h>
#include <stdlib.h>
#include "gtk/gtk.h"
static gboolean delete_event( GtkWidget *widget,
GdkEvent *event,
gpointer data )
{
gtk_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. */
static GtkWidget *make_box( gboolean homogeneous,
gint spacing,
gboolean expand,
gboolean fill,
guint padding )
{
GtkWidget *box;
GtkWidget *button;
char padstr[80];
/* Create a new hbox with the appropriate homogeneous
* and spacing settings */
box = gtk_hbox_new (homogeneous, spacing);
/* Create a series of buttons with the appropriate settings */
button = gtk_button_new_with_label ("gtk_box_pack");
gtk_box_pack_start (GTK_BOX (box), button, expand, fill, padding);
gtk_widget_show (button);
button = gtk_button_new_with_label ("(box,");
gtk_box_pack_start (GTK_BOX (box), button, expand, fill, padding);
gtk_widget_show (button);
button = gtk_button_new_with_label ("button,");
gtk_box_pack_start (GTK_BOX (box), button, expand, fill, padding);
gtk_widget_show (button);
/* Create a button with the label depending on the value of
* expand. */
if (expand == TRUE)
button = gtk_button_new_with_label ("TRUE,");
else
button = gtk_button_new_with_label ("FALSE,");
gtk_box_pack_start (GTK_BOX (box), button, expand, fill, padding);
gtk_widget_show (button);
/* This is the same as the button creation for "expand"
* above, but uses the shorthand form. */
button = gtk_button_new_with_label (fill ? "TRUE," : "FALSE,");
gtk_box_pack_start (GTK_BOX (box), button, expand, fill, padding);
gtk_widget_show (button);
sprintf (padstr, "%d);", padding);
button = gtk_button_new_with_label (padstr);
gtk_box_pack_start (GTK_BOX (box), button, expand, fill, padding);
gtk_widget_show (button);
return box;
}
int main( int argc,
char *argv[])
{
GtkWidget *window;
GtkWidget *button;
GtkWidget *box1;
GtkWidget *box2;
GtkWidget *separator;
GtkWidget *label;
GtkWidget *quitbox;
int which;
/* Our init, don't forget this! :) */
gtk_init (&argc, &argv);
if (argc != 2) {
fprintf (stderr, "usage: packbox num, where num is 1, 2, or 3.\n");
/* This just does cleanup in GTK and exits with an exit status of 1. */
exit (1);
}
which = atoi (argv[1]);
/* Create our window */
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
/* You should always remember to connect the delete_event signal
* to the main window. This is very important for proper intuitive
* behavior */
g_signal_connect (G_OBJECT (window), "delete_event",
G_CALLBACK (delete_event), NULL);
gtk_container_set_border_width (GTK_CONTAINER (window), 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 = gtk_vbox_new (FALSE, 0);
/* which example to show. These correspond to the pictures above. */
switch (which) {
case 1:
/* create a new label. */
label = gtk_label_new ("gtk_hbox_new (FALSE, 0);");
/* Align the label to the left side. We'll discuss this function and
* others in the section on Widget Attributes. */
gtk_misc_set_alignment (GTK_MISC (label), 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. */
gtk_box_pack_start (GTK_BOX (box1), label, FALSE, FALSE, 0);
/* Show the label */
gtk_widget_show (label);
/* Call our make box function - homogeneous = FALSE, spacing = 0,
* expand = FALSE, fill = FALSE, padding = 0 */
box2 = make_box (FALSE, 0, FALSE, FALSE, 0);
gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);
gtk_widget_show (box2);
/* Call our make box function - homogeneous = FALSE, spacing = 0,
* expand = TRUE, fill = FALSE, padding = 0 */
box2 = make_box (FALSE, 0, TRUE, FALSE, 0);
gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);
gtk_widget_show (box2);
/* Args are: homogeneous, spacing, expand, fill, padding */
box2 = make_box (FALSE, 0, TRUE, TRUE, 0);
gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);
gtk_widget_show (box2);
/* Creates a separator, we'll learn more about these later,
* but they are quite simple. */
separator = gtk_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. */
gtk_box_pack_start (GTK_BOX (box1), separator, FALSE, TRUE, 5);
gtk_widget_show (separator);
/* Create another new label, and show it. */
label = gtk_label_new ("gtk_hbox_new (TRUE, 0);");
gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
gtk_box_pack_start (GTK_BOX (box1), label, FALSE, FALSE, 0);
gtk_widget_show (label);
/* Args are: homogeneous, spacing, expand, fill, padding */
box2 = make_box (TRUE, 0, TRUE, FALSE, 0);
gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);
gtk_widget_show (box2);
/* Args are: homogeneous, spacing, expand, fill, padding */
box2 = make_box (TRUE, 0, TRUE, TRUE, 0);
gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);
gtk_widget_show (box2);
/* Another new separator. */
separator = gtk_hseparator_new ();
/* The last 3 arguments to gtk_box_pack_start are:
* expand, fill, padding. */
gtk_box_pack_start (GTK_BOX (box1), separator, FALSE, TRUE, 5);
gtk_widget_show (separator);
break;
case 2:
/* Create a new label, remember box1 is a vbox as created
* near the beginning of main() */
label = gtk_label_new ("gtk_hbox_new (FALSE, 10);");
gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
gtk_box_pack_start (GTK_BOX (box1), label, FALSE, FALSE, 0);
gtk_widget_show (label);
/* Args are: homogeneous, spacing, expand, fill, padding */
box2 = make_box (FALSE, 10, TRUE, FALSE, 0);
gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);
gtk_widget_show (box2);
/* Args are: homogeneous, spacing, expand, fill, padding */
box2 = make_box (FALSE, 10, TRUE, TRUE, 0);
gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);
gtk_widget_show (box2);
separator = gtk_hseparator_new ();
/* The last 3 arguments to gtk_box_pack_start are:
* expand, fill, padding. */
gtk_box_pack_start (GTK_BOX (box1), separator, FALSE, TRUE, 5);
gtk_widget_show (separator);
label = gtk_label_new ("gtk_hbox_new (FALSE, 0);");
gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
gtk_box_pack_start (GTK_BOX (box1), label, FALSE, FALSE, 0);
gtk_widget_show (label);
/* Args are: homogeneous, spacing, expand, fill, padding */
box2 = make_box (FALSE, 0, TRUE, FALSE, 10);
gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);
gtk_widget_show (box2);
/* Args are: homogeneous, spacing, expand, fill, padding */
box2 = make_box (FALSE, 0, TRUE, TRUE, 10);
gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);
gtk_widget_show (box2);
separator = gtk_hseparator_new ();
/* The last 3 arguments to gtk_box_pack_start are: expand, fill, padding. */
gtk_box_pack_start (GTK_BOX (box1), separator, FALSE, TRUE, 5);
gtk_widget_show (separator);
break;
case 3:
/* 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 = gtk_label_new ("end");
/* Pack it using gtk_box_pack_end(), so it is put on the right
* side of the hbox created in the make_box() call. */
gtk_box_pack_end (GTK_BOX (box2), label, FALSE, FALSE, 0);
/* Show the label. */
gtk_widget_show (label);
/* Pack box2 into box1 (the vbox remember ? :) */
gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);
gtk_widget_show (box2);
/* A separator for the bottom. */
separator = gtk_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. */
gtk_widget_set_size_request (separator, 400, 5);
/* pack the separator into the vbox (box1) created near the start
* of main() */
gtk_box_pack_start (GTK_BOX (box1), separator, FALSE, TRUE, 5);
gtk_widget_show (separator);
}
/* Create another new hbox.. remember we can use as many as we need! */
quitbox = gtk_hbox_new (FALSE, 0);
/* Our quit button. */
button = gtk_button_new_with_label ("Quit");
/* Setup the signal to terminate the program when the button is clicked */
g_signal_connect_swapped (G_OBJECT (button), "clicked",
G_CALLBACK (gtk_main_quit),
G_OBJECT (window));
/* Pack the button into the quitbox.
* The last 3 arguments to gtk_box_pack_start are:
* expand, fill, padding. */
gtk_box_pack_start (GTK_BOX (quitbox), button, TRUE, FALSE, 0);
/* pack the quitbox into the vbox (box1) */
gtk_box_pack_start (GTK_BOX (box1), quitbox, FALSE, FALSE, 0);
/* Pack the vbox (box1) which now contains all our widgets, into the
* main window. */
gtk_container_add (GTK_CONTAINER (window), box1);
/* And show everything left */
gtk_widget_show (button);
gtk_widget_show (quitbox);
gtk_widget_show (box1);
/* Showing the window last so everything pops up at once. */
gtk_widget_show (window);
/* And of course, our main function. */
gtk_main ();
/* Control returns here when gtk_main_quit() is called, but not when
* exit() is used. */
return 0;
}
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 gtk_table_new() function:
GtkWidget *gtk_table_new( guint rows,
guint columns,
gboolean homogeneous );
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 gtk_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:
void gtk_table_attach( GtkTable *table,
GtkWidget *child,
guint left_attach,
guint right_attach,
guint top_attach,
guint bottom_attach,
GtkAttachOptions xoptions,
GtkAttachOptions yoptions,
guint xpadding,
guint 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 and may be bitwise OR'ed together to allow multiple options.
These options are:
GTK_FILL
If the table box is larger than the widget, and GTK_FILL is specified, the widget will expand to use all the room available.
GTK_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 GTK_SHRINK is specified, the widgets will shrink with the table.
GTK_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.
gtk_table_attach() has a lot of options. So, there's a shortcut:
void gtk_table_attach_defaults( GtkTable *table,
GtkWidget *widget,
guint left_attach,
guint right_attach,
guint top_attach,
guint bottom_attach );
The X and Y options default to GTK_FILL | GTK_EXPAND, and X and Y padding are set to 0. The rest of the arguments are identical to the previous function.
We also have gtk_table_set_row_spacing() and gtk_table_set_col_spacing(). These places spacing between the rows at the specified row or column.
void gtk_table_set_row_spacing( GtkTable *table,
guint row,
guint spacing );
and
void gtk_table_set_col_spacing ( GtkTable *table,
guint column,
guint 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:
void gtk_table_set_row_spacings( GtkTable *table,
guint spacing );
And,
void gtk_table_set_col_spacings( GtkTable *table,
guint spacing );
Note that with these calls, the last row and last column do not get any spacing.
Table Packing Example
Burda 3 butonla birlikte bir pencereyi 2x2 ebatlarında bir tablo içerisinde yapacağız. Öncelikle 2 butonu ilk satıra yerleştireceğiz. Bir çıkış butonuda aşağı satıra, iki hücreye birlikte yerleştireceğiz. Daha iyi anlamak için aşağıya bakınız:
Kaynak Kod:
#include <gtk/gtk.h>
/* Çağrımlarımız.
* Veriler bilgileri ekrana yazarken bu fonksiyonları es geçeceklerdir.*/
static void callback( GtkWidget *widget,
gpointer data )
{
g_print ("Hello again - %s was pressed\n", (char *) data);
}
/* BU çağrım programdan çıkar */
static gboolean delete_event( GtkWidget *widget,
GdkEvent *event,
gpointer data )
{
gtk_main_quit ();
return FALSE;
}
int main( int argc,
char *argv[] )
{
GtkWidget *window;
GtkWidget *button;
GtkWidget *table;
gtk_init (&argc, &argv);
/* Yeni bir pencere oluşturuluyor */
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
/* Pencere başlığı atanıyor */
gtk_window_set_title (GTK_WINDOW (window), "Table");
/* GTK dan çıkarken işlenen delete_event için denetim oluşturuyoruzet */
g_signal_connect (G_OBJECT (window), "delete_event",
G_CALLBACK (delete_event), NULL);
/* Pencerenin sınır genişliğini(border) ayarlıyoruz . */
gtk_container_set_border_width (GTK_CONTAINER (window), 20);
/* 2x2 tablo oluşturuyoruz */
table = gtk_table_new (2, 2, TRUE);
/* Tabloyu ana pencereye koyuyoruz */
gtk_container_add (GTK_CONTAINER (window), table);
/* İlk butonu oluşturuyoruz */
button = gtk_button_new_with_label ("button 1");
/* Buton tıklandığında, bir pointer olan "button 1" argümanımızla birlikte "callback" fonksiyonunu *çağırıyoruz*/
g_signal_connect (G_OBJECT (button), "clicked",
G_CALLBACK (callback), (gpointer) "button 1");
/* Buton 1 i üst sol hücreye yerleştiriyoruz */
gtk_table_attach_defaults (GTK_TABLE (table), button, 0, 1, 0, 1);
gtk_widget_show (button);
/* İkinci butonu oluşturuyoruz */
button = gtk_button_new_with_label ("button 2");
/* Buton tıklandığında, bir pointer olan "button 2" argümanımızla birlikte "callback" fonksiyonunu *çağırıyoruz*/
g_signal_connect (G_OBJECT (button), "clicked",
G_CALLBACK (callback), (gpointer) "button 2");
/* Buton 1 i üst sağ hücreye yerleştiriyoruz */
gtk_table_attach_defaults (GTK_TABLE (table), button, 1, 2, 0, 1);
gtk_widget_show (button);
/* "Çıkış" butonunu oluşturuyoruz */
button = gtk_button_new_with_label ("Quit");
/* Buton tıklandğında, "delete_event" fonksiyonunu çağırıyoruz ve programdan çıkıyoruz. */
g_signal_connect (G_OBJECT (button), "clicked",
G_CALLBACK (delete_event), NULL);
/* Çıkış butonunu alt iki hücreye birlikte yerleştiriyoruz. */
gtk_table_attach_defaults (GTK_TABLE (table), button, 0, 2, 1, 2);
gtk_widget_show (button);
gtk_widget_show (table);
gtk_widget_show (window);
gtk_main ();
return 0;
}
Widget Overview
The general steps to creating a widget in GTK are:
1. gtk_*_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 gtk_container_add() or gtk_box_pack_start(). 5. gtk_widget_show() the widget.
gtk_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 gtk_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 gtk_widget_show() function.
Casting
You'll notice as you go on that GTK uses a type casting system. This is always done using macros that both test the ability to cast the given item, and perform the cast. Some common ones you will see are:
G_OBJECT (object) GTK_WIDGET (widget) GTK_OBJECT (object) GTK_SIGNAL_FUNC (function) GTK_CONTAINER (container) GTK_WINDOW (window) GTK_BOX (box)
These are all used to cast arguments in functions. You'll see them in the examples, and can usually tell when to use them simply by looking at the function's declaration.
As you can see below in the class hierarchy, all GtkWidgets are derived from the GObject base class. This means you can use a widget in any place the function asks for an object - simply use the G_OBJECT() macro.
For example:
g_signal_connect( G_OBJECT (button), "clicked",
G_CALLBACK (callback_function), callback_data);
This casts the button into an object, and provides a cast for the function pointer to the callback.
Many widgets are also containers. If you look in the class hierarchy below, you'll notice that many widgets derive from the Container class. Any one of these widgets may be used with the GTK_CONTAINER macro to pass them to functions that ask for containers.
Unfortunately, these macros are not extensively covered in the tutorial, but I recommend taking a look through the GTK header files or the GTK API reference manual. It can be very educational. In fact, it's not difficult to learn how a widget works just by looking at the function declarations.
Widget Hierarchy
For your reference, here is the class hierarchy tree used to implement widgets. (Deprecated widgets and auxiliary classes have been omitted.)
GObject | GtkObject +GtkWidget | +GtkMisc | | +GtkLabel | | | `GtkAccelLabel | | +GtkArrow | | `GtkImage | +GtkContainer | | +GtkBin | | | +GtkAlignment | | | +GtkFrame | | | | `GtkAspectFrame | | | +GtkButton | | | | +GtkToggleButton | | | | | `GtkCheckButton | | | | | `GtkRadioButton | | | | `GtkOptionMenu | | | +GtkItem | | | | +GtkMenuItem | | | | +GtkCheckMenuItem | | | | | `GtkRadioMenuItem | | | | +GtkImageMenuItem | | | | +GtkSeparatorMenuItem | | | | `GtkTearoffMenuItem | | | +GtkWindow | | | | +GtkDialog | | | | | +GtkColorSelectionDialog | | | | | +GtkFileSelection | | | | | +GtkFontSelectionDialog | | | | | +GtkInputDialog | | | | | `GtkMessageDialog | | | | `GtkPlug | | | +GtkEventBox | | | +GtkHandleBox | | | +GtkScrolledWindow | | | `GtkViewport | | +GtkBox | | | +GtkButtonBox | | | | +GtkHButtonBox | | | | `GtkVButtonBox | | | +GtkVBox | | | | +GtkColorSelection | | | | +GtkFontSelection | | | | `GtkGammaCurve | | | `GtkHBox | | | +GtkCombo | | | `GtkStatusbar | | +GtkFixed | | +GtkPaned | | | +GtkHPaned | | | `GtkVPaned | | +GtkLayout | | +GtkMenuShell | | | +GtkMenuBar | | | `GtkMenu | | +GtkNotebook | | +GtkSocket | | +GtkTable | | +GtkTextView | | +GtkToolbar | | `GtkTreeView | +GtkCalendar | +GtkDrawingArea | | `GtkCurve | +GtkEditable | | +GtkEntry | | `GtkSpinButton | +GtkRuler | | +GtkHRuler | | `GtkVRuler | +GtkRange | | +GtkScale | | | +GtkHScale | | | `GtkVScale | | `GtkScrollbar | | +GtkHScrollbar | | `GtkVScrollbar | +GtkSeparator | | +GtkHSeparator | | `GtkVSeparator | +GtkInvisible | +GtkPreview | `GtkProgressBar +GtkAdjustment +GtkCellRenderer | +GtkCellRendererPixbuf | +GtkCellRendererText | +GtkCellRendererToggle +GtkItemFactory +GtkTooltips `GtkTreeViewColumn
Widgets Without Windows
The following widgets do not have an associated window. If you want to capture events, you'll have to use the EventBox. See the section on the EventBox widget.
GtkAlignment GtkArrow GtkBin GtkBox GtkButton GtkCheckButton GtkFixed GtkImage GtkLabel GtkMenuItem GtkNotebook GtkPaned GtkRadioButton GtkRange GtkScrolledWindow GtkSeparator GtkTable GtkToolbar GtkAspectFrame GtkFrame GtkVBox GtkHBox GtkVSeparator GtkHSeparator
We'll further our exploration of GTK by examining each widget in turn, creating a few simple functions to display them. Another good source is the testgtk program that comes with GTK. It can be found in tests/testgtk.c.
The Button Widget
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 the gtk_button_new_with_label() or gtk_button_new_with_mnemonic() to create a button with a label, use gtk_button_new_from_stock() to create a button containing the image and text from a stock item or use gtk_button_new() 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 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.
#include <stdlib.h>
#include <gtk/gtk.h>
/* Create a new hbox with an image and a label packed into it
* and return the box. */
static GtkWidget *xpm_label_box( gchar *xpm_filename,
gchar *label_text )
{
GtkWidget *box;
GtkWidget *label;
GtkWidget *image;
/* Create box for image and label */
box = gtk_hbox_new (FALSE, 0);
gtk_container_set_border_width (GTK_CONTAINER (box), 2);
/* Now on to the image stuff */
image = gtk_image_new_from_file (xpm_filename);
/* Create a label for the button */
label = gtk_label_new (label_text);
/* Pack the image and label into the box */
gtk_box_pack_start (GTK_BOX (box), image, FALSE, FALSE, 3);
gtk_box_pack_start (GTK_BOX (box), label, FALSE, FALSE, 3);
gtk_widget_show (image);
gtk_widget_show (label);
return box;
}
/* Our usual callback function */
static void callback( GtkWidget *widget,
gpointer data )
{
g_print ("Hello again - %s was pressed\n", (char *) data);
}
int main( int argc,
char *argv[] )
{
/* GtkWidget is the storage type for widgets */
GtkWidget *window;
GtkWidget *button;
GtkWidget *box;
gtk_init (&argc, &argv);
/* Create a new window */
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_window_set_title (GTK_WINDOW (window), "Pixmap'd Buttons!");
/* It's a good idea to do this for all windows. */
g_signal_connect (G_OBJECT (window), "destroy",
G_CALLBACK (gtk_main_quit), NULL);
g_signal_connect (G_OBJECT (window), "delete_event",
G_CALLBACK (gtk_main_quit), NULL);
/* Sets the border width of the window. */
gtk_container_set_border_width (GTK_CONTAINER (window), 10);
/* Create a new button */
button = gtk_button_new ();
/* Connect the "clicked" signal of the button to our callback */
g_signal_connect (G_OBJECT (button), "clicked",
G_CALLBACK (callback), (gpointer) "cool button");
/* This calls our box creating function */
box = xpm_label_box ("info.xpm", "cool button");
/* Pack and show all our widgets */
gtk_widget_show (box);
gtk_container_add (GTK_CONTAINER (button), box);
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 ();
return 0;
}
The xpm_label_box() function could be used to pack images and labels into any widget that can be a container.
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
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:
GtkWidget *gtk_toggle_button_new( void ); GtkWidget *gtk_toggle_button_new_with_label( const gchar *label ); GtkWidget *gtk_toggle_button_new_with_mnemonic( const gchar *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 _mnemonic() variant additionally parses 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. This tests the state of the toggle button, 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:
void toggle_button_callback (GtkWidget *widget, gpointer data)
{
if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)))
{
/* 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 this function:
void gtk_toggle_button_set_active( GtkToggleButton *toggle_button,
gboolean is_active );
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" and "toggled" signals to be emitted from the button.
gboolean gtk_toggle_button_get_active (GtkToggleButton *toggle_button);
This returns the current state of the toggle button as a boolean TRUE/FALSE value.
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.
GtkWidget *gtk_check_button_new( void ); GtkWidget *gtk_check_button_new_with_label ( const gchar *label ); GtkWidget *gtk_check_button_new_with_mnemonic ( const gchar *label );
The gtk_check_button_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.
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:
GtkWidget *gtk_radio_button_new( GSList *group );
GtkWidget *gtk_radio_button_new_from_widget( GtkRadioButton *group );
GtkWidget *gtk_radio_button_new_with_label( GSList *group,
const gchar *label );
GtkWidget* gtk_radio_button_new_with_label_from_widget( GtkRadioButton *group,
const gchar *label );
GtkWidget *gtk_radio_button_new_with_mnemonic( GSList *group,
const gchar *label );
GtkWidget *gtk_radio_button_new_with_mnemonic_from_widget( GtkRadioButton *group,
const gchar *label );
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() or gtk_radio_button_new_with_label() should pass NULL as the first argument. Then create a group using:
GSList *gtk_radio_button_get_group( GtkRadioButton *radio_button );
The important thing to remember is that gtk_radio_button_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 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:
button2 = gtk_radio_button_new_with_label(
gtk_radio_button_get_group (GTK_RADIO_BUTTON (button1)),
"button2");
The _from_widget() variants of the creation functions allow you to shorten this further, by omitting the gtk_radio_button_get_group() call. This form is used in the example to create the third button:
button2 = gtk_radio_button_new_with_label_from_widget(
GTK_RADIO_BUTTON (button1),
"button2");
It is also a good idea to explicitly set which button should be the default depressed button with:
void gtk_toggle_button_set_active( GtkToggleButton *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.
#include <glib.h>
#include <gtk/gtk.h>
static gboolean close_application( GtkWidget *widget,
GdkEvent *event,
gpointer data )
{
gtk_main_quit ();
return FALSE;
}
int main( int argc,
char *argv[] )
{
GtkWidget *window = NULL;
GtkWidget *box1;
GtkWidget *box2;
GtkWidget *button;
GtkWidget *separator;
GSList *group;
gtk_init (&argc, &argv);
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
g_signal_connect (G_OBJECT (window), "delete_event",
G_CALLBACK (close_application),
NULL);
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 (NULL, "button1");
gtk_box_pack_start (GTK_BOX (box2), button, TRUE, TRUE, 0);
gtk_widget_show (button);
group = gtk_radio_button_get_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_from_widget (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");
g_signal_connect_swapped (G_OBJECT (button), "clicked",
G_CALLBACK (close_application),
G_OBJECT (window));
gtk_box_pack_start (GTK_BOX (box2), button, TRUE, TRUE, 0);
GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
gtk_widget_grab_default (button);
gtk_widget_show (button);
gtk_widget_show (window);
gtk_main ();
return 0;
}
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.
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:
GtkObject *gtk_adjustment_new( gdouble value,
gdouble lower,
gdouble upper,
gdouble step_increment,
gdouble page_increment,
gdouble 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.
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 adjustments */ viewport = gtk_viewport_new (NULL, NULL); /* uses the newly-created adjustment for the scrollbar as well */ vscrollbar = gtk_vscrollbar_new (gtk_viewport_get_vadjustment (viewport));
Adjustment Internals
Ok, you say, that's nice, but what if I want to create my own handlers to respond when the user adjusts a range widget or a spin button, and how do I get at the value of the adjustment in these handlers? To answer these questions and more, let's start by taking a look at struct _GtkAdjustment itself:
struct _GtkAdjustment {
GtkObject parent_instance; gdouble lower; gdouble upper; gdouble value; gdouble step_increment; gdouble page_increment; gdouble page_size;
};
If you don't like to poke directly at struct internals like a real C programmer, you can use the following accessor to inspect the value of an adjustment:
gdouble gtk_adjustment_get_value( GtkAdjustment *adjustment);
Since, when you set the value of an Adjustment, you generally want the change to be reflected by every widget that uses this adjustment, GTK provides this convenience function to do this:
void gtk_adjustment_set_value( GtkAdjustment *adjustment,
gdouble value );
As mentioned earlier, Adjustment is a subclass of Object just like all the various widgets, and thus it is able to emit signals. This is, of course, why updates happen automagically when you share an adjustment object between a scrollbar and another adjustable widget; all adjustable widgets connect signal handlers to their adjustment's value_changed signal, as can your program. Here's the definition of this signal in struct _GtkAdjustmentClass:
void (* value_changed) (GtkAdjustment *adjustment);<






