Un semplice TextEditor
Creiamo un nuovo progetto seguendo i passaggi "Creazione di un progetto vuoto". In questo primo esempio creeremo un editor di testo in una finestra sul desktop. Questo rappresenta un semplice programma Qt dotato di interfaccia utente.Windows | Ubuntu | Mac OSX |
![]() |
![]() |
![]() |
Inseriamo nel main il codice seguente:
1 2 3 4 5 6 7 8 9 10 11 |
#include <QApplication> #include <QTextEdit> int main(int argv, char **args) { QApplication app(argv, args); QTextEdit textEdit; textEdit.show(); return app.exec(); } |
Analizziamo ora il codice riga per riga.
-
Nelle prime due righe includiamo i file header per QApplication e QTextEdit, le due classi che ci servono per questo esempio. Tutte le classi Qt hanno un file header avente il nome stesso della classe.
- La riga 6 crea un oggetto QApplication. Questo oggetto gestisce le risorse all’interno di tutta l’applicazione ed è necessario per eseguire ogni programma Qt che possiede una GUI. QApplication richiede argv e args poiché Qt accetta gli argomenti a linea di comando.
- La riga 7 crea un oggetto QTextEdit. Un editor di testo è un elemento grafico nella GUI. In Qt questi elementi grafici vengono definiti widgets. Esempi di altri widgets sono barre di scorrimento, labels e radio buttons. Un widget può inoltre contenere altri widgets: ne sono un esempio la finestra di dialogo e la main window dell’applicazione.
- La riga 9 mostra nella sua finestra sullo schermo l’editor di testo. I widgets funzionano anche come contenitori (ad esempio un QMainWindow contiene toolbar, menù, barre di stato ed altri widgets), ma è comunque possibile visualizzare il widget singolo nella sua finestra. I widgets sono nascosti di default: la funzione show()ha il compito di visualizzarli sul desktop.
- La riga 10 avvia il ciclo di eventi di QApplication. Quando un programma Qt è in esecuzione, sono generati degli eventi che vengono inviati ai widgets dell’applicazione. Esempi di eventi sono pressione dei tasti del mouse o della tastiera. Quando si digita del testo nel widget di editor testuale, esso riceve eventi di pressione dei tasti e risponde visualizzando il testo digitato.
Per eseguire l’applicazione è sufficiente fare run dal menu build. L’eseguibile verrà posizionato nelle cartelle TextEditor-build-Desktop_Qt_5_0_1_MinGW_32bit-Debug/debug o TextEditor-build-Desktop_Qt_5_0_1_MinGW_32bit-Debug/release).
qmake è il tool di compilazione di Qt e richiede un file di configurazione. qmake genera questo file per noi quando gli diamo come argomento “-project” . Dato un file di configurazione (con suffisso .pro), qmake crea un makefile per compilare il programma.
Aggiungiamo un bottone di uscita
Nelle applicazioni reali solitamente si utilizzano più widgets. Introdurremo ora un QPushButton al di sotto dell’editor di testo. Quando premuto (ad esempio con il mouse), il bottone permetterà di uscire dall’applicazione.
riutilizziamo il progetto precedente sostituendo il main.cpp con il seguente sorgente:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
#include <QApplication> #include <QTextEdit> #include <QPushButton> #include <QWidget> #include <QVBoxLayout> int main(int argc, char *argv[]) { QApplication app(argc, argv); QTextEdit textEdit; QPushButton quitButton("Esci"); QVBoxLayout layout; QWidget window; QObject::connect(&quitButton,SIGNAL(clicked()),&app,SLOT(quit())); layout.addWidget(&textEdit); layout.addWidget(&quitButton); window.setLayout(&layout); window.show(); return app.exec(); } |
La linea 15 utilizza il meccanismo di segnali e slot per far uscire l’applicazione quando il quitButton viene premuto. Uno slot è una funzione che può essere invocata a runtime utilizzando il suo nome (come stringa letterale). Un segnale è una funzione che quando chiamata invoca gli slots registrati ad esso.
quit()è uno slot di QApplication che chiude l’applicazione quando invocato. clicked()è un segnale che QPushButton emette quando viene premuto. La funzione statica QObject::connect() si occupa di connettere lo slot ed il segnale. SIGNAL e SLOT sono due macro che richiedono i prototipi delle funzioni del segnale e dello slot al fine di connetterli. E’ inoltre necessario fornire i puntatori agli oggetti che devono inviare e ricevere il segnale.
La linea 12 crea un QVBoxLayout . Come menzionato precedentemente, i widgets possono contenere altri widgets. E’ possibile impostare direttamente i limiti (locazione e dimensioni) di widget figli, ma di solito è più semplice e comodo utilizzare un layout. Un layout gestisce le dimensioni dei widget figli. QVBoxLayout, per esempio, posiziona i widget figli in righe verticali.
Le linee 16 e 17 aggiungono al layout l’editor di testo ed il bottone, mentre la riga 17 imposta il layout sul widget window.
Sottoclassare Widget
Si poteva scrivere lo stesso programma utilizzando le classi in un unico flat file main.cpp
Al termine dell’operazione I 3 file ottenuti dovranno essere completati in questo modo:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
#include <QApplication> #include <QObject> #include <QWidget> #include <QTextEdit> #include <QPushButton> #include <QVBoxLayout> class Notepad: public QWidget { public: QWidget window; Notepad() { textEdit = new QTextEdit; quitButton = new QPushButton(tr("Exit")); layout = new QVBoxLayout; layout->addWidget(textEdit); layout->addWidget(quitButton); setLayout(layout); setWindowTitle(tr("Notepad")); window.setLayout(layout); connect(quitButton,SIGNAL(clicked()),qApp,SLOT(quit())); } private: QTextEdit *textEdit; QVBoxLayout *layout; QPushButton *quitButton; }; int main(int argc, char *argv[]) { QApplication app(argc, argv); Notepad *vNote=new Notepad; // il metodo show mostrerebbe il widget contenitore vNote->window.show(); return app.exec(); } |
oppure, come è buona norma di programmazione suddividere la definizione di una classe in un file header (i file intestazione, con estensione ".h") mentre tutte le implementazioni dei metodi della classe andranno, invece, inseriti nel file con estensione .cpp. Quando, nella definizione di una classe, si lasciano solo i prototipi dei metodi, si suole dire che viene creata un'intestazione di classe (solitamente distribuite in header-files).
Procediamo quindi nella definizione del progetto in questo modo: Aggiungiamo una classe c++
Al termine dell’operazione dovremmo avere i seguenti 3 files:
Modifichiamo i files con il codice seguente
main.cpp | notepad.h | notepad.cpp | |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
#include "notepad.h" int main(int argc, char *argv[]) { QApplication app(argc, argv); Notepad *vNote=new Notepad; // il metodo show mostrerebbe il widget contenitore vNote->window.show(); return app.exec(); } |
#ifndef NOTEPAD_H #define NOTEPAD_H #include <QApplication> #include <QObject> #include <QWidget> #include <QTextEdit> #include <QPushButton> #include <QVBoxLayout> class Notepad: public QWidget { // Q_OBJECT public: QWidget window; Notepad(); private: QTextEdit *textEdit; QVBoxLayout *layout; QPushButton *quitButton; }; #endif // NOTEPAD_H |
#include "notepad.h" Notepad::Notepad() { textEdit = new QTextEdit; quitButton = new QPushButton(tr("Exit")); layout = new QVBoxLayout; layout->addWidget(textEdit); layout->addWidget(quitButton); setLayout(layout); setWindowTitle(tr("Notepad")); window.setLayout(layout); connect(quitButton,SIGNAL(clicked()),qApp,SLOT(quit())); }; |
Come si può vedere nella definizione della classe, utilizziamo puntatori ai nostri QObject (textEdit e quitButton). Come regole, si dovrebbe sempre allocare i QObject nella memoria heap e mai copiarli. Utilizziamo anche la funzione tr() attorno alle stringhe da visualizzare all’utente. Questa funzione è necessaria quando si vuole distribuire l’applicazione in più di una lingua (ad esempio Inglese ed Italiano).
Piuttosto che impostare la GUI e connettere lo slot nella funzione main(), utilizziamo ora il costruttore della classe Notepad. Quando gli utilizzatori vogliono uscire dall’applicazione, si potrebbe voler visualizzare una finestra di dialogo per chiedere all’utente se è effettivamente convinto di voler uscire. In questo esempio, sottoclassiamo QWidget ed connettiamo allo slot predefinito quit() di QApplication al Quit button.
La macro Q_OBJECT in questo esempio non è necessaria. Comunque se inserita, deve essere la prima nella definizione della classe e definisce questa come un QObject (è necessario che la classe erediti da QObject). Un QObject aggiunge molte capacità ad una normale classe C++. In particolare, il nome della classe e degli slots può essere recuperato a tempo di esecuzione. E’ inoltre possibile invocare direttamente uno slot o recuperare i tipi dei suoi paramentri. Se in compilazione, dopo aver aggiunto Q_OBJECT, appaiono errori
provare ad aggiungere al file .pro l'istruzione:
QT +=widgets core
Aggiungiamo un bottone di uscita
Aggiungiamo al bottone la possibilità di confermare o meno l'uscita:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
#include <QObject> #include <QApplication> #include <QTextEdit> #include <QPushButton> #include <QWidget> #include <QVBoxLayout> #include <QMessageBox> void Uscita() { int button = QMessageBox::question( 0, QObject::tr("Notepad - Quit"), QObject::tr("Vuoi sgommare dall'applicazione ?"), QMessageBox::Yes | QMessageBox::No); if (button == QMessageBox::Yes) exit(0); } int main(int argc, char *argv[]) { QApplication app(argc, argv); QTextEdit textEdit; QPushButton quitButton("Esci"); QVBoxLayout layout; QWidget window; QObject::connect(&quitButton,&QPushButton::clicked,Uscita); layout.addWidget(&textEdit); layout.addWidget(&quitButton); window.setLayout(&layout); window.show(); return app.exec(); } |
La funzione di traduzione tr() (utilizzata nella funzione uscita) è un metodo statico di QObject. Poichè QWidget è una sottoclasse di QObject, tr() risulta disponibile tra i metodi di QWidget, ma nel main() occorre includere l'header <QObject> e scrivere QObject::tr() al fine di poter utilizzare la funzione tr().
L'applicazione è accessibile mediante la varabile puntatore globale qApp. Si noti che per il connect() è stata usata la sintassi che consente di collegare una generica funzione ad un evento di un oggetto il cui puntatore è passato come primo argomento.
Quando clicco su Esci appare la seguente richiesta di uscita: