Development/Tutorials/Saving and loading (ru): Difference between revisions

    From KDE TechBase
    m (Text replace - "<code cppqt n>" to "<syntaxhighlight lang="cpp-qt" line>")
    m (Text replace - "<code>" to "<syntaxhighlight lang="text">")
    Line 382: Line 382:


    С помощью этого файла, урок может быть собран и запущен тем же путем, что и урок 3. Подробнее см. урок 3.
    С помощью этого файла, урок может быть собран и запущен тем же путем, что и урок 3. Подробнее см. урок 3.
    <code>
    <syntaxhighlight lang="text">
    mkdir build && cd build
    mkdir build && cd build
    cmake .. -DCMAKE_INSTALL_PREFIX=$HOME
    cmake .. -DCMAKE_INSTALL_PREFIX=$HOME

    Revision as of 20:45, 29 June 2011


    Development/Tutorials/Saving_and_loading


    Загрузка и сохранение файлов
    Tutorial Series   Beginner Tutorial
    Previous   Урок 3 - KActions
    What's Next   Tutorial 5 - Using KCmdLineArgs
    Further Reading   Tutorial: Using KIO Slaves in your Program KIO::NetAccess QFile

    Введение

    Мы сделали интерфейс простого текстового редактора, самое время добавить к нему чего-нибудь полезного. Самое основное в чем нуждается текстовый редактор - это загрузка файлов с диска, сохранение файлов, которые были созданы/изменены и создание новых файлов.

    KDE предоставляет набор классов для работы с файлами, которые делают жизнь разработчика намного легче. Библиотека KIO предоставляет вам легкий доступ к файлам через сеть, а так же стандартные диалоги выбора файла.

    Листинг кода

    main.cpp

    <syntaxhighlight lang="cpp-qt" line>

    1. include <KApplication>
    2. include <KAboutData>
    3. include <KCmdLineArgs>
    1. include "mainwindow.h"

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

     KAboutData aboutData( "tutorial4", "tutorial4",
         ki18n("Tutorial 4"), "1.0",
         ki18n("A simple text area which can load and save."),
         KAboutData::License_GPL,
         ki18n("Copyright (c) 2007 Developer") );
     KCmdLineArgs::init( argc, argv, &aboutData );
     KApplication app;
    
     MainWindow* window = new MainWindow();
     window->show();
     return app.exec();
    

    } main.cpp практически не изменился с урока 3.

    mainwindow.h

    <syntaxhighlight lang="cpp-qt" line>

    1. ifndef MAINWINDOW_H
    2. define MAINWINDOW_H
    1. include <KXmlGuiWindow>
    2. include <KTextEdit>

    class MainWindow : public KXmlGuiWindow {

     Q_OBJECT //новое
     
     public:
       MainWindow(QWidget *parent=0);
     
     private:
       KTextEdit* textArea;
       void setupActions();
       QString fileName; //новое
    
     private slots: //новое
       void newFile(); //новое
       void openFile(); //новое
       void saveFile(); //новое
       void saveFileAs(); //новое
       void saveFileAs(const QString &outputFileName); //новое
    

    };

    1. endif

    Если мы хотим добавить возможность загружать и сохранять файлы, мы должны добавить функции которые будут делать это. Т.к. функции будут вызваны через механизм Qt сигнал/слот мы должны обозначить, что эти функции - слоты, что мы и делаем на строке 19. Раз мы используем слоты, мы должны добавить макрос Q_OBJECT.

    Так же нам нужно хранить имя открытого файла, поэтому объявляем переменную QString fileName.

    mainwindow.cpp

    <syntaxhighlight lang="cpp-qt" line>

    1. include "mainwindow.h"
    1. include <KApplication>
    2. include <KAction>
    3. include <KLocale>
    4. include <KActionCollection>
    5. include <KStandardAction>
    6. include <KFileDialog> //новое
    7. include <KMessageBox> //новое
    8. include <KIO/NetAccess> //новое
    9. include <KSaveFile> //новое
    10. include <QTextStream> //новое

    MainWindow::MainWindow(QWidget *parent)

       : KXmlGuiWindow(parent),
         fileName(QString()) //новое
    

    {

     textArea = new KTextEdit;
     setCentralWidget(textArea);
    
     setupActions();
    

    }

    void MainWindow::setupActions() {

     KAction* clearAction = new KAction(this);
     clearAction->setText(i18n("Clear"));
     clearAction->setIcon(KIcon("document-new"));
     clearAction->setShortcut(Qt::CTRL + Qt::Key_W);
     actionCollection()->addAction("clear", clearAction);
     connect(clearAction, SIGNAL(triggered(bool)),
             textArea, SLOT(clear()));
    
     KStandardAction::quit(kapp, SLOT(quit()),
                           actionCollection());
    
     KStandardAction::open(this, SLOT(openFile()),
                           actionCollection()); //новое
    
     KStandardAction::save(this, SLOT(saveFile()),
                           actionCollection()); //новое
    
     KStandardAction::saveAs(this, SLOT(saveFileAs()),
                           actionCollection()); //новое
    
     KStandardAction::openNew(this, SLOT(newFile()),
                           actionCollection()); //новое
    
     setupGUI();
    

    }

    //Дальше все новое

    void MainWindow::newFile() {

     fileName.clear();
     textArea->clear();
    

    }

    void MainWindow::saveFileAs(const QString &outputFileName) {

     KSaveFile file(outputFileName);
     file.open();
     
     QByteArray outputByteArray;
     outputByteArray.append(textArea->toPlainText().toUtf8());
     file.write(outputByteArray);
     file.finalize();
     file.close();
     
     fileName = outputFileName;
    

    }

    void MainWindow::saveFileAs() {

     saveFileAs(KFileDialog::getSaveFileName());
    

    }

    void MainWindow::saveFile() {

     if(!fileName.isEmpty())
     {
       saveFileAs(fileName);
     }
     else
     {
       saveFileAs();
     }
    

    }

    void MainWindow::openFile() {

     QString fileNameFromDialog = KFileDialog::getOpenFileName();
    
     QString tmpFile;
     if(KIO::NetAccess::download(fileNameFromDialog, tmpFile, 
            this))
     {
       QFile file(tmpFile);
       file.open(QIODevice::ReadOnly);
       textArea->setPlainText(QTextStream(&file).readAll());
       fileName = fileNameFromDialog;
    
       KIO::NetAccess::removeTempFile(tmpFile);
     }
     else
     {
       KMessageBox::error(this, 
           KIO::NetAccess::lastErrorString());
     }
    

    }

    tutorial4ui.rc

    <?xml version="1.0" encoding="UTF-8"?> <gui name="tutorial4"

        version="1"
        xmlns="http://www.kde.org/standards/kxmlgui/1.0"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://www.kde.org/standards/kxmlgui/1.0
                            http://www.kde.org/standards/kxmlgui/1.0/kxmlgui.xsd" >
    
     <MenuBar>
       <Menu name="file" >
         <Action name="clear" />
       </Menu>
     </MenuBar>
    
     <ToolBar name="mainToolBar" >
       <text>Main Toolbar</text>
       <Action name="clear" />
     </ToolBar>
    

    </gui> Этот файл идентичен tutorial3ui.rc из урока 3, только поле name изменилось на 'tutorial4'. Нам не нужно добавлять никакую информацию о любом KStandardAction т.к. их обработкой занимается KDE автоматически.

    Объяснение

    Хорошо, теперь напишем код, который будет загружать и сохранять. Все это мы допишем в mainwindow.cpp

    Первая штука, которую добавим <syntaxhighlight lang="cpp-qt"> fileName(QString()) в конструктор MainWindow на строке 16. Это очищает fileName с самого начала.

    Добавление действий

    Первое, что мы собираемся сделать, это предоставить пользователю интерфейс, что бы он мог сказать приложению "загрузить" и "сохранить". Как действие quit в уроке 3, мы будем использовать KStandardActions. На строках с 37 по 47 мы добавляем действия тем же путем, что и действие quit. Каждое из них мы соединяем с соответствующим слотом, который мы объявили в заголовочном файле.

    Создание нового документа

    Первая функция, которую мы создали - newFile(): <syntaxhighlight lang="cpp-qt"> void MainWindow::newFile() {

     fileName.clear();
     textArea->clear();
    

    } fileName.clear() очищает переменную fileName типа QString, что отражает тот факт, что этого документа еще нет на диске. textArea->clear() очищает область текста, используя функцию clear, которую мы подключили к KAction в уроке 3.

    Сохранение файла

    saveFileAs(QString)

    Теперь разберем наш первый обработчик. Мы собираемся реализовать функцию, которая сохраняет содержимое текстовой области в файл имя которого передается в функцию. KDE предоставляет класс для безопасного сохранения файла именуемый KSaveFile, который является производным от Qt'шного QFile.

    Прототип функции: <syntaxhighlight lang="cpp-qt"> void MainWindow::saveFileAs(const QString &outputFileName)

    Мы создаем объект KSaveFile и открываем файл: <syntaxhighlight lang="cpp-qt"> KSaveFile file(outputFileName); file.open();

    Теперь у нас есть файл для записи, нам нужно отформатировать текст в текстовой области в формат, который может быть записан в файл. Для этого, мы создаем QByteArray и заполняем его обычным текстом из текстовой области: <syntaxhighlight lang="cpp-qt"> QByteArray outputByteArray; outputByteArray.append(textArea->toPlainText().toUtf8()); Теперь у нас есть QByteArray, запишем его в файл с помощью KSaveFile::write(). Если бы мы использовали QFile, он бы моментально начал бы изменения в файле. Однако, если возникнет проблема посреди записи, файл будет поврежден. По этой причине KSaveFile сначала записывает данные во временный файл, а потом, когда вы вызываете KSaveFile::finalize() изменения попадают в текущий файл. <syntaxhighlight lang="cpp-qt"> file.write(outputByteArray); file.finalize(); file.close(); Наконец, мы записываем в переменную MainWindow fileName имя файла в который только, что сохранили: <syntaxhighlight lang="cpp-qt"> fileName = outputFileName;

    saveFileAs()

    Эта функция к которой подключек слот saveAs. Она просто вызывает saveFileAs(QString) и передает ей имя файла возвращенное KFileDialog::getSaveFileName().

    <syntaxhighlight lang="cpp-qt"> void MainWindow::saveFileAs() {

     saveFileAs(KFileDialog::getSaveFileName());
    

    }

    Это наше первое использование библиотеки KIO. KFileDialog предоставляет набор функций для отображения файловых диалогов, которые используют все приложения KDE. Вызов KFileDialog::getSaveFileName() отобразит диалог, где пользователь может выбрать файл в который сохранить или создать новый файл для сохранения. Функция возвращает полный путь к файлу, который передается функции saveFileAs(QString).

    saveFile()

    <syntaxhighlight lang="cpp-qt"> void MainWindow::saveFile() {

     if(!fileName.isEmpty())
     {
       saveFileAs(fileName);
     }
     else
     {
       saveFileAs();
     }
    

    }

    Тут ничего сложного или нового в этой функции, просто условие показывать или нет диалог сохранения. Если fileName не пустой, тогда файл сохраняется под именем fileName. Но если это не так, то отображаем диалог сохранения, чтобы пользователь мог выбрать файл для сохранения.

    Загрузка файла

    Наконец, мы не можем обойтись без загрузки фала с диска. Код для этого содержится в MainWindow::openFile().

    Сначала мы должны спросить пользователя, какой файл он хочет открыть. Это делается с помощью KFileDialog, на этот раз getOpenFileName(): <syntaxhighlight lang="cpp-qt"> QString fileNameFromDialog = KFileDialog::getOpenFileName();

    Затем мы используем библиотеку KIO, чтобы загрузить файл. Это позволяет нам открывать файлы с помощью QFile даже если они расположены удаленно. Мы вызываем функцию NetAccess download() <syntaxhighlight lang="cpp-qt"> KIO::NetAccess::download(fileNameFromDialog, tmpFile, this) Первый аргумент имя файла, который вы хотите загрузить. Второй -- QString, содержащий путь ко временной копии фала. Именно с tmpFile мы будем работать.

    Функция возвращает true или false в зависимости от результата передачи (успешно/ошибка). Если произошла ошибка, мы отображаем сообщение с ошибкой: <syntaxhighlight lang="cpp-qt"> KMessageBox::error(this, KIO::NetAccess::lastErrorString());

    В противном случае, мы продолжаем открывать файл.

    Мы создаем QFile работая со временным файлом созданным NetAccess::download() и открываем его в режиме только для чтения: <syntaxhighlight lang="cpp-qt"> QFile file(tmpFile); file.open(QIODevice::ReadOnly);

    Для отображения содержимого файла, мы используем QTextStream. Мы создаем QTextStream и передаем в его конструктор содержимое фала, вызываем функцию QFile readAll(), что бы прочитать текст из файла. И вот это передается в функцию setPlainText() нашей текстовой области.

    <syntaxhighlight lang="cpp-qt"> textArea->setPlainText(QTextStream(&file).readAll());

    Затем мы сохраняем путь к только что открытому файлу: <syntaxhighlight lang="cpp-qt"> fileName = fileNameFromDialog; и наконец, удаляем временный файл созданный NetAccess::download(): <syntaxhighlight lang="cpp-qt"> KIO::NetAccess::removeTempFile(tmpFile);

    Сборка, Установка и Запуск

    CMakeLists.txt

    project(tutorial4)

    find_package(KDE4 REQUIRED) include_directories(${KDE4_INCLUDES})

    set(tutorial4_SRCS

     main.cpp
     mainwindow.cpp
    

    )

    kde4_add_executable(tutorial4 ${tutorial4_SRCS})

    target_link_libraries(tutorial4 ${KDE4_KDEUI_LIBS}

                                   ${KDE4_KIO_LIBS})
    
    

    install(TARGETS tutorial4 DESTINATION ${BIN_INSTALL_DIR}) install(FILES tutorial4ui.rc

           DESTINATION ${DATA_INSTALL_DIR}/tutorial4)
    

    Теперь мы используем библиотеку KIO, мы должны сказать CMake слинковать приложение с ней. Это делается путем передачи ${KDE4_KIO_LIBS} функции target_link_libraries().

    С помощью этого файла, урок может быть собран и запущен тем же путем, что и урок 3. Подробнее см. урок 3. <syntaxhighlight lang="text"> mkdir build && cd build cmake .. -DCMAKE_INSTALL_PREFIX=$HOME make install $HOME/bin/tutorial4

    Note
    Измененные настройки сохраняются в вашем каталоге KDE: $HOME/.kde/share/apps/tutorial4.


    Moving On

    Теперь вы можете переходить к уроку KCmdLineArgs.