Un semplice TextEditor che usa QMainWindow

Molte applicazioni trarranno beneficio dall’ultilizzo di QMainWindow, poiché possiede il proprio layout a cui si può aggiungere una barra dei menù, dock widgets, tool bars e una barra di stato. QMainWindow ha un’area centrale che può essere occupata da ogni tipo di widget. Nel nostro caso posizioneremo qui il nostro editor di testo.

Partiamo dal file di intestazione della classe:

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
#ifndef NOTEPAD_H
#define NOTEPAD_H

#include <QApplication>
#include <QMainWindow>
#include <QTextEdit>
#include <QAction>
#include <QMenuBar>
#include <QMenu>
#include <QString>
#include <QFile>
#include <QFileDialog>
#include <QMessageBox>
#include <QTextStream>

class Notepad : public QMainWindow
{
    Q_OBJECT

public:
    Notepad();

private slots:
    void open();
    void save();
    void quit();

private:
    QTextEdit *textEdit;
    QAction *openAction;
    QAction *saveAction;
    QAction *exitAction;
    QMenu *fileMenu;

};

#endif // NOTEPAD_H

Nel costruttore della classe imposteremo gli elementi relativi alla GUI. Solitamente in una QMainWindow lo stesso slot può essere invocato da vari widgets presenti. Sono esempi gli elementi del menù ed i bottoni nella tool bar. Per semplificare ciò, Qt fornisce QAction, una classe che può essere data a vari widgets e connessa ad uno slot. Per esempio, QMenu  e QToolBar possono creare elementi del menù e tool buttons dalle stesse QActions. Presto vedremo in che modo.
Le QActions  sono create con il testo che dovrebbe apparire sui widgets a cui vengono aggiunte (nel nostro caso gli elementi del menù). Se inoltre volessimo aggiungerle ad una tool bar potremmo impostare delle icone  alle varie azioni.
Quando un elemento del menù è cliccato, esso attiva la QAction che si occupa di invocare il rispettivo slot.

Notepad::Notepad()
{
    openAction = new QAction(tr("&Open"), this);
    saveAction = new QAction(tr("&Save"), this);
    exitAction = new QAction(tr("E&xit"), this);

    connect(openAction, SIGNAL(triggered()), this, SLOT(open()));
    connect(saveAction, SIGNAL(triggered()), this, SLOT(save()));
    connect(exitAction, SIGNAL(triggered()), this, SLOT(quit()));

    // Creazione del Menu()
    fileMenu = menuBar()->addMenu(tr("&File"));
    fileMenu->addAction(openAction);
    fileMenu->addAction(saveAction);
    fileMenu->addSeparator();
    fileMenu->addAction(exitAction);

    textEdit = new QTextEdit;
    setCentralWidget(textEdit);     // metodo del QMainWindow
    setWindowTitle(tr("Notepad"));  // metodo del QMainWindow

}

Includiamo ora gli slots necessari:

- chiudere l'applicazione chiedendo conferma
- per salvare un documento
- aprire un documento.

Ecco la definizione dello quit() che viene connesso al segnale abbinato al menu Exit.

void Notepad::quit()
{
    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);
}

SALVATAGGIO ED APERTURA

In questo sezione analizzeremo la funzionalità degli slot di open() e save() che abbiamo aggiunto nella sezione precedente.

Iniziamo con lo slot open(). Il primo passo è chiedere all’utente il nome del file da aprire. Qt contiene QFileDialog, una finestra di dialogo dalla quale l’utente può selezionare un file. La funzione statica getOpenFileName() visualizza una finestra di dialogo modale e non ritorna fintanto che l’utente non seleziona un file. La funzione ritorna una stringa contenente il path del file selezionato, oppure una stringa vuota nel caso l’utente annulli l’operazione.

Se otteniamo il nome di un file, proviamo ad aprirlo tramite la funzione open(), che ritorna true se il file può essere aperto. Qui non andremo nel dettaglio della gestione errori. Se il file non può essere aperto, utilizziamo una QMessageBox  per visualizzare una finestra con un messaggio d’errore.

La lettura dei dati è banale, grazie alla funzione readAll()che ritorna tutti i dati nel file in un QByteArray. La funzione constData() ritorna il contenuto dell’array sotto forma di const char*, per il quale QString ha un costrutture. Il contenuto ora può essere visualizzato nell’editor di testo. Possiamo ora chudere il file con close() per far ritornare il file descriptor al sistema operativo.

void Notepad::open()
{
    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();
    }
}

Iniziamo con lo slot save(). Quando scriviamo il contenuto dell’editor di testo nel file, utilizziamo una classe QTextStream che utilizza un oggetto QFile. La text stream può scrivere stringe direttamente nel file, mentre QFile  accetta solo dati grezzi (char*) nella funzione write() di QIODevice

void Notepad::save()
{
    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();
        }
    }
}

Il file contenente la definizione dei metodi e degli slot della classe notepad è quindi:

#include "notepad.h"

Notepad::Notepad()
{
    openAction = new QAction(tr("&Open"), this);
    saveAction = new QAction(tr("&Save"), this);
    exitAction = new QAction(tr("E&xit"), this);

    connect(openAction, SIGNAL(triggered()), this, SLOT(open()));
    connect(saveAction, SIGNAL(triggered()), this, SLOT(save()));
    connect(exitAction, SIGNAL(triggered()), this, SLOT(quit()));

    // Creazione del Menu()
    fileMenu = menuBar()->addMenu(tr("&File"));
    fileMenu->addAction(openAction);
    fileMenu->addAction(saveAction);
    fileMenu->addSeparator();
    fileMenu->addAction(exitAction);

    textEdit = new QTextEdit;
    setCentralWidget(textEdit);     // metodo del QMainWindow
    setWindowTitle(tr("Notepad"));  // metodo del QMainWindow

}

void Notepad::open()
{
    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();
    }
}

void Notepad::save()
{
    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();
        }
    }
}

void Notepad::quit()
{
    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);
}

mentre il main.cpp è:

#include "notepad.h"
#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Notepad w;
    w.show();
    
    return a.exec();
}

L'esecuzione nei 3 sistemi operativi principali è visibile nelle immagini sottostanti.