Qt – 3 – Σχεδιασμός Γραφικής Διεπαφής GUI με χρήση του Qt Designer

3 – Σχεδιασμός Γραφικής Διεπαφής GUI με χρήση του Qt Designer

 

Ενώ απλές γραφικές διεπαφές, σαν αυτή του μετατροπέα που δημιουργήσαμε στο Κεφάλαιο 2, μπορούν να συνταχθούν “χειροκίνητα” χωρίς ιδιαίτερα προβλήματα, υπάρχει η ανάγκη για ένα γραφικό εργαλείο σχεδιασμού διεπαφής, ειδικά για τον σχεδιασμό παραθύρων διαλόγων με πολλά γραφικά στοιχεία. Η Qt παρέχει ακριβώς αυτό, με το Qt Designer.

 

3.1 Διαλόγοι “Με Κλικ Ποντικιού”

 

3.1 Dialogs “By Mouse Click”

 

Παρακάτω θα δημιουργήσουμε ένα παράθυρο διαλόγου ByteConverter του προηγούμενου κεφαλαίου με χρήση αυτού του εργαλείου σχεδίασης διεπαφών (GUI tool)

 

Το γεγονός ότι πολλά διαφορετικά παράθυρα ανοίγουν κατά την έναρξη του Designer, είναι κάτι που οι χρήστες των Windows δεν έχουν συνηθίσει.. Αν θέλετε να κάνετε χρήση της διάταξης παραθύρων dock mode, η οποία είναι εξορισμού στο Visual Studio, για παράδειγμα, μπορείτε να την αλλάξετε πηγαίνοντας στο Edit→User Interface Mode→Docked Window. Ένα από τα παράθυρα διαλόγου είναι το “New Form” με επιλογή προτύπου εμφάνισης. Τα πρότυπα αυτά είναι διαθέσιμα για κύρια παράθυρα, πλαίσια διαλόγων και widgets. Η Qt 4 κάνει διαχωρισμό μεταξύ των πλαισίων διαλόγων που τοποθετούν τα κουμπιά, ενεργείων του χρήστη, Επιβεβαίωσης (OK) και Ακύρωσης (Cancel), στο κάτω μέρος και αυτά που τα τοποθετούν στην κάτω δεξιά γωνία. Επιλέγουμε ένα από αυτά τα πρότυπα εμφάνισης για το πλαίσιο διαλόγου ByteConverter : Το σχήμα 3.1 δείχνει τον διάλογο με κουμπιά στο κάτω μέρος.

 

Δεν απαιτείται ο καθορισμός των κουμπιών. Για την διαγραφή τους  τα σχεδιάζουμε με χρήση του αριστερού πλήκτρου του πονιτκιού, επιλέγοντας ένα πλαίσιο επιλογής που να περικλείει τα κουμπιά και το πλαίσιο κενού (spacer). Πατώντας το κουμπί Del αφαιρούμε τα widgets που καλύπτουν τις απαιτήσεις στην περίπτωση μας.

 

Το επόμενο βήμα είναι να προσθέσουμε γραμμές εισόδου και ετικέτες στο πλαίσιο του παραθύρου διαλόγου. Μπορούμε να τα βρούμε μέσα στο Widget Box, που το πρόγραμμα συνήθως τοποθετεί στο αριστερό μέρος της οθόνης. Για να δημιουργήσουμε μια νέα ετικέτα, ψάχνουμε για την ομάδα Display Widgets (στο κάτω μέρος του κουτιού) και μέσω συρσίματος και εναπόθεσης (drag and drop) βάζουμε την ετικέτα εισόδου στο πλαίσιο διαλόγου .

 

Τώρα προσθέτουμε το widget της γραμμής επεξεργασίας. Ως στοιχείο εισόδου αυτό το στοιχείο της διεπαφής ανήκει στην κατηγορία των widgets εισόδου και τοποθετείται στην θέση του μέσω συρσίματος και εναπόθεσης (drag and drop). Επιπλέον για τις τρεις ετικέτες και γραμμές επεξεργασίας, χρειαζόμαστε ένα κουμπί  (Buttons→Push Button) και ένα οριζόντιο και κάθετο κενό (spacer). Αυτά τα πλαίσια στο πρόγραμμα Designer λειτουργούν σαν τεντωτές (stretches): προσαρμόζουν τις αποστάσεις μεταξύ των widgets του παραθύρου όταν αλλάζουμε το μέγεθος του.

 

Ένα στοιχείο της διεπαφής χρήστη (GUI) που έχει τοποθετηθεί, μπορεί να επανατοποθετηθεί σέρνοντας το με χρήση του αριστερού κουμπιού του ποντικιού. Στο σχήμα 3.2 δείχνει την φόρμα του παραθύρου διαλόγου ByteConverter μετά την τοποθέτηση όλων των Widget.

 

Οι χρήστες της Qt 4.2 ή νεότερων εκδόσεων πιθανόν να συναντήσουν διαφορές στην εμφάνιση του Designer όπως περιγράψαμε παραπάνω, καθώς η Trolltech έχει αλλάξει τη πρακαθορισμένη εμφάνιση του παραθύρου. Η σκοπιμότητα ήταν να ξεπεραστούν προβλήματα που αφορούσαν την διάταξη των κουμπιών σε διαφορετικές πλατφόρμες: Το τρέχον στυλ καθορίζει την διάταξη των κουμπιών. Στο Mac OS X και στο GNOME, η ενέργεια ακύρωσης (cancel) βρίσκεται στην αριστερή πλευρά, ενώ η ενέργεια επιβεβαιώσης (ΟΚ) βρίσκεται στην δεξιά πλευρά. Στα Windows και στο KDE, η διάταξη των κουμπιών είναι αντίστροφη.

 

Η λύση της Trolltech ήταν να εισάγει μια νέα κλάση με όνομα QButtonBox στην Qt 4.2, η οποία αυτομάτως παρείχε αυτά, που απαιτούνταν παλαιότερα να δημιουργήσει ο χρήστης χειροκίνητα: ένα σετ προκαθορισμένων κουμπιών και ένα πλαίσιο κενού (spacer). Με την χρήση του QButtonBox, η εφαρμογή θα επιλέξει αυτόματα την σωστή σειρά και την επιλεγμένη εμφάνιση. Το σχέδιο 3.3 δείχνει τον προκαθορισμένο πρότυπο εμφάνισης σε στυλ Cleanlooks (GNOME) και Plastique (KDE).

 

Αν το Qt Designer μπορεί να κάνει χρήση του QButtonBox, η καλύτερη λύση δεν είναι ούτε να το αφαιρέσουμε όπως συνίσταται για τα παραπάνω κουμπιά αλλά ούτε η προσθήκη ενός νέου κουμπιού. Αντ’αυτού, τροποποιούμε την ιδιότητα standardButtons στον Property Editor του Designer με τέτοιον τρόπο ώστε να κάνουμε χρήση μόνο του κουμπιού κλεισίματος. Αυτό γίνεται απενεργοποιώντας δύο ενεργές εγγραφές στο κουτί drop-down της ιδιότητας standardButtons και μετά επιλέγοντας QDialogButtonBox::Close.

 

