Panel użytkownika
Nazwa użytkownika:
Hasło:
Nie masz jeszcze konta?
Autor: Badman
Biblioteki C++

Podstawy obsługi programu Glade

[lekcja] Rozdział 1. Do czego służy program Glade oraz w jaki sposób używać projekt interfejsu stworzony w Glade we własnym programie.

Wstęp

Glade to narzędzie, które pozwala tworzyć w wygodny sposób interfejs programu, tzw. RAD. Rapid Application Development oznacza szybkie tworzenie aplikacji. Projektowany interfejs zapisany jest w postaci pliku XML.
Kurs dotyczy wersji programu Glade 3.8.1 i wyższych.

Poniżej przykładowy kompletny interfejs w postaci pliku XML:

<?xml version="1.0"?>
<interface>
  <requires lib="gtk+" version="2.16"/>
  <!-- interface-naming-policy project-wide -->
  <object class="GtkWindow" id="window1">
    <property name="visible">True</property>
    <property name="title" translatable="yes">Okno</property>
    <child>
      <placeholder/>
    </child>
  </object>
</interface>

Strona domowa programu Glade http://glade.gnome.org. Stamtąd również dla systemów operacyjnych Windows należy pobrać paczkę jeżeli masz już zainstalowaną bibliotekę Gtk+ i znajduje się ona w ścieżce przeszukiwań (PATH), albo tą paczkę, który zawiera wraz z programem kompletną bibliotekę Gtk+ wraz z bibliotekami towarzyszącymi.
W przypadku systemów Linux w zależności od dystrybucji należy za pomocą menadżera pakietów zainstalować pakiet Glade, lub pobrać kod źródłowy i samodzielnie skompilować program Glade.


Po uruchomieniu programu Glade pojawi się okno:

Poeksperymentuj, wciskając różne ikonki obserwując co się dzieje :)

Pierwszy projekt - okno najwyższego poziomu

Aby utworzyć okno programu:
1. Wybierz ikonkę symbolizującą podstawowe okno. Pojawi się okno.
2. Ustaw tytuł okna "Kurs Glade", który będzie widoczny na belce okna.
3. Ustaw domyślne rozmiary okna.


Następnie zapisz plik jako kurs.ui, przyjęło się że pliki tworzone Glad'em mają rozszerzenie ui. Ważne by plik był zapisany w formacie GtkBuilder, domyślnym zapisem jest właśnie GtkBuilder. Kiedyś powszechnie używanym formatem był Libglade, jednak nie będę się nim zajmował, bo to już historia.

Mając już projekt interfejsu (co prawda tylko same okno), opiszę jak wykorzystać go w programie.

Należy utworzyć obiekt GtkBuilder funkcją:
GtkBuilder * gtk_builder_new( void );


Następnie:
guint gtk_builder_add_from_file( GtkBuilder * builder, const gchar * filename, GError ** error );

Funkcja przetwarza plik (drugi argument filename) zawierający definicje interfejsu i łączy go z bieżącą zawartością obiektu GtkBuilder (pierwszy argument builder) Trzecim argumentem jest wskaźnik na strukturę GError, która jest odpowiednio wypełniana w przypadku błędu. W przypadku błędu funkcja zwróci wartość 0.

Przykład użycia:
C/C++
#include <gtk/gtk.h>

/* Definicja pliku z zawartością GUI */
#define UI_FILE "kurs.ui"


GtkWidget * utworz_okno( void )
{
    GtkWidget * okno;
    GtkBuilder * builder;
    GError * error = NULL;
   
    /* Tworzy obiekt buildera */
    builder = gtk_builder_new();
    /* Wczytuje zawartość interfejsu i sprawdza ewentualne błędy */
    if( !gtk_builder_add_from_file( builder, UI_FILE, & error ) )
    {
        g_warning( "Nie można wczytać plilu buildera: %s", error->message );
        g_error_free( error );
    }
   
    /* Łączy sygnały zawarte w pliku interfejsu z odpowiednimi funkcjami */
    gtk_builder_connect_signals( builder, NULL );
   
    /* Pobiera obiekt z nazwą "window1" */
    okno = GTK_WIDGET( gtk_builder_get_object( builder, "window1" ) );
   
    /* Obiekt buildera nie będzie już nam potrzebny, więc go "zwalniamy" */
    g_object_unref( builder );
   
    return okno;
}


