519 γραμμές
46 KiB
Markdown
519 γραμμές
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, κ.λπ.) και τις
|
|||
|
ιδιότητές τους. Μέχρι τότε, πολλά φιλάκια.
|
|||
|
|