Χρήστες που δεν είναι εξοικειωμένοι με την επεξεργασία ιδιοτήτων (property editors) π.χ. από άλλους γραφικούς σχεδιαστές διεπαφών χρήστη (GUI), θα πρέπει να διαβάσουν την ενότητα 5 για μια σύντομη εισαγωγή.

 

3.1.1 Δημιουργία Διατάξεων Εμφάνισης με χρήση του Designer

 

Τα widgets ακόμη δεν έχουν ξεκάθαρη διάταξη. Για την αποφυγή οριστικής τοποθέτησης στοιχείων διεπαφής μέχρι και το τελευταίο εικονοστοιχείο, ο Qt Designer παρέχει προκαθορισμένες διατάξεις εμφάνισης. Για την ομαδοποίηση ενός αιρθμού widgets μεταξύ τους και μετά για την επιλογή της επιθυμητής διάταξης, μπορούμε είτε να τα επιλέξουμε με χρήση ενός πλαισίου επιλογής, είτε από μενού επιλογών που εμφανίζεται κατά την επιλογή όταν κάνουμε δεξί κλικ ή από το μενού εργαλείων. Το τελευταίο συνίσταται ειδικά για τους χρήστες Mac με ποντίκι ενός πλήκτρου.

 

Στην περίπτωση των γραμμών επεξεργασίας και των ετικετών, η διάταξη πλέγματος είναι η καλύτερη επιλογή: αυτή επιλέγεται στο μενού στοιχείων διαμέσου του στοιχείου Lay out→Lay Out σε ένα πλέγμα. Η διάταξη μετά εμφανίζεται με κόκκινο περίγραμμα και τα αντικείμενα εμφανίζονται μαζί ομαδοποιημένα, όπως θα είναι στην τελική εφαρμογή.

 

Κατά την εφαρμογή μιας διάταξης, ο Designer προσπαθεί να δείχνει ανοχή στις ανακρίβειες εικονοστοιχείων τις οποίες μπορεί να προκάλεσε ο προγραματιστής κατά την τοποθέτηση των widgets. Εάν η επιλεγμένη διάταξη δεν τοποθετεί τα widgets όπως θέλαμε ή υπάρχει λάνθασμένος αριθμός στοιχείων για την διάταξη, η σειρά τοποθέτησης μπορεί να ακυρωθεί μέσω του στοιχείου μενού επιλογών Lay out→Break Layout.

 

Αν επιλέξαμε να μην κάνουμε χρήση του QButtonBox παραπάνω, επιλέγουμε μια οριζόντια διάταξη για το πλαίσιο κενού και τα κουμπιά, καθώς το QButtonBox ήδη περιλαμβάνει την ορίζοντια αυτή διάταξη με κενό (Lay out→Lay Out Horizontally).

 

Τέλος, φέρνουμε μαζί και τις δύο διατάξεις, μαζί με τα μέχρι τώρα μη ομαδοποιημένα κάθετα πλαίσια κενού, επιλέγοντας στο μενού επιλογών το στοιχείο Lay out→ Lay Out Vertically σε μια μοναδική κάθετη διάταξη. Αυτή η καθολική διάταξη δεν χρειάζεται να επιλεχθεί συγκεκριμένα: ο Designer δεν το επισημαίνει με ένα ξεχωριστό πλαίσιο και το μενού επιλογών ανοίγει εάν κλικάρουμε το κενό διάστημα στο παράθυρο διαλόγου. Επηρεάζει όλες τις προηγούμενες διατάξεις και τα μέχρι τώρα μη ομαδοποιημένα στοιχεία (π.χ. το κάθετο πλάισιο κενού)

 

Το αποτέλεσμα όπως φαίνεται στο σχήμα 3.4, πλησιάζει στην επιθυμητή μας γραφική διεπαφή χρήστη (GUI). Οι ετικέτες ωστόσο δεν είναι ακόμη σωστές. Αυτές μπορούν να αλλάξουν μέσω του στοιχείου μενού επιλογών Change text. . . ή μέσω της επεξεργασίας ιδιοτήτων στο δεξί μέρος της οθόνης. Για να κατανοήσουμε καλύτερα το τελευταίο, αξίζει να δούμε την έννοια των ιδιοτήτων της Qt.

 

3.1.2. Μενού επεξεργασίας ιδιοτήτων

 

Οι κλάσεις βασισμένες στο QObject έχουν ξεχωριστές ιδιότητες που μπορούν να οριστούν με χρήση της setProperty() και να ανακληθούν με χρήση της property(). Παραδείγματα πληροφοριών γραφικών διεπαφών χρήστη μπορούν να αναπαρασταθούν μέσω ιδιοτήτων συμπεριλαμβανομένου μεγέθους, ετικετών, στοιχείων μορφοποίησης, βοηθητικών κειμένων και πολλά αλλά στοιχεία.

 

Σχήμα 3.5: Το μενού επεξεργασίας ιδιοτήτων κατατάσει κάθε ιδότητα μιας κλάσης σύμφωνα με την κλάση που ορίστηκε αρχικά (είτε η κλάση αυτή καθευατή, είτε μια γονική)

 

Αυτό γίνεται στο Μενού Επεξεργασίας Ιδιοτήτων (Σχήμα 3.5). Απαριθμεί τις μεταβαλλόμενες ιδιότητες που παραθέτονται από την κλάση στην οποία κάθε ιδιότητα υλοποιήθηκε αρχικά—είτε την ίδια την κλάση ή έναν γονέα της. Για παράδειγμα, η QLabel κληρονομεί από την κλάση QFrame, που με την σειρά της είναι απόγονος του QWidget: Αντιστοίχως μπορούμε να καθορίσουμε όχι μόνο ιδιότητες ετικετών QLabel όπως το κείμενο της ετικέτας (ιδιότητα κειμένου) αλλά και τις ιδιότητες του QFrame όπως το frameShape1 και την ιδιότητα του QWidget όπως το μέγεθος του (γεωμετρία).

 

Εφόσον όλα τα widgets κληρονομούν από το QObject, μπορούμε πάντα να λαμβάνουμε και να ορίζουμε την ιδιότητα του QObject με όνομα objectName, η οποία παρέχει μια εσωτερική περιγραφή και δεν πρέπει να συγχέεται με την ετικέτα που θα εμφανίζεται στο widget μέσα στην διεπαφή χρήστη—για παράδειγμα, το κείμενο της ετικέτας ή το κουμπί (που καθορίζεται με την ιδιότητα κειμένου). Η μεταβλητή του ονόματος αντικειμένου καθώς και άλλες ιδιότητες προέρχονται από το objectName.

 

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

 

1 Οι ετικέτες είναι συνήθως χωρίς πλαίσιο, γι’αυτό ορίζουμε το frameShape σε QFrame::None εξ’ ορισμού

 

Από την άλλη πλευρά, παρακάτω χρειάζεται να επέμβουμε χειροκίνητα στα widget γραμμής επεξεργασίας, γι’αυτό τους δίνουμε τα ίδια ονόματα με αυτά που είχαν στο κεφάλαιο 2 (αυτά είναι decEdit, hexEdit και binEdit) με χρήση του Property Editor. Αυτό διασφαλίζει ότι ο κώδικας θα παραχθεί από τον μεταγλωτιστή διεπαφής χρήστη και οι αντίστοιχοι δείκτες θα έχουν τα ίδια ονόματα. Ο τρόπος με τον οποίο μπορούμε να αποκτήσουμε πρόσβαση στα widgets γραμμών επεξεργασίας που δημιουργήθηκαν από τον Designer περιγράφεται στην σελίδα 92.

 

Για την αλλαγή των ιδιοτήτων στον Designer, επιλέγουμε το σχετικό widget κλικάροντας με το αριστερό πλήκτρο του ποντικιού. Τα περιεχόμενα του παραθύρου επεξεργασίας ιδιοτήτων (Property Editor) διαμορφώνονται αντιστοίχως και μπορούμε να αλλάξουμε τις ιδιότητες των επιλεγμένων widget. Οι ιδιότητες των οποίων οι τιμές είναι ήδη διαφορετικές από τις προεπιλεγμένες, εμφανίζονται με έντονους χαρακτήρες.

 

Αλλαγή Τίτλων Παραθύρων

 

Για την αλλαγή του τίτλου παραθύρου ολόκληρου του διαλόγου, κλικάρουμε σε οποιοδήποτε σημείο μέσα στο παράθυρο κατασκευής widget που δεν καλύπτεται από θυγατρικά widgets ή διατάξεις, όπως την περιοχή ανάμεσα στο πλαίσιο διάταξης και στο περιθώριο του παραθύρου διαλόγου. Τώρα το παράθυρο επεξεργασίας ιδιοτήτων (Property Editor) εμφανίζει την ιδιότητα windowTitle στην ενότητα QWidget. Κλικάροντας την αντίστοιχη γραμμή, μας επιτρέπει την αλλαγή της τιμής της ιδιότητας, για παράδειγμα την αλλαγή του παραθύρου διαλόγου σε μετατροπέα αριθμών. 2 Το κουμπί με το μικρό κόκκινο βελάκι  δίπλα στην τιμή της ιδιότητας, επιτρέπει την επαναφορά της στην προεπιλεγμένη τιμή.

 

Παρά το γεγονός ότι κάθε widget έχει την ιδιότητα windowTitle, γίνεται ορατό στην περίπτωση των widget ανωτέρου επιπέδου (top-level), δηλαδή σε παράθυρα ή πλαίσια διαλόγων.

 

Ρυθμίσεις Χαρακτήρων

 

Το παράθυρο διαλόγου μας δεν εμφανίζει τις σωστές συμβολοσειρές κειμένου στις ετικέτες και στα κουμπιά. Προκειμένου να γίνει αυτό απαιτείται η ιδιότητα κειμένου.

 

Σχήμα 3.6: Οι ετικέτες περιέχουν το σωστό κείμενο

 

2 Για νέες εργασίες, ο Designer εμφανίζει μόνο το αλλαγμένο τίτλο παραθύρου μετά την αποθήκευση του παραθύρου διαλόγου. Πριν ο διάλογος αποθηκευτεί για πρώτη φορά, η γραμμή τίτλου περιέχει μόνο την λέξη untitled.

 

Θέτουμε αυτήν την ιδιότητα στα τρία QLabels, με την σειρά τους, σε δεκαδική, δεκαεξαδική και δυαδική. Για το κουμπί θέτουμε την τιμή της ιδιότητας (στο τμήμα QAbstractButton της γονικής κλάσης) σε τερματισμό (Exit). Το σχήμα 3.6 μας δείχνει το αποτέλεσμα.

 

Ορισμός του προεπιλεγμένου κουμπιού

 

Εάν επιθυμούμε επίσης, μπορούμε να αλλάξουμε την προκαθορισμένη ιδιότητα για το QPushButton. Εάν αυτό οριστεί εώς αληθές, πατώντας το πλήκτρο [Enter] οπουδήποτε εντός του παραθύρου διαλόγου αυτό ενεργοποιεί το κουμπί. Ωστόσο αυτό δεν έχει ιδιαίτερο νόημα, καθώς όταν το κουμπί ενεργοποιείται, η εφαρμογή πραγματοποιεί μια ενέργεια τερματισμού, κάτι που θα ενοχλούσε τον χρήστη εάν το ενεργοποιούσε καταλάθος.

 

Παρόλο που ο Designer επιτρέπει την αλλαγή σε αληθής (true) των προκαθορισμένων ιδιοτήτων πολλών κουμπιών σε ένα widget, μόνο ένα από αυτά μπορεί να λειτουργήσει σαν το προεπιλεγμένο κουμπί. Η Qt μεταχειρίζεται το τελευταίο κουμπί του widget να έχει την προκαθορισμένη ιδιότητα σε τιμή αληθής (true) ως το προκαθορισμένο κουμπί του widget.

 

Σε περίπτωση που το widget που εμπλέκεται είναι παράθυρο διαλόγου, η Qt από την έκδοση 4.1 και μετά ενεργοποιεί αυτόματα την ιδιότητα  autoDefault για όλα τα κουμπιά που είναι τοποθετημένα σε αυτό. Η ιδιότητα αυτή έρχεται σε ισχύ εάν ο χρηστης μεταπηδήσει από ένα κομμάτι του widget στο επόμενο με χρήστη του [Tab] (Βλέπε σελίδα 89): Εάν φτάσει σε γραμμή επεξεργασίας κατά την ενέργεια αυτή, πατώντας το πλήκτρο [Enter] ενεργοποιεί το επόμενο κουμπί στην καρτέλα ακολουθίας, δεδομένου ότι η ιδιότητα  autoDefault έχει οριστεί.

 

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

 

Αλλαγή Μεγέθους Παραθύρου

 

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

 

Σχήμα 3.7: Καθορίζουμε το μέγεθος του παραθύρου διαλόγου στο σωστό μέγεθος

 

Εναλλακτικά, το παράθυρο διαλόγου μπορεί να διαμορφωθεί στο επιθυμητό μέγεθος με χρήση του ποντικιού. Σαν κανόνας όμως είναι πιο απλό να επιλέξουμε την λειτουργία καθορισμού μεγέθους (Adjust Size) από το μενού Form στην εργαλειοθήκη (μέσω του εικονιδίου με το διαγώνιο βέλος στο τέρμα δεξιά), δεδομένου ότι ενεργοποιήσαμε το παράθυρο διαλόγου χειροκίνητα. Αυτό θα μικρύνει το παράθυρο διαλόγου στο κατάλληλο μέγεθος που εκτιμάται από τον Designer (Σχήμα 3.7)

 

3.1.3 Η Προεπισκόπηση

 

