Come iniziare a programmare con Qt.

Benvenuti nel mondo di Qt Lo strumento di sviluppo visuale multi-piattaforma. In questa guida su come iniziare, vi daremo una infarinatura su Qt implementando un semplice programma di testo. Dopo aver letto questa guida dovreste essere in grado di scartabellare la documentazione dei nostri moduli e le nostre API, così da trovare le informazioni che vi occorrono per realizzare l’applicazione che state sviluppando.

Hello Notepad

In questo primo esempio creeremo semplicemente un editor di testo e lo mostreremo all’interno di una finestra sul desktop. Questo rappresenta l’esempio più semplice di un programma Qt che utilizzi una interfaccia grafica.
editor

Ecco il codice:

  1.         #include <QApplication>
  2.           #include <QTextEdit>
  3.  
  4.           int main(int argv, char **args)
  5.           {
  6.               QApplication app(argv, args);
  7.  
  8.               QTextEdit textEdit;
  9.               textEdit.show();
  10.  
  11.              return app.exec();
  12.          }

Analizziamo il codice linea per linea. Nelle prime due linee includiamo i file di header per QApplication e QTextEdit, che sono le due classi di cui avremo bisogno per questo esempio. Tutte le classi Qt hanno un file di header che porta il loro nome.

La linea 6 crea un oggetto QApplication. Questo oggetto gestisce le risorse inerenti l’applicazione ed è necessario per eseguire qualunque programma Qt che abbia una interfaccia grafica (GUI). Necessita di argv e args poiché Qt accetta alcuni argomenti da riga di comando.

La linea 8 crea l’oggetto QTextEdit. Un editor di testo è un elemento visuale all’interno della GUI. In Qt noi chiamiamo questi elementi “widget”. Esempi di altri widget sono le barre di scorrimento, etichette, e i pulsanti a esclusione. Un widget può fungere da contenitore per altri widget; un “dialog” (finestra informativa) o la finestra principale dell’applicativo, per esempio.

La linea 9 mostra l’editor di testo all’interno della cornice della finestra. Poiché gli widget fungono da contenitori (ad esempio una QMainWindow, che ha barra degli strumenti, menu, barra di stato, e qualche altro widget) è possibile mostrare un singolo widget all’interno della propria finestra. Gli widget non sono visibili di default; la funzione show() rende il widget visibile.

La linea 11 consente alla QApplication di entrare nel suo circolo degli eventi (evet loop). Quando una applicazione Qt è in esecuzione, gli eventi sono generati e inviati ai widget della applicazione. Un esempio di evento può essere la pressione di un pulsante del mouse o la digitazione di alcuni tasti della tastiera. Quando inserisci del testo nel widget dell’editor di testo, esso riceve l’evento di pressione del tasto e reagisce scrivendo il testo digitato.
Per eseguire l’applicazione, aprite il promp dei comandi e spostatevi all’interno della cartella in cui avete il file .cpp del programma. I seguenti comandi di shell generano il programma.

  1.          qmake -project
  2.          qmake
  3.          make

Questi genereranno un eseguibile all’interno della cartella part1 (notate che su Windows potreste dover usare nmake al posto di make. In più l’eseguibile sarà piazzato in part1/debug o part1/release). qmake è lo strumento Qt per la creazione degli eseguibili (build) che utilizza un file di configurazione. qmake genera questo file per noi quando invocato con l’argomento —project. Attraverso il file di configurazione (con suffisso .pro), qmake produce un make file che creeà il programma per voi. Esamineremo come scrivere il nostro personale file .pro in seguito.

Approfondisci.
About Here
Widgets and Window Geometry
Window and Dialog Widgets
Events and event handling
The Event System

Aggiungiamo un pulsante di chiusura. editor_with_button In una applicazione vera avrete bisogno di più di un widget. Ora introdurremo un QPushButton al di sotto dell’editor di testo. Il pulsante terminerà l’applicazione Notepad non appena premuto (cioè quando premuto con il mouse).

Diamo un’occhiata al codice.

  1.           #include <QtGui>
  2.  
  3.           int main(int argv, char **args)
  4.           {
  5.               QApplication app(argv, args);
  6.  
  7.               QTextEdit textEdit;
  8.               QPushButton quitButton("Quit");
  9.  
  10.              QObject::connect(&quitButton, SIGNAL(clicked()), qApp, SLOT(quit()));
  11.  
  12.              QVBoxLayout layout;
  13.              layout.addWidget(&textEdit);
  14.              layout.addWidget(&quitButton);
  15.  
  16.              QWidget window;
  17.              window.setLayout(&layout);
  18.  
  19.              window.show();
  20.  
  21.              return app.exec();
  22.          }

La linea 1 riporta l’include a QtGui che contiene tutte le classi Qt che gestiscono l’interfaccia grafica.

La linea 10 usa il meccanismo Qt di Signals and Slots che fa sì che il programma esca quando il pulsante Quit viene premuto. Uno “slot” è una funzione che può essere invocata durante l’esecuzione del programma (runtime) utilizzando il suo nome (come una literal string). Un “signal” è una funzione che quando richiamata invocherà lo slot alla quale è stata registrata; la utilizziamo per connettere lo slot al signal e per emettere il signal.
quit() è uno slot di QApplication che chiude l’applicazione. clicked() è un signal che emette QPushButton quando premuto. La funzione static QObject::connect() si preoccupa di connettere lo slot al signal . SIGNAL e SLOT sono due macro che prendono le signatures (nome delle funzioni) del segnale e dello slot per connetterle tra loro. Abbiamo bisogno di fornire anche i puntatori agli oggetti che dovrebbero inviare e ricevere i signal.

La linea 12 crea un QVBoxLayout. Come già riportato in precedenza i widget possono contenerne altri. E’ possibile impostare direttamente i limiti (posizione e dimensione) dei widget figli ma è generalmente più semplice utilizzare un layout. Un layout gestisce i posizione e dimensione di un widget figlio. QVBoxLayout, ad esempio, piazza il figlio in una riga verticale.

Le linee 13 e 14 aggiungono l’edito di testo e il pulsante al layout. Con la 17 impostiamo il layout al widget.

Learn More
About Here
Signals and slots
Signals & Slots
Layouts
Layout Management, Widgets and Layouts, Layout Examples
The widgets that come with Qt
Qt Widget Gallery, Widget Examples

Deriviamo QWidget Quando l’utente vuol chiudere un applicativo, potreste voler far apparire un dialog che chieda a lui o lei se realmente abbia intenzione di chiudere il programma. In questo esempio noi deriveremo un QWidget e gli aggiungeremo uno slot che connetteremo al pulsante di chiusura. subclassing Esaminiamo il codice:
  1.   5        class Notepad : public QWidget
  2.   6        {
  3.   7            Q_OBJECT
  4.   8
  5.   9        public:
  6.  10            Notepad();
  7.  11
  8.  12        private slots:
  9.  13            void quit();
  10.  14
  11.  15        private:
  12.  16            QTextEdit *textEdit;
  13.  17            QPushButton *quitButton;
  14.  18        };

La macro Q_OBJECT deve apparire in testa alla definizione della classe, e dichiara la nostra classe come un QObject, naturalmente deve derivare da QObject.
Un QObject aggiunge diverse capacità ad una normale classe C++. La più importante, il nome dei signal e degli slot può essere recuperato a run-time. E’ persino possibile ottenere il tipo del parametro di uno slot così da invocare lo slot stesso.
La linea 13 dichiara lo slot quit(). Questo semplifica l’utilizzo delle macro slot, lo slot quit() ora può essere connesso ai signal con una signature corrispondente (qualunque signature che non utilizza alcun parametro).

Invece di impostare la GUI e connettere lo slot nella funzione main(), ora utilizzeremo il costruttore del Notepad.

  1.          Notepad::Notepad()
  2.          {
  3.              textEdit = new QTextEdit;
  4.              quitButton = new QPushButton(tr("Quit"));
  5.  
  6.              connect(quitButton, SIGNAL(clicked()), this, SLOT(quit()));
  7.  
  8.              QVBoxLayout *layout = new QVBoxLayout;
  9.              layout->addWidget(textEdit);
  10.              layout->addWidget(quitButton);
  11.  
  12.              setLayout(layout);
  13.  
  14.              setWindowTitle(tr("Notepad"));
  15.          }

Come avete visto nella definizione della classe, utilizziamo i puntatori ai nostri QObject, textEdit e quitButton. Di regola dovreste allocare i QObject sull’heap, mai copiarli.

Ora utilizzeremo la funzione tr() attorno alle stringhe visibili all’utente. Questa funzione è necessaria quando volete fornire la vostra applicazione in più lingue (tipo inglese e cinese). Non ci addentreremo nei dettagli ora, ma potete il link a Qt Linguist dalla tabella degli approfondimenti.

Approfondimenti
About Here
tr() and internationalization
Qt Linguist Manual, Writing Source Code for Translation, Hello tr() Example, Internationalization with Qt
QObjects and the Qt Object model (This is essential to understand Qt)
Object Model
qmake and the Qt build system
qmake Manual

Creiamo un file .pro

Per questo esempio scriveremo il nostro file .pro anziché usare l’opzione —project di qmake.

  1.          HEADERS =  notepad.h
  2.          SOURCES =  notepad.cpp \
  3.                     main.cpp

I prossimi comandi shell compileranno l’esempio.
  1.          qmake
  2.          make