int
main( int argc, char * argv[] )
{
    GtkWidget * okno;
   
   
    gtk_set_locale();
    gtk_init( & argc, & argv );
   
    okno = utworz_okno();
    gtk_widget_show( okno );
   
    gtk_main();
    return 0;
}

Po skompilowaniu i uruchomieniu pojawi się okno programu:

Program nie reaguje na sygnał zamknięcia okna, należy odpowiednio obsłużyć to zdarzenie poprzez standardowe skojarzenie sygnału destroy z funkcją biblioteki Gtk+ gtk_main_quit w kodzie pisanego programu, albo obsłużyć z poziomu Glade'a:

We właściwościach obiektu "window1" w zakładce Sygnały należy odszukać sygnału destroy i w uchwycie do tego sygnału wybrać gtk_main_quit. Teraz program po zamknięciu okna kończy działanie zwracając do systemu wartość 0 poprzez wyjście z pętli funkcji gtk_main.

Tak na marginesie w nowszych wesjach programu Glade 3.10 i wzwyż zrezygnowano z podpowiedzi użycia funkcji w formie listy rozwijanej. Sam musisz wpisać nazwę funkcji :(

Bardziej rozbudowany przykład - obsługa sygnału przycisku

Rozbudujmy interfejs użytkownika:
1. Wybierz w palecie kontrolek dotyczącej kontenerów ikonkę Pionowa skrzynka i przeciągnij ją do stworzonego wcześniej okna. Utwórz 2 elementy. Kontrolka "Pionowa skrzynka" to po prostu GtkVBox.
2. Przeciągnij ikonkę symbolizującą zwykły przycisk w miejsce 1 elementu "Pionowej skrzynki".
3. Analogicznie zrób z ikonką zmiennego przycisku (togglebutton) dodając go w miejsce 2 elementu.

Mając utworzone przyciski należy skojarzyć je z odpowiednimi sygnałami dołączając odpowiednią funkcje zwrotną dla danego sygnału.
Wybierz obiekt togglebutton1, w jego właściwościach w zakładce sygnały odszukaj sygnał toggled. Następnie w kolumnie "Program obsługujący" / "uchwyt" (zależy od wersji językowej) wybierz z listy rozwijanej sugerowaną nazwę funkcji zwrotnej on_togglebutton1_toggled.
Wykonaj podobną operację dla obiektu button1, z tym że skojarz sygnał clicked z funkcją on_button1_clicked.

Zawartośc pliku kurs.ui wygląda następująco:

<?xml version="1.0" encoding="UTF-8"?>
<interface>
  <requires lib="gtk+" version="2.22"/>
  <!-- interface-naming-policy project-wide -->
  <object class="GtkWindow" id="window1">
    <property name="can_focus">False</property>
    <property name="title" translatable="yes">Kurs Glade</property>
    <property name="default_width">450</property>
    <property name="default_height">250</property>
    <signal name="destroy" handler="gtk_main_quit" swapped="no"/>
    <child>
      <object class="GtkVBox" id="vbox1">
        <property name="visible">True</property>
        <property name="can_focus">False</property>
        <child>
          <object class="GtkButton" id="button1">
            <property name="label" translatable="yes">button</property>
            <property name="use_action_appearance">False</property>
            <property name="visible">True</property>
            <property name="can_focus">True</property>
            <property name="receives_default">True</property>
            <signal name="clicked" handler="on_button1_clicked" swapped="no"/>
          </object>
          <packing>
            <property name="expand">False</property>
            <property name="fill">False</property>
            <property name="position">0</property>
          </packing>
        </child>
        <child>
          <object class="GtkToggleButton" id="togglebutton1">
            <property name="label" translatable="yes">togglebutton</property>
            <property name="use_action_appearance">False</property>
            <property name="visible">True</property>
            <property name="can_focus">True</property>
            <property name="receives_default">True</property>
            <signal name="toggled" handler="on_togglebutton1_toggled" swapped="no"/>
          </object>
          <packing>
            <property name="expand">False</property>
            <property name="fill">False</property>
            <property name="position">1</property>
          </packing>
        </child>
      </object>
    </child>
  </object>
</interface>

Poniżej przedstawię jak obsłużyć funkcję zwrotne zadeklarowane w Glade'ie. Proponuję, żeby oddzielić obsługę zdarzeń od głównego pliku programu. Utwórz dwa pliki funkcje_zwrotne.h, gdzie będą przechowywane prototypy funkcji i funkcje_zwrotne.c, w których będzie zawarta właściwa obsługa zdarzeń. Przyda się to zwłaszcza w dużych projektach.

Zawartość pliku funkcje_zwrotne.h:
C/C++
#include <gtk/gtk.h>

void on_button1_clicked( GtkButton * button, gpointer user_data );
void on_togglebutton1_toggled( GtkToggleButton * togglebutton, gpointer user_data );

Zawartość pliku funkcje_zwrotne.c:
C/C++
#include "funkcje_zwrotne.h"

void on_button1_clicked( GtkButton * button, gpointer user_data )
{
    g_print( "Wcisnięto zwykły przycisk\n" );
}

void on_togglebutton1_toggled( GtkToggleButton * togglebutton, gpointer user_data )
{
    g_print( "Wcisnięto zmienny przycisk\n" );
}

Uwaga!
Kompiluj program z biblioteką gmodule używając opcji -lgmodule-2.0.
Dodatkowo pod systemem Windows wymagane jest, żeby funkcje zwrotne były zaopatrzone w makro G_MODULE_EXPORT:
G_MODULE_EXPORT void on_button1_clicked( GtkButton * button, gpointer user_data );


W przypadku używania kompilatora g++, należy poinformować go o sposobie nazywania funkcji używając makr G_BEGIN_DECLS i G_END_DECLS. Przykład:
C/C++
G_BEGIN_DECLS

G_MODULE_EXPORT void on_checkbutton_toggled( GtkToggleButton * togglebutton, gpointer user_data );
G_MODULE_EXPORT void on_radiobutton_toggled( GtkToggleButton * togglebutton, gpointer user_data );

G_END_DECLS


Główny plik u mnie (czyli main.c) jest bardzo podobny do poprzedniego, została dodana tylko linia
#include "funkcje_zwrotne.h"
:
C/C++
#include <gtk/gtk.h>
#include "funkcje_zwrotne.h"

/* Definicja pliku z zawartością GUI */
#define UI_FILE "kurs.ui"


GtkWidget * utworz_okno( void )
{
    GtkWidget * okno;
    GtkBuilder * builder;
    GError * error = NULL;
   
    /* Tworzy obiekt buildera */
    builder = gtk_builder_new();
    /* Wczytuje zawartość interfejsu i sprawdza ewentualne błędy */
    if( !gtk_builder_add_from_file( builder, UI_FILE, & error ) )
    {
        g_warning( "Nie można wczytać plilu buildera: %s", error->message );
        g_error_free( error );
    }
   
    /* Łączy sygnały zawarte w pliku interfejsu z odpowiednimi funkcjami */
    gtk_builder_connect_signals( builder, NULL );
   
    /* Pobiera obiekt z nazwą "window1" */
    okno = GTK_WIDGET( gtk_builder_get_object( builder, "window1" ) );
   
    /* Obiekt buildera nie będzie już nam potrzebny, więc go "zwalniamy" */
    g_object_unref( builder );
   
    return okno;
}


int
main( int argc, char * argv[] )
{
    GtkWidget * okno;
   
   
    gtk_set_locale();
    gtk_init( & argc, & argv );
   
    okno = utworz_okno();
    gtk_widget_show( okno );
   
    gtk_main();
    return 0;
}

Program po naciśnięciu przycisku wyświetli w konsoli odpowiedni komunikat. Jeżeli będziesz zwiększał rozmiary okna zauważysz rozszerzanie się przycisków. Aby zmienić zachowanie się przycisków wraz z rozszerzaniem rozmiarów okna zmień we właściwościach danego przycisku w zakładce Pakowanie np. rozszerzanie na Nie.

Poeksperymentuj z opcjami w zakładce Pakowanie zapisując zmiany i następnie uruchamiając program. Pamiętaj raz skompilowany działający program nie wymaga ponownej kompilacji. Ponownej kompilacji wymagałby tylko i wyłącznie program dla, którego została dodana obsługa nowych funkcji zwrotnych sygnałów.
Poprzedni dokument Następny dokument
Kurs Glade, GTK+ Tworzenie GUI - menu, pola tekstowe i etykiety