518 γραμμές
		
	
	
	
		
			46 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
			
		
		
	
	
			518 γραμμές
		
	
	
	
		
			46 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
+++
 | 
						||
title = 'Προγραμματισμός με GTK(\...)'
 | 
						||
date = '1999-10-01T00:00:00Z'
 | 
						||
description = ''
 | 
						||
author = 'Παπαδογιαννάκης Βαγγέλης για το Magaz ( magaz.hellug.gr(http://magaz.hellug.gr) )'
 | 
						||
issue = ['Magaz 20']
 | 
						||
issue_weight = 4
 | 
						||
+++
 | 
						||
 | 
						||
----------------------------------------------------------------------------------------------------------------------------------------------------------------
 | 
						||
 | 
						||
*GTK\... Gimp ToolΚit. Αυτό το πρόγραμμα τα άρχισε όλα. Από τη στιγμή που βγήκε το gimp, δημιουργήθηκαν ένα σωρό εφαρμογές που χρησιμοποιούν τις βιβλιοθήκες
 | 
						||
απεικόνισής του, οι οποίες σημειωτέον είναι από τις καλύτερες που υπάρχουν. Πάνω σε αυτή βασίζεται ολόκληρο το gnome, και είναι από τα προτιμούμενα της REDHAT
 | 
						||
