Traduzione a cura di Fabrizio Angius [qtsolutions -A-T- gmx.net]
English TOC.
Questa sara' la nostra prima applicazione reale: una rubrica. Progetteremo ed implementeremo le classi di base e l'interfaccia utente in questo capitolo.
Iniziamo aprendo un progetto C++ vuoto nel Designer di Qt. Ho deciso di chiamare il mio progetto adbook.pro, ovviamente potete dargli il nome che preferite.
La prima cosa che vogliamo fare e' aggiungere una finestra principale (Main Window). La finestra che useremo dovra' essere vuota, per cui assicuratevi di deselezionare tutte le opzioni nel wizard mostrato nella figura 7-1.
Ora cambiate il nome del form chiamandolo frmMain e impostate come titolo (caption) 'Address Book'.
Invece di connettere pulsanti o altro direttamente agli slot come abbiamo fatto fino ad ora useremo i gruppi di azioni. In questo modo avremmo un unico segnale per gestire eventi multipli, come una voce di menu, una scorciatoia da tastiera e un pulsante della barra degli strumenti. Aggiungete quattro azioni usando il pulsante "new action" (evidenziato nella figura 7-2) in base a quanto mostrato nella tabella 7-1. Per fare cio', fate semplicemente click sul pulsante "new action" e cambiate l'azione usando l'editor delle proprieta'. Le azioni finali sono mostrate nella figura 7-2.
Il carattere & indica a Qt che deve sottolineare il carattere successivo e usarlo come scorciatoia da tastiera.
name | text | menuText |
aExit | Exit | E&xit |
aAddContact | Add Contact | &Add Contact |
aEditContact | Edit Contact | &Edit Contact |
aRemoveContact | Remove Contact | &Remove Contact |
Fate click destro allinterno del form per mostrare il menu contestuale. Da questo, selezionate la voce "add menu entry". Fatelo due volte in modo da avere un form con due voci di menu.
Ora dobbiamo rinominare le voci di menu. Nelle prime versioni 3.x di Qt questo veniva fatto facendo click destro sulle voci di menu e selezionando l'opzione relativa dal menu contestuale. Nelle versioni successive basta un singolo click per selezionare la voce di menu e il nome puo' essere impostato nell'editor delle proprieta'. In ogni caso, rinominate le voci come "&File" e "&Contacts" partendo da sinistra.
Ora trascinate semplicemente le azioni sui menu. Assegnate l'azione aExit al menu File e le altre azioni al menu Contacts. La figura 7-4 mostra il risultato.
A questo punto aggiungete una list view al form e chiamatela lvContacts. Cambiate la proprieta' allColumnsShowFocus impostandola su True. Fatto cio', fate click destro sulla list view e selezionate la voce "edit". Eliminate tutti gli elementi nella lista (figura 7-5) e create le colonne "Name", "E-Mail" e "Phone Number" (figura 7-6).
Per fare in modo che la list view diventi il widget principale del form, selezionate il form e applicate un layout a griglia. Nelle proprieta', impostate il valore di layoutMargin a zero per fare in modo che la list view occuppi tutta la finestra. Il form finale viene mostrato nella figura 7-7.
Ora dobbiamo aggiungere un po' di funzionalita' al progetto. iniziamo aggiungendo i seguenti slot privati al form: init(), addContact(), editContact() e removeContact(). La figura 7-8 mostra la finestra dell'editor di slot al completo.
Nell'editor delle azioni, fate click sul pulsante "connect current action" e collegate i segnali come mostrato nella tabella 7-2. Per verificare se avete fatto tutto bene, fate click destro sul form e selezionate "Connections" dal menu contestuale. Dovrebbe apparire una finestra simile a quella della figura 7-9. (Notate che l'aspetto della finestra cambia nelle varie versioni di Qt, mentre il contenuto e' sempre lo stesso in tutte le versioni di Qt che ho visto.)
Sender | Signal | Slot (sempre uno di frmMain) |
aExit | activated() | close() |
aAddContact | activated() | addContact() |
aEditContact | activated() | editContact() |
aRemoveContact | activated() | removeContact() |
Il prossimo passo consiste nel creare un dialog per inserire un nuovo contatto o per modificarne uno esistente. La figura 7-10 mostra l'aspetto del dialog una volta terminato.
Le istruzioni per creare questo dialog sono minime dato che tutti i dettagli sono gia' stati descritti nel capitolo 5.
Iniziate aggiungendo un nuovo dialog al progetto. Chiamatelo dlgContact e impostate "Contact" come titolo della finestra. Aggiungete due pulsanti e chiamateli pbOk e pbCancel. ora aggiungete un group box, tre label e tre line edit. Chiamate questi ultimi leName, leEMail e lePhone. Non dimenticatevi i due spacer mostrati nella figura 7-11. Impostate anche il testo dei pulsanti e delle label come mostrato nella figura.
Ora proseguiamo con i layout. Selezionate i line edit e applicate un layout verticale. Fate lo stesso con le label. Quindi selezionate i pulsanti e lo spacer orizzontale e applicate un layout orizzontale. Infine selezionate il frame e applicate un layout orizzontale per poi selezionare il form e applicarne uno verticale. Non ci resta che ridimensionare il dialog per farlo apparire piu' piacevole.
Passiamo a segnali e slot. Non dobbiamo fare altro che collegare i segnali clicked() dei pulsanti agli slot accept e reject del dialog. Fatto cio', il dialog e' pronto. Ecco dimostrato come sia facile creare un dialog semplice.
Prima di poter aggiungere del codice alla finestra principale ci serve una struttura dati per mantenere i contatti. In effetti non e' necessaria per quello che faremo in questo capitolo, ma ci servira' successivamente. Aggiungete quindi un file header C++ al progetto e inseriteci il codice dell'esempio 7-1 per poi salvarlo come contact.h.
#ifndef CONTACT_H
#define CONTACT_H
#include <qstring.h>
class Contact
{
public:
QString name,
eMail,
phone;
};
#endif
Per poter usare questa struttura dalla finestra principale dobbiamo includerla. Inseriremo i contatti in una QValueList, quindi dobbiamo dichiarare la lista come QValueList<Contact> m_contacts nell'elenco delle variabili di classe. Ci sono ancora tre file da includere, il dialog - dlgcontact.h, un message box - qmessagebox.h e la classe line edit - qlineedit.h. Se non includiamo l'header della classe line edit non sara' possibile accedere ai membri della classe dal codice della finestra principale, dato che quest'ultima non contiene dei line edit. Tutti i widget in una finestra, dialog oppure qudget composti verranno inclusi automaticamente. I file da includere e le dichiarazioni sono riassunti nella figura 7-12.
Infine un po' di codice "attivo". Iniziamo inizializzando il dialog nello slot init(). Il codice mostrato nell'esempio 7-2 svuota semplicemente la list view. In realta' non e' necessario, ma serve a mostrare dove va inserito il codice per l'inizializzazione.
void frmMain::init()
{
// Svuota la lista
lvContacts->clear();
}
Ora aggiungiamo il codice per poter inserire dei contatti. L'esempio 7-3 mostra il codice; e' abbastanza intuitivo e mostra come si possono utilizzare efficacemente i dialog. Prima vanno inizializzati, quindi si mostrano e se l'utente lo ha "accettato" si verificano i dati e si procede. Nel nostro caso si tratta di aggiungere un contatto alla lista.
void frmMain::addContact()
{
// Crea un nuovo dialog
dlgContact dlg( this );
// Mostralo e attendi l'Ok o il Cancel
if( dlg.exec() == QDialog::Accepted )
{
// Abbiamo un Ok
Contact c;
// Recupera le informazioni dal dialog
c.name = dlg.leName->text();
c.eMail = dlg.leEMail->text();
c.phone = dlg.lePhone->text();
// Controlla se ci sono nomi duplicati
for( QValueList<Contact>::iterator it = m_contacts.begin(); it != m_contacts.end(); ++it )
{
if( (*it).name == c.name )
{
// Avvisa se ci sono duplicati e ritorna
QMessageBox::warning( this, tr("Duplicate"), tr("The person ") + (*it).name + tr(" allready exists.") );
return;
}
}
// Nessun duplicato, aggiungi il contatto alla lista e mostralo nella listview
m_contacts.append( c );
lvContacts->insertItem( new QListViewItem( lvContacts, c.name , c.eMail, c.phone ) );
}
}
E' un bel pezzo di codice, quindi ecco le osservazioni. I link puntano alla documentazione ufficiale di Qt, in questo modo potete seguirli per ulteriori chiarimenti.
Come prima cosa creiamo un'istanza della nostra sottoclasse di QDialog, dlg. Con l'invocazione del metodo exec() attendiamo che il dialog venga chiuso e che ritorni un valore. Ci interessa sapere se il dialog e' stato accettato dall'utente, ecco perche' c'e' quell'if.
Inizializziamo un'istanza di Contact, c, con le informazioni prese dal dialog. Prendiamo il testo dei line edit con il metodo text().
Quindi usiamo una classe container di Qt, QValueList. Qt e' compatibile con la STL, ma i container di Qt sono piu' versatili. Se troviamo un duplicato mostriamo un messaggio di errore usando il metodo warning() della classe QMessageBox.
Per concludere, inseriamo il contatto nella lista e aggiungiamo un nuovo QListViewItem alla list view.
Continuiamo con il codice nell'esempio 7-4. Ci permette di rimuovere dei contatti dalla lista. Notate come sia facile rimuovere un elemento da una list view. Grazie a Qt, non c'e' alcun bisogno di preoccuparsi della gestione della memoria.
void frmMain::removeContact()
{
// Ottieni un puntatore all'elemento corrente
QListViewItem *item = lvContacts->currentItem();
// Verifica se e' valido
if( item )
{
// Cerca l'elemento corrispondente nella lista interna
for( QValueList<Contact>::iterator it = m_contacts.begin(); it != m_contacts.end(); ++it )
{
if( (*it).name == item->text(0) )
{
// Trovato - ora va rimosso dalla lista e dalla list view
m_contacts.remove( it );
delete item;
return;
}
}
}
}
Ora ci manca solo il codice per un'ultimo slot. L'esempio 7-5 mostra il codice da usare per modificare un contatto. Puo' essere visto come una variante dei casi di inserimento e rimozione. Prima cerca l'elemento selezionato, quindi imposta il dialog e lo mostra. Se tutto e' andato bene modifica l'elemento nella lista. L'algoritmo per trovare elementi duplicati potrebbe meritare un breve commento. Se il nome e' stato cambiato, dobbiamo impedire che ci siano degli elementi con lo stesso.
void frmMain::editContact()
{
// Ottieni un puntatore all'elemento selezionato
QListViewItem *item = lvContacts->currentItem();
// Verifica che sia valido
if( item )
{
// Cerca nella lista interna
for( QValueList<Contact>::iterator it = m_contacts.begin(); it != m_contacts.end(); ++it )
{
if( (*it).name == item->text(0) )
{
// trovato - mostralo nel dialog
dlgContact dlg( this );
dlg.leName->setText( (*it).name );
dlg.leEMail->setText( (*it).eMail );
dlg.lePhone->setText( (*it).phone );
// Mostra il dialog e attendi il risultato
if( dlg.exec() == QDialog::Accepted )
{
// Cerca eventuali duplicati controllando il numero di occorrenze
int occurencies = 0;
for( QValueList<Contact>::iterator i2 = m_contacts.begin(); i2 != m_contacts.end(); ++i2 )
if( (*i2).name == dlg.leName->text() )
occurencies++;
// Se il nome e' cambiato non ci devono essere duplicati
if( (dlg.leName->text() != (*it).name && occurencies > 0) || (occurencies > 1) )
{
// Avvisa l'utente e ritorna
QMessageBox::warning( this, tr("Duplicate"), tr("The person ") + dlg.leName->text() + tr(" allready exists.") );
return;
}
// Aggiorna la lista interna
(*it).name = dlg.leName->text();
(*it).eMail = dlg.leEMail->text();
(*it).phone = dlg.lePhone->text();
// Aggiorna la list view
item->setText( 0, (*it).name );
item->setText( 1, (*it).eMail );
item->setText( 2, (*it).phone );
}
return;
}
}
}
}
Prima di poter avere un'applicazione funzionante, dobbiamo aggiungere una banale funzione main(). Aggiungete un nuovo file C++ chiamato main.cpp e inseritevi il codice dell'esempio 7-6. Il codice mostra una finestra e attende che questa venga chiusa.
#include <qapplication.h>
#include "frmmain.h"
int main( int argc, char **argv )
{
QApplication a( argc, argv );
frmMain *m = new frmMain();
m->show();
a.connect( &a, SIGNAL(lastWindowClosed()), &a, SLOT(quit()) );
return a.exec();
}
La figura 7-13 mostra l'applicazione in esecuzione. Per compilare il tutto, eseguite qmake adbook.pro && make da una console, quindi eseguite il file prodotto e divertitevi.
Main window, dialog e widget
Molti dei nuovi utenti Qt restano confusi nel vedere quanti metodi ci sono per
mostrare una finestra con alcuni widget.
Il primo esempio di Qt usa direttamente un widget dal main(),
mentre altri utilizzano dei dialog.
Aggiungete la possibilita' di usare una main window e la confusione sara' totale.
Per farla breve, utilizzate le main window in applicazioni grosse e i dialog
negli altri casi.
Se vi interessano le mie spiegazioni continuate a leggere.
La possibilita' di usare un widget direttamente dal main()
e' molto utile in fase di testing dei widget e per scrivere applicazioni molto piccole
senza del vero e proprio codice attivo.
Appena avete la necessita' di usare slot per azioni come aggiungere un nuovo elemento,
cancellare una lista o altro, sarebbe consigliablie abbandonare l'approccio del widget.
Le applicazioni basate su dialog sono grandiose dato che
sono facili da comporre e che possono essere usate per eseguire
operazioni semplici senza utilizzare una main window.
Non e' necessario limitare un'applicazione basata su dialog ad un unico
dialog, ma se avete bisogno di avere un dialog che mostri un
"documento" su cui altri dialog lavorano, dovreste considerare
realmente la possibilita' di utilizzare una main window.
Come e' stato detto nel paragrafo precedente,
l'approccio basato su main window e' consigliabile appena si rende
necessario lavorare su un "documento".
Questo non significa necessariamente un documento di testo, come in un
moderno editor di testo di tipo WYSIWYG (What You see Is What You Get -
letteralmente "quello che vedete e' quello che ottenete"), ma piu'
in generale una collezione di dati (come nel design pattern MVC,
model - view - controller).
Un esempio di "documento" cosi' inteso e' l'elenco di contatti usato nella rubrica di
questo capitolo. Ci sono molteplici vantaggi nell'utilizzo di una main window.
Quello che mi piace di piu' e' la possibilita' di creare facilmente applicazioni
che lavorino contemporaneamente su piu' documenti creando semplicemente piu' istanze
della main window.
Questo significa che ogni documento ottiene una propria finestra ed e' anche l'approccio
raccomandato dalla ricerca sull'interazione uomo-computer.
Fino a poco fa la maggior parte delle applicazioni di questo tipo (come Office)
utilizzavano un'interfaccia MDI (molti documenti in un'unica finestra), mentre nelle
versioni piu' recenti utilizzano un'interfaccia SDI (una finestra per ogni documento).
Utilizzando una main window realizzerete proprio un'interfaccia SDI.
Il codice sorgente per questo capitolo puo' essere scaricato qui.
In questo capitolo si e' visto come viene costruita un'intera applicazione utilizzando una main window e un dialog per lavorare con il "documento" presentato nella finestra principale. E' stata anche chiarita la differenza tra main window e dialog.
This is a part of digitalfanatics.org and is valid XHTML.
Copyright (c) 2002-2004 by Johan Thelin (e8johan -at- digitalfanatics.org). This material may be distributed only subject to the terms and conditions set forth in the Open Publication License, v1.0 (local copy) or later (the latest version is presently available at http://www.opencontent.org/openpub/). Distribution of substantively modified versions of this document is prohibited without the explicit permission of the copyright holder. Distribution of the work or derivative of the work in any standard (paper) book form is prohibited unless prior permission is obtained from the copyright holder.