Taryn Fox
jewelfox@fursona.net 2012
Μάθετε πώς να τακτοποιήσετε συστατικά διεπαφής χρήστη όπως εικόνες και ετικέτες.
Δημήτρης Σπίγγος
dmtrs32@gmail.com 2012, 2013

# 2. Καλώς ήλθατε στο πλέγμα

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

# Πηγαίνοντας εγγενώς

Στο τελευταίο μάθημα, δημιουργήσαμε βασικά ένα πλαίσιο παραθύρου GNOME για μια εφαρμογή ιστού. Ο συγκεκριμένος κώδικας GNOME που χρειαζόμαστε να μάθουμε περιστρέφεται γύρω από την τοποθέτηση της WebView -- το γραφικό συστατικό που περιέχει την εφαρμογή μας -- σε ένα ApplicationWindow και την εμφάνιση του. Η ίδια η εφαρμογή γράφτηκε σε HTML και JavaScript, ακριβώς όπως οι περισσότερες σελίδες στον ιστό.

Αυτήν τη φορά, πρόκειται να χρησιμοποιήσουμε μόνο τα εγγενή γραφικά συστατικά του GNOME. Ένα γραφικό συστατικό είναι μόνο ένα πράγμα, όπως ένα πλαίσιο ελέγχου ή εικόνα και το GNOME έχει μια πλατιά ποικιλία για επιλογή. Τα λέμε "εγγενή" γραφικά συστατικά για να τα διακρίνουμε από πράγματα όπως το κουμπί και η κεφαλίδα στην εφαρμογή ιστού που γράψαμε. Αντί να χρησιμοποιήσουμε κώδικα ιστού, αυτά πρόκειται να είναι 100% GNOME, χρησιμοποιώντας Gtk+.

Το Gtk+ σημαίνει "GIMP Toolkit". Είναι σαν μια εργαλειοθήκη γραφικών συστατικών που μπορείτε να προσπελάσετε, ενώ δομείτε τις εφαρμογές σας. Γράφτηκε αρχικά για το GIMP, που είναι ένας επεξεργαστής εικόνας ελεύθερου λογισμικού.

# Εγκατάσταση της εφαρμογής μας

Πριν να ψάξουμε οποιαδήποτε γραφικά συστατικά από την εργαλειοθήκη Gtk+, χρειαζόμαστε πρώτα να γράψουμε το βασικό κώδικα στερεότυπου που η εφαρμογή μας απαιτεί.

#!/usr/bin/gjs

const Gtk = imports.gi.Gtk; const Lang = imports.lang;

Αυτό το μέρος πηγαίνει πάντα στην αρχή του κώδικά σας. Ανάλογα με το τι θα κάνετε με αυτό, μπορεί να θέλετε να δηλώσετε περισσότερες εισαγωγές εδώ. Αυτό που γράφουμε σήμερα είναι αρκετά βασικό, έτσι αυτά είναι που χρειαζόμαστε· Gtk για γραφικά συστατικά και Lang ώστε να μπορούμε να χρησιμοποιήσουμε Lang.bind για να συνδέσουμε τα σήματα ενεργοποίησης και εκκίνησης της εφαρμογής στις αναγκαίες συναρτήσεις.

Μιλώντας για:

const WelcomeToTheGrid = new Lang.Class({ Name: 'Welcome to the Grid',

// Δημιουργία αυτής καθεαυτής της εφαρμογής _init: function() { this.application = new Gtk.Application()·

// Σύνδεση σημάτων 'activate' και 'startup' στις συναρτήσεις επανάκλησης this.application.connect('activate', Lang.bind(this, this._onActivate))· this.application.connect('startup', Lang.bind(this, this._onStartup))· },

// Η συνάρτηση επανάκλησης για το σήμα 'activate' παρουσιάζει παράθυρα όταν είναι ενεργή _onActivate: function() { this._window.present()· },

// Η συνάρτηση επανάκλησης για το σήμα 'startup' δομεί τη διεπαφή χρήστη _onStartup: function() { this._buildUI ()· },

Αυτή είναι η αρχή της ίδιας της εφαρμογής και η συνάρτηση _init που τη δημιουργεί. Λέει στη _buildUI να δημιουργήσει μια ApplicationWindow, που πρόκειται να καλέσει το _window και λέει στο παράθυρό μας να εμφανίζει τον εαυτό του όποτε απαιτείται.

Αυτό το μέρος, πάλι, είναι αρκετά αντιγραφή και επικόλληση, αλλά θέλετε πάντα να δώσετε στην εφαρμογή σας ένα μοναδικό όνομα.

// Δημιουργία της διεπαφής χρήστη της εφαρμογής _buildUI: function() {

// Δημιουργία του παραθύρου της εφαρμογής this._window = new Gtk.ApplicationWindow({ application: this.application, window_position: Gtk.WindowPosition.CENTER, border_width: 10, title: "Welcome to the Grid"})·

Τελικά, ξεκινάμε τη συνάρτηση _buildUI δημιουργώντας ένα νέο ApplicationWindow. Ορίζουμε ότι πηγαίνει με αυτήν την εφαρμογή, που θα πρέπει να εμφανιστεί στο κέντρο της οθόνης και εκεί θα πρέπει να είναι τουλάχιστον 10 εικονοστοιχεία μεταξύ της εξωτερικής άκρης και όλων των γραφικών συστατικών της. Επίσης της δίνουμε έναν τίτλο, που θα εμφανιστεί στην κορυφή του παραθύρου.

# Φτάνοντας στην εργαλειοθήκη Gtk+

Ποια γραφικά συστατικά πρέπει να χρησιμοποιήσουμε; Λοιπόν, ας πούμε ότι θέλουμε να γράψουμε μια εφαρμογή που φαίνεται σαν αυτή:

Πρόκειται να χρειαστούμε, τουλάχιστον, μια εικόνα και μια ετικέτα κειμένου να πάει μαζί της. Ας αρχίσουμε με την εικόνα:

// Δημιουργία εικόνας this._image = new Gtk.Image ({ file: "gnome-image.png" })·

Μπορείτε να κατεβάσετε το χρησιμοποιούμενο αρχείο εικόνας σε αυτό το παράδειγμα εδώ. Βεβαιωθείτε να το βάλετε στον ίδιο κατάλογο με τον κώδικα που γράφετε.

// Δημιουργία ετικέτας this._label = new Gtk.Label ({ label: "Welcome to GNOME, too!" })·

Αυτός ο κώδικας προσθέτει κάτω από την ετικέτα. Μπορείτε να δείτε πώς δημιουργούμε γραφικά συστατικά, εδώ· καθένα είναι μέρος του Gtk και μπορούμε να του δώσουμε ιδιότητες που προσαρμόζουν τη συμπεριφορά του. Σε αυτήν την περίπτωση, ορίζουμε την ιδιότητα αρχείο της εικόνας να είναι το όνομα αρχείου της εικόνας που θέλουμε και η ιδιότητα ετικέτα της ετικέτας να είναι η πρόταση που θέλουμε κάτω από την εικόνα.

Ναι, ακούγεται περιττό για μια ετικέτα να έχει μια ιδιότητα ετικέτας, αλλά δεν ισχύει. Άλλα γραφικά συστατικά που περιέχουν κείμενο έχουν μια ιδιότητα ετικέτας, έτσι είναι συνεπές για το γραφικό συστατικό ετικέτας να έχει επίσης μία.

Δεν μπορούμε απλά να προσθέσουμε αυτά τα γραφικά συστατικά στο παράθυρό μας στη σειρά, όμως, με τον ίδιο τρόπο που τα στοιχεία HTML εμφανίζονται στη σειρά που τα γράφετε. Αυτό συμβαίνει επειδή ένα ApplicationWindow μπορεί να περιέχει μόνο ένα γραφικό συστατικό.

Πώς το παρακάμπτουμε; Κάνοντας αυτό το γραφικό συστατικό έναν αποδέκτη γραφικού συστατικού, που μπορεί να κρατά περισσότερα από ένα γραφικά συστατικά και να τα οργανώσουμε μέσα του. Ιδού: το πλέγμα.

// Δημιουργία του πλέγματος this._grid = new Gtk.Grid ()·

Δεν του δίνουμε ακόμα ιδιότητες. Αυτές θα έρθουν αργότερα, καθώς μαθαίνουμε πώς να χρησιμοποιούμε τις δυνάμεις του πλέγματος. Πρώτα, ας προσαρτήσουμε την εικόνα και την ετικέτα που κάναμε στο πλέγμα μας.

// Προσάρτηση της εικόνας και της ετικέτας στο πλέγμα this._grid.attach (this._image, 0, 0, 1, 1)· this._grid.attach (this._label, 0, 1, 1, 1)·

Αυτός ο κώδικας φαίνεται πολύ περίπλοκος, αλλά δεν είναι. Να τι σημαίνουν αυτοί οι αριθμοί:

Ο πρώτος αριθμός είναι σε ποια θέση από αριστερά προς τα δεξιά θα τοποθετηθούν πράγματα, αρχίζοντας από 0. Κάθε γραφικό συστατικό που χρησιμοποιεί ένα 0 εδώ πηγαίνει όλο αριστερά.

Ο δεύτερος αριθμός είναι σε ποια θέση από πάνω προς τα κάτω θα μπει το δεδομένο γραφικό συστατικό, ξεκινώντας από 0. Η ετικέτα πηγαίνει κάτω από την εικόνα, έτσι δίνουμε στην εικόνα ένα 0 και στην ετικέτα ένα 1 εδώ.

Ο τρίτος και ο τέταρτος αριθμός είναι πόσες στήλες και γραμμές πρέπει να καταλάβει ένα γραφικό συστατικό. Θα δούμε πώς αυτά δουλεύουν σε ένα λεπτό.

// Προσθήκη του πλέγματος στο παράθυρο this._window.add (this._grid)·

// Εμφάνιση του παραθύρου και όλων των θυγατρικών γραφικών συστατικών this._window.show_all()· }

})·