και άλλων φυσικά διανομών. \`Ελα όμως που ο προγραμματισμός σε GTK είναι σχετικά δύσκολος\... Μη μασάτε, δεν είναι και τόσο όσο νομίζετε\... Είναι
 | 
						||
ευκολούτσικος, και δεν θα σας πάρει πάνω από 10 λεπτά διάβασμα για να καταφέρετε να στήσετε το πρώτο σας προγραμματάκι σε GTK. Πολλά από τα σημεία αυτού του
 | 
						||
άρθρου, έχουν βασιστεί στα tutorials της gtk. όπως αυτά έρχονται με το gtk-devel.rpm πακέτο. Για περισσότερες πληροφορίες σε σημεία του κειμένου, παρακαλώ ρίξτε
 | 
						||
τους μια ματιά.*
 | 
						||
 | 
						||
----------------------------------------------------------------------------------------------------------------------------------------------------------------
 | 
						||
 | 
						||
**1. Περί αυτού του άρθρου. (the boring stuff)**
 | 
						||
----------------------------------------------------------------------
 | 
						||
 | 
						||
**2. Που θα βρείτε το GTK**
 | 
						||
-------------------------------------------------
 | 
						||
 | 
						||
**3. Προϋποθέσεις**
 | 
						||
-----------------------------------------
 | 
						||
 | 
						||
**4. Βασικές έννοιες προγραμματισμού σε gtk**
 | 
						||
-------------------------------------------------------------------
 | 
						||
 | 
						||
-   [4.1 Βασικά χαρακτηριστικά](#ss4.1)
 | 
						||
-   [4.2 widgets, signals, κ.λπ.](#ss4.2)
 | 
						||
-   [4.3 Γεια σας μάγκες! Το πρώτο πρόγραμμα σε gtk](#ss4.3)
 | 
						||
-   [4.4 Εξήγηση του Hello Magez](#ss4.4)
 | 
						||
-   [4.5 Πως θα το τρέξετε.](#ss4.5)
 | 
						||
 | 
						||
**5. Tοποθέτηση των widgets**
 | 
						||
---------------------------------------------------
 | 
						||
 | 
						||
-   [5.1 Boxes, ή αλλιώς κουτιά :)](#ss5.1)
 | 
						||
-   [5.2 Tοποθέτηση των widgets, partII (λέγε με tables)](#ss5.2)
 | 
						||
-   [5.3 Η συνέχεια](#ss5.3)
 | 
						||
 | 
						||
 | 
						||
### [1. Περί αυτού του άρθρου. (the boring stuff)]{#s1}
 | 
						||
 | 
						||
Αυτό το άρθρο, δεν έχει σκοπό να σας μάθει gtk, αλλά να σας εξοικειώσει με τις δυσκολότερες έννοιες στο toolkit αυτό, ώστε να αρχίσετε μόνοι σας να
 | 
						||
προγραμματίζετε. Σε καμία περίπτωση δεν είμαι υπεύθυνος εγώ ειδικά και το magaz γενικότερα αν κάνετε μπάχαλο τον υπολογιστή σας. Στο δικό μου δουλεύουν όλα μια
 | 
						||
χαρά, και δεν έπαθε τίποτα από όσα γράφω σε αυτό το άρθρο.
 | 
						||
 | 
						||
\`Εχω προγραμματίσει να ολοκληρωθεί σε τρεις συνέχειες. Πολύ πιθανόν και 4. Αυτό δεν σημαίνει ότι είμαι υποχρεωμένος να συνεχίσω και με τα άλλα, αλλά δεσμεύομαι
 | 
						||
να προσπαθήσω. Και αυτό το λεω, γιατί στο παρελθόν είχα κάνει κάτι αντίστοιχό που δεν ολοκληρώθηκε (bash, enlightenment) αλλά είχα δικαιολογία και για τα δύο.
 | 
						||
Μπορεί κάτι να συμβεί και να μη συνεχιστεί. Σε μια τέτοια περίπτωση, παρακαλώ όποιος έχει την όρεξη να συνεχίσει.
 | 
						||
 | 
						||
Επίσης, δεν υπάρχει ούτε ΜΙΑ πιθανότητα να μην έχω κάνει εκφραστικά -και ίσως νοηματικά- λαθάκια. Είναι η δυσκολία που υπάρχει στη μετάφραση αυτού που θέλουμε
 | 
						||
να εκφράσουμε, καθώς και η μεταφορά του σε λέξεις. \`Οσοι από εσάς προγραμματίζουν, ή έχουν τέλος πάντων μια εξοικείωση με την αγγλική ορολογία, καταλαβαίνετε
 | 
						||
τι θέλω να πω. Να είσαστε λοιπόν ελαστικοί στην κρίση σας.
 | 
						||
 | 
						||
\`Οσοι από εσάς ασχολούνται με gtk και βρουν τίποτα ανακρίβειες (πράγμα πολύ πιθανό γιατί ναι μεν προγραμματίζω σε gtk αλλά δεν χρησιμοποιώ όλα όσα εξηγώ σε
 | 
						||
αυτό το άρθρο), παρακαλώ να μου στείλετε με [mail στο papas\@hellug.gr](mailto:papas@hellug.gr) τις παρατηρήσεις σας, και θα προσπαθήσω να διορθώσω τα λάθη σε
 | 
						||
ένα από τα επόμενα άρθρα.
 | 
						||
 | 
						||
Καλό διάβασμα, και happy programming!
 | 
						||
 | 
						||
 | 
						||
### [2. Που θα βρείτε το GTK]{#s2}
 | 
						||
 | 
						||
Κατά πάσα πιθανότητα, το έχετε ήδη εγκατεστημένο στον υπολογιστή σας. Για όσους από εσάς αρνούνται να εγκαταστήσουν το GTK, επιμένοντας σε υποκατάστατα του
 | 
						||
τύπου QT - καλά, μη βαράτε :) - σας παραπέμπω στη διεύθυνση <http://www.gtk.org> να το κατεβάσετε σε ότι μορφή θέλετε. Αν θέλετε **rpm**, μην ξεχάσετε να πάρετε
 | 
						||
και το **devel** πακέτο, γιατί αυτό ουσιαστικά θα μας χρειαστεί ώστε να κάνουμε τα προγράμματά μας να τρέχουν.
 | 
						||
 | 
						||
 | 
						||
### [3. Προϋποθέσεις]{#s3}
 | 
						||
 | 
						||
Δεν υπάρχουν ουσιαστικές προϋποθέσεις. Απλά θα πρέπει να ξέρετε τα βασικά του προγραμματισμού, λίγη C, και να έχετε πολύ όρεξη. Επίσης, θα πρέπει να έχετε στο
 | 
						||
PATH σας το script **gtk-config**, για να αποφύγουμε να βρίσκουμε λεπτομέρειες που είναι διαφορετικές για κάθε μηχάνημα. Περισσότερες πληροφορίες θα βρείτε στο
 | 
						||
**man page** του gtk-config (`man gtk-config`)
 | 
						||
 | 
						||
 | 
						||
### [4. Βασικές έννοιες προγραμματισμού σε gtk]{#s4}
 | 
						||
 | 
						||
Ας ξεκινήσουμε με τις αρχικές έννοιες, που στη συνέχεια θα μας βοηθήσουν στην κατανόηση του προγραμματισμού σε gtk.
 | 
						||
 | 
						||
### [4.1 Βασικά χαρακτηριστικά]{#ss4.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, κ.λπ.]{#ss4.2}
 | 
						||
 | 
						||
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]{#ss4.3}
 | 
						||
 | 
						||
Επειδή ήδη αρχίσαμε να κολυμπάμε σε βαθύτερα νερά, ας φτιάξουμε ένα μικρό προγραμματάκι, και ας το εξηγήσουμε στη συνέχεια. Κάντε 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]{#ss4.4}
 | 
						||
 | 
						||
Ας δούμε τώρα πως δουλεύει. Θα εξηγούμε το πρόγραμμα με μικρά βήματα. Αλλά ας μην αρχίσουμε από την αρχή, πάμε κατευθείαν στην 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 Πως θα το τρέξετε.]{#ss4.5}
 | 
						||
 | 
						||
Για να κάνετε compile to πρόγραμμα, χρησιμοποιήστε την εντολή:
 | 
						||
 | 
						||
----------------------------------------------------------------------------------------------------------------------------------------------------------------
 | 
						||
 | 
						||
    gcc -Wall -g HelloMagez.c -o hellomagez `gtk-config --cflags` `gtk-config --libs`
 | 
						||
 | 
						||
----------------------------------------------------------------------------------------------------------------------------------------------------------------
 | 
						||
 | 
						||
\`Οσοι δεν ξέρουν τι είναι αυτό, **man gcc**. Μόλις ολοκληρωθεί η παραπάνω εντολή, στο τρέχον directory θα έχει δημιουργηθεί το αρχείο **hellomagez**, που είναι
 | 
						||
και το εκτελέσιμο. Τρέξτε το!
 | 
						||
 | 
						||
 | 
						||
### [5. Tοποθέτηση των widgets]{#s5}
 | 
						||
 | 
						||
Το πιο πιθανό είναι να μην δημιουργήσουμε ποτέ μια εφαρμογή που θα έχει μονάχα ένα *widget*, όπως στην περίπτωση του **HelloMagez**. Μάλλον, αυτό που θα θέλουμε
 | 
						||
να κάνουμε θα αποτελείται από περισσότερα από ένα *widget*. Ας δούμε πως μπορούμε να τα τοποθετήσουμε στο παράθυρο του προγράμματός μας.
 | 
						||
 | 
						||
### [5.1 Boxes, ή αλλιώς κουτιά :)]{#ss5.1}
 | 
						||
 | 
						||
Τα *boxes* θα γίνουν οι καλύτεροι φίλοι μας στη διαδικασία σύνταξης εφαρμογών για **gtk**, αλλά - πιστέψτε με - και οι χειρότεροί μας εχθροί. Θα μάθουμε να
 | 
						||
ζούμε με τα *boxes*, και να είστε σίγουροι ότι δεν θα τα γλιτώσουμε. Τι είναι όμως αυτά τα *boxes*;\
 | 
						||
Είναι widgets, όπως και όλα τα άλλα. Απλά, μπορούν να περιέχουν περισσότερα από 1 widgets πάνω τους. Δεν είναι ορατά, και χρησιμοποιούνται μονάχα για την
 | 
						||
ομαδοποίηση αντικειμένων. Τα boxes μπορεί να είναι δύο ειδών, ανάλογα με τον προσανατολισμό που θέλουμε να πάρουν τα προς τοποθέτηση σε αυτό *widgets*:
 | 
						||
 | 
						||
-   **HORIZONTAL BOXES**\
 | 
						||
    Τα τοποθετημένα σε αυτό *widgets* θα μπουν στη σειρά από αριστερά προς τα δεξιά ή από τα δεξιά προς τα αριστερά όπως θα δούμε παρακάτω στην εξήγηση των
 | 
						||
    συναρτήσεων **`gtk_box_pack_start`** και **`gtk_box_pack_end`**.\
 | 
						||
    H δημιουργία ενός τέτοιου *box* επιτυγχάνεται με τη χρήση της συνάρτησης **`gtk_hbox_new`**.
 | 
						||
-   **VERTICAL BOXES**\
 | 
						||
    Τα τοποθετημένα σε αυτό *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 στην προηγούμενη ενότητα\... \`Οπως θα δείτε, η εργασία με τα 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 απ\' ότι βλέπετε) και τα ορίσματα που δέχονται εξηγούνται παρακάτω:
 | 
						||
 | 
						||
-   **`GtkBox *box`**\
 | 
						||
    Το όνομα του *box* στο οποίο αναφερόμαστε.
 | 
						||
-   **`GtkWidget *child`**\
 | 
						||
    Το όνομα του *widget* το οποίο προσθέτουμε.
 | 
						||
-   **`gint expand`**\
 | 
						||
    Είναι μορφής *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`** χρησιμοποιήσουμε.
 | 
						||
