Создание SDI-приложений в Qt


Типичным примером SDI-приложения является программа ОС Windows Notepad (Блокнот). Пример, приведенный в листинге ниже, реализует упрощенный вариант этой программы, представляющей собой текстовый редактор. Результат показан на рисунке.

{рисунок}


#ifndef _DocWindow_h_
#define _DocWindow_h_

#include <QTextEdit>

class DocWindow: public QTextEdit {
Q_OBJECT
private:
    QString m_strFileName;

public:
    DocWindow(QWidget* pwgt = 0);

signals:
    void changeWindowTitle(const QString&);

public slots:
    void slotLoad  ();
    void slotSave  ();
    void slotSaveAs();
};
#endif //_DocWindow_h_

Класс DocWindow, унаследованный от класса QTextEdit, представляет собой окно для редактирования. В его определении содержится атрибут m_strFileName, в котором хранится имя изменяемого файла. Сигнал changeWindowTitle()предназначен для информирования о том, что текстовая область заголовка должна быть изменена. Слоты slotLoad(), slotSave() и slotSaveAs() необходимы для проведения операций чтения и записи файлов.


DocWindow::DocWindow(QWidget* pwgt/*=0*/) : QTextEdit(pwgt)
{
}

В конструктор класса передается указатель на виджет предка.


void DocWindow::slotLoad()
{
    QString str = QFileDialog::getOpenFileName();
    if (str.isEmpty()) {
        return;
    }

    QFile file(str);
    if (file.open(QIODevice::ReadOnly)) {
        QTextStream stream(&file);
        setPlainText(stream.readAll());
        file.close();

        m_strFileName = str;
        emit changeWindowTitle(m_strFileName);
    }
}

Метод slotLoad() отображает диалоговое окно открытия файла вызовом статического метода QFileDialog::getOpenFileName(), с помощью которого пользователь выбирает файл для чтения. В том случае, если пользователь отменит выбор, нажав на кнопку Cancel (Отмена), этот метод вернет пустую строку. В нашем примере это проверяется с помощью метода QString::isEmpty(). Если метод getOpenFileName() возвратит непустую строку, то будет создан объект класса QFile, который будет проинициализирован этой строкой. Передача QIODevice::ReadOnly В метод QFile::open() говорит о том, что файл открывайся только для чтения. В случае успешного открытия файла создается объект потока stream, который в нашем примере используется для чтения текста из файла. Чтение всего содержимого файла выполняется при помощи метода QTextStream::readAll(), который возвращает его в объекте строкового типа QString. Текст устанавливается в виджете методом setPlainText(). После этого файл закрывается методом close(), а местонахождения и имя файла оповещаются высылкой сигнала changeWindowTitie() для того, чтобы использующее виджет DocWindow приложение было в состоянии отобразить эту информацию путем изменения заголовка окна.


void DocWindow::slotSaveAs() 
{
    QString str = QFileDialog::getSaveFileName(0, m_strFileName);
    if (!str.isEmpty()) {
        m_strFileName = str;
        slotSave();
    }
}

Слот slotSaveAs() отображает диалоговое окно сохранения файла с помощью статического метода QFileDialog::getSaveFileName(). Если это окно не было отменено пользователем (метод вернул непустую строку), то в атрибут mstrFileName записывается имя файла, указанное пользователем в диалоговом окне, и вызывается слот slotSave().


void DocWindow::slotSave()
{
    if (m_strFileName.isEmpty()) {
        slotSaveAs();
        return;
    }

    QFile file(m_strFileName);
    if (file.open(QIODevice::WriteOnly)) {
        QTextStream(&file) << toPlainText();
        file.close();
        emit changeWindowTitle(m_strFileName);
    }
}

Запись в файл представляет собой более серьезный процесс, чем считывание, так как связана с рядом обстоятельств, которые могут сделать ее невозможной. Например, на диске не хватит места или он будет недоступен для записи. Для записи в файл нужно создать объект класса QFile и передать в него строку с именем файла. Затем нужно вызвать метод open(), передав в него значение QIODevice::writeOnly (флаг, говорящий о том, что будет выполняться запись в файл). В том случае, если файл с таким именем на диске не существует, он будет создан, если существует — то он будет открыт для записи. В случае успешного открытия создается промежуточный объект потока, в который посредством оператора << передается текст виджета с помощью метода toPlainText(). После этого файл закрывается методом QFile::close() и высылается сигнал с новым именем и местонахождением файла. Это делается для того, чтобы эту информацию могло отобразить приложение, использующее наш виджет DocWindow.


#ifndef _SDIProgram_h_
#define _SDIProgram_h_

#include <QtGui>
#include "DocWindow.h"
#include "SDIProgram.h"

class SDIProgram : public QMainWindow {
Q_OBJECT
public:
    SDIProgram(QWidget* pwgt = 0) : QMainWindow(pwgt) 
    {
        QMenu*     pmnuFile = new QMenu("&File");
        QMenu*     pmnuHelp = new QMenu("&Help");
        DocWindow* pdoc     = new DocWindow;

        pmnuFile->addAction("&Open...", 
                            pdoc, 
                            SLOT(slotLoad()), 
                            QKeySequence("CTRL+O")
                           );
        pmnuFile->addAction("&Save", 
                            pdoc, 
                            SLOT(slotSave()), 
                            QKeySequence("CTRL+S")
                           );
        pmnuFile->addAction("S&ave As...", 
                            pdoc, 
                            SLOT(slotSaveAs())
                           );
        pmnuFile->addSeparator();
        pmnuFile->addAction("&Quit", 
                            qApp, 
                            SLOT(quit()), 
                            QKeySequence("CTRL+Q")
                           );
        pmnuHelp->addAction("&About", 
                            this, 
                            SLOT(slotAbout()), 
                            Qt::Key_F1
                           );

        menuBar()->addMenu(pmnuFile);
        menuBar()->addMenu(pmnuHelp);

        setCentralWidget(pdoc);
        connect(pdoc, 
                SIGNAL(changeWindowTitle(const QString&)), 
                SLOT(slotChangeWindowTitle(const QString&))
               );

        statusBar()->showMessage("Ready", 2000);
    }

public slots:
    void slotAbout()
    {
        QMessageBox::about(this, "Application", "SDI Example");
    }

    void slotChangeWindowTitle(const QString& str)
    {
        setWindowTitle(str);
    }
};
#endif  //_SDIProgram_h_

Класс SDIProgram унаследован от класса QMainWindow. В его конструкторе создаются три виджета — всплывающие меню File (Файл) (указатель pmnuFile), Help (Помощь) (указатель pmnuHelp) и виджет созданного нами окна редактирования (указатель pdoc). Затем производится с помощью метода addAction() неявное создание объектов действий и одновременное их добавление для команд меню. Третьим параметром указывают слот, с которым должна быть соединена команда, во втором параметре указан сам объект, который содержит этот слот. Таким образом, команда Open... (Открыть...) соединяется со слотом slotLoad(), команда Save (Сохранить) — со слотом slotSave(), а команда Save As... (Сохранить как...) — со слотом slotSaveAs(). Все эти слоты реализованы в классе DocwWndow. Команда About (О программе) соединяется со слотом slotAbout(), предоставляемым классом SDIProgram. Вызов метода menuBar() возвращает указатель на виджет меню верхнего уровня, а вызов методов addMenu() добавляет созданные всплывающие меню File (Файл) и Help (Помощь). Вызов метода setCentralWidget() делает окно редактирования центральным виджетом, то есть рабочей областью нашей программы. Для изменения текстового заголовка программы, после загрузки файла, сигнал changeWindowTitle(), высылаемый виджетом окна редактирования, соединяется со слотом SlotChangeWindowTitle(). Метод showMessage(), вызываемый из виджета строки состояния, отображает надпись "Ready" на время, установленное во втором параметре (в нашем примере оно соответствует двум секундам).

Читать далее: Создание MDI-приложений в Qt