Development/Tutorials/Saving and loading (fi)

    From KDE TechBase


    Development/Tutorials/Saving_and_loading


    Tallettaminen ja Avaaminen
    Tutorial Series   Beginner Tutorial
    Previous   Perehdytys 3 - KActions ja XMLGUI
    What's Next   Tutorial 5 - Using KCmdLineArgs
    Further Reading   KIO::NetAccess QFile

    Lyhyesti

    Nyt kun meillä on perus tekstieditori, on aika tehdä siitä jollain tavalla hyödyllinen. Jokaisen tekstieditorilla pitäisi pystyä vähintäänkin tallettamaan tehdyt työt levylle sekä avata tekstitiedostoja käsittelyä varten.

    KDE tarjoaa joukon luokkia tiedostojen kanssa työskentelyä varten jotka helpottavat ohjelmistokehittäjän elämää. KIO-kirjasto mahdollistaa helpon tiedostojen käsittelyn verkkoprotokollien yli sekä se tarjoaa vakioidun tiedosto valikon tiedostojen avaamista ja tallettamista varten.

    Koodi

    main.cpp

    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-tiedostoa ei ole muutettu mitenkään paitsi kaikki tutorial 3:t on vaihdettu tutorial 4:ksi.

    mainwindow.h

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

    class MainWindow : public KXmlGuiWindow {

     Q_OBJECT // *uusi*
     
     public:
       MainWindow(QWidget *parent=0);
     
     private:
       KTextEdit* textArea;
       void setupActions();
       QString fileName; // tiedoston nimi *uusi*
    
     private slots: // *uusi*
       void newFile(); // uusi tiedosto *uusi*
       void openFile(); // avaa tiedosto *uusi*  
       void saveFile(); // tallenna tiedosto *uusi*
       void saveFileAs(); // tallenna tiedosto nimellä *uusi*
       void saveFileAs(const QString &outputFileName); // *uusi*
    

    };

    1. endif

    Tiedostojen avaus ja tallennus mahdollisuuden aikaansaamiseksi meidän tarvitsee lisätä funktiot jotka tekevät tarvittavat asiat. Koska näitä funktoita tullaan kutsumaan Qt:n signal/slot-yhdistämismekanismin kautta, meidän tarvitsee määritellä nämä funktiot slot:ksi. Tämä on toteutettu rivillä 19. Koska käytämme slot:a tässä otsikkotiedostossa, meidän tarvitsee lisätä myös Q_OBJECT makro.

    Haluamme myös pitää muistissa avaamamme tiedoston nimen, joten meidän tarvitsee esitellä myös QString fileName.

    mainwindow.cpp

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

    MainWindow::MainWindow(QWidget *parent)

       : KXmlGuiWindow(parent),
         fileName(QString()) //*uusi*
    

    {

     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()); //*uusi*
    
     KStandardAction::save(this, SLOT(saveFile()),
                           actionCollection()); //*uusi*
    
     KStandardAction::saveAs(this, SLOT(saveFileAs()),
                           actionCollection()); //*uusi*
    
     KStandardAction::openNew(this, SLOT(newFile()),
                           actionCollection()); //*uusi*
    
     setupGUI();
    

    }

    // * uutta tästä eteenpäin *

    void MainWindow::newFile() {

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

    }

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

     KSaveFile file(outputFileName);
     file.open();
     
     QByteArray outputByteArray;
     outputByteArray.append(textArea->toPlainText());
     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"?> <!DOCTYPE kpartgui SYSTEM "kpartgui.dtd"> <gui name="tutorial4" version="1">

     <ToolBar name="mainToolBar" >
       <text>Main Toolbar</text>
       <Action name="clear" />
     </ToolBar>
     <MenuBar>
       <Menu name="file" >
         <Action name="clear" />
       </Menu>
     </MenuBar>
    

    </gui> Tämä on muuten identtinen tutorial3ui.rc kanssa, paitsi nimi on muutettu 'tutorial4':ksi. Koska kaikki uudet toiminnot ovat KStandardAction:ta jotka KDE osaa automaattisesti käsitellä, tähän tiedostoon ei tarvitse lisätä mitään.

    Explanation

    Okay, now to implement the code that will do the loading and saving. This will all be happening in mainwindow.cpp

    The first thing we do is add fileName(QString()) to the MainWindow constructor list on line 16. This makes sure that fileName is empty right from the beginning.

    Adding the actions

    The first thing we are going to do is provide the outward interface for the user so they can tell the application to load and save. Like with the quit action in tutorial 3, we will use KStandardActions. On lines 37 to 47 we add the actions in the same way as for the quit action. For each one, we connect it to the appropriate slot that we declared in the header file.

    Creating a new document

    The first function we create is the newFile() function. void MainWindow::newFile() {

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

    } fileName.clear() sets the fileName QString to be empty to reflect the fact that this document does not yet have a presence on disc. textArea->clear() then clears the central text area using the same function that we connected the clear KAction to in tutorial 3.

    Saving a file

    saveFileAs(QString)

    Now we get onto our first file handling code. We're going to implement a function which will save the contents of the text area to the file name given as a parameter. KDE provides a class for safely saving a file called KSaveFile which is derived from Qt's QFile.

    The function's prototype is void MainWindow::saveFileAs(const QString &outputFileName)

    We then create our KSaveFile object and open it with KSaveFile file(outputFileName); file.open();

    Now that we have our file to write to, we need to format the text in the text area to a format which can be written to file. For this, we create a QByteArray and fill it with the plain text version of whatever is in the text area: QByteArray outputByteArray; outputByteArray.append(textArea->toPlainText()); Now that we have our QByteArray, we use it to write to the file with KSaveFile::write(). If we were using a normal QFile, this would make the changes immediately. However, if a problem occurred partway through writing, the file would become corrupted. For this reason, KSaveFile works by first writing to a temporary file and then, when you call KSaveFile::finalize() the changes are made to the actual file. file.write(outputByteArray); file.finalize(); file.close(); Finally, we set MainWindows's fileName member to point to the file name we just saved to. fileName = outputFileName;

    saveFileAs()

    This is the function that the saveAs slot is connected to. It simply calls the generic saveFileAs(QString) function and passes the file name returned by KFileDialog::getSaveFileName().

    void MainWindow::saveFileAs() {

     saveFileAs(KFileDialog::getSaveFileName());
    

    }

    This our first actual use of the KIO library. KFileDialog provides a number of static functions for displaying the common file dialog that is used by all KDE applications. Calling KFileDialog::getSaveFileName() will display a dialog where the user can select the name of the file to save to or choose a new name. The function returns the full file name, which we then pass to saveFileAs(QString).

    saveFile()

    void MainWindow::saveFile() {

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

    }

    There's nothing exciting or new in this function, just the logic to decide whether or not to show the save dialog. If fileName is not empty, then the file is saved to fileName. But if it is, then the dialog is shown to allow the user to select a file name.

    Loading a file

    Finally, we get round to being able to load a file from disc. The code for this is all contained in MainWindow::openFile().

    First we must ask the user for the name of the file they wish to open. We do this using another one of the KFileDialog functions, this time getOpenFileName(): QString fileNameFromDialog = KFileDialog::getOpenFileName();

    Then we use the KIO library to retrieve our file. This allows us to open the file with QFile even if it's stored in a remote location like an FTP site. We make the following call to NetAccess's download() function KIO::NetAccess::download(fileNameFromDialog, tmpFile, this) The first argument is the name of the file you wish to download. The second is a QString which, after the download is complete, will contain the location of the temporary copy of the file. It is this tmpFile we will work with from now on.

    The function returns true or false depending on whether the transfer was successful. If it failed, we display a message box giving the error: KMessageBox::error(this, KIO::NetAccess::lastErrorString());

    Otherwise, we continue with opening the file.

    We create a QFile by passing the temporary file created by NetAccess::download() to its constructor and then open it in read-only mode QFile file(tmpFile); file.open(QIODevice::ReadOnly);

    In order to display the contents of the file, we must use a QTextStream. We create one by passing the contents of our file to its constructor and then call QFile's readAll() function to get the text from the file. This is then passed to the setPlainText() function of our text area.

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

    We then store the path of the file we just opened: fileName = fileNameFromDialog; and finally, we remove the temporary file that was created by NetAccess::download(): KIO::NetAccess::removeTempFile(tmpFile);

    Make, Install And Run

    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)
    

    Since we are now using the KIO library, we must tell CMake to link against it. We do this by passing ${KDE4_KIO_LIBS} to the target_link_libraries() function.

    With this file, the tutorial can built and run in the same way as tutorial 3. For more information, see tutorial 3.

    mkdir build && cd build
    cmake .. -DCMAKE_INSTALL_PREFIX=$HOME
    make install
    $HOME/bin/tutorial4
    

    Moving On

    Now you can move on to the KCmdLineArgs tutorial.