-   **`gint fill`**\
 | 
						||
    Η τιμή αυτής της παραμέτρου έχει σημασία μόνο αν η τιμή της **`expand`** είναι TRUE. Και αυτό γιατί περιγράφει αν ο επιπλέον χώρος που μένει μετά τη
 | 
						||
    δημιουργία των *widgets* θα ανήκει σε αυτά (Δηλ. θα τα μεγαλώσει) στην περίπτωση που πάρει την τιμή TRUE ή αν θα ανήκει στο *box* σαν αδιάθετος χώρος στην
 | 
						||
    περίπτωση που της δώσουμε την τιμή FALSE
 | 
						||
-   **`gint padding`**\
 | 
						||
    Αριθμός τύπου *gint*, που καθορίζει πόσος χώρος (σε pixels) γύρω από το κάθε *widget* δεν θα χρησιμοποιείται.
 | 
						||
 | 
						||
### [5.2 Tοποθέτηση των widgets, partII (λέγε με tables)]{#ss5.2}
 | 
						||
 | 
						||
Υπάρχει και άλλος ένας τρόπος για να στοιχίσουμε τα 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. Δοκιμάστε το πάντως, και πολλές φορές θα σας λύσει τα χέρια
 | 
						||
 | 
						||
### [5.3 Η συνέχεια]{#ss5.3}
 | 
						||
 | 
						||
Τον επόμενο μήνα θα συνεχίσουμε αυτό το άρθρο και θα ασχοληθούμε περισσότερο με τα widgets (buttons, radio/check/toggle buttons, text boxes, κ.λπ.) και τις
 | 
						||
ιδιότητές τους. Μέχρι τότε, πολλά φιλάκια.
 | 
						||
 |