Utilizziamo una QMainWindow

Molte applicazioni traggono beneficio dall’utilizzo di QMainWindow che ha il proprio layout con il quale potete aggiungere un menu, dock widget (widget all’interno di finestre che possono essere chiuse, riaperte e spesso spostate), barre degli strumenti e barre di stato. QMainWindow ha un’area centrale che può essere occupata da qualunque tipo di widget. Nel nostro caso vi piazzeremo il nostro editor di testo.
mainwindow
Diamo un’occhiata alla nuova definizione della classe Notepad.

  1.          #include <QtGui>
  2.  
  3.          class Notepad : public QMainWindow
  4.          {
  5.              Q_OBJECT
  6.  
  7.          public:
  8.              Notepad();
  9.  
  10.          private slots:
  11.              void open();
  12.              void save();
  13.              void quit();
  14.  
  15.          private:
  16.              QTextEdit *textEdit;
  17.  
  18.              QAction *openAction;
  19.              QAction *saveAction;
  20.              QAction *exitAction;
  21.  
  22.              QMenu *fileMenu;
  23.          };

Vi abbiamo inserito altri due slot che possono salvare e aprire un documento, le implementeremo nella prossima sezione.
Spesso, in una finestra principale, lo stesso slot può essere invocato da più di un widget. Menù e pulsanti su una barra strumenti ne sono un esempio.
Often, in a main window, the same slot should be invoked by several widgets. Examples are menu items and buttons on a tool bar. To make this easier, Qt provides QAction, which can be given to several widgets, and be connected to a slot. For instance, both QMenu and QToolBar can create menu items and tool buttons from the same QActions. We will see how this works shortly.
As before, we use the Notepads constructor to set up the GUI. Notepad::Notepad() { saveAction = new QAction(tr(”&Open”), this); saveAction = new QAction(tr(”&Save”), this); exitAction = new QAction(tr(“E&xit”), this);

connect(openAction, SIGNAL), this, SLOT)); connect(saveAction, SIGNAL), this, SLOT)); connect(exitAction, SIGNAL), qApp, SLOT)); fileMenu = menuBar()->addMenu(tr(”&File”)); fileMenu->addAction(openAction); fileMenu->addAction(saveAction); fileMenu->addSeparator(); fileMenu->addAction(exitAction); textEdit = new QTextEdit; setCentralWidget(textEdit); setWindowTitle(tr(“Notepad”)); } QActions are created with the text that should appear on the widgets that we add them to (in our case, menu items). If we also wanted to add them to a tool bar, we could have given icons to the actions. When a menu item is clicked now, the item will trigger the action, and the respective slot will be invoked. Learn More About Here Main windows and main window classes Application Main Window, Main Window Examples MDI applications QMdiArea, MDI Example Saving and Loading In this example, we will implement the functionality of the open() and save() slots that we added in the previous example.

We will start with the open() slot: QString fileName = QFileDialog::getOpenFileName(this, tr(“Open File”), “”, tr(“Text Files (.txt);;C++ Files (.cpp *.h)”));

if (fileName != “”) { QFile file(fileName); if (!file.open(QIODevice::ReadOnly)) { QMessageBox::critical(this, tr(“Error”), tr(“Could not open file”)); return; } QString contents = file.readAll().constData(); textEdit->setPlainText(contents); file.close(); } The first step is asking the user for the name of the file to open. Qt comes with QFileDialog, which is a dialog from which the user can select a file. The image above shows the dialog on Kubuntu. The static getOpenFileName() function displays a modal file dialog, and does not return until the user has selected a file. It returns the file path of the file selected, or an empty string if the user canceled the dialog. If we have a file name, we try to open the file with open(), which returns true if the file could be opened. We will not go into error handling here, but you can follow the links from the learn more section. If the file could not be opened, we use QMessageBox to display a dialog with an error message (see the QMessageBox class description for further details). Actually reading in the data is trivial using the readAll() function, which returns all data in the file in a QByteArray. The constData() returns all data in the array as a const char*, which QString has a constructor for. The contents can then be displayed in the text edit. We then close() the file to return the file descriptor back to the operating system. Now, let us move on to the the save() slot. QString fileName = QFileDialog::getSaveFileName(this, tr(“Save File”), “”, tr(“Text Files (.txt);;C++ Files (.cpp *.h)”)); if (fileName != “”) { QFile file(fileName); if (!file.open(QIODevice::WriteOnly)) { // error message } else { QTextStream stream(&file); stream << textEdit->toPlainText(); stream.flush(); file.close(); } } When we write the contents of the text edit to the file, we use the QTextStream class, which wraps the QFile object. The text stream can write QStrings directly to the file; QFile only accepts raw data (char*) with the write() functions of QIODevice. Learn More About Here Files and I/O devices QFile, QIODevice