Επόμενο Προηγούμενο Περιεχόμενα
Ας ξεκινήσουμε με τις αρχικές έννοιες, που στη συνέχεια
θα μας βοηθήσουν στην κατανόηση του προγραμματισμού σε
gtk.
Κάθε πρόγραμμα που θα φτιάχνουμε, πρέπει να περιέχει,
μεταξύ άλλων, και τα εξής:
Πρώτα από όλα, πρέπει να περιέχει τις βιβλιοθήκες που θα
κάνουμε 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. Φυσικά, για να γραφεί ένα
ολοκληρωμένο και λειτουργικό πρόγραμμα απαιτούνται πολύ
περισσότερα που θα δούμε στη συνέχεια, γιαύτο μη
βιάζεστε... Συνεχίστε το διάβασμα για την βουτά στα
βαθιά... )))
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
δηλαδή). ώστε να ξέρει τι να κάνει με τα δεδομένα που της
εστάλησαν (ΜΠΕΡΔΕΥΤΗΚΑΤΕ;)
Επειδή ήδη αρχίσαμε να κολυμπάμε σε βαθύτερα νερά, ας
φτιάξουμε ένα μικρό προγραμματάκι, και ας το εξηγήσουμε
στη συνέχεια. Κάντε 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 σας).
Παρακάτω θα δούμε πως μπορούμε να δημιουργήσουμε το
εκτελέσιμο αρχείο.
Ας δούμε τώρα πως δουλεύει. Θα εξηγούμε το πρόγραμμα με
μικρά βήματα. Αλλά ας μην αρχίσουμε από την αρχή, πάμε
κατευθείαν στην 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
Για να κάνετε compile to πρόγραμμα, χρησιμοποιήστε την
εντολή:
gcc -Wall -g HelloMagez.c -o hellomagez `gtk-config --cflags` `gtk-config --libs`
`Οσοι δεν ξέρουν τι είναι αυτό, man gcc. Μόλις
ολοκληρωθεί η παραπάνω εντολή, στο τρέχον directory θα
έχει δημιουργηθεί το αρχείο hellomagez, που είναι
και το εκτελέσιμο. Τρέξτε το!
Επόμενο Προηγούμενο Περιεχόμενα