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

    From KDE TechBase
    No edit summary
     
    (6 intermediate revisions by one other user not shown)
    Line 1: Line 1:
    {{Template:I18n/Language Navigation Bar|Development/Tutorials/Saving_and_loading}}
     


    {{TutorialBrowser|
    {{TutorialBrowser|
    Line 25: Line 25:


    ===main.cpp===
    ===main.cpp===
    <code cppqt n>
    <syntaxhighlight lang="cpp-qt" line>
    #include <KApplication>
    #include <KApplication>
    #include <KAboutData>
    #include <KAboutData>
    Line 46: Line 46:
       return app.exec();
       return app.exec();
    }
    }
    </code>
    </syntaxhighlight>
    <tt>main.cpp</tt> практически не изменился с урока 3.
    <tt>main.cpp</tt> практически не изменился с урока 3.


    ===mainwindow.h===
    ===mainwindow.h===
    <code cppqt n>
    <syntaxhighlight lang="cpp-qt" line>
    #ifndef MAINWINDOW_H
    #ifndef MAINWINDOW_H
    #define MAINWINDOW_H
    #define MAINWINDOW_H
    Line 78: Line 78:


    #endif
    #endif
    </code>
    </syntaxhighlight>
    Если мы хотим добавить возможность загружать и сохранять файлы, мы должны добавить функции которые будут делать это. Т.к. функции будут вызваны через механизм Qt [http://doc.trolltech.com/latest/signalsandslots.html сигнал/слот] мы должны обозначить, что эти функции - слоты, что мы и делаем на строке 19. Раз мы используем слоты, мы должны добавить макрос [http://doc.trolltech.com/latest/qobject.html#Q_OBJECT <tt>Q_OBJECT</tt>].
    Если мы хотим добавить возможность загружать и сохранять файлы, мы должны добавить функции которые будут делать это. Т.к. функции будут вызваны через механизм Qt [http://doc.trolltech.com/latest/signalsandslots.html сигнал/слот] мы должны обозначить, что эти функции - слоты, что мы и делаем на строке 19. Раз мы используем слоты, мы должны добавить макрос [http://doc.trolltech.com/latest/qobject.html#Q_OBJECT <tt>Q_OBJECT</tt>].


    Line 84: Line 84:


    ===mainwindow.cpp===
    ===mainwindow.cpp===
    <code cppqt n>
    <syntaxhighlight lang="cpp-qt" line>
    #include "mainwindow.h"
    #include "mainwindow.h"


    Line 196: Line 196:
       }
       }
    }
    }
    </code>
    </syntaxhighlight>


    ===tutorial4ui.rc===
    ===tutorial4ui.rc===
    <code xml n>
    <syntaxhighlight lang="xml" line>
    <?xml version="1.0" encoding="UTF-8"?>
    <?xml version="1.0" encoding="UTF-8"?>
    <gui name="tutorial4"
    <gui name="tutorial4"
    Line 220: Line 220:


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


    Line 228: Line 228:


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


    Line 240: Line 240:


    Первая функция, которую мы создали - <tt>newFile()</tt>:
    Первая функция, которую мы создали - <tt>newFile()</tt>:
    <code cppqt>
    <syntaxhighlight lang="cpp-qt">
    void MainWindow::newFile()
    void MainWindow::newFile()
    {
    {
    Line 246: Line 246:
       textArea->clear();
       textArea->clear();
    }
    }
    </code>
    </syntaxhighlight>
    <tt>fileName.clear()</tt> очищает переменную <tt>fileName</tt> типа QString, что отражает тот факт, что этого документа еще нет на диске. <tt>textArea->clear()</tt> очищает область текста, используя функцию <tt>clear</tt>, которую мы подключили к <tt>KAction</tt> в уроке 3.
    <tt>fileName.clear()</tt> очищает переменную <tt>fileName</tt> типа QString, что отражает тот факт, что этого документа еще нет на диске. <tt>textArea->clear()</tt> очищает область текста, используя функцию <tt>clear</tt>, которую мы подключили к <tt>KAction</tt> в уроке 3.


    Line 256: Line 256:


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


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


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


    ====saveFileAs()====
    ====saveFileAs()====
    Line 286: Line 286:
    Эта функция к которой подключек слот <tt>saveAs</tt>. Она просто вызывает <tt>saveFileAs(QString)</tt> и передает ей имя файла возвращенное <tt>{{class|KFileDialog}}::[http://api.kde.org/4.0-api/kdelibs-apidocs/kio/html/classKFileDialog.html#8891356c249c5911e1ab15cc2739a89b getSaveFileName()]</tt>.
    Эта функция к которой подключек слот <tt>saveAs</tt>. Она просто вызывает <tt>saveFileAs(QString)</tt> и передает ей имя файла возвращенное <tt>{{class|KFileDialog}}::[http://api.kde.org/4.0-api/kdelibs-apidocs/kio/html/classKFileDialog.html#8891356c249c5911e1ab15cc2739a89b getSaveFileName()]</tt>.


    <code cppqt>
    <syntaxhighlight lang="cpp-qt">
    void MainWindow::saveFileAs()
    void MainWindow::saveFileAs()
    {
    {
       saveFileAs(KFileDialog::getSaveFileName());
       saveFileAs(KFileDialog::getSaveFileName());
    }
    }
    </code>
    </syntaxhighlight>


    Это наше первое использование библиотеки KIO. {{class|KFileDialog}} предоставляет набор функций для отображения файловых диалогов, которые используют все приложения KDE. Вызов <tt>KFileDialog::getSaveFileName()</tt> отобразит диалог, где пользователь может выбрать файл в который сохранить или создать новый файл для сохранения. Функция возвращает полный путь к файлу, который передается функции <tt>saveFileAs(QString)</tt>.
    Это наше первое использование библиотеки KIO. {{class|KFileDialog}} предоставляет набор функций для отображения файловых диалогов, которые используют все приложения KDE. Вызов <tt>KFileDialog::getSaveFileName()</tt> отобразит диалог, где пользователь может выбрать файл в который сохранить или создать новый файл для сохранения. Функция возвращает полный путь к файлу, который передается функции <tt>saveFileAs(QString)</tt>.
    Line 297: Line 297:
    ====saveFile()====
    ====saveFile()====


    <code cppqt>
    <syntaxhighlight lang="cpp-qt">
    void MainWindow::saveFile()
    void MainWindow::saveFile()
    {
    {
    Line 309: Line 309:
       }
       }
    }
    }
    </code>
    </syntaxhighlight>


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


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


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


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


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


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


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


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


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


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


    ===CMakeLists.txt===
    ===CMakeLists.txt===
    <code cmake n>
    <syntaxhighlight lang="make" line>
    project(tutorial4)
    project(tutorial4)
       
       
    Line 378: Line 378:
    install(FILES tutorial4ui.rc  
    install(FILES tutorial4ui.rc  
             DESTINATION ${DATA_INSTALL_DIR}/tutorial4)
             DESTINATION ${DATA_INSTALL_DIR}/tutorial4)
    </code>
    </syntaxhighlight>
    Теперь мы используем библиотеку KIO, мы должны сказать CMake слинковать приложение с ней. Это делается путем передачи <tt>${KDE4_KIO_LIBS}</tt> функции <tt>target_link_libraries()</tt>.
    Теперь мы используем библиотеку KIO, мы должны сказать CMake слинковать приложение с ней. Это делается путем передачи <tt>${KDE4_KIO_LIBS}</tt> функции <tt>target_link_libraries()</tt>.


    С помощью этого файла, урок может быть собран и запущен тем же путем, что и урок 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
    make install
    make install
    $HOME/bin/tutorial4
    $HOME/bin/tutorial4
    </code>
    </syntaxhighlight>


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

    Latest revision as of 16:56, 19 July 2012


    Загрузка и сохранение файлов
    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

    #include <KApplication>
    #include <KAboutData>
    #include <KCmdLineArgs>
     
    #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

    #ifndef MAINWINDOW_H
    #define MAINWINDOW_H
    
    #include <KXmlGuiWindow>
    #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); //новое
    };
    
    #endif
    

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

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

    mainwindow.cpp

    #include "mainwindow.h"
    
    #include <KApplication>
    #include <KAction>
    #include <KLocale>
    #include <KActionCollection>
    #include <KStandardAction>
    #include <KFileDialog> //новое
    #include <KMessageBox> //новое
    #include <KIO/NetAccess> //новое
    #include <KSaveFile> //новое
    #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

    Первая штука, которую добавим

    fileName(QString())
    

    в конструктор MainWindow на строке 16. Это очищает fileName с самого начала.

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

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

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

    Первая функция, которую мы создали - newFile():

    void MainWindow::newFile()
    {
      fileName.clear();
      textArea->clear();
    }
    

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

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

    saveFileAs(QString)

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

    Прототип функции:

    void MainWindow::saveFileAs(const QString &outputFileName)
    

    Мы создаем объект KSaveFile и открываем файл:

    KSaveFile file(outputFileName);
    file.open();
    

    Теперь у нас есть файл для записи, нам нужно отформатировать текст в текстовой области в формат, который может быть записан в файл. Для этого, мы создаем QByteArray и заполняем его обычным текстом из текстовой области:

    QByteArray outputByteArray;
    outputByteArray.append(textArea->toPlainText().toUtf8());
    

    Теперь у нас есть QByteArray, запишем его в файл с помощью KSaveFile::write(). Если бы мы использовали QFile, он бы моментально начал бы изменения в файле. Однако, если возникнет проблема посреди записи, файл будет поврежден. По этой причине KSaveFile сначала записывает данные во временный файл, а потом, когда вы вызываете KSaveFile::finalize() изменения попадают в текущий файл.

    file.write(outputByteArray);
    file.finalize();
    file.close();
    

    Наконец, мы записываем в переменную MainWindow fileName имя файла в который только, что сохранили:

    fileName = outputFileName;
    

    saveFileAs()

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

    void MainWindow::saveFileAs()
    {
      saveFileAs(KFileDialog::getSaveFileName());
    }
    

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

    saveFile()

    void MainWindow::saveFile()
    {
      if(!fileName.isEmpty())
      {
        saveFileAs(fileName);
      }
      else
      {
        saveFileAs();
      }
    }
    

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

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

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

    Сначала мы должны спросить пользователя, какой файл он хочет открыть. Это делается с помощью KFileDialog, на этот раз getOpenFileName():

    QString fileNameFromDialog = KFileDialog::getOpenFileName();
    

    Затем мы используем библиотеку KIO, чтобы загрузить файл. Это позволяет нам открывать файлы с помощью QFile даже если они расположены удаленно. Мы вызываем функцию NetAccess download()

    KIO::NetAccess::download(fileNameFromDialog, tmpFile, this)
    

    Первый аргумент имя файла, который вы хотите загрузить. Второй -- QString, содержащий путь ко временной копии фала. Именно с tmpFile мы будем работать.

    Функция возвращает true или false в зависимости от результата передачи (успешно/ошибка). Если произошла ошибка, мы отображаем сообщение с ошибкой:

    KMessageBox::error(this, KIO::NetAccess::lastErrorString());
    

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

    Мы создаем QFile работая со временным файлом созданным NetAccess::download() и открываем его в режиме только для чтения:

    QFile file(tmpFile);
    file.open(QIODevice::ReadOnly);
    

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

    textArea->setPlainText(QTextStream(&file).readAll());
    

    Затем мы сохраняем путь к только что открытому файлу:

    fileName = fileNameFromDialog;
    

    и наконец, удаляем временный файл созданный NetAccess::download():

    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.

    mkdir build && cd build
    cmake .. -DCMAKE_INSTALL_PREFIX=$HOME
    make install
    $HOME/bin/tutorial4
    
    Note
    Измененные настройки сохраняются в вашем каталоге KDE: $HOME/.kde/share/apps/tutorial4.


    Moving On

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