// Εκτέλεση της εφαρμογής let app = new WelcomeToTheGrid ()· app.application.run (ARGV)·

Τώρα που έχουμε δημιουργήσει το πλέγμα και προσαρτήσαμε σε αυτό όλα τα γραφικά συστατικά μας, το προσθέτουμε στο παράθυρο και λέμε στο παράθυρο να εμφανίσει τον εαυτόν του, όπως το τελευταίο μέρος της συνάρτησης _buildUI. Όπως πάντα, για να τελειώσουμε δημιουργούμε ένα νέο στιγμιότυπο της κλάσης της εφαρμογής και του λέμε να εκτελεστεί.

Αποθηκεύστε την εφαρμογή σας ως welcome_to_the_grid.js. Έπειτα, για να εκτελέσετε την εφαρμογή σας ανοίξτε απλά έναν τερματικό, πηγαίνετε στον κατάλογο όπου η εφαρμογή σας είναι και πληκτρολογήστε

     $ gjs welcome_to_the_grid.js

Να! Αλλά περιμένετε. Αυτό δεν φαίνεται σωστό. Γιατί στριμώχνεται η ετικέτα δίπλα στην εικόνα έτσι; Αυτό δεν φαίνεται ωραίο και δυσκολεύει την ανάγνωση. Τι μπορούμε να κάνουμε για αυτό;

# Μικρορύθμιση του πλέγματος

Ένα πράγμα που μπορούμε να κάνουμε, είναι να δώσουμε στην ετικέτα μια ιδιότητα margin_top όταν τη δημιουργούμε. Αυτό δουλεύει όπως η ρύθμιση ενός περιθωρίου για ένα στοιχείο HTML χρησιμοποιώντας ενσωματωμένη μορφή CSS.

