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.