Magaz, The Greek Linux Magazine
Magaz Logo

Επόμενο  Προηγούμενο  Περιεχόμενα

4. Βασικές έννοιες προγραμματισμού σε gtk

Ας ξεκινήσουμε με τις αρχικές έννοιες, που στη συνέχεια θα μας βοηθήσουν στην κατανόηση του προγραμματισμού σε gtk.

4.1 Βασικά χαρακτηριστικά

Κάθε πρόγραμμα που θα φτιάχνουμε, πρέπει να περιέχει, μεταξύ άλλων, και τα εξής:

Πρώτα από όλα, πρέπει να περιέχει τις βιβλιοθήκες που θα κάνουμε import. Για την gtk, αυτή είναι ή gtk.h, που βρίσκεται μέσα στο directory gtk


#include <gtk/gtk.h>

Φυσικά, όπως όλα τα προγράμματα, περιέχει την πολύ γνωστή main. Αυτή είναι η πρώτη συνάρτηση που καλείται, και πρέπει οπωσδήποτε να υπάρχει, είναι int, γιατί πρέπει να επιστρέφει μια τιμή τύπου int στο shell (το γνωστό σε όλους μας exit status).


int main(int argc, char *argv[]){
  ...
  ...
  }

Μέσα σε αυτήν τώρα, πρέπει να υπάρχει μια άλλη, η gtk_init που καλείται από όλα τα προγράμματα που είναι γραμμένα σε gtk για την αρχικοποίηση.


gtk_init(&argc, &argv);

Επίσης, σε κάποιο σημείο πρέπει να καλείται η gtk_main η οποία δεν είναι τίποτα άλλο από την συνάρτηση η οποία περιμένει για ενέργειες του χρήστη, όπως το πάτημα ενός κουμπιού στο ποντίκι, ή το πάτημα ενός πλήκτρου.


gtk_main();

Φυσικά δεν πρέπει να ξεχάσουμε την τιμή που θα επιστρέφει το όλο πρόγραμμά μας (είναι είπαμε int), και αυτό γίνεται με την γνωστή return


return(0);

Αυτά είναι τα βασικά και απαραίτητα που πρέπει να περιέχει ένα πρόγραμμα σε gtk. Φυσικά, για να γραφεί ένα ολοκληρωμένο και λειτουργικό πρόγραμμα απαιτούνται πολύ περισσότερα που θα δούμε στη συνέχεια, γιαύτο μη βιάζεστε... Συνεχίστε το διάβασμα για την βουτά στα βαθιά... )))

4.2 widgets, signals, κ.λπ.

First things first...

Widgets

Πριν αρχίσουμε, να δούμε μερικά πράγματα, όπως πχ. τι είναι το gtk_widget που θα συναντάμε κατ κόρον. Είναι μια δομή, που μέσω αυτής μπορούμε να έχουμε πρόσβαση σε όλα τα widgets της gtk. Αυτά μπορεί να είναι buttons, radio buttons, check buttons, lists, combo boxes, boxes, toolbars, και γενικότερα οτιδήποτε βλέπετε στα παραθυράκια των προγραμμάτων gtk.

signals

Ο έλεγχος σε ένα πρόγραμμα gtk δίδεται χρησιμοποιώντας τα signals. Ας σας εξηγήσω όμως με ένα παράδειγμα.
Για να συνδέσουμε ένα συμβάν με μια λειτουργία, μπορούμε να χρησιμοποιούμε μια συνάρτηση όπως η gtk_signal_connect.
Αυτή, συντάσσεται όπως βλέπουμε παρακάτω, και επιστρέφει μια τιμή τύπου gint (Μια μορφή ακεραίου που χρησιμοποιεί η gtk).


     gint gtk_signal_connect(GtkObject *object, gchar name, GtkSignalFunc func, gpointer func_data);

Παρακάτω δίνονται οι απαραίτητες εξηγήσεις για να καταλάβετε τι κάνει το κάθε όρισμα:

  • GtkObject *object
    Το widget που θα παρακολουθούμε για signal
  • gchar name
    Το signal για το οποίο παρακολουθούμε
  • GtkSignalFunc func
    Η συνάρτηση που θα κληθεί όταν γίνει trap στο σινιάλο που παρακολουθούμε
  • gpointer func_data
    Το όρισμα που θα περάσει στην καλούμενη συνάρτηση (μπορεί να είναι πχ, το πλήκτρο που πατήθηκε)
Το τρίτο όρισμα, είναι μια συνάρτηση που δέχεται σαν ορίσματα ένα δείκτη (pointer) στο widget από το οποίο προκλήθηκε το signal, και ένα δείκτη που αναφέρεται στο τέταρτο όρισμα της καλούσας συνάρτησης (το func_data δηλαδή). ώστε να ξέρει τι να κάνει με τα δεδομένα που της εστάλησαν (ΜΠΕΡΔΕΥΤΗΚΑΤΕ;)

4.3 Γεια σας μάγκες! Το πρώτο πρόγραμμα σε gtk

Επειδή ήδη αρχίσαμε να κολυμπάμε σε βαθύτερα νερά, ας φτιάξουμε ένα μικρό προγραμματάκι, και ας το εξηγήσουμε στη συνέχεια. Κάντε copy-paste, σε ένα αρχείο το παρακάτω:


#include <gtk/gtk.h>
void hello(GtkWidget *widget, gpointer data){
    g_print("Hello Magez!");
    }

gint del_eve(GtkWidget *widget, GdkEvent *event, gpointer data){
    g_print("close pressed\n");
    return(TRUE);
    }

void dest(GtkWidget *widget, gpointer data){
    gtk_main_quit();
    }

int main(int argc, char *argv[]){
    GtkWidget *window, *button;

    gtk_init(&argc, &argv);

    window = gtk_window_new(GTK_WINDOW_TOPLEVEL); 

    gtk_signal_connect(GTK_OBJECT(window), "delete_event", GTK_SIGNAL_FUNC(del_eve), NULL);         
    gtk_signal_connect(GTK_OBJECT(window), "destroy", GTK_SIGNAL_FUNC(dest), NULL);

    gtk_container_set_border_width(GTK_CONTAINER(window), 10);

    button = gtk_button_new_with_label("Hello Magez");

    gtk_signal_connect(GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(hello), NULL);
    gtk_signal_connect_object(GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(gtk_widget_destroy), GTK_OBJECT(window));

    gtk_container_add(GTK_CONTAINER(window), button);

    gtk_widget_show(button);
    gtk_widget_show(window);

    gtk_main();

    return(0);
    }

Ονομάστε το αρχείο HelloMagez.c και αποθηκεύστε το κάπου που έχετε δικαιώματα (πχ. στο home directory σας). Παρακάτω θα δούμε πως μπορούμε να δημιουργήσουμε το εκτελέσιμο αρχείο.

4.4 Εξήγηση του Hello Magez

Ας δούμε τώρα πως δουλεύει. Θα εξηγούμε το πρόγραμμα με μικρά βήματα. Αλλά ας μην αρχίσουμε από την αρχή, πάμε κατευθείαν στην main.

  • GtkWidget *window, *button;
    Εδώ ορίζουμε ότι θα χρησιμοποιήσουμε δύο widgets, με ονόματα window και button
  • gtk_init(&argc, &argv);
    Η γνωστή gtk_init. Την αναφέραμε προηγουμένως.
  • window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    Ορισμός στο widget window ενός νέου παραθύρου τύπου GTK_WINDOW_TOPLEVEL, δηλαδή κανονικού παραθύρου προγράμματος.
  • gtk_signal_connect(GTK_OBJECT(window), "delete_event", GTK_SIGNAL_FUNC(del_eve), NULL);
    Εδώ αρχίζουν τα δύσκολα. Μη φοβηθείτε όμως, θα τα εξηγήσουμε όλα. Αυτό που κάνουμε, είναι να συνδέσουμε το συμβάν delete_event με την συνάρτηση del_eve. To delete_event το στέλνει ο window manager που χρησιμοποιούμε όταν πατήσουμε το close, ή αντίστοιχα το κουμπί close στην bar του προγράμματος. Γιατί το παγιδεύουμε αυτό; Μα γιατί ίσως να θέλουμε να κάνουμε κάποιες εργασίες πριν να κλείσουμε το παράθυρο, πχ. να αποθηκεύσουμε ένα αρχείο ρυθμίσεων, ή να εμφανίσουμε ένα μήνυμα του τύπου ``Είστε σίγουρος;''
    To GTK_OBJECT και το GTK_SIGNAL_FUNC είναι ουσιαστικά μακροεντολές, που ελέγχουν αν είναι σωστές οι παράμετροι που περνάμε στην gtk_signal_connect και (σύμφωνα με μερικούς-μερικούς) βοηθάει να είναι ο κώδικας πιο ευανάγνωστος και πιο κατανοητός.
  • gtk_signal_connect(GTK_OBJECT(window), "destroy", GTK_SIGNAL_FUNC(dest), NULL);
    `Aλλη μια σύνδεση. Σε αυτή συνδέουμε το συμβάν destroy με τη συνάρτηση dest. Το συμβάν destroy συμβαίνει όταν δίνουμε στο delete_event την τιμή FALSE, ή όταν καλούμε το gtk_widget_destroy() που είναι μια συνάρτηση στην οποία περνάμε σαν παράμετρο το όνομα του παραθύρου που θέλουμε να καταστρέψουμε (κλείσουμε). Με αυτό τον τρόπο, με μια συνάρτηση, ελέγχουμε και τις δύο περιπτώσεις.
  • gtk_container_set_border_width(GTK_CONTAINER(window), 10);
    Αυτή η εντολή, απλά θέτει μια ιδιότητα για ένα αντικείμενο. Συγκεκριμένα, την ιδιότητα border στο widget window, που είναι ο χώρος γύρω από το παράθυρο που μένει ανεκμετάλλευτος και δεν μπορεί να χρησιμοποιηθεί από άλλα widgets. Αυτό το κάνουμε για αισθητικούς λόγους.
    Το GTK_CONTAINER είναι και αυτό μια μακροεντολή, για type casting, όπως τα GTK_OBJECT και GTK_SIGNAL_FUNC.
  • button = gtk_button_new_with_label("Hello Magez");
    Συνάρτηση για τη δημιουργία ενός κουμπιού που γράφει "Hello Magez". Φυσικά, κουμπιά μπορούν να δημιουργηθούν και αλλιώς, χωρίς να είμαστε αναγκασμένοι να τους δώσουμε ένα κείμενο, σε περίπτωση πχ. που το κείμενο θα εξαρτάται από κάποιες μεταβλητές, ή θα πρέπει να αλλάξει μετά από λίγο.
  • gtk_signal_connect(GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(hello), NULL);
    Συνδέουμε το click στο κουμπί, με τη συνάρτηση hello. Αυτό είναι πολύ απλό, και εύκολο στην κατανόηση.
  • gtk_signal_connect(GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(gtk_widget_destroy), GTK_OBJECT(window));
    Να και κάτι καινούριο. Με αυτή την εντολή, παρατηρούμε ότι μπορούμε να συνδέσουμε πολλές συναρτήσεις με ένα event (στην περίπτωση αυτή με το click του ποντικιού στο κουμπί button. Αυτό που θα συμβεί είναι ότι πρώτα θα καλέσουμε τη συνάρτηση hello και αμέσως μετά τη συνάρτηση gtk_widget_destroy που είναι μια συνάρτηση που ``καταστρέφει'' το widget που της δίνεται σαν όρισμα, στη συγκεκριμένη περίπτωση το window, που είναι το παράθυρο του προγράμματός μας.
    Θα παρατηρήσατε ότι εδώ χρησιμοποιείται η gtk_signal_connect_object αντί της gtk_signal_connect. Αυτό συμβαίνει γιατί πρέπει να περάσουμε σαν όρισμα το widget που πρέπει να καταστραφεί. Περισσότερα για τα signals αργότερα.
  • gtk_container_add(GTK_CONTAINER(window), button);
    Κι άλλα καινούρια! η gtk_container_add είναι μια συνάρτηση που προσθέτει ένα widget σε ένα container. Εδώ, widget=button και container=window.
    Σημειώστε ότι ένα gtk_container μπορεί να περιέχει μόνο ένα widget. Υπάρχουν όμως άλλα widgets, που πάνω τους μπορούν να φιλοξενούν πολλά άλλα widgets. Αυτό, στην ενότητα των widgets θα αναλυθεί πολύ καλύτερα. Προς το παρών αρκεστείτε σε αυτό, και τυχόν απορίες σας θα λυθούν παρακάτω.
  • gtk_widget_show(button);
    Αυτή η συνάρτηση εμφανίζει το widget που δέχεται σαν όρισμα. Παρατηρείστε ότι τα widgets, δεν εμφανίζονται μόνα τους. Δεν φτάνει δηλαδή η συνάρτηση gtk_container_add για να εμφανιστεί. Πρέπει πρώτα να προστεθούν όλα, και μετά να εμφανιστούν. Αυτό, μας προστατεύει από συμπεριφορές του τύπου να γίνονται όλα render στην οθόνη ένα-ένα το οποίο οπτικά είναι πολύ άσχημο, πιστέψτε με. Εμφανίζουμε λοιπόν με την συγκεκριμένη εντολή το κουμπάκι.
  • gtk_widget_show(window);
    `Οπως και το παραπάνω, μόνο που εδώ εμφανίζουμε το παράθυρο.
  • gtk_main();
    Περνάμε τον έλεγχο στην gtk_main όπως περιγράψαμε προηγουμένως.
  • retutn(0);
    Είναι το exit status του προγράμματός μας. Θα μπορούσαμε με κάποιο έλεγχο να είχαμε διαφορετικό exit status, ανάλογα με το αν το πρόγραμμα απέτυχε, αν το πρόγραμμα δεν ολοκληρώθηκε, κ.λπ.

