💾 Archived View for magaz.hellug.gr › 20 › 04_gtk › index.gmi captured on 2024-08-18 at 17:40:18. Gemini links have been rewritten to link to archived content
⬅️ Previous capture (2024-02-05)
-=-=-=-=-=-=-
Παπαδογιαννάκης Βαγγέλης για το Magaz ( magaz.hellug.gr(http://magaz.hellug.gr) ) Οκτ 1999
GTK... Gimp ToolΚit. Αυτό το πρόγραμμα τα άρχισε όλα. Από τη στιγμή που βγήκε το gimp, δημιουργήθηκαν ένα σωρό εφαρμογές που χρησιμοποιούν τις βιβλιοθήκες απεικόνισής του, οι οποίες σημειωτέον είναι από τις καλύτερες που υπάρχουν. Πάνω σε αυτή βασίζεται ολόκληρο το gnome, και είναι από τα προτιμούμενα της REDHAT και άλλων φυσικά διανομών. `Ελα όμως που ο προγραμματισμός σε GTK είναι σχετικά δύσκολος... Μη μασάτε, δεν είναι και τόσο όσο νομίζετε... Είναι ευκολούτσικος, και δεν θα σας πάρει πάνω από 10 λεπτά διάβασμα για να καταφέρετε να στήσετε το πρώτο σας προγραμματάκι σε GTK. Πολλά από τα σημεία αυτού του άρθρου, έχουν βασιστεί στα tutorials της gtk. όπως αυτά έρχονται με το gtk-devel.rpm πακέτο. Για περισσότερες πληροφορίες σε σημεία του κειμένου, παρακαλώ ρίξτε τους μια ματιά.
1. Περί αυτού του άρθρου. (the boring stuff)
2. Που θα βρείτε το GTK
3. Προϋποθέσεις
4. Βασικές έννοιες προγραμματισμού σε gtk
5. Tοποθέτηση των widgets
Αυτό το άρθρο, δεν έχει σκοπό να σας μάθει gtk, αλλά να σας εξοικειώσει με τις δυσκολότερες έννοιες στο toolkit αυτό, ώστε να αρχίσετε μόνοι σας να προγραμματίζετε. Σε καμία περίπτωση δεν είμαι υπεύθυνος εγώ ειδικά και το magaz γενικότερα αν κάνετε μπάχαλο τον υπολογιστή σας. Στο δικό μου δουλεύουν όλα μια χαρά, και δεν έπαθε τίποτα από όσα γράφω σε αυτό το άρθρο.
`Εχω προγραμματίσει να ολοκληρωθεί σε τρεις συνέχειες. Πολύ πιθανόν και 4. Αυτό δεν σημαίνει ότι είμαι υποχρεωμένος να συνεχίσω και με τα άλλα, αλλά δεσμεύομαι να προσπαθήσω. Και αυτό το λεω, γιατί στο παρελθόν είχα κάνει κάτι αντίστοιχό που δεν ολοκληρώθηκε (bash, enlightenment) αλλά είχα δικαιολογία και για τα δύο. Μπορεί κάτι να συμβεί και να μη συνεχιστεί. Σε μια τέτοια περίπτωση, παρακαλώ όποιος έχει την όρεξη να συνεχίσει.
Επίσης, δεν υπάρχει ούτε ΜΙΑ πιθανότητα να μην έχω κάνει εκφραστικά -και ίσως νοηματικά- λαθάκια. Είναι η δυσκολία που υπάρχει στη μετάφραση αυτού που θέλουμε να εκφράσουμε, καθώς και η μεταφορά του σε λέξεις. `Οσοι από εσάς προγραμματίζουν, ή έχουν τέλος πάντων μια εξοικείωση με την αγγλική ορολογία, καταλαβαίνετε τι θέλω να πω. Να είσαστε λοιπόν ελαστικοί στην κρίση σας.
`Οσοι από εσάς ασχολούνται με gtk και βρουν τίποτα ανακρίβειες (πράγμα πολύ πιθανό γιατί ναι μεν προγραμματίζω σε gtk αλλά δεν χρησιμοποιώ όλα όσα εξηγώ σε αυτό το άρθρο), παρακαλώ να μου στείλετε με mail στο papas@hellug.gr[1] τις παρατηρήσεις σας, και θα προσπαθήσω να διορθώσω τα λάθη σε ένα από τα επόμενα άρθρα.
Καλό διάβασμα, και happy programming!
Κατά πάσα πιθανότητα, το έχετε ήδη εγκατεστημένο στον υπολογιστή σας. Για όσους από εσάς αρνούνται να εγκαταστήσουν το GTK, επιμένοντας σε υποκατάστατα του τύπου QT - καλά, μη βαράτε :) - σας παραπέμπω στη διεύθυνση http://www.gtk.org[2] να το κατεβάσετε σε ότι μορφή θέλετε. Αν θέλετε rpm, μην ξεχάσετε να πάρετε και το devel πακέτο, γιατί αυτό ουσιαστικά θα μας χρειαστεί ώστε να κάνουμε τα προγράμματά μας να τρέχουν.
Δεν υπάρχουν ουσιαστικές προϋποθέσεις. Απλά θα πρέπει να ξέρετε τα βασικά του προγραμματισμού, λίγη C, και να έχετε πολύ όρεξη. Επίσης, θα πρέπει να έχετε στο PATH σας το script gtk-config, για να αποφύγουμε να βρίσκουμε λεπτομέρειες που είναι διαφορετικές για κάθε μηχάνημα. Περισσότερες πληροφορίες θα βρείτε στο man page του gtk-config (man gtk-config)
Ας ξεκινήσουμε με τις αρχικές έννοιες, που στη συνέχεια θα μας βοηθήσουν στην κατανόηση του προγραμματισμού σε 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...
Πριν αρχίσουμε, να δούμε μερικά πράγματα, όπως πχ. τι είναι το gtk_widget που θα συναντάμε κατ κόρον. Είναι μια δομή, που μέσω αυτής μπορούμε να έχουμε πρόσβαση σε όλα τα widgets της gtk. Αυτά μπορεί να είναι buttons, radio buttons, check buttons, lists, combo boxes, boxes, toolbars, και γενικότερα οτιδήποτε βλέπετε στα παραθυράκια των προγραμμάτων gtk.
Ο έλεγχος σε ένα πρόγραμμα gtk δίδεται χρησιμοποιώντας τα signals. Ας σας εξηγήσω όμως με ένα παράδειγμα.
Για να συνδέσουμε ένα συμβάν με μια λειτουργία, μπορούμε να χρησιμοποιούμε μια συνάρτηση όπως η gtk_signal_connect.
Αυτή, συντάσσεται όπως βλέπουμε παρακάτω, και επιστρέφει μια τιμή τύπου gint (Μια μορφή ακεραίου που χρησιμοποιεί η gtk).
gint gtk_signal_connect(GtkObject *object, gchar name, GtkSignalFunc func, gpointer func_data);
Παρακάτω δίνονται οι απαραίτητες εξηγήσεις για να καταλάβετε τι κάνει το κάθε όρισμα:
Το widget που θα παρακολουθούμε για signal
Το signal για το οποίο παρακολουθούμε
Η συνάρτηση που θα κληθεί όταν γίνει trap στο σινιάλο που παρακολουθούμε
Το όρισμα που θα περάσει στην καλούμενη συνάρτηση (μπορεί να είναι πχ, το πλήκτρο που πατήθηκε)
Το τρίτο όρισμα, είναι μια συνάρτηση που δέχεται σαν ορίσματα ένα δείκτη (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.
Εδώ ορίζουμε ότι θα χρησιμοποιήσουμε δύο widgets, με ονόματα window και button
Η γνωστή gtk_init. Την αναφέραμε προηγουμένως.
Ορισμός στο widget window ενός νέου παραθύρου τύπου GTK_WINDOW_TOPLEVEL, δηλαδή κανονικού παραθύρου προγράμματος.
Εδώ αρχίζουν τα δύσκολα. Μη φοβηθείτε όμως, θα τα εξηγήσουμε όλα. Αυτό που κάνουμε, είναι να συνδέσουμε το συμβάν delete_event με την συνάρτηση del_eve. To delete_event το στέλνει ο window manager που χρησιμοποιούμε όταν πατήσουμε το close, ή αντίστοιχα το κουμπί close στην bar του προγράμματος. Γιατί το παγιδεύουμε αυτό; Μα γιατί ίσως να θέλουμε να κάνουμε κάποιες εργασίες πριν να κλείσουμε το παράθυρο, πχ. να αποθηκεύσουμε ένα αρχείο ρυθμίσεων, ή να εμφανίσουμε ένα μήνυμα του τύπου ``Είστε σίγουρος;''
To GTK_OBJECT και το GTK_SIGNAL_FUNC είναι ουσιαστικά μακροεντολές, που ελέγχουν αν είναι σωστές οι παράμετροι που περνάμε στην gtk_signal_connect και (σύμφωνα με μερικούς-μερικούς) βοηθάει να είναι ο κώδικας πιο ευανάγνωστος και πιο κατανοητός.
`Aλλη μια σύνδεση. Σε αυτή συνδέουμε το συμβάν destroy με τη συνάρτηση dest. Το συμβάν destroy συμβαίνει όταν δίνουμε στο delete_event την τιμή FALSE, ή όταν καλούμε το gtk_widget_destroy() που είναι μια συνάρτηση στην οποία περνάμε σαν παράμετρο το όνομα του παραθύρου που θέλουμε να καταστρέψουμε (κλείσουμε). Με αυτό τον τρόπο, με μια συνάρτηση, ελέγχουμε και τις δύο περιπτώσεις.
Αυτή η εντολή, απλά θέτει μια ιδιότητα για ένα αντικείμενο. Συγκεκριμένα, την ιδιότητα border στο widget window, που είναι ο χώρος γύρω από το παράθυρο που μένει ανεκμετάλλευτος και δεν μπορεί να χρησιμοποιηθεί από άλλα widgets. Αυτό το κάνουμε για αισθητικούς λόγους.
Το GTK_CONTAINER είναι και αυτό μια μακροεντολή, για type casting, όπως τα GTK_OBJECT και GTK_SIGNAL_FUNC.
Συνάρτηση για τη δημιουργία ενός κουμπιού που γράφει "Hello Magez". Φυσικά, κουμπιά μπορούν να δημιουργηθούν και αλλιώς, χωρίς να είμαστε αναγκασμένοι να τους δώσουμε ένα κείμενο, σε περίπτωση πχ. που το κείμενο θα εξαρτάται από κάποιες μεταβλητές, ή θα πρέπει να αλλάξει μετά από λίγο.
Συνδέουμε το click στο κουμπί, με τη συνάρτηση hello. Αυτό είναι πολύ απλό, και εύκολο στην κατανόηση.
Να και κάτι καινούριο. Με αυτή την εντολή, παρατηρούμε ότι μπορούμε να συνδέσουμε πολλές συναρτήσεις με ένα event (στην περίπτωση αυτή με το click του ποντικιού στο κουμπί button. Αυτό που θα συμβεί είναι ότι πρώτα θα καλέσουμε τη συνάρτηση hello και αμέσως μετά τη συνάρτηση gtk_widget_destroy που είναι μια συνάρτηση που ``καταστρέφει'' το widget που της δίνεται σαν όρισμα, στη συγκεκριμένη περίπτωση το window, που είναι το παράθυρο του προγράμματός μας.
Θα παρατηρήσατε ότι εδώ χρησιμοποιείται η gtk_signal_connect_object αντί της gtk_signal_connect. Αυτό συμβαίνει γιατί πρέπει να περάσουμε σαν όρισμα το widget που πρέπει να καταστραφεί. Περισσότερα για τα signals αργότερα.
Κι άλλα καινούρια! η gtk_container_add είναι μια συνάρτηση που προσθέτει ένα widget σε ένα container. Εδώ, widget=button και container=window.
Σημειώστε ότι ένα gtk_container μπορεί να περιέχει μόνο ένα widget. Υπάρχουν όμως άλλα widgets, που πάνω τους μπορούν να φιλοξενούν πολλά άλλα widgets. Αυτό, στην ενότητα των widgets θα αναλυθεί πολύ καλύτερα. Προς το παρών αρκεστείτε σε αυτό, και τυχόν απορίες σας θα λυθούν παρακάτω.
Αυτή η συνάρτηση εμφανίζει το widget που δέχεται σαν όρισμα. Παρατηρείστε ότι τα widgets, δεν εμφανίζονται μόνα τους. Δεν φτάνει δηλαδή η συνάρτηση gtk_container_add για να εμφανιστεί. Πρέπει πρώτα να προστεθούν όλα, και μετά να εμφανιστούν. Αυτό, μας προστατεύει από συμπεριφορές του τύπου να γίνονται όλα render στην οθόνη ένα-ένα το οποίο οπτικά είναι πολύ άσχημο, πιστέψτε με. Εμφανίζουμε λοιπόν με την συγκεκριμένη εντολή το κουμπάκι.
`Οπως και το παραπάνω, μόνο που εδώ εμφανίζουμε το παράθυρο.
Περνάμε τον έλεγχο στην gtk_main όπως περιγράψαμε προηγουμένως.
Είναι το exit status του προγράμματός μας. Θα μπορούσαμε με κάποιο έλεγχο να είχαμε διαφορετικό exit status, ανάλογα με το αν το πρόγραμμα απέτυχε, αν το πρόγραμμα δεν ολοκληρώθηκε, κ.λπ.
Και ας ρίξουμε μια γρήγορη ματιά στις συναρτήσεις που περιέχει το πρόγραμμα. Αν και είναι πολύ εύκολες στην κατανόηση, υπάρχουν κάνα-δυο σημεία που χρειάζονται εξήγηση.
`Οπως βλέπουμε, είναι μια συνάρτηση που απλά καλείται όταν πατήσει ο χρήστης το πλήκτρο, και το μόνο που κάνει είναι να τυπώνει Hello Magez και να προσθέτει μια νέα γραμμή.
Η συνάρτηση που καλείται όταν πατηθεί το κουμπί που τερματίζει το πρόγραμμα. Επιστρέφει τιμή ακεραίου, και όπως βλέπουμε (στο συγκεκριμένο παράδειγμα τουλάχιστον) επιστρέφει TRUE. Αυτός είναι και ο λόγος που δεν έχουμε έξοδο από το πρόγραμμα όταν -θεωρητικά- το κλείνουμε με το κουμπί κλεισίματος. Αν η τιμή που επιστρέφει αλλαχθεί σε FALSE, τότε θα έχουμε έξοδο από το πρόγραμμα (δοκιμάστε το). Αυτό συμβαίνει, γιατί η συνάρτηση που καλείται από την delete_event εξ ορισμού από τo gtk επιστρέφει μια τιμή που αν είναι FALSE, καλεί το event destroy (θυμηθείτε ότι το έχουμε συνδέσει με τη συνάρτηση dest)
Με τη συνάρτηση αυτή έχουμε έξοδο από το πρόγραμμα, μέσω μιας ενσωματωμένης στο gtk συνάρτησης, της gtk_main_quit
Για να κάνετε compile to πρόγραμμα, χρησιμοποιήστε την εντολή:
gcc -Wall -g HelloMagez.c -o hellomagez `gtk-config --cflags` `gtk-config --libs`
`Οσοι δεν ξέρουν τι είναι αυτό, man gcc. Μόλις ολοκληρωθεί η παραπάνω εντολή, στο τρέχον directory θα έχει δημιουργηθεί το αρχείο hellomagez, που είναι και το εκτελέσιμο. Τρέξτε το!
Το πιο πιθανό είναι να μην δημιουργήσουμε ποτέ μια εφαρμογή που θα έχει μονάχα ένα widget, όπως στην περίπτωση του HelloMagez. Μάλλον, αυτό που θα θέλουμε να κάνουμε θα αποτελείται από περισσότερα από ένα widget. Ας δούμε πως μπορούμε να τα τοποθετήσουμε στο παράθυρο του προγράμματός μας.
Τα boxes θα γίνουν οι καλύτεροι φίλοι μας στη διαδικασία σύνταξης εφαρμογών για gtk, αλλά - πιστέψτε με - και οι χειρότεροί μας εχθροί. Θα μάθουμε να ζούμε με τα boxes, και να είστε σίγουροι ότι δεν θα τα γλιτώσουμε. Τι είναι όμως αυτά τα boxes;
Είναι widgets, όπως και όλα τα άλλα. Απλά, μπορούν να περιέχουν περισσότερα από 1 widgets πάνω τους. Δεν είναι ορατά, και χρησιμοποιούνται μονάχα για την ομαδοποίηση αντικειμένων. Τα boxes μπορεί να είναι δύο ειδών, ανάλογα με τον προσανατολισμό που θέλουμε να πάρουν τα προς τοποθέτηση σε αυτό widgets:
Τα τοποθετημένα σε αυτό widgets θα μπουν στη σειρά από αριστερά προς τα δεξιά ή από τα δεξιά προς τα αριστερά όπως θα δούμε παρακάτω στην εξήγηση των συναρτήσεων gtk_box_pack_start και gtk_box_pack_end.
H δημιουργία ενός τέτοιου box επιτυγχάνεται με τη χρήση της συνάρτησης gtk_hbox_new.
Τα τοποθετημένα σε αυτό widgets θα μπουν στη σειρά από πάνω προς τα κάτω ή από κάτω προς τα πάνω, πάλι με χρήση των gtk_box_pack_start και gtk_box_pack_end.
Σε γενικές γραμμές, η δημιουργία ενός τέτοιου box επιτυγχάνεται με τη χρήση της συνάρτησης gtk_hbox_new.
Για τη δημιουργία ενός box, χρησιμοποιούμε τις gtk_hbox_new και gtk_vbox_new όπως αναφέραμε και παραπάνω.
Η σύνταξη και ο τύπος των συναρτήσεων αυτών είναι:
GtkWidget *gtk_hbox_new(gint homogeneous, gint spacing); GtkWidget *gtk_vbox_new(gint homogeneous, gint spacing);
Στη συνέχεια θα αναφέρομαι μονάχα στην εντολή gtk_hbox_new και ότι ισχύει για αυτήν ισχύει και για την gtk_vbox_new. `Οπως παρατηρούμε, επιστρέφει τιμή τύπου GtkWidget, και παίρνει δύο τιμές, τύπου gint, τις homogenous και spacing.
Η homogenous μπορεί να είναι TRUE ή FALSE (και φυσικά οποιαδήποτε λογική έκφραση) και μας δίνει τη δυνατότητα να ελέγξουμε αν τα widgets που θα τοποθετηθούν στο box θα έχουν το ίδιο πλάτος όλα ή όσο χρειάζεται το κάθε ένα.
Η παράμετρος spacing καθορίζει πόσα pixels θα απέχουν μεταξύ τους τα widgets.
Δεν πιστεύω να σας τρόμαξα με τα όσα έγραψα για τα boxes στην προηγούμενη ενότητα... `Οπως θα δείτε, η εργασία με τα boxes είναι μια εύκολη υπόθεση, αν ξέρετε να δουλεύετε τις συναρτήσεις gtk_box_pack_start και gtk_box_pack_end. Στη συνέχεια θα δούμε πόσο εύκολη είναι η εργασία με αυτές τις συναρτήσεις.
gtk_box_pack_start
gtk_box_pack_end
Η μορφή αυτών των συναρτήσεων είναι:
void gtk_box_pack_start(GtkBox *box, GtkWidget *child, gint expand, gint fill, gint padding ); void gtk_box_pack_end(GtkBox *box, GtkWidget *child, gint expand, gint fill, gint padding );
Δεν επιστρέφουν καμία τιμή (είναι void απ' ότι βλέπετε) και τα ορίσματα που δέχονται εξηγούνται παρακάτω:
Το όνομα του box στο οποίο αναφερόμαστε.
Το όνομα του widget το οποίο προσθέτουμε.
Είναι μορφής gint, και αυτό που περιγράφει είναι αν το widget που τοποθετούμε θα πιάνει όλη την περιοχή του box. Μπορούμε να του αποδώσουμε τις τιμές TRUE και FALSE. TRUE στην περίπτωση που θέλουμε να πιάνει όλη την διαθέσιμη περιοχή, και FALSE αν θέλουμε να πιάνει μόνο όση περιοχή του είναι απαραίτητη. Δίδοντας την τιμή FALSE μπορούμε να επιτύχουμε (αριστερή στην περίπτωση της gtk_box_pack_start ή δεξιά στην περίπτωση της gtk_box_pack_end) στοίχιση.
`Οπως καταλαβαίνετε, αν δοθεί η τιμή TRUE, δεν έχει σημασία πια από τις gtk_box_pack_start ή gtk_box_pack_end χρησιμοποιήσουμε.
Η τιμή αυτής της παραμέτρου έχει σημασία μόνο αν η τιμή της expand είναι TRUE. Και αυτό γιατί περιγράφει αν ο επιπλέον χώρος που μένει μετά τη δημιουργία των widgets θα ανήκει σε αυτά (Δηλ. θα τα μεγαλώσει) στην περίπτωση που πάρει την τιμή TRUE ή αν θα ανήκει στο box σαν αδιάθετος χώρος στην περίπτωση που της δώσουμε την τιμή FALSE
Αριθμός τύπου gint, που καθορίζει πόσος χώρος (σε pixels) γύρω από το κάθε widget δεν θα χρησιμοποιείται.
Υπάρχει και άλλος ένας τρόπος για να στοιχίσουμε τα widgets στο παράθυρο της εφαρμογής μας. Αυτός επιτυγχάνεται με τη χρήση της συνάρτησης gtk_table_new που δημιουργεί έναν πίνακα στο παράθυρο της εφαρμογής μας. Φυσικά, και αυτή η συνάρτηση χρησιμοποιείται μόνο για την τοποθέτηση των διαφόρων widgets στο παράθυρο, και δεν σχεδιάζεται κανένας πίνακας στην εφαρμογή μας.
Η σύνταξή της είναι η εξής:
GtkWidget *gtk_table_new(gint rows, gint columns, gint homogeneous );
Είναι προφανές ότι rows είναι ο αριθμός των γραμμών του πίνακά μας και columns ο αριθμός των στηλών του.
Το homogenous περιγράφει τον τρόπο που τα κελιά του πίνακα θα διανέμονται και θα τοποθετούνται πάνω στο παράθυρό μας. Αν είναι TRUE, τότε όλα τα κελιά του πίνακα μεγαλώνουν ή μικραίνουν, σύμφωνα με το μεγαλύτερο widget στον πίνακα. Αν πάρει την τιμή FALSE, το μέγεθος ορίζεται από το πιο μακρύ widget στην κάθε στήλη και το πιο ψηλό widget στην σειρά. Αυτό που ουσιαστικά συμβαίνει είναι ότι αν το HOMOGENEOUS είναι TRUE, όλα τα widgets έχουν το ίδιο μέγεθος, και είναι ομοιόμορφα τοποθετημένα πάνω στο box.
Η διάταξη των κελιών του πίνακα, διαμορφώνεται από το 0 μέχρι τις τιμές rows και columns. Μoιάζει δηλαδή με το παρακάτω. Η αρίθμηση όπως βλέπετε, ξεκινάει από την πάνω αρισ��ερή γωνία.
0 1 2 0+----------+----------+ | | | 1+----------+----------+ | | | 2+----------+----------+
Για την τοποθέτηση ενός widget σε ένα πίνακα, χρησιμοποιούμε την συνάρτηση gtk_table_attach, της οποίας η σύνταξη είναι η παρακάτω
void gtk_table_attach(GtkTable *table, GtkWidget *child, gint left_attach, gint right_attach, gint top_attach, gint bottom_attach, gint xoptions, gint yoptions, gint xpadding, gint ypadding);
Δεν επιστρέφei καμία τιμή και τα ορίσματα που δέχεται είναι
GtkTable *table
Το Table στο οποίο αναφερόμαστε
GtkWidget *child
Το widget που θέλουμε να τοποθετήσουμε
gint left_attach
Η αριστερή συντεταγμένη από όπου θα αρχίζει το widget
gint right_attach
Η δεξιά συντεταγμένη από όπου θα τελειώνει το widget
gint top_attach
Η πάνω συντεταγμένη από όπου θα αρχίζει το widget
gint bottom_attach
Η κάτω συντεταγμένη που θα τελειώνει το widget
gint xoptions
χρησιμοποιείται για τον καθορισμό του τρόπου πακεταρίσματος, και θα το δούμε στη συνέχεια πιο αναλυτικά
gint yoptions
`Οπως και το παραπάνω, και οι τιμές που μπορεί να πάρει τόσο αυτό όσο και το xoptions είναι οι παρακάτω:
* GTK_FILL
Αν η περιοχή (συνήθως το κουτί του πίνακα) είναι μεγαλύτερο από το widget που τοποθετούμε, το widget θα μεγαλώσει τόσο ώστε να καλύψει όλο τον χώρο που υπάρχει.
* GTK_SHRINK
Αν κατά την απόδοση χώρου σε ένα κουτί ο χώρος που δίνεται σε ένα widget είναι μικρότερος από το μέγεθός του ίδιου του widget, τότε αυτό θα μικράνει ώστε να χωράει. Κάτι τέτοιο συμβαίνει όταν ο χρήστης κάνει resize σε ένα παράθυρο. Αν δεν είναι ορισμένο το GTK_SHRINK είναι πολύ πιθανό σε μια τέτοια περίπτωση να μην εμφανίζονται τα widgets μέσα στον χώρο του παραθύρου μας.
* GTK_EXPAND
Με αυτό τον τρόπο μπορούμε να αποδώσουμε στο table μας όλο τον χώρο που απομένει στο παράθυρο μετά τη δημιουργία του.
Οι παραπάνω τιμές που μπορούν να πάρουν, είναι δυνατό να συνδυαστούν με το OR για να καλύψουμε περισσότερες από μια περιπτώσεις.
gint xpadding
Το γνωστό και πολλάκις εξηγημένο padding στον οριζόντιο άξονα
gint ypadding
`Οτι και το παραπάνω, αναφέρεται όμως στον κατακόρυφο άξονα
Για όλους εσάς που όλα αυτά τα ορίσματα στην παραπάνω συνάρτηση σας φάνηκαν πολλά, υπάρχει και μια άλλη, που κάνει την ίδια (περίπου) δουλειά απλούστερα.
void gtk_table_attach_defaults(GtkTable *table, GtkWidget *widget, gint left_attach, gint right_attach, gint top_attach, gint bottom_attach);
`Οπως παρατηρούμε, μόνο οι βασικές παραμέτροι περνάνε στη συνάρτηση. Ουσιαστικά δηλαδή, μόνο τα controls θέσης και μεγέθους. Οι παραμέτροι που δεν αναφέρονται, παίρνουν κάποιες προκαθορισμένες τιμές. Αυτές είναι οι πλέον χρησιμοποιούμενες, και συγκεκριμένα τα X και Y options γίνονται GTK_FILL | GTK_EXPAND, (ελπίζω να ξέρετε ότι το ``|'' είναι το OR) και στα X και Y padding δίδεται η τιμή 0.
Για να καθορίσουμε το spacing ανάμεσα σε συγκεκριμένες γραμμές και στήλες ενός πίνακα, χρησιμοποιούμε τις παρακάτω συναρτήσεις:
void gtk_table_set_row_spacing(GtkTable *table, gint row, gint spacing); void gtk_table_set_col_spacing(GtkTable *table, gint col, gint spacing);
όπου row ή col η γραμμή ή η στήλη στην οποία αναφερόμαστε, και spacing η απόσταση που θέλουμε να ορίσουμε σαν spacing. Για τις στήλες το spacing προστίθεται στα ΔΕΞΙΑ και για τις γραμμές ΚΑΤΩ.
Είναι επίσης δυνατό να καθορίσουμε για ΟΛΕΣ τις στήλες ή γραμμές το spacing, χρησιμοποιώντας τις συναρτήσεις:
void gtk_table_set_row_spacings(GtkTable *table, gint spacing); void gtk_table_set_col_spacings(GtkTable *table, gint spacing);
`Οπως πολύ καλά καταλάβατε, η τελευταία στήλη και η τελευταία σειρά δεν λαμβάνουν αυτή την τιμή, γιατί θα δημιουργούσε ένα κενό στα αριστερά και ένα κενό κάτω.
Είναι φανερό ότι το να δουλεύει κανείς με tables είναι εύκολο. Εγώ προσωπικά δεν το προτιμώ, παρόλο που είναι βασικά η μόνη λύση στην περίπτωση που θέλουμε απόλυτα στοιχισμένα κουτάκια και κουμπάκια και widgets. Δοκιμάστε το πάντως, και πολλές φορές θα σας λύσει τα χέρια
Τον επόμενο μήνα θα συνεχίσουμε αυτό το άρθρο και θα ασχοληθούμε περισσότερο με τα widgets (buttons, radio/check/toggle buttons, text boxes, κ.λπ.) και τις ιδιότητές τους. Μέχρι τότε, πολλά φιλάκια.