// Δημιουργία ετικέτας this._label = new Gtk.Label ({ label: "Welcome to GNOME, too!", margin_top: 20 })·

Φυσικά, εάν κάνουμε αυτό τότε εάν αντικαταστήσουμε την ετικέτα με κάτι άλλο -- ή προσθέτοντας σε ένα άλλο γραφικό συστατικό -- τότε πρέπει να επαναλάβουμε το margin_top σε αυτό ξανά. Αλλιώς τελειώνουμε μα κάτι σαν αυτό:

Μπορούμε να δώσουμε στην εικόνα μια ιδιότητα margin_bottom, αλλά δεν θα δουλέψει όταν η νέα ετικέτα είναι σε μια ξεχωριστή στήλη. Έτσι ας δοκιμάσουμε αυτό:

// Δημιουργία του πλέγματος this._grid = new Gtk.Grid ({ row_spacing: 20 })·

Αυτό το κάνει έτσι ώστε να υπάρχουν πάντα 20 εικονοστοιχεία χώρου μεταξύ κάθε οριζόντιας γραμμής.

Ναι, μπορείτε επίσης να ορίσετε την ιδιότητα column_spacing σε ένα πλέγμα, ή τις ιδιότητες margin_left και margin_right σε κάθε γραφικό συστατικό. Δοκιμάστε τες εάν θέλετε!

# Προσθήκη περισσότερων γραφικών συστατικών

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

// Δημιουργία δεύτερης ετικέτας this._labelTwo = new Gtk.Label ({ label: "The cake is a pie." })·

// Προσάρτηση εικόνας και ετικετών στο πλέγμα this._grid.attach (this._image, 0, 0, 2, 1)· this._grid.attach (this._label, 0, 1, 1, 1)· this._grid.attach (this._labelTwo, 1, 1, 1, 1)·

Μετά τη δημιουργία της δεύτερης ετικέτας, την προσκολλάμε στο πλέγμα στα δεξιά της πρώτης ετικέτας. Να θυμάστε, οι πρώτοι δύο αριθμοί υπολογίζουν στήλες και γραμμές από αριστερά προς τα δεξιά και από πάνω προς τα κάτω, ξεκινώντας από 0. Έτσι εάν η πρώτη ετικέτα είναι στη στήλη 0 και στη γραμμή 1, μπορούμε να βάλουμε τη δεύτερη στη στήλη 1 και στη γραμμή 1 για να τη βάλουμε στα δεξιά της πρώτης ετικέτας.

Σημειώστε τον αριθμό 2 στη δήλωση προσάρτησης για την εικόνα. Αυτό κάνει εδώ. Αυτός ο αριθμός είναι πόσες στήλες καλύπτει η εικόνα, θυμάστε; Έτσι όταν το βάζουμε μαζί, πάρουμε κάτι σαν αυτό:

Υπάρχουν δύο πράγματα που θα πρέπει να σημειώσετε, εδώ.

Ο ορισμός της εικόνας να καλύψει δύο στήλες δεν τεντώνει την ίδια την εικόνα οριζόντια. Αντίθετα, τεντώνει το αόρατο πλαίσιο που πάρθηκε από το γραφικό συστατικό της εικόνας για να γεμίσει και τις δύο στήλες, έπειτα βάζει την εικόνα στο κέντρο αυτού του πλαισίου.

Αν και έχουμε ορίσει τη row_spacing του πλέγματος και τις ιδιότητες border_width του ApplicationWindow, δεν έχουμε ορίσει ακόμα οτιδήποτε βάζει ένα περίγραμμα μεταξύ των δύο ετικετών. Ήταν ξεχωριστές νωρίτερα όταν η εικόνα ήταν μόνο μια στήλη, αλλά τώρα που καλύπτει και τις δύο το GNOME δεν βλέπει λόγο να τις κρατήσει ξέχωρα.