Και ας ρίξουμε μια γρήγορη ματιά στις συναρτήσεις που περιέχει το πρόγραμμα. Αν και είναι πολύ εύκολες στην κατανόηση, υπάρχουν κάνα-δυο σημεία που χρειάζονται εξήγηση.

  • void hello(GtkWidget *widget, gpointer data)
    `Οπως βλέπουμε, είναι μια συνάρτηση που απλά καλείται όταν πατήσει ο χρήστης το πλήκτρο, και το μόνο που κάνει είναι να τυπώνει Hello Magez και να προσθέτει μια νέα γραμμή.
  • gint del_eve(GtkWidget *widget, GdkEvent *event, gpointer data)
    Η συνάρτηση που καλείται όταν πατηθεί το κουμπί που τερματίζει το πρόγραμμα. Επιστρέφει τιμή ακεραίου, και όπως βλέπουμε (στο συγκεκριμένο παράδειγμα τουλάχιστον) επιστρέφει TRUE. Αυτός είναι και ο λόγος που δεν έχουμε έξοδο από το πρόγραμμα όταν -θεωρητικά- το κλείνουμε με το κουμπί κλεισίματος. Αν η τιμή που επιστρέφει αλλαχθεί σε FALSE, τότε θα έχουμε έξοδο από το πρόγραμμα (δοκιμάστε το). Αυτό συμβαίνει, γιατί η συνάρτηση που καλείται από την delete_event εξ ορισμού από τo gtk επιστρέφει μια τιμή που αν είναι FALSE, καλεί το event destroy (θυμηθείτε ότι το έχουμε συνδέσει με τη συνάρτηση dest)
  • void dest(GtkWidget *widget, gpointer data)
    Με τη συνάρτηση αυτή έχουμε έξοδο από το πρόγραμμα, μέσω μιας ενσωματωμένης στο gtk συνάρτησης, της gtk_main_quit

4.5 Πως θα το τρέξετε.

Για να κάνετε compile to πρόγραμμα, χρησιμοποιήστε την εντολή:


gcc -Wall -g HelloMagez.c -o hellomagez `gtk-config --cflags` `gtk-config --libs`

`Οσοι δεν ξέρουν τι είναι αυτό, man gcc. Μόλις ολοκληρωθεί η παραπάνω εντολή, στο τρέχον directory θα έχει δημιουργηθεί το αρχείο hellomagez, που είναι και το εκτελέσιμο. Τρέξτε το!

Επόμενο  Προηγούμενο  Περιεχόμενα


Valid HTML 4.01!   Valid CSS!