GTK+/Glade - budowniczy interfejsu
Glade to narzędzie, które pozwala tworzyć w wygodny sposób interfejs programu, tzw. RAD. Do przedstawionego przykładu używałem Glade w wersji 3.4.3. Program ma za zadanie pokazać jak korzystać z wygenerowanego pliku *.glade (w formacie xml).
W programie Glade tworzymy zwykłe okno (window1). Następnie dodajemy do niego przycisk (button1). Dla przycisku ustawiamy zdarzenie „clicked” a jako uchwyt wybieramy zaproponowaną nazwę funkcji on_button1_clicked()
. Zapisujemy projekt pod nazwą test. Oto jak teraz wygląda szablon interfejsu wygenerowany przez Glade:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd">
<!--Generated with glade3 3.4.3 on Sat May 24 14:24:41 2008 -->
<glade-interface>
<widget class="GtkWindow" id="window1">
<child>
<widget class="GtkButton" id="button1">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="label" translatable="yes">button</property>
<property name="response_id">0</property>
<signal name="clicked" handler="on_button1_clicked"/>
</widget>
</child>
</widget>
</glade-interface>
Widzimy, że zawiera on obiekt klasy GtkWidget o nazwie window1
. W jego wnętrzu (jak w kontenerze) znajduje się kontrolka GtkButton o nazwie button1
. Oprócz właściwości ma też zdefiniowany sygnał "clicked", który wskazuje na funkcję on_button1_clicked
.
Programy pisane w GTK+ do korzystania z funkcjonalności programu Glade potrzebują struktury GladeXML. Oto kod programu, który będzie wyświetlał dialog, gdy klikniemy na przycisk.
// gcc gladetest.c -Wall -o gladetest `pkg-config gtk+-2.0 --cflags --libs` -I/usr/include/libglade-2.0 -lglade-2.0 -export-dynamic
#include <gtk/gtk.h>
#include <glade/glade.h>
void on_button1_clicked(GtkWidget* widget, gpointer user_data)
{
GtkWidget *dialog = gtk_message_dialog_new (user_data,
GTK_DIALOG_MODAL,
GTK_MESSAGE_INFO,
GTK_BUTTONS_OK,
"Zdarzenie zadziałało ;)");
gtk_dialog_run (GTK_DIALOG(dialog));
gtk_widget_destroy (dialog);
}
int main(int argc, char *argv[])
{
GtkWidget *window1, *button1;
GladeXML *fileglade;
gtk_init(&argc,&argv);
fileglade = glade_xml_new ("/home/grzesiek/Pulpit/gtk/test.glade",NULL,NULL);
window1 = glade_xml_get_widget(fileglade,"window1");
glade_xml_signal_autoconnect(fileglade);
g_signal_connect (G_OBJECT(window1), "destroy", G_CALLBACK(gtk_main_quit), NULL);
gtk_widget_show_all(window1);
gtk_main();
return 0;
}
Jeżeli chodzi o kompilacje to nowością mogą tu być opcje: -I/usr/include/libglade-2.0 -lglade-2.0
oraz -export-dynamic
. Pierwsza z nich wskazuje dla kompilatora gdzie ma szukać pliku glade.h
, druga jest niezbędna do prawidłowego działania łączenia autosygnałów.
int main(int argc, char *argv[])
{
GtkWidget *window1;
GladeXML *fileglade;
gtk_init(&argc,&argv);
fileglade = glade_xml_new ("/home/grzesiek/Pulpit/gtk/test.glade",NULL,NULL);
window1 = glade_xml_get_widget(fileglade,"window1");
Program zaczynamy od zadeklarowania widgetu window1
oraz obiektu GladeXML. Za pomocą funkcji glade_xml_new() tworzymy obiekt GladeXML, któremu przypisujemy strukturę pliku test.glade
. Wskaźnik do głównego okna pobieramy za pomocą funkcji glade_xml_get_widget(), gdzie podajemy obiekt GladeXML oraz kontrolkę, którą chcemy pobrać.
glade_xml_signal_autoconnect(fileglade);
g_signal_connect (G_OBJECT(window1), "destroy", G_CALLBACK(gtk_main_quit), NULL);
gtk_widget_show_all(window1);
gtk_main();
return 0;
}
Pobraliśmy już potrzebne nam kontrolki z pliku test.glade. Nie musimy pobierać wszystkich kontrolek, zwłaszcza gdy w Glade podczas projektowania interfejsu zdefiniowaliśmy nazwy funkcji przypisane sygnałom, które mają obsługiwać kontrolki. Dlatego nie pobieramy wskaźnika button1
. Zamiast tego wywołujemy funkcję glade_xml_signal_autoconnect(), która wyszuka w pliku test.glade wszystkie sygnały i przypisze je za nas do funkcji obsługi, o ile te funkcje istnieją. Sygnał "destroy" dla window1
jest już przypisywany ręcznie a nie automatycznie. Co nie znaczy, że nie możnaby było tego zrobić za pomocą mechanizmu autołączenia sygnałów. Wystarczyłoby w Glade dla kontrolki window1
zdefiniować sygnał "destroy" i jako uchwyt przypisać mu od razu funkcje gtk_main_quit() zamiast innej - własnej, w której z kolei wywołujemy gtk_main_quit(). Można też tworzyć interfejs w Glade lub tylko jego część a sygnały łączyć samemu. Wtedy musielibyśmy pobrać kontrolkę button1
tak jak window1
i jak to było opisywane w przykładowym programie połączyć sygnał "clicked" z jakąś funkcją. Oczywiście ta funkcja musi pasować do szablonu funkcji zwrotnej.
void on_button1_clicked(GtkWidget* widget, gpointer user_data)
{
GtkWidget *dialog = gtk_message_dialog_new (user_data,
GTK_DIALOG_MODAL,
GTK_MESSAGE_INFO,
GTK_BUTTONS_OK,
"Zdarzenie zadziałało ;)");
gtk_dialog_run (GTK_DIALOG(dialog));
gtk_widget_destroy (dialog);
}
To funkcja, która zostanie automatycznie przypisana do sygnału "clicked" dla kontrolki button1
. W ciele funkcji tworzymy okienko dialogowe, którego zadaniem zazwyczaj jest informowanie użytkownika o jakiś zdarzeniach. My z kolei wykorzystujemy je tu po to, aby zobaczyć, że interfejs programu może być jednocześnie tworzony za pomocą programu Glade z automatycznym łączeniem sygnałów lub bez oraz jednocześnie niektóre elementy interfejsu nadal można/trzeba budować "ręcznie" poprzez pisanie kodu.
Glade + PyGTK = szybki interfejs
[edytuj]Połączenie budowania interfejsu programu w Glade oraz jego wykorzystanie za pomocą języka Python pozwala na bardzo szybkie budowanie aplikacji z GUI. Zbiór narzedzi PyGTK + Glade sprawuje się prawie jak C++ Builder.
#!/usr/bin/env python
import pygtk
pygtk.require('2.0')
import gtk
import gtk.glade
def on_button1_clicked(ok_button):
dialog = gtk.MessageDialog(window1,
gtk.DIALOG_MODAL,
gtk.MESSAGE_INFO,
gtk.BUTTONS_OK,
'Zdarzenie zadzialalo ;)')
dialog.run()
dialog.destroy()
fileglade = gtk.glade.XML('/home/grzesiek/Pulpit/gtk/test.glade')
window1 = fileglade.get_widget('window1')
sygnaly = {"on_button1_clicked":on_button1_clicked}
fileglade.signal_autoconnect(sygnaly)
window1.connect("delete_event", gtk.main_quit)
window1.show_all()
gtk.main()
To analogiczny program do przedstawionego powyżej. Korzysta z tego samego projektu test.glade. Wykorzystanie PyGTK sprawia, że pisanie jest szybsze i prostsze.