Υπάρχουν τουλάχιστον τρεις τρόποι που μπορούμε να παρακάμψουμε την τελευταία. Πρώτα, μπορούμε να ορίσουμε ένα margin_left ή margin_right σε μία από τις ετικέτες:

Δεύτερο, μπορούμε να ορίσουμε την ιδιότητα column_homogenous σε αληθή.

// Δημιουργία του πλέγματος this._grid = new Gtk.Grid ({ column_homogeneous: true, row_spacing: 20 })·

Αυτό το κάνει να μοιάζει σαν αυτό:

Και τρίτο, μπορούμε να ορίσουμε την ιδιότητα column_spacing του πλέγματος, με τον ίδιο τρόπο που ορίζουμε το row_spacing του.

// Δημιουργία του πλέγματος this._grid = new Gtk.Grid ({ column_spacing: 20, row_spacing: 20 })·

Αυτό το κάνει να μοιάζει σαν αυτό:

# Χρήση εικόνων παρακαταθήκης

Το GNOME έχει πολλές εικόνες παρακαταθήκης ήδη, που μπορούμε να χρησιμοποιήσουμε εάν δεν θέλουμε να δημιουργήσουμε τις δικές μας ή εάν θέλουμε ένα γενικά αναγνωρισμένο εικονίδιο. Να πώς δημιουργούμε μια εικόνα παρακαταθήκης, συγκρινόμενη με το πώς δημιουργούμε μια κανονική:

// Δημιουργία εικόνας this._image = new Gtk.Image ({ file: "gnome-image.png" })·

// Δημιουργία δεύτερης εικόνας χρησιμοποιώντας έτοιμο εικονίδιο this._icon = new Gtk.Image ({ stock: 'gtk-about' })·

Μετά από αυτό, το προσκολλάμε στο πλέγμα στα αριστερά της πρώτης ετικέτας. (Δεν χρησιμοποιούμε τη δεύτερη για αυτό το παράδειγμα.)

// Προσάρτηση εικόνων και ετικέτας στο πλέγμα this._grid.attach (this._image, 0, 0, 2, 1)· this._grid.attach (this._icon, 0, 1, 1, 1)· this._grid.attach (this._label, 1, 1, 1, 1)·

Αυτά μας δίνει αυτό, όταν το εκτελούμε:

Αυτό είναι πώς το εικονίδιο παρακαταθήκης "About" μοιάζει. Μπορείτε να δείτε μια λίστα όλων των στοιχείων παρακαταθήκης ξεκινώντας με το gtk-about στο τεκμηρίωση ανάπτυξης του GNOME. Γράφτηκε για προγραμματιστές C, αλλά δεν χρειάζεστε να ξέρετε C για να το χρησιμοποιήσετε· απλά κοιτάξτε το μέρος σε εισαγωγικά, όπως "gtk-about" και αντιγράψτε αυτό το μέρος για να χρησιμοποιήσετε το εικονίδιο δίπλα του.

Βάζουμε μονά εισαγωγικά γύρω από το 'gtk-about' εδώ επειδή, αντίθετα με τις συμβολοσειρές κειμένου που έχουν διπλά εισαγωγικά γύρω τους, αυτό το μέρος δεν θα χρειαστεί να μεταφραστεί ποτέ σε άλλη γλώσσα. Πράγματι, εάν μεταφραζότανε θα έσπαγε το εικονίδιο, επειδή το όνομά του είναι ακόμα "gtk-about" ανεξάρτητα από την ομιλούμενη γλώσσα.

# Ποιο είναι το επόμενο;

Πριν πάμε στο επόμενο μάθημα, ας δοκιμάσουμε κάτι λίγο διαφορετικό:

// Δημιουργία κουμπιού this._button = new Gtk.Button ({ label: "Welcome to GNOME, too!"})·

// Προσάρτηση εικόνων και κουμπιού στο πλέγμα this._grid.attach (this._image, 0, 0, 2, 1)· this._grid.attach (this._icon, 0, 1, 1, 1)· this._grid.attach (this._button, 1, 1, 1, 1)·

Ακριβώς, μετατρέπουμε την ετικέτα σε ένα κουμπί αλλάζοντας απλά το όνομα! Εάν εκτελείτε την εφαρμογή και πατήστε πάνω της, όμως, θα δείτε ότι δεν κάνει τίποτα. Πώς κάνουμε το κουμπί μας να κάνει κάτι; Αυτό θα μάθουμε στο επόμενο μάθημά μας.

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

Ένα κόλπο που μπορείτε να χρησιμοποιήσετε για να κάνετε πιο σύνθετες διατάξεις είναι η ένθεση πλεγμάτων το ένα μέσα στο άλλο. Αυτό σας επιτρέπει να ομαδοποιήσετε μαζί σχετικά γραφικά συστατικά και να τα αναδιατάξετε εύκολα. Ρίξτε μια ματιά στο δείγμα κώδικα RadioButton εάν θα θέλατε να δείτε πώς γίνεται αυτό.

# Δείγμα πλήρους κώδικα

#!/usr/bin/gjs

const Gtk = imports.gi.Gtk· const Lang = imports.lang·

const WelcomeToTheGrid = new Lang.Class({ Name: 'Welcome to the Grid',

// Δημιουργία αυτής καθεαυτής της εφαρμογής _init: function() { this.application = new Gtk.Application()·

// Σύνδεση σημάτων 'activate' και 'startup' στις συναρτήσεις επανάκλησης this.application.connect('activate', Lang.bind(this, this._onActivate))· this.application.connect('startup', Lang.bind(this, this._onStartup))· },

// Η συνάρτηση επανάκλησης για το σήμα 'activate' παρουσιάζει παράθυρα όταν είναι ενεργή _onActivate: function() { this._window.present()· },

// Η συνάρτηση επανάκλησης για το σήμα 'startup' δομεί τη διεπαφή χρήστη _onStartup: function() { this._buildUI ()· },

// Δόμηση της διεπαφής χρήστη της εφαρμογής _buildUI: function() {

// Δημιουργία του παραθύρου της εφαρμογής this._window = new Gtk.ApplicationWindow({ application: this.application, window_position: Gtk.WindowPosition.CENTER, border_width: 10, title: "Welcome to the Grid"})·

// Δημιουργία του πλέγματος this._grid = new Gtk.Grid ({ // column_homogeneous: true, // column_spacing: 20, row_spacing: 20 })·

// Δημιουργία εικόνας this._image = new Gtk.Image ({ file: "gnome-image.png" })·

// Δημιουργία δεύτερης εικόνας χρησιμοποιώντας έτοιμο εικονίδιο this._icon = new Gtk.Image ({ stock: 'gtk-about' })·

// Δημιουργία ετικέτας this._label = new Gtk.Label ({ label: "Welcome to GNOME, too!", /* margin_top: 20 */ })·

/* Δημιουργία δεύτερης ετικέτας this._labelTwo = new Gtk.Label ({ label: "The cake is a pie." })· */

/* Δημιουργία κουμπιού this._button = new Gtk.Button ({ label: "Welcome to GNOME, too!"})· */

// Προσάρτηση των εικόνων και του κουμπιού στο πλέγμα this._grid.attach (this._image, 0, 0, 2, 1)· this._grid.attach (this._icon, 0, 1, 1, 1)· this._grid.attach (this._label, 1, 1, 1, 1)·

// this._grid.attach (this._label, 0, 1, 1, 1)· // this._grid.attach (this._labelTwo, 1, 1, 1, 1)·

// this._grid.attach (this._button, 1, 1, 1, 1)·

// Προσθήκη του πλέγματος στο παράθυρο this._window.add (this._grid)·

// Εμφάνιση του παραθύρου και όλων των θυγατρικών γραφικών συστατικών this._window.show_all()· }

})·

// Εκτέλεση της εφαρμογής let app = new WelcomeToTheGrid ()· app.application.run (ARGV)·