Για τον έλεγχο του αποτελέσματος, μπορούμε να κάνουμε χρήση της λειτουργίας της προεπισκόπησης που παρέχεται στο μενού Form του Designer. Εάν θέλουμε μπορούμε να δούμε το παράθυρο διαλόγου σε widgets άλλου στυλ, μέσω του Preview στο υπομενού. Το σχήμα 3.8 δείχνει την προεπισκόπιση σε περιβάλλον Linux. Η Trolltech ορίζει το στυλ Plastique ώς το προκαθορισμένο, το οποίο μοιάζει με το προκαθορισμένο στυλ του KDE 3. Σε Mac OS X, ή Qt κάνει χρήση του Aqua Style, κάνοντας χρήση της ρουτίνας απεικόνισης Aqua Style. Παρομοίως σε Windows XP κάνει χρήση των Windows APIs για σχεδιασμό του στυλ εμφάνισης. Ως εκ τούτου, τα στυλ Aqua και XP είναι διαθέσιμα μόνο σε αυτά τα λειτουργικά συστήματα.

 

Σχήμα 3.8: Μια προεπισκόπηση ενός τελικού widget

 

3.1.4 Συνδέσεις Σημάτων/Υποδοχών

 

Εκτός από την λειτουργία του σχεδιασμού διεπαφής, ο Designer περιλαμβάνει ένα εργαλείο γραφικής σχεδίασης στο οποίο τα σήματα των widgets μπορούν να σχεδιαστούν γραφικά και να συσχετισθούν με τα slots. Πατώντας το πλήκτρο [F4] ή επιλέγοντας την λειτουργία Edit Signals/Slots στο μενού Edit για αλλαγή σε αυτήν την λειτουργία: μπορούμε να μεταβούμε εκεί με Edit→Edit Widgets ή πατώντας το πλήκτρο [F3]

 

Η σύνδεση των σημάτων και υποδοχέων στον Designer γίνεται σε δύο βήματα. Αρχικά, σχηματίζουμε μια σύνδεση από το widget του επιθυμητού σήματος με το επιθυμητή υποδοχή ενός widget. Το φόντο του widget ή του παραθύρου διαλόγου μπορούν να γίνουν περιοχές εναπόθεσης στοιχείων (drop target). Οι συνδέσεις που τοποθετούνται εκεί παρέχονται μέσω ενός εικονηδίου γείωσης από τον Designer: όλες οι υπόλοιπες συνδέσεις τερματίζονται με ένα βελάκι στο widget στόχου (Σχήμα 3.9 δείχνει και τις δύο περιπτώσεις)

 

Σχήμα 3.9: Σήματα/Υποδοχείς δημιουργούνται στον Designer μέσω drag and drop

 

Το δεύτερο βήμα περιλαμβάνει τον καθορισμό του επιθυμητού ζευγαριού σήματος και υποδοχέα για τα δύο widgets. Μόλις απελευθερώσουμε το πλήκτρο ποντικιού από το widget στόχο, ο Designer ανοίγει ένα παράθυρο διαλόγου, όπως φαίνεται στο σχήμα 3.10: Στα αριστερά εμφανίζεται ένα μενού με τα σήματα που χρησιμοποιήσαμε πιο συχνά. Εάν το σήμα που ψάχνουμε δεν είναι εκεί, κλικάρουμε το Show all signals and slots κουτάκι επιλογής για εμφάνιση όλων των πιθανών σημάτων του widget στόχου. Το δεξί κουτί επιλογής εμφανίζει όλους τους υποδοχείς του widget στόχου αντιστοιχώντας τα σήματα που επιλέγονται στα αριστερά. Εάν επιβεβαιώσουμε την επιλογή, η σύνδεση θα εγκαθιδρυθεί.

 

Σχήμα 3.10: Σήματα και υποδοχείς τών δύο επιλεγμένων widgets συνδέονται από τον προγραμματιστή σε αυτόν τον διάλογο.

 

Ένα κλικ στην γραμμή σύνδεσης ακολουθούμενη από το πάτημα του πλήκτρου [Del] αφαιρεί όλες τις συνδέσεις.

 

3.1.5 Η ακολουθία του Tab

 

Η λεγόμενη ακολουθία του tab είναι σημαντική για τους χρήστες πληκτρολογίων. Η λειτουργία αυτή επιτρέπει στην αλλαγή εστίασης μέσω του πλήκτρου [Tab] στο επόμενο widget που αναμένει κάποια είσοδο. Ο Designer καθορίζει την ακολουθία tab έτσι ώστε το πρώτο widget του διαλόγου να έχει την εστίαση του πληκτρολογίου. Η εστίαση αλλάζει στο επόμενο στοιχείο της γραφικής διεπαφής όταν πατηθεί το πλήκτρο [Tab]. Όταν σχεδιάζουμε την διεπαφή του χρήστη πρέπει να δώσουμε προσοχή στην προκαθορισμένη ακολουθία tab και να την αλλάξουμε έτσι ώστε να κάνουμε την εφαρμογή μας πιο φιλική προς τον χρήστη.

 

Σχήμα 3.11: Η αλλαγή εστίασης μετακυλίεται πατώντας το πλήκτρο [Tab] που καθορίζεται σειρά αλλαγής του tab.

 

3.1.6 Συντομεύσεις και Βοηθήματα

 

Αυτοί που προτιμούν έλεγχο μέσω πληκτρολογίου θα σας ευχαριστήσουν έαν μπορούν να μεταβούν απευθείας σε όσα περισσότερα widget που χρησιμοποιούνται συνήθως. Τα στοιχεία της γραφικής διεπαφής που εμφανίζουν κείμενο όπως κουμπιά, συσχετίζονται με ένα πλήκτρο συντόμευσης  τοποθετώντας το σύμβολο εμπορικού και (&) πριν τον χαρακτήρα που θα αποτελέσει το πλήκτρο συντόμευσης. Έαν το κείμενο εμπεριέχει ένα σύμβολο εμπορικού και (&) αντικαθίσταται από το διπλό &&.

 

Εάν ο χρήστης πατήσει τον συνδιασμό [Alt] + [Χαρακτήρα] από εδώ και στο εξής, το widget αποκτά την εστίαση και ενεργοποιείται. Στο σχήμα 3.12 κάνουμε χρήση της τεχνικής αυτής με το κουμπί εξόδου (Quit).

Αντικείμενα QLabel σχηματίζουν μια εξαίρεση ωστόσο. Εφόσον χρησιμοποιούνται σε διατάξεις με σκοπό την περιγραφή ενός παρακείμενου widget “εταίρο”, αυτά δεν επιδέχονται εστίασης. Ωστόσο η ιδιότητα Buddy μιας ετικέτας μπορεί να χρησιμοποιηθεί για τον καθορισμό της συντόμευσης πληκτρολογίου και τον συσχετισμό της με ένα widget “εταίρο”, όπως και εαν το κείμενο περιγραφής ήταν συσχετισμένο άμεσα στο γονικό στοιχείο αυτοκαθευτό.

 

Στην επιλογή έμφανισης Edit Buddies στον Designer, μπορούμε τώρα να συσχετίσουμε με ποιο widget η ετικέτα είναι έταιρος. Για να γίνει αυτό, κλικάρουμε την μελλοντική ετικέτα Buddy που θα επισημανθεί με κόκκινο χρώμα. Κρατώντας πατημένο το κουμπί ποντικιού, θα επισυνάψουμε μια σύνδεση στο widget που στην συνέχεια θα είναι συσχετισμένο με το label.

 

Σχήμα 3.12: Οι ετικέτες σχετίζονται με άλλα widgets: Οι κατανομές των Buddy μπορούν να βρεθούν στο Buddy mode του Qt Designer.

Figure 3.12: Labels are friends to other widgets: The Buddy allocations can be found in the Buddy mode of the Qt Designer.

 

Στο παράδειγμα από το Σχήμα 3.12 η αντίστοιχη γραμμή επεξεργασίας έχει την εστίαση εάν ο χρήστης πατήσει τα γράμματα που υπογραμίζονται στην περιγραφή της ετικέτας ενώ κρατάμε πατημένο το πλήκτρο [Alt].

 

Εναλλακτικά, ενώ βρισκόμαστε στη κανονική κατάσταση σχεδίασης, μπορούμε να ορίσουμε το όνομα του επιθυμητού Buddy widget στον Property Editor κάνοντας χρήση της ιδιότητας Buddy.3  Χρησιμοποιώντας αυτήν την προσέγγιση, θα ορίσουμε την τιμή της ιδιότητας Buddy του αντικειμένου QLabel, που εμφανίζει το δεκαδικό κείμενο στο παράθυρο διαλόγου μετατροπέα byte έτσι ώστε, να ταιριάζει στην τιμή της ιδιότητας objectName του αντίστοιχου αντικειμένου γραμμής επεξεργασίας, δηλλαδή την συμβολοσειρά decEdit.

 

Για την αναίρεση της συσχέτισης, το μόνο που χρειάζεται να κάνουμε είναι να κλικάρουμε στην γραμμή σύνδεσης στην λειτουργία Buddy πατώντας το πλήκτρο [Del].

 

3.2 Ενσωμάτωση αρχείων του Designer μέσα στο Qt Project.

 

Κατά την αποθήκευση με χρήση του στοιχείου του μενού File→Save Form ή Save Form As. .. ο Designer δημιουργεί αρχεία .ui από τις πληροφορίες που διαθέτει για κάθε widget στην φόρμα.4 Αυτό το αρχείο .ui καθορίζεται στο αρχείο qmake όπως φαίνεται στην παρακάτω γραμμή κώδικα:

 

FORMS = byteconverterdialog.ui

 

Στην περίπτωση μας, το qmake λαμβάνει υπόψιν το αρχείο διεπαφής χρήστη byteconverterdialog.ui : μπορούν να καθοριστούν πολλά αρχεία, διαχωριζόμενα με χαρακτήρα κενού ή να καθοριστούν άλλες γραμμές σύμφωνα με το πρότυπο FORMS +=file.ui.

 

3 Παρόλο που η ιδιότητα αυτή υπάρχει από την Qt 3.x, η Qt 4.0 δεν την εμφανίζει. Μόνο στην έκδοση 4.1 εμφανίζεται ξανά.

4 Κάνοντας χρήση του τρίτου στοιχείου το μενού Save Form As Template. .. μπορούμε να αποθηκεύσουμε την φόρμα μας εώς πρότυπο, που εμφανίζεται μετά στον διάλογο επιλογής new Forms.

 

Κατά την κατασκευή του έργου, το make βασίζεται στο μεταγλωτιστή διεπαφής χρήστη uic για την μετατροπή των αρχείων .ui που δημιουργεί ο Designer σε αρχεία κεφαλής C/C++. Υπάρχει μια σταθερή σύμβαση ονομασίας στο βήμα αυτό: για παράδειγμα η κλάση που αναπαρίσταται από το αρχείο .ui ,που δημιουργούνται από τον Designer, ονομάζονται ByteConverterDialog (η τιμή της ιδιότητας objectName μπορεί να ελενχθεί για την διαπίστωση του ονόματος κλάσς), τότε το αρχείο κεφαλής που προκτύπτει παίρνει το όνομα ui_byteconverterdialog.h από το uic.

 

Είναι σημαντικό έστω ένα ακόμη αρχείο στο project να περιλαμβάνει το παραγόμενο αρχείο κεφαλής. Πρέπει να προσθέσουμε τις κατάλληλες δηλώσεις #include πριν εκτελεσθεί το qmake. Διαφορετικά το make δεν θα καλέσει το uic με το κατάλληλο αρχείο περιγραφής της διεπαφής σαν όρισμα, στην επόμενη εκτέλεση.

 

Παρατηρούμε οτι το παραγόμενο αρχείο κεφαλής περιέχει μια βοηθητική κλάση με δύο μεθόδους: setupUi() που παράγει την γραφική διεπαφή (GUI) και  etranslateUi() η οποία μπορεί να κληθεί όταν το πρόγραμμα θέλει να επιτρέψει στον χρήστη την αλλαγή γλώσσας κατά την εκτέλεση.

 

Και οι δύο οι μεθόδοι αναμένουν (ως όρισμα) έναν δείκτη στο widget στο οποίο το αντικείμενο GUI που περιγράφεται στον Designer, είναι να δεσμευτεί. Ακόμη και εάν έχουμε επιλέξει το πρότυπο εμφάνισης στον Designer, μπορούμε να επιλέξουμε ελεύθερα στο σημείο αυτό την κλάση του widget για την οποία προορίζεται η διεπαφή. Το κυρίως πρότυπο MainWindow είναι το μόνο που μπορεί να χρησιμοποιηθεί μαζί με ένα QMainWindow.6

 

Η κλάση που δημιουργείται από το uic είναι τώρα διαθέσιμη εώς Ui::ByteConverterDialog ή Ui_ ByteConverterDialog γενικά ως Ui::classname ή Ui_class σύμφωνα με τον οποίο το όνομα της κλάσης αντοιστοιχεί στο γνώρισμα objectName της φόρμας που δημιουργείται από τον Designer.

 

Υπάρχουν τρεις τρόποι χρήσης και λειτουργικής ανάπτυξης του widget που δημιουργήσαμε. Το πιο είναι το καταλληλότερο εξαρτάται από την συγκεκριμένη περίπτωση.

 

3.2.1 Χρήση κλάσεων παραγόμενων από τον Designer σαν βοηθητικές κλάσεις.

 

Εάν το μόνο που επιθυμούμε είναι την εμφάνιση μιας διεπαφής φτιαγμένης με τον Designer μόνο μια φορά, χωρίς να επέμβουμε στο αντίστοιχο αντικείμενο ξανά εφόσον έχει τρέξει, είναι σκόπιμο εκκινήσοουμε κατευθείαν την παραγόμενη κλάση και δέσμευση του στιγμιοτύπου σε ένα widget που δημιουργήσαμε με χρήση της  setupUi(). Η μέθοδος αυτή διορθώνει τα γραφικά στοιχεία που περιγράφονται στο αρχείο .ui πάνω στο widget και τα εδραιώνει υπό τον όρο ότι αυτό ορίζεται μέσα στον Designer με πρότυπα εμφάνισης (layouts).

 

Θα παρουσιάσουμε την τεχνική αυτή με χρήση του ByteConverterDialog που δημιουργήσε ο Designer:

 

5 Σημείωση για χρήστες Qt 3: Το uic δεν συνεχίζει να παράγει ολόκληρες κλάσεις βασισμένες στο QObject στην Qt 4, απλώς ένα δομικό πλαίσιο (framework) που μπορεί να εφαρμοστεί στο widget του αντίστοιχου τύπου.

6 Το widget που δημιουργήθηκε στον Designer χρησιμοποιείται στην περίπτωση αυτή εώς κεντρικό widget του στιγμιοτύπου QMainWindow και τοποθετείται με setCentralWidget(), αντί της βοήθειας ενός προτύπου εμφάνισης (layout) όπως συνηθίζεται. Επιπλέον οι γραμμές εργαλείων του Designer αντιμετωπίζονται ξεχωριστά από την Qt 4.1 μια λειτουργία που είναι εξίσου διαθέσιμες μόνο στα στιγμιότυπα QMainWindow.

 

// simple/main.cpp

 

#include <QtGui>

 

#include “ui_byteconverterdialog.h”

 

int main(int argc, char*argv[])

{

QApplication app(argc, argv);

QDialog dlg;

Ui::ByteConverterDialog ui;

ui.setupUi(&dlg);

dlg.setAttribute(Qt::WA_QuitOnClose);

dlg.show();

return app.exec();

}

 

Εφόσον τα widgets διαλόγου του δημιουργήθηκαν από τον Designer είναι διαθέσιμα ως δημόσια πρσβάσιμα μέλη της κλάσης UI, μπορούν να τελειοποιηθούν στον κώδικα αργότερα με κλήση των μεθόδων των αντίστοιχων widgets. Τα σήματα και οι υποδοχείς τους, συμμετέχουν στις συνδέσεις σημάτων/υποδοχών. Είτε η κλάση Ui::ByteConverterDialog τεκμηριώθηκε στην συνάρτηση main(), είτε στον κατασκευαστή μιας κλάσης που κληρονομεί από την QDialog, δεν κάνει καμία διαφορά.

 

Στο παράδειγμα μας ωστόσο η προσέγγιση που φαίνεται στον παραπάνω κατάλογο δημιουργεί προβλήματα: Δεν μπορέσαμε να συνδέσουμε το σήμα του πατήματος του κουμπιού Quit στην υποδοχή accept() του παραθύρου διαλόγου και τότε θα είμασταν σε θέση να συνδέσουμε τις υποδοχές binChanged(), hexChanged() και binChanged() στα σήματα textChanged() των αντίστοιχων QTextEdit widgets. Τότε όμως δεν θα είμασταν σε θέση να αποκτήσουμε πρόσβαση σε οποιοδήποτε widget παραγόμενο από το uic στην ίδια την υποδοχή.

 

Για τον λόγο αυτό, η χρήση της άμεσης κλήσης της setupUi() είναι πολύ περιορισμένη: Εάν γίνει αυτό θα περιοριστούμε στην εφαρμογή στιγμιοτύπων της κλάσης που δημιουργεί το uic με στιγμιότυπα τυπικών κλάσεων όπως QWidget ή QDialog. Ωστόσο σε ορισμένες περιπτώσεις η διαδικασία αυτή μπορεί να είναι απολύτως αποδοτική, για παράδειγμα σε ένα τυπικό διαλόγο εισόδου που καλείται με την exec(). Η κλήση exec καλεί ένα ξεχωριστό βρόγχο ενεργειών και επιστρέφει μόνο έαν οι accept(), reject() ή εάν μια άλλη μέθοδος κλείσει το παράθυρο διαλόγου. Εφόσον το αντικείμενο του διαλόγου δεν παύει να υπάρχει όταν κλείσει ο διάλογος, ο κώδικας που ακολουθεί μπορεί να διαβάσει τις τιμές των widget που τοποθετήθηκαν μέσα στον διάλογο από την setupUi() χωρίς κίνδυνο, έτσι ώστε να μην χρειαζόμαστε την υποκλάση QDialog στις περιπτώσεις αυτές.

 

Είναι σημαντικό να καλούμε πάντα την μέθοδο setupUi() ενός στιγμιοτύπου μιας κλάσης που πρώτα παρήγαγε ο Designer, πριν επιχειρήσει να διαβάσει μεταβλητές μελών της διεπαφής αντικειμένου (στο τρέχων παράδειγμα, αυτά της γραφικής διεπαφής). Σε άλλη περίπτωση το πρόγραμμα θα χαλάσει τους μη αρχικοποιημένους δείκτες και θα τερματίσει ξαφνικά.

 

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

 

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

 

3.2.2 Έχοντας πάντα διαθέσιμα τα Widget φτιαγμένα με τον Designer

 

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

 

Η σημαντική διαφορά είνα στην δήλωση της κλάσης. Δηλώνουμε την κλάση που δημιουργείται από το uic ως ιδιωτικό μέλος μιας υποκλάσης QDialog. Αυτό επιτρέπει στην επιλεκτική πρόσβαση στα widgets μέσα στις κλάσεις που δημιουργεί ο Designer μέσω αυτού του νέου μέλους μεταβλητής της γραφικής διεπαφής  (ui) της κλάσης ByteConverterDialog που κληρονομείται από το QWidget:

 

// member/byteconverterdialog.h

#include <QDialog>

#include “ui_byteconverterdialog.h”

 

class QLineEdit;

 

class ByteConverterDialog : public QDialog

{

private:

Ui::ByteConverterDialog ui;

};

 

Ο κατασκευαστής και όλες οι υποδοχές αποκτούν πλέον πρόσβαση στις παραγόμενες κλάσεις μέσω του μέλους μεταβλητής της γραφικής διεπαφής (ui):

 

The constructor and all slots now access the generated class via the ui member variable:

 

// member/byteconverterdialog.cpp

 

ByteConverterDialog::ByteConverterDialog(QWidget *parent)

: QDialog(parent)

{

ui.setupUi(this);

 

connect(ui.decEdit, SIGNAL(textChanged(const QString&)),

this, SLOT(decChanged(const QString&)));

connect(ui.hexEdit, SIGNAL(textChanged(const QString&)),

this, SLOT(hexChanged(const QString&)));

connect(ui.binEdit, SIGNAL(textChanged(const QString&)),

this, SLOT(binChanged(const QString&)));

}

 

void ByteConverterDialog::decChanged(const QString& newValue)

{

bool ok;

int num = newValue.toInt(&ok);

if (ok) {

ui.hexEdit->setText(QString::number(num, 16));

ui.binEdit->setText(QString::number(num, 2));

} else {

ui.hexEdit->setText(“”);

ui.binEdit->setText(“”);

}

}

 

Η υπερκείμενη αρχή ισχύει και εδώ: Είναι σημαντικό η συνάρτηση setupUi() να καλείται πρώτη πριν μπορούμε να κάνουμε χρήση της κλάσης γραφικής διεπαφής UI σε οποιοδήποτε τρόπο. Τα μειονεκτήματα της μεθόδου αυτής είναι η μή-αμεσότητα της, μέσω της μεταβλητής του μέλους. Το πλεονέκτημα της προσέγγισης αυτής είναι οτι διογκώνει το πρόβλημα ενθυλάκωσης, περιορίζοντας το πρόβλημα με το πεδίο εφαρμογής στην κλάση διαλόγου. Οποιαδήποτε εξωτερική πρόσβαση πρόσβαση εκτός του διαλόγου δεν είναι δυνατή σε οποιαδήποτε περίπτωση. Ένα έξτρα bonus: Είναι ξεκάθαρο από τον κώδικα ποιά widgets δημιουργήθηκαν από τον Designer. Επιπλέον, η προσέγγιση αυτή ταιριάζει περισσότερο σε widgets σε βιβλιοθήκες που έχου παραμείνει συμβατές ψηφιακά, καθώς μόνο ο δείκτης του στιγμιοτοτύπου των παραγόμενων κλάσεων αλλάζουν την δυαδική διάταξη στην έξοδο του μεταγλωτιστή.7

 

3.2.3 Πολλαπλή Κληρονομικότητα

 

Ως ιδανική λύση, η Trolltech προτείνει πολλαπλή κληρονομικότητα. Όπως και στην προηγούμενη λύση αυτή δουλεύει.

 

Στην μέθοδο αυτή το νέο widget κληρονομεί όχι μόνο από το QWidget αλλά και από την κλάση UI που παράγεται από την uic. Ένα ιδιαίτερο χαρακηριστικό είναι η χρήση μιας ιδιωτική λέξης κλειδί κατά την σύνθεση της κληρονομικότητας. Αυτό διασφαλίζει πως όλες σε όλες τις μεθόδους της κλάσεις UI δίνονται η κατάστασεις μεταβλητών των ιδιωτικών κλάσεων στις νέες κλάσεις, παρόλο που αυτές είναι δημοσίως προσβάσιμες στις προηγούμενες κλάσεις.

 

7 Περισσότερες πληροφοριές αναφορικά με την συμβατότητα C++ έχουν μεταγλωτιστεί από το KDE project στο http://developer.kde.org/documentation/other/binarycompatibility.html.

 

// inherit/byteconverterdialog.h

 

 

class ByteConverterDialog : public QDialog,

private Ui::ByteConverterDialog

 

 

Η μέθοδος αυτή επιλύει πολλά προβλήματα μονομιάς: Μπορούμε να κάνουμε χρήση των δεικτών των widgets που παράγονται από την uic ως κοινές μεταβλητές μελών, χωρίς πολύ κόπο μέσω ενός βοηθητικού αντικειμένου και παραμένουν ιδιωτικές έτσι ώστε να παραμένει η ενθυλάκωση του αντικειμενου προς τον εξωτερικό κόσμο.

 

Για το παράδειγμα μας αυτό σημαίνει ότι ο κατασκευαστής αλλάζει όπως ακολούθως:

 

// inherit/byteconverterdialog.cpp

 

ByteConverterDialog::ByteConverterDialog(QWidget *parent)

: QDialog(parent)

{

setupUi(this);

 

connect(decEdit, SIGNAL(textChanged(const QString&)),

this, SLOT(decChanged(const QString&)));

connect(hexEdit, SIGNAL(textChanged(const QString&)),

this, SLOT(hexChanged(const QString&)));

connect(binEdit, SIGNAL(textChanged(const QString&)),

this, SLOT(binChanged(const QString&)));

}

 

void ByteConverterDialog::decChanged(const QString& newValue)

{

bool ok;

int num = newValue.toInt(&ok);

if (ok) {

hexEdit->setText(QString::number(num, 16));

binEdit->setText(QString::number(num, 2));

} else {

hexEdit->setText(“”);

binEdit->setText(“”);

}

}

 

 

Όπως και πριν, χρειαζόμαστε μόνο να καλέσουμε την μέθοδο setupUi() στην πρώτη θέση σαν όρισμα που χρησιμοποιούμε ξανά σαν δείκτη στο widget που είναι στην τρέχουσα κλάση πεδίου.

 

Προσοχή: Η προσέγγιση της αλληλουχίας κληρονομικότητας είναι σημαντική. Η πρώτη κλάση πρέπει να κληρονομεί από την QDialog και μέτα από την κλάση του Designer. Σε άλλη περίπτωση ο μεταγλωτιστής θα βγάλει λάθος δυσνόητο, που οδηγεί τον  προγραματιστή σε απόγνωση.

 

moc_byteconverterdialog.cpp:43: error: ‘staticMetaObject’ is not a member of type ‘Ui::ByteConverterDialog’

moc_byteconverterdialog.cpp: In member function ‘virtual void* ByteConverterDialog::qt_metacast(const char*)’: moc_byteconverterdialog.cpp:60: error: ’class Ui::ByteConverterDialog’ has no member named ’qt_metacast’ moc_byteconverterdialog.cpp: In member function ‘virtual int ByteConverterDialog::qt_metacall(QMetaObject::Call, int, void**)’: moc_byteconverterdialog.cpp:66: error: ’class Ui::ByteConverterDialog’ has no member named ’qt_metacall’ make: *** [moc_byteconverterdialog.o] Error 1

 

Ο λόγος της συμπεριφοράς του μεταγλωτιστή μετά-αντικειμένου (meta-object) το οποίο ελέγχει εάν η πρώτη γονική κλάση της λίστας κληρονομικότητας, κληρονομεί από το QObject ή οχι. Αυτό επίσης σημαίνει ότι δεν μπορεί γενικά να κληρονομεί από πολλαπλές που έχουν όλες το QObject σαν κύρια κλάση.

 

3.3 Αυτόματες Συνδέσεις σημάτων/υποδοχών

 

Προγραματιστές εξικοιωμένοι με την Visual Basic ή Delphi που αρχίζουν την ανάπτυξη Qt/C++ βρίσκουν την λογική των σημάτων/υποδοχών ασυνίθηστη, παραβλέπουν την διαχειριστή ενεργειών (event handler). Η Qt 4 τους επιτρέπει να ακολουθούν την λογική που έχουν συνηθήσει, επιτρέποντας δηλώσεις υποδοχών στην φόρμα που μετατρέπονται σε εντολές connect() που αποθηκεύει uic στην setupUi() . Παρεμπιπτόντως η σύμβαση ονομασίας αυξάνει την αναγνωσιμότητα του κειμένου κώδικα.

 

void on objectname signalname();

 

Το όλο θέμα της λειτουργικότητας της στατικής μεθόδου QMetaObject::connectSlotsBy Name() : Αναμένει έναν δείκτη στο QObject και ψάχνει μέσα του για υποδοχείς που ταιριάζουν ονομαστικά. Μετά η QMetaObject::connectSlotsByName() συνδέει τους υποδοχείς που βρήκε με τα κατάλληλα σήματα. Για να γίνει αυτό χρησιμοποιεί πληροφορίες από το μετα-αντικείμενο που παράγεται από τον μεταγλωτιστή moc. Το αντικείμενο αυτό δίνει μια δυνατότητα στην C++ γνωστή ως ενδοσκόπηση (γνωστή στην Java ως reflection) σε όλες τις κλάσεις που κληρονομούν από το QObject. Κατά την εκτέλεση “γνωρίζει” τις μεθόδους της, τα σήματα και τους υποδοχείς της. Η connectSlotsByName() εξετάζει αναδρομικά τα ονόματα των υποδοχών του αντικειμένου πίσω από τους δείκτες και όλα τα παιδιά τους συνδέοντας τα αντίστοιχα σήματα σε αυτά.

 

Η Trolltech προτείνει την λογική που φαίνεται παραπάνω μόνο στις κλάσεις που δημιουργήθηκαν από τον Designer, δεδομένου ότι στην περίπτωση αυτή το όνομα αντικειμένου της κλάσης και του δείκτη που δημιούργησε η uic στην συσχέτηση widget, επειδή η setupUi() καλεί κατ ακολουθία την connectSlotsByName(). Όσοι βρίσκουν την σύμβαση ονοματολογίας δελεαστική, όλα τα σχετιζόμενα αντικείμενα πρέπει να ονομαστούν μέσω της setObjectName(), να κληθούν στον κατασκευαστή εκτός της QMetaObject::connectSlotsByName() και πρέπει να δίνουν έναν δείκτη στην τρέχουσα κλάση (this) στην κλήση αυτή.

 

Επειδή η παραπάνω λογική είναι επιρρεπής στα λάθη,8 πρέπει να κάνουμε αυτόματες συνδέσεις μόνο με τα widget που παράγει ο Designer με πολλαπλή κληρονομικότητα.

 

Θα τροποποιήσουμε τα παραπάνω παραδείγματα έτσι ώστε τα ονόματα των υποδοχέων να ακολουθούν στην σύμβαση για αυτόματες συνδέσεις. Την ίδια ώρα που η κλήσεις σύνδεσεις connect() παύουν να ισχύουν έτσι ώστε να παραμένουν μόνο οι εντολές setupUi().

 

// autoconnect/byteconverterdialog.h

 

private slots:

void on_decEdit_textChanged(const QString&);

void on_hexEdit_textChanged(const QString&);

void on_binEdit_textChanged(const QString&);

 

// autoconnect/byteconverterdialog.cpp

 

 

ByteConverterDialog::ByteConverterDialog(QWidget *parent)

: QDialog(parent)

{

setupUi(this);

}

 

void ByteConverterDialog::on_decEdit_textChanged(const QString& newValue)

{

bool ok;

int num = newValue.toInt(&ok);

if (ok) {

hexEdit->setText(QString::number(num, 16));

binEdit->setText(QString::number(num, 2));

} else {

hexEdit->setText(“”);

binEdit->setText(“”);

}

}

 

 

8 Θυμόμαστε ότι μόνο τα ονόματα αντικειμένων σχετίζεται και ότι αυτή είναι η διαδικασία, ή Qt δεν μπορεί να εκδόσει προδειποιήσεις αναφορικά με συνδέσεις που αποτυγχάνουν κατά την εκτέλεση.

 

3.4 Συμπερίληψη των παραγόμενων κλάσεων στον Designer

 

Είναι σημαντικό σε ορισμένες περιπτώσεις να κάνουμε μικρές αλλαγές σε ένα συνηθισμένο Qt widget. Σε τέτοιες περιπτώσεις δεν μπορούμε τον Designer χωρίς να δηλώσουμε το νέο widget εκεί ως τροποποιημένο το οποίο περιλαμβάνει πολύ κόπο 9

 

Για να μπορούμε να συνεχίσουμε να κάνουμε χρήση του widget στον Designer, πρέπει να επιλέξουμε το βασικό Qt widget στον Designer και να το κλικάρουμε με το δεξί πλήκτρο του ποντικιού όταν έχει προσαρμοστεί. Από το μενού επιλέγουμε την λειτουργία Promote to Custom Widget. Στον διάλογο που εμφανίζει (βλέπε σχήμα 3.13) ορίζουμε το όνομα της νέας κλάσης του ονόματος και του αρχείου κεφαλής. Παρόλο που ο Designer συνεχίζει να δείχνει  το αρχικό Qt widget, το τελικό πρόγραμμα  κάνει χρήση του τροποποιημένου widget: ώστε στην υλοποίηση να παρέχουμε έναν δείκτη στο αντικείμενο τύπου του widget που κληρονομήθηκε.

 

Σχήμα 3.13: Χρησιμοποιώντας κλάσεις στον Designer ειναι πολύ απλό χάρη στην προώθηση widget. Συνήθως χρειαζόμαστε μόνο αυτό.

 

Για να αναιρέσουμε μια τέτοια προώθηση, η καταχώρηση Demote στην βασική κλάση μπορεί να βρεθεί στην ίδια θέση στο μενού επιλογών.

 

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

 

3.5 Ο επεξεργαστής πόρων (Resource Editor)

 

Από την Qt 4 και μετά, ο Designer υποστηρίζει την παραμετροποίηση και διαχείριση όλων των πόρων που έχουν ήδη συζητθεί στην σελίδα 57. Ο συντάκτης που έχει ενσωματωθεί εδώ (Σχήμα 3.14) μπορεί να κληθεί από την επιλογή Tools → Resource Editor σε περίπτωση που δεν είναι ήδη ορατή. Η πλοήγηση σε αυτή απαιτεί εξοικείωση στην χρήση του. Το αναδυόμενο κουτί δίπλα στις επιλογές New και Open δείχνει μια λίστα με αρχεία πόρων που έχουν ήδη ανοιχτεί. Δεν περιλαμβάνει την ενέργεια αποθήκευσης καθώς αυτό γίνεται εμέσως από τον συντάκτη (editor).

 

Σχήμα3.14: Το παράδειγμα πόρων της σελίδας 57 στον Resource Editor του Designer

 

Επιπλέον ο κατάλογος των πόρων που εμφανίζονται στον Designer είναι ανεξάρτητος από αυτά στο αρχείο .pro . Για τον λόγο αυτό είναι σημαντικό να διασφαλίζουμε ότι όλοι οι πόροι εκχωρούνται εκεί με το όνομα RESOURCES.  Με την εκτέλεση της qmake οι πόροι γίνονται μέρος του project.

 

Για τον συσχετισμό μιας εικόνας από έναν πόρο σε ένα QLabel μέσα στον Designer, για παράδειγμα ψάχνουμε πρώτα στον Property Editor για την ιδιότητα pixmap και να κλικάρουμε πάνω στο εικονίδιο φακέλου. Στον επόμενο διάλογο βρίσκουμε τον Resource Editor επιλέγοντας Specify τον καθορισμό πηγής όπου επιλέγουμε μια από τις εικόνες. Για να εμφανιστούν τα επιθυμητά γραφικά στο παρόν μέγεθος widget, η ιδιότητα scaledContents στον  Property Editor πρέπει να οριστεί σε αληθές: αλλιώς θα παραμείνει στο αρχικό μέγεθος της εικόνας.

 


Source: Quantaofnoise

Leave a Reply

Your email address will not be published. Required fields are marked *