БлогNot. QT: работа с файлом, настройки приложения и файл ресурсов...

QT: работа с файлом, настройки приложения и файл ресурсов...

... и всё в одном проекте хотелось бы, так что примера лучше текстового редактора всё равно не придумаешь. Пример будем делать в актуальном QT 5.2.1, а редактор пока будет однооконным как "Блокнот".

Создадим виджет на основе QMainWindow, встроенная пустая форма нам не нужна, поэтому просто перепишем файл проекта (который с расширением .pro) в виде

QT += widgets

HEADERS       = mainwindow.h
SOURCES       = main.cpp \
                mainwindow.cpp
RESOURCES     = application.qrc

а файл mainwindow.ui можно убить. Последней строкой мы сказали, что у нашего приложения будут ещё и ресурсы. Не интересовался, как их добавить через графический интерфейс Qt Creator (наверное, можно), мне проще создать в папке проекта любым редактором текста файл application.qrc и написать там:

<!DOCTYPE RCC><RCC version="1.0">
<qresource>
    <file>images/copy.png</file>
    <file>images/cut.png</file>
    <file>images/new.png</file>
    <file>images/open.png</file>
    <file>images/paste.png</file>
    <file>images/save.png</file>
</qresource>
</RCC>

...а в папке проекта создать вложенную папку images с картинками, имена которых заданы в XML-файле. Это будут картинки и для пунктов меню, и для графических кнопок.

Главная программа main.cpp будет парой строк отличаться от стандарта, я написал комментарием, для чего могут понадобиться добавленные строки:

#include <QApplication>

#include "mainwindow.h"

int main(int argc, char *argv[])
{
    Q_INIT_RESOURCE(application);

    QApplication app(argc, argv);
    QString myOrganization = "My organization";
    QString myApplicationName = "Text Editor Example";
     //Имена нашей "организации" и "приложения" - потом пригодятся при
     //сохранении/восстановлении настроек программы
    app.setOrganizationName(myOrganization);
    app.setApplicationName(myApplicationName);
    MainWindow mainWindow(myOrganization,myApplicationName);
    mainWindow.show();
    return app.exec();
}

Файл mainwindow.h приведу без комментариев, там нет ничего особенного, просто описаны:

  • прототипы конструкторов (понадобился всего один);
  • прототипы функций, реализующих виртуальные методы класса QWidget (нам понадобится тоже один - обработчик закрытия окна closeEvent);
  • публичные слоты для обработки команд приложения;
  • приватные методы и элементы интерфейса приложения.

Вот полный текст файла:

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>

class QAction;
class QMenu;
class QPlainTextEdit;

class MainWindow : public QMainWindow {
    Q_OBJECT

public:
    MainWindow(QString myOrganization, QString myApplicationName);

protected:
    void closeEvent(QCloseEvent *event); //Обработка события закрытия приложения

private slots:
    void newFile();
    void open();
    bool save();
    bool saveAs();
    void about();
    void documentWasModified();

private:
    void createActions();
    void createMenus();
    void createToolBars();
    void createStatusBar();
    void readSettings();
    void writeSettings();
    bool maybeSave();
    void loadFile(const QString &fileName);
    bool saveFile(const QString &fileName);
    void setCurrentFile(const QString &fileName);
    QString strippedName(const QString &fullFileName);

    QPlainTextEdit *textEdit;
    QString curFile;

    QMenu *fileMenu;
    QMenu *editMenu;
    QMenu *helpMenu;
    QToolBar *fileToolBar;
    QToolBar *editToolBar;
    QAction *newAct;
    QAction *openAct;
    QAction *saveAct;
    QAction *saveAsAct;
    QAction *exitAct;
    QAction *cutAct;
    QAction *copyAct;
    QAction *pasteAct;
    QAction *aboutAct;
    QAction *aboutQtAct;
    QString myName, myOrganization, myApplicationName;
};

#endif

Всё остальное будет делать mainwindow.cpp. Я включил комментарии прямо в текст программы, внимательно читая его, Вы легко найдёте ответы на поставленные в заголовке вопросы.

#include <QtWidgets>

#include "mainwindow.h"

MainWindow::MainWindow(QString myOrganization, QString myApplicationName)
//Конструктор главного окна виджета
{
    myName = "Текстовый редактор"; //Именуем приложение
    this->myOrganization = myOrganization;
    this->myApplicationName = myApplicationName;
    textEdit = new QPlainTextEdit; //Основное окно редактирования
    setCentralWidget(textEdit);

    createActions(); //Для разгрузки конструктора создадим действия (команды меню),
    createMenus(); //главное меню,
    createToolBars(); //кнопочные панели инструментов и
    createStatusBar(); //строку состояния в отдельных функциях

    readSettings(); //Прочитать установки приложения

    connect(textEdit->document(), SIGNAL(contentsChanged()),
            this, SLOT(documentWasModified()));
            //определили слот для отслежиивания изменений в документе

    setCurrentFile(""); //Установить текущий файл, в начале - пусто
    setUnifiedTitleAndToolBarOnMac(true);
       //Только в QT5.2 и выше - совместимость с Mac OS
}

void MainWindow::createActions() //Создадим действия для пунктов меню
{
    newAct = new QAction(QIcon(":/images/new.png"), tr("&Новый"), this);
     //Параметры конструктора QAction: иконка из ресурсов, текст меню, родитель
    newAct->setShortcuts(QKeySequence::New);
     //В классе QKeySequence уже определены стандартные комбинации клавиш
    newAct->setStatusTip(tr("Создать новый файл"));
     //Подсказка для строки состояния;
     //tr() - макрос, который может пригодиться
     //при автоматической локализации приложения
    connect(newAct, SIGNAL(triggered()), this, SLOT(newFile()));
     //Назначили слот для обработки выбора пункта меню

    openAct = new QAction(QIcon(":/images/open.png"), tr("&Открыть..."), this);
    openAct->setShortcuts(QKeySequence::Open);
    openAct->setStatusTip(tr("Открыть существующий файл"));
    connect(openAct, SIGNAL(triggered()), this, SLOT(open()));

    saveAct = new QAction(QIcon(":/images/save.png"), tr("&Сохранить"), this);
    saveAct->setShortcuts(QKeySequence::Save);
    saveAct->setStatusTip(tr("Сохранить документ на диск"));
    connect(saveAct, SIGNAL(triggered()), this, SLOT(save()));

    saveAsAct = new QAction(tr("Сохранить к&ак..."), this);
     //Более простой конструктор QAction: текст меню, родитель
    saveAsAct->setShortcuts(QKeySequence::SaveAs);
    saveAsAct->setStatusTip(tr("Сохранить копию документа под новым именем"));
    connect(saveAsAct, SIGNAL(triggered()), this, SLOT(saveAs()));

    exitAct = new QAction(tr("В&ыход"), this);
    exitAct->setShortcuts(QKeySequence::Quit);
    exitAct->setStatusTip(tr("Закрыть приложение"));
    connect(exitAct, SIGNAL(triggered()), this, SLOT(close()));

    cutAct = new QAction(QIcon(":/images/cut.png"), tr("&Вырезать"), this);
    cutAct->setShortcuts(QKeySequence::Cut);
    cutAct->setStatusTip(tr("Переместить выделенный фрагмент в Буфер Обмена"));
    connect(cutAct, SIGNAL(triggered()), textEdit, SLOT(cut()));

    copyAct = new QAction(QIcon(":/images/copy.png"), tr("&Копировать"), this);
    copyAct->setShortcuts(QKeySequence::Copy);
    copyAct->setStatusTip(tr("Скопировать выделенный фрагмент в Буфер Обмена"));
    connect(copyAct, SIGNAL(triggered()), textEdit, SLOT(copy()));

    pasteAct = new QAction(QIcon(":/images/paste.png"), tr("Вст&авить"), this);
    pasteAct->setShortcuts(QKeySequence::Paste);
    pasteAct->setStatusTip(
                tr("Вставить в текущее место документа содержимое Буфера Обмена"));
    connect(pasteAct, SIGNAL(triggered()), textEdit, SLOT(paste()));

    aboutAct = new QAction(tr("&О программе"), this);
    aboutAct->setStatusTip(tr("Информация о приложении"));
    connect(aboutAct, SIGNAL(triggered()), this, SLOT(about()));

    aboutQtAct = new QAction(tr("О &Qt"), this);
    aboutQtAct->setStatusTip(tr("Информация о библиотеке Qt"));
    connect(aboutQtAct, SIGNAL(triggered()), qApp, SLOT(aboutQt()));
     //qApp - глобальный указатель на экземпляр приложения
     //у QWidget уже есть стандартный слот aboutQt()

    cutAct->setEnabled(false); //Некоторые пункты меню сначала недоступны
    copyAct->setEnabled(false);
    connect(textEdit, SIGNAL(copyAvailable(bool)), cutAct, SLOT(setEnabled(bool)));
    connect(textEdit, SIGNAL(copyAvailable(bool)), copyAct, SLOT(setEnabled(bool)));
     //Слоты для управления состоянием этих пунктов
}

void MainWindow::createMenus() //Создание главного меню
{
    fileMenu = menuBar()->addMenu(tr("&Файл"));
     //В QMainWindow уже есть menuBar, просто добавим туда меню
    fileMenu->addAction(newAct);
    fileMenu->addAction(openAct);
    fileMenu->addAction(saveAct);
    fileMenu->addAction(saveAsAct);
    fileMenu->addSeparator();
    fileMenu->addAction(exitAct);

    editMenu = menuBar()->addMenu(tr("&Правка"));
    editMenu->addAction(cutAct);
    editMenu->addAction(copyAct);
    editMenu->addAction(pasteAct);

    menuBar()->addSeparator();

    helpMenu = menuBar()->addMenu(tr("&Справка"));
    helpMenu->addAction(aboutAct);
    helpMenu->addAction(aboutQtAct);
}

void MainWindow::createToolBars() //Создание кнопочных тулбаров
{
    fileToolBar = addToolBar(tr("File"));
    fileToolBar->addAction(newAct);
    fileToolBar->addAction(openAct);
    fileToolBar->addAction(saveAct);

    editToolBar = addToolBar(tr("Edit"));
    editToolBar->addAction(cutAct);
    editToolBar->addAction(copyAct);
    editToolBar->addAction(pasteAct);
    //Добавили команды, для которых определили QIcon
}

void MainWindow::createStatusBar() //Создание строки состояния
{
    statusBar()->showMessage(tr("Готово"));
}

bool MainWindow::maybeSave() //Есть ли несохранённыеи изменения?
{
    if (textEdit->document()->isModified()) {
        QMessageBox::StandardButton ret;
        ret = QMessageBox::warning(this, tr("Приложение"),
                     tr("В документе есть несохранённые изменения.\n"
                        "Хотите сохранить документ?"),
                     QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel);
        if (ret == QMessageBox::Save) return save();
        else if (ret == QMessageBox::Cancel) return false;
    }
    return true;
}

void MainWindow::setCurrentFile(const QString &fileName)
//Управляет отображением имени текущего файла
{
    curFile = fileName;
    textEdit->document()->setModified(false);
    setWindowModified(false);

    QString shownName = curFile;
    if (curFile.isEmpty())
        shownName = "untitled.txt";
    setWindowFilePath(shownName);
     //В QWidget уже есть метод для связи пути к файлу с виджетом
    //setWindowTitle(strippedName(shownName)); //лишнее
}

QString MainWindow::strippedName(const QString &fullFileName)
//Вернёт имя файла без пути
{
    return QFileInfo(fullFileName).fileName();
}

void MainWindow::closeEvent(QCloseEvent *event) //Обработчик закрытия приложения
{
    if (maybeSave()) { //Если нужно сохранять,
        writeSettings(); //то сохранить и
        event->accept(); //подтвердить событие закрытия
    } else {
        event->ignore(); //иначе отменить обработку
    }
}

void MainWindow::newFile() //Создаёт новый файл
{
    if (maybeSave()) {
        textEdit->clear();
        setCurrentFile("");
    }
}

void MainWindow::open() //Открывает существующий файл
{
    if (maybeSave()) {
        QString fileName = QFileDialog::getOpenFileName(this);
        if (!fileName.isEmpty())
            loadFile(fileName);
    }
}

bool MainWindow::save() //Обработка пункта меню "Сохранить"
{
    if (curFile.isEmpty()) {
        return saveAs();
    } else {
        return saveFile(curFile);
    }
}

bool MainWindow::saveAs() //Обработка пункта меню "Сохранить как"
{
    QString fileName = QFileDialog::getSaveFileName(this);
    if (fileName.isEmpty())
        return false;

    return saveFile(fileName);
}

bool MainWindow::saveFile(const QString &fileName) //Непосредственно сохранение файла
{
    QFile file(fileName); //Свяжем файловую переменную с именем файла
    if (!file.open(QFile::WriteOnly | QFile::Text)) {
        QMessageBox::warning(this, tr("%1").arg(myName),
                             tr("Не могу сохранить файл %1\nПричина: %2.")
                             .arg(fileName)
                             .arg(file.errorString()));
        return false;
    }

    QTextStream out(&file); //...и будем сохранять через поток...
    //...и, если надо, управлять курсором (менять на "песочные часы")
#ifndef QT_NO_CURSOR
    QApplication::setOverrideCursor(Qt::WaitCursor);
#endif
    out << textEdit->toPlainText(); //Само сохранение
#ifndef QT_NO_CURSOR
    QApplication::restoreOverrideCursor();
#endif

    setCurrentFile(fileName);
    statusBar()->showMessage(tr("Файл сохранён"), 2000);
     //Покажем сообщение в строке статуса на 2 секунды
    return true;
}


void MainWindow::about() //Выводит окно "О программе"
{
   QMessageBox::about(this, tr("О программе"),
            tr("Это просто <b>%1</b>, показывающий, "
       "как сделать на QT современную программу с графическим интерфейсом, "
       "используя меню, панели инструментов и строку состояния.").arg(myName));
}

void MainWindow::documentWasModified() //Ставит метку модификации главного окна
{
    setWindowModified(textEdit->document()->isModified());
}

void MainWindow::readSettings() //Читает настройки приложения
{
    QSettings settings(myOrganization, myApplicationName);
     //Параметры конструктора QSettings: имя организации, имя приложения
     //Класс обеспечивает кросс-платрофменное сохранение настроек.
     //Для Windows - в реестре, для Unix-платформ с KDE будет создан ini-файл,
     //для Mac OS - XML-файл
    QPoint pos = settings.value("pos", QPoint(200, 200)).toPoint();
                               //ключ, значение по умолчанию
    QSize size = settings.value("size", QSize(400, 400)).toSize();
    resize(size);
    move(pos);
}

void MainWindow::writeSettings() //Запоминает настройки приложения
{
    QSettings settings(myOrganization, myApplicationName);
    settings.setValue("pos", pos());
    settings.setValue("size", size());
     //Просто будем помнить позицию и размер окна
}

void MainWindow::loadFile(const QString &fileName) //Открытие файла
{
    QFile file(fileName);
    if (!file.open(QFile::ReadOnly | QFile::Text)) {
        QMessageBox::warning(this, tr("%1").arg(myName),
                             tr("Не могу открыть файл %1\nПричина: %2.")
                             .arg(fileName)
                             .arg(file.errorString()));
        return;
    }

    QTextStream in(&file);
#ifndef QT_NO_CURSOR
    QApplication::setOverrideCursor(Qt::WaitCursor);
#endif
    textEdit->setPlainText(in.readAll());
#ifndef QT_NO_CURSOR
    QApplication::restoreOverrideCursor();
#endif

    setCurrentFile(fileName);
    statusBar()->showMessage(tr("Файл открыт"), 2000);
}

Вот какое красивое приложение у нас вышло:

Окно приложения QT "Текстовый редактор с меню и кнопками"
Окно приложения QT "Текстовый редактор с меню и кнопками"

 Скачать архив .zip с проектом QT 5 "Текстовый редактор и меню" (15 Кб)

08.05.2014, 19:39 [24894 просмотра]


теги: qt c++ textprocessing программирование форматы

К этой статье пока нет комментариев, Ваш будет первым