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

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


    {{TutorialBrowser|
    {{TutorialBrowser|
    Line 5: Line 5:
    series=Beginner Tutorial|
    series=Beginner Tutorial|


    name=Tallettaminen ja Avaaminen|
    name=Tallentaminen ja avaaminen|


    pre=[[Development/Tutorials/Using_KActions (fi)|Perehdytys 3 - KActions ja XMLGUI]]|
    pre=[[Development/Tutorials/Using_KActions (fi)|Opas 3 - KActions ja XMLGUI]]|


    next=[[Development/Tutorials/KCmdLineArgs|Tutorial 5 - Using KCmdLineArgs]]|  
    next=[[Development/Tutorials/KCmdLineArgs|Tutorial 5 - Using KCmdLineArgs]]|  
    Line 16: Line 16:
    ==Lyhyesti==
    ==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.
    Nyt kun meillä on yksinkertainen tekstieditori, on aika tehdä siitä jollain tavalla hyödyllinen. Jokaisella tekstieditorilla pitäisi pystyä ainakin tallentamaan tehdyt työt levylle sekä avaamaan 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.
    KDE tarjoaa tiedostojen kanssa työskentelyä varten joukon luokkia, jotka helpottavat ohjelmistokehittäjän elämää. KIO-kirjasto mahdollistaa helpon tiedostojen käsittelyn verkkoprotokollien kautta sekä tarjoaa vakioidun tiedostovalikon tiedostojen avaamista ja tallentamista varten.


    [[image:introtokdetutorial4.png|frame|center]]
    [[image:introtokdetutorial4.png|frame|center]]
    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>-tiedostoa ei ole muutettu mitenkään paitsi kaikki ''tutorial 3'':t on vaihdettu ''tutorial 4'':ksi.
    <tt>main.cpp</tt>-tiedostoa ei ole muutettu mitenkään paitsi kaikki ''tutorial 3'':t on vaihdettu ''tutorial 4'':ksi.


    ===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>
    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 [http://doc.trolltech.com/latest/signalsandslots.html signal/slot]-yhdistämismekanismin kautta, meidän tarvitsee määritellä nämä funktiot <tt>slot</tt>:ksi. Tämä on toteutettu rivillä 19. Koska käytämme <tt>slot</tt>:a tässä otsikkotiedostossa, meidän tarvitsee lisätä myös [http://doc.trolltech.com/latest/qobject.html#Q_OBJECT <tt>Q_OBJECT</tt>] makro.
    Tiedostojen avaus- ja tallennusmahdollisuuden aikaansaamiseksi meidän tarvitsee lisätä funktiot, jotka tekevät tarvittavat asiat.  Koska näitä funktoita tullaan kutsumaan Qt:n [http://doc.trolltech.com/latest/signalsandslots.html signal/slot]-yhdistämismekanismin kautta, meidän on määriteltävä nämä funktiot <tt>slot</tt>:ksi. Tämä on toteutettu rivillä 19. Koska käytämme <tt>slot</tt>:a tässä otsikkotiedostossa, on myös [http://doc.trolltech.com/latest/qobject.html#Q_OBJECT <tt>Q_OBJECT</tt>] -makro lisättävä.


    Haluamme myös pitää muistissa avaamamme tiedoston nimen, joten meidän tarvitsee esitellä myös <tt>{{qt|QString}} fileName</tt>.
    Haluamme myös pitää muistissa avaamamme tiedoston nimen, joten myösmyös <tt>{{qt|QString}} fileName</tt> on esiteltävä.


    ===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"?>
    <!DOCTYPE kpartgui SYSTEM "kpartgui.dtd">
    <gui name="tutorial4"
    <gui name="tutorial4" version="1">
        version="1"
      <ToolBar name="mainToolBar" >
        xmlns="http://www.kde.org/standards/kxmlgui/1.0"
        <text>Main Toolbar</text>
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        <Action name="clear" />
        xsi:schemaLocation="http://www.kde.org/standards/kxmlgui/1.0
      </ToolBar>
                            http://www.kde.org/standards/kxmlgui/1.0/kxmlgui.xsd" >
     
       <MenuBar>
       <MenuBar>
         <Menu name="file" >
         <Menu name="file" >
    Line 212: Line 213:
         </Menu>
         </Menu>
       </MenuBar>
       </MenuBar>
      <ToolBar name="mainToolBar" >
        <text>Main Toolbar</text>
        <Action name="clear" />
      </ToolBar>
    </gui>
    </gui>
    </code>
    </syntaxhighlight>
    This is identical to <tt>tutorial3ui.rc</tt> from tutorial 3 except the <tt>name</tt> has changed to 'tutorial4'. We do not need to add any information about any of the <tt>KStandardAction</tt>s since the placement of those actions is handled automatically by KDE.
    Tämä on muuten identtinen <tt>tutorial3ui.rc</tt> kanssa, paitsi <tt>nimi</tt> on muutettu 'tutorial4':ksi. Koska kaikki uudet toiminnot ovat <tt>KStandardAction</tt> -toimintoja, joita KDE osaa automaattisesti käsitellä, tähän tiedostoon ei tarvitse lisätä mitään.


    ==Explanation==
    ==Selvitys==


    Okay, now to implement the code that will do the loading and saving. This will all be happening in <tt>mainwindow.cpp</tt>
    Selvitetään nyt hieman koodia, joka tekee avaamisen sekä ja tallentamisen. Kaikki tarvittava on sijoitettu <tt>mainwindow.cpp</tt>-tiedostoon.


    The first thing we do is add
    Ensimmäisenä, rivillä 16, meidän pitää lisätä
    <code cppqt>
    <syntaxhighlight lang="cpp-qt">
    fileName(QString())
    fileName(QString())
    </code>
    </syntaxhighlight>
    to the <tt>MainWindow</tt> constructor list on line 16. This makes sure that <tt>fileName</tt> is empty right from the beginning.
    <tt>MainWindow</tt>:n muodostajaan. Tällä varmistetaan, että tiedoston nimi on tyhjä aloitettaessa.


    ===Adding the actions===
    ===Toimintojen lisääminen===


    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 <tt>quit</tt> action in tutorial 3, we will use <tt>KStandardActions</tt>. On lines 37 to 47 we add the actions in the same way as for the <tt>quit</tt> action. For each one, we connect it to the appropriate slot that we declared in the header file.
    Ensinnäkin meidän pitää lisätä liittymä ohjelman ulkopuolelle, jotta käyttäjät voivat kertoa ohjelmalle, avataanko vai talletetaanko tiedosto. Käytämme tähänkin tarkoitukseen <tt>KStandardActions</tt>:a riveillä 37-47 samaan tapaan kuin oppaassa 3 toteuttaessamme <tt>lopeta</tt>-toimintoa. Kaikki uudet toiminnot on kytketty sopiviin sloteihin, jotka esittelimme otsaketiedostossa riveillä 20-24.  


    ===Creating a new document===
    ===Uusi asiakirja===


    The first function we create is the <tt>newFile()</tt> function.
    Ensimmäisenä funktio jonka loimme on <tt>newFile()</tt>-funktio.
    <code cppqt>
    <syntaxhighlight lang="cpp-qt">
    void MainWindow::newFile()
    void MainWindow::newFile()
    {
    {
    Line 239: Line 246:
       textArea->clear();
       textArea->clear();
    }
    }
    </code>
    </syntaxhighlight>
    <tt>fileName.clear()</tt> sets the <tt>fileName</tt> QString to be empty to reflect the fact that this document does not yet have a presence on disc. <tt>textArea->clear()</tt> then clears the central text area using the same function that we connected the <tt>clear</tt> <tt>KAction</tt> to in tutorial 3.
    <tt>fileName.clear()</tt> asettaa <tt>fileName</tt> (QString) merkkijonon tyhjäksi kuvaamaan, että tätä tiedostoa ei ole olemassa tallennettuna minnekkään.  
    <tt>textArea->clear()</tt> tyhjentää tekstialueen käyttäen samaa <tt>KAction</tt>:n <tt>clear</tt>-funktiota johon kytkimme tyhjennä-toiminnon oppaassa 3.


    ===Saving a file===
    ===Tiedoston tallentaminen===


    ====saveFileAs(QString)====
    ====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 {{class|KSaveFile}} which is derived from Qt's {{qt|QFile}}.
    Siirrymme nyt ensimmäiseen tiedostoja käsittelevään koodiin. Seuraavaksi syvennymme funktioon, jonka tehtävänä on tallentaa tekstialueen sisältö parametrina annetulla tiedostonimellä. KDE tarjoaa turvalliseen tiedoston tallentamiseen Qt:n {{qt|QFile}}:a johdetun {{class|KSaveFile}}-luokan.


    The function's prototype is
    Funktion prototyyppi on:
    <code cppqt>
    <syntaxhighlight lang="cpp-qt">
    void MainWindow::saveFileAs(const QString &outputFileName)
    void MainWindow::saveFileAs(const QString &outputFileName)
    </code>
    </syntaxhighlight>


    We then create our <tt>KSaveFile</tt> object and open it with
    Seuraavaksi luomme <tt>KSaveFile</tt>-objektin ja avaamme sen
    <code cppqt>
    <syntaxhighlight lang="cpp-qt">
    KSaveFile file(outputFileName);
    KSaveFile file(outputFileName);
    file.open();
    file.open();
    </code>
    </syntaxhighlight>


    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 {{qt|QByteArray}} and fill it with the plain text version of whatever is in the text area:
    Nyt  meillä on tiedosto, johon voimme kirjoittaa. Seuraavaksi pitää tekstialueen sisältö muotoilla muotoon, joka on mahdollista kirjoittaa tiedostoon. Tätä tarkoitusta varten luomme {{qt|QByteArray}}:n, johon syötämme tekstialueen sisällön paljaana tekstinä (ilman muotoiluja) seuraavasti:  
    <code cppqt>
    <syntaxhighlight lang="cpp-qt">
    QByteArray outputByteArray;
    QByteArray outputByteArray;
    outputByteArray.append(textArea->toPlainText());
    outputByteArray.append(textArea->toPlainText());
    </code>
    </syntaxhighlight>
    Now that we have our <tt>QByteArray</tt>, we use it to write to the file with <tt>KSaveFile::write()</tt>. If we were using a normal <tt>QFile</tt>, this would make the changes immediately. However, if a problem occurred partway through writing, the file would become corrupted. For this reason, <tt>KSaveFile</tt> works by first writing to a temporary file and then, when you call <tt>KSaveFile::finalize()</tt> the changes are made to the actual file.
    Nyt meillä on <tt>KSaveFile::write()</tt>:n kanssa käytettävä <tt>QByteArray</tt> valmiina. Normaalin <tt>QFile</tt>:n käyttäminen olisi myös mahdollista, ja se tallentaisi muutokset välittömästi. Jos tällöin kuitenkin tapahtuisi jokin virhe tallentamisen aikana, tulisi tiedostosta viallinen. Tästä syystä <tt>KSaveFile</tt> luo ensin väliaikaisen tiedoston, ja varsinainen tiedosto luodaan vasta kun kutsutaan <tt>KSaveFile::finalize()</tt>-funktiota.
    <code cppqt>
     
    <syntaxhighlight lang="cpp-qt">
    file.write(outputByteArray);
    file.write(outputByteArray);
    file.finalize();
    file.finalize();
    file.close();
    file.close();
    </code>
    </syntaxhighlight>
    Finally, we set <tt>MainWindows</tt>'s <tt>fileName</tt> member to point to the file name we just saved to.
    Ja lopuksi asetetaan <tt>MainWindows</tt>:n <tt>fileName</tt> osoittamaan tiedostoon, jonka juuri tallensimme:
    <code cppqt>
    <syntaxhighlight lang="cpp-qt">
    fileName = outputFileName;
    fileName = outputFileName;
    </code>
    </syntaxhighlight>


    ====saveFileAs()====
    ====saveFileAs()====


    This is the function that the <tt>saveAs</tt> slot is connected to. It simply calls the generic <tt>saveFileAs(QString)</tt> function and passes the file name returned by <tt>{{class|KFileDialog}}::[http://api.kde.org/4.0-api/kdelibs-apidocs/kio/html/classKFileDialog.html#8891356c249c5911e1ab15cc2739a89b getSaveFileName()]</tt>.
    Tähän funktioon on yhdistetty <tt>saveAs</tt>(Tallenna nimellä) toiminto. Tämä funktio kutsuu <tt>saveFileAs(QString)</tt>-funktiota, jolle lähetetään parametrinä
    <tt>{{class|KFileDialog}}::[http://api.kde.org/4.0-api/kdelibs-apidocs/kio/html/classKFileDialog.html#8891356c249c5911e1ab15cc2739a89b getSaveFileName()]</tt>:n palauttama tiedoston nimi.


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


    This our first actual use of the KIO library. {{class|KFileDialog}} provides a number of static functions for displaying the common file dialog that is used by all KDE applications. Calling <tt>KFileDialog::getSaveFileName()</tt> 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 <tt>saveFileAs(QString)</tt>.
    Tässä kohdassa käytämme ensimmäisen kerran KIO-kirjastoa. {{class|KFileDialog}}-luokka tarjoaa meille joukon funktioita, joita kaikki KDE ohjelmat käyttävät yleisten tiedostovalikoiden näyttämiseen. <tt>KFileDialog::getSaveFileName()</tt>-funktion kutsuminen tuo esille tiedostovalikon,jossa käyttäjä voi valita tai vaihtaa tiedoston nimeä tai tallennussijaintia. Funktio palauttaa täydellisen tiedoston nimen, jonka me välitimme <tt>saveFileAs(QString)</tt>-funktiolle.


    ====saveFile()====
    ====saveFile()====


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


    There's nothing exciting or new in this function, just the logic to decide whether or not to show the save dialog. If <tt>fileName</tt> is not empty, then the file is saved to <tt>fileName</tt>. But if it is, then the dialog is shown to allow the user to select a file name.
    Tässä funktiossa ei ole mitään erityistä tai uutta. Tässä vain päätellään, näytetäänkö tallennusvalikko vai ei. Jos <tt>fileName</tt> ei ole tyhjä, tallennetaan tiedosto mitään kysymättä nimellä, joka on sijoitettuna <tt>fileName</tt>:n kutsumalla saveFileAs(QString)-funktiota, muuten kutsutaan yllä olevaa saveFileAs()-funktiota.


    ===Loading a file===
    ===Tiedoston avaaminen===


    Finally, we get round to being able to load a file from disc. The code for this is all contained in <tt>MainWindow::openFile()</tt>.
    Lopulta olemme valmiita tarkastelemaan tiedoston avaamista levyltä. Kaikki koodi tätä tarkoitusta varten on sijoitettu <tt>MainWindow::openFile()</tt>-funktioon.


    First we must ask the user for the name of the file they wish to open. We do this using another one of the <tt>KFileDialog</tt> functions, this time <tt>getOpenFileName()</tt>:
    Ensin meidän pitää kysyä käyttäjältä, mikä tiedosto halutaan avata. Tätä tarkoitusta varten käytetään toista <tt>KFileDialog</tt>-funktiota, <tt>getOpenFileName()</tt>:
    <code cppqt>
    <syntaxhighlight lang="cpp-qt">
    QString fileNameFromDialog = KFileDialog::getOpenFileName();
    QString fileNameFromDialog = KFileDialog::getOpenFileName();
    </code>
    </syntaxhighlight>


    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 {{class|NetAccess}}'s <tt>download()</tt> function
    Seuraavaksi käytämme KIO-kirjastoa tiedoston avaamiseen. KIO-kirjaston avulla voimme avata tiedoston QFilen avulla, paikalliselta levyltä tai etäkoneelta, vaikkapa FTP:n yli. Tiedoston avaamiseksi käytämme {{class|NetAccess}}:n <tt>download()</tt>-funktiota:
    <code cppqt>
    <syntaxhighlight lang="cpp-qt">
    KIO::NetAccess::download(fileNameFromDialog, tmpFile, this)
    KIO::NetAccess::download(fileNameFromDialog, tmpFile, this)
    </code>
    </syntaxhighlight>
    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 <tt>tmpFile</tt> we will work with from now on.
    Ensimmäinen argumentti on avattavan tiedoston nimi. Toinen on QString, joka tiedoston hakemisen jälkeen sisältää väliaikaisen tiedoston sijainnin. Tämän <tt>tmpFile</tt>:n kanssa jatkamme tästä eteenpäin.


    The function returns <tt>true</tt> or <tt>false</tt> depending on whether the transfer was successful. If it failed, we display a message box giving the error:
    Funktio palauttaa boolen arvon <tt>tosi</tt> tai <tt>epätosi</tt>, riippuen onnistuiko tiedoston siirto. Jos tiedoston haku epäonnistui, näytetään virheilmoitus:
    <code cppqt>
    <syntaxhighlight lang="cpp-qt">
    KMessageBox::error(this, KIO::NetAccess::lastErrorString());
    KMessageBox::error(this, KIO::NetAccess::lastErrorString());
    </code>
    </syntaxhighlight>


    Otherwise, we continue with opening the file.
    Muussa tapauksessa jatketaan tiedoston avaamista.


    We create a QFile by passing the temporary file created by <tt>NetAccess::download()</tt> to its constructor and then open it in read-only mode
    Luomme uuden QFile:n välittämällä sen muodostajaan <tt>NetAccess::download()</tt>:n luoman väliaikaistiedoston, minkä jälkeen avataan se kirjoitussuojattuna:
    <code cppqt>
    <syntaxhighlight lang="cpp-qt">
    QFile file(tmpFile);
    QFile file(tmpFile);
    file.open(QIODevice::ReadOnly);
    file.open(QIODevice::ReadOnly);
    </code>
    </syntaxhighlight>


    In order to display the contents of the file, we must use a {{class|QTextStream}}. We create one by passing the contents of our file to its constructor and then call QFile's <tt>readAll()</tt> function to get the text from the file. This is then passed to the <tt>setPlainText()</tt> function of our text area.
    Tuodaksemme avaamamme tiedoston sisällön ruudulle näkyviin, tarvitsee meidän käyttää {{class|QTextStream}}-luokkaa. {{class|QTextStream}}:n muodostajalle välitämme äsken avaamamme tiedoston (QFile file) ja kutsumme QFile:n <tt>readAll()</tt>-funktiota saadaksemme tekstin tiedostosta. Seuraavaksi välitämme tämän kaiken tekstialueen <tt>setPlainText()</tt>-funktiolle:
     
    <syntaxhighlight lang="cpp-qt">
    <code cppqt>
    textArea->setPlainText(QTextStream(&file).readAll());
    textArea->setPlainText(QTextStream(&file).readAll());
    </code>
    </syntaxhighlight>


    We then store the path of the file we just opened:
    Tallennetaan avatun tiedoston nimi(polkuineen):
    <code cppqt>
    <syntaxhighlight lang="cpp-qt">
    fileName = fileNameFromDialog;
    fileName = fileNameFromDialog;
    </code>
    </syntaxhighlight>
    and finally, we remove the temporary file that was created by <tt>NetAccess::download()</tt>:
     
    <code cppqt>
    Ja lopuksi poistetaan <tt>NetAccess::download()</tt>:n luoma väliaikaistiedosto:
    <syntaxhighlight lang="cpp-qt">
    KIO::NetAccess::removeTempFile(tmpFile);
    KIO::NetAccess::removeTempFile(tmpFile);
    </code>
    </syntaxhighlight>


    ==Make, Install And Run==
    ==Käännä, Asenna ja Suorita==


    ===CMakeLists.txt===
    ===CMakeLists.txt===
    <code ini n>
    <syntaxhighlight lang="ini" line>
    project(tutorial4)
    project(tutorial4)
       
       
    Line 371: Line 381:
    install(FILES tutorial4ui.rc  
    install(FILES tutorial4ui.rc  
             DESTINATION ${DATA_INSTALL_DIR}/tutorial4)
             DESTINATION ${DATA_INSTALL_DIR}/tutorial4)
    </code>
    </syntaxhighlight>
    Since we are now using the KIO library, we must tell CMake to link against it. We do this by passing <tt>${KDE4_KIO_LIBS}</tt> to the <tt>target_link_libraries()</tt> function.
    Koska otimme nyt KIO-kirjaston käyttöön, täytyy CMakelle kertoa, että linkittäjän tarvitsee ottaa myös tämä mukaan. Tätä varten lisäämme <tt>target_link_libraries()</tt>-funktioon tiedon <tt>${KDE4_KIO_LIBS}</tt>.


    With this file, the tutorial can built and run in the same way as tutorial 3. For more information, see tutorial 3.
    === Komennot ===
    Itse käännös, asennus ja suoritus tehdään samoin kuin oppaassa 3.


      mkdir build && cd build
      mkdir build && cd build
    Line 381: Line 392:
      $HOME/bin/tutorial4
      $HOME/bin/tutorial4


    ==Moving On==
    ==Seuraavaksi==
    Now you can move on to the [[Development/Tutorials/KCmdLineArgs|KCmdLineArgs]] tutorial.
    Seuraavana perehdytys: [[Development/Tutorials/KCmdLineArgs|Komentoriviparametrit.]] (keskeneräinen, englanniksi)


    [[Category:C++]]
    [[Category:C++]]

    Latest revision as of 09:45, 15 July 2012


    Tallentaminen ja avaaminen
    Tutorial Series   Beginner Tutorial
    Previous   Opas 3 - KActions ja XMLGUI
    What's Next   Tutorial 5 - Using KCmdLineArgs
    Further Reading   KIO::NetAccess QFile

    Lyhyesti

    Nyt kun meillä on yksinkertainen tekstieditori, on aika tehdä siitä jollain tavalla hyödyllinen. Jokaisella tekstieditorilla pitäisi pystyä ainakin tallentamaan tehdyt työt levylle sekä avaamaan tekstitiedostoja käsittelyä varten.

    KDE tarjoaa tiedostojen kanssa työskentelyä varten joukon luokkia, jotka helpottavat ohjelmistokehittäjän elämää. KIO-kirjasto mahdollistaa helpon tiedostojen käsittelyn verkkoprotokollien kautta sekä tarjoaa vakioidun tiedostovalikon tiedostojen avaamista ja tallentamista varten.

    Koodi

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

    mainwindow.h

    #ifndef MAINWINDOW_H
    #define MAINWINDOW_H
    
    #include <KXmlGuiWindow>
    #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*
    };
    
    #endif
    

    Tiedostojen avaus- ja tallennusmahdollisuuden 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 on määriteltävä nämä funktiot slot:ksi. Tämä on toteutettu rivillä 19. Koska käytämme slot:a tässä otsikkotiedostossa, on myös Q_OBJECT -makro lisättävä.

    Haluamme myös pitää muistissa avaamamme tiedoston nimen, joten myösmyös QString fileName on esiteltävä.

    mainwindow.cpp

    #include "mainwindow.h"
    
    #include <KApplication>
    #include <KAction>
    #include <KLocale>
    #include <KActionCollection>
    #include <KStandardAction>
    #include <KFileDialog> // *uusi*
    #include <KMessageBox> //*uusi*
    #include <KIO/NetAccess> //*uusi*
    #include <KSaveFile> //*uusi*
    #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"?>
    <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>
    

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

    Selvitys

    Selvitetään nyt hieman koodia, joka tekee avaamisen sekä ja tallentamisen. Kaikki tarvittava on sijoitettu mainwindow.cpp-tiedostoon.

    Ensimmäisenä, rivillä 16, meidän pitää lisätä

    fileName(QString())
    

    MainWindow:n muodostajaan. Tällä varmistetaan, että tiedoston nimi on tyhjä aloitettaessa.

    Toimintojen lisääminen

    Ensinnäkin meidän pitää lisätä liittymä ohjelman ulkopuolelle, jotta käyttäjät voivat kertoa ohjelmalle, avataanko vai talletetaanko tiedosto. Käytämme tähänkin tarkoitukseen KStandardActions:a riveillä 37-47 samaan tapaan kuin oppaassa 3 toteuttaessamme lopeta-toimintoa. Kaikki uudet toiminnot on kytketty sopiviin sloteihin, jotka esittelimme otsaketiedostossa riveillä 20-24.

    Uusi asiakirja

    Ensimmäisenä funktio jonka loimme on newFile()-funktio.

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

    fileName.clear() asettaa fileName (QString) merkkijonon tyhjäksi kuvaamaan, että tätä tiedostoa ei ole olemassa tallennettuna minnekkään. textArea->clear() tyhjentää tekstialueen käyttäen samaa KAction:n clear-funktiota johon kytkimme tyhjennä-toiminnon oppaassa 3.

    Tiedoston tallentaminen

    saveFileAs(QString)

    Siirrymme nyt ensimmäiseen tiedostoja käsittelevään koodiin. Seuraavaksi syvennymme funktioon, jonka tehtävänä on tallentaa tekstialueen sisältö parametrina annetulla tiedostonimellä. KDE tarjoaa turvalliseen tiedoston tallentamiseen Qt:n QFile:a johdetun KSaveFile-luokan.

    Funktion prototyyppi on:

    void MainWindow::saveFileAs(const QString &outputFileName)
    

    Seuraavaksi luomme KSaveFile-objektin ja avaamme sen

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

    Nyt meillä on tiedosto, johon voimme kirjoittaa. Seuraavaksi pitää tekstialueen sisältö muotoilla muotoon, joka on mahdollista kirjoittaa tiedostoon. Tätä tarkoitusta varten luomme QByteArray:n, johon syötämme tekstialueen sisällön paljaana tekstinä (ilman muotoiluja) seuraavasti:

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

    Nyt meillä on KSaveFile::write():n kanssa käytettävä QByteArray valmiina. Normaalin QFile:n käyttäminen olisi myös mahdollista, ja se tallentaisi muutokset välittömästi. Jos tällöin kuitenkin tapahtuisi jokin virhe tallentamisen aikana, tulisi tiedostosta viallinen. Tästä syystä KSaveFile luo ensin väliaikaisen tiedoston, ja varsinainen tiedosto luodaan vasta kun kutsutaan KSaveFile::finalize()-funktiota.

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

    Ja lopuksi asetetaan MainWindows:n fileName osoittamaan tiedostoon, jonka juuri tallensimme:

    fileName = outputFileName;
    

    saveFileAs()

    Tähän funktioon on yhdistetty saveAs(Tallenna nimellä) toiminto. Tämä funktio kutsuu saveFileAs(QString)-funktiota, jolle lähetetään parametrinä KFileDialog::getSaveFileName():n palauttama tiedoston nimi.

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

    Tässä kohdassa käytämme ensimmäisen kerran KIO-kirjastoa. KFileDialog-luokka tarjoaa meille joukon funktioita, joita kaikki KDE ohjelmat käyttävät yleisten tiedostovalikoiden näyttämiseen. KFileDialog::getSaveFileName()-funktion kutsuminen tuo esille tiedostovalikon,jossa käyttäjä voi valita tai vaihtaa tiedoston nimeä tai tallennussijaintia. Funktio palauttaa täydellisen tiedoston nimen, jonka me välitimme saveFileAs(QString)-funktiolle.

    saveFile()

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

    Tässä funktiossa ei ole mitään erityistä tai uutta. Tässä vain päätellään, näytetäänkö tallennusvalikko vai ei. Jos fileName ei ole tyhjä, tallennetaan tiedosto mitään kysymättä nimellä, joka on sijoitettuna fileName:n kutsumalla saveFileAs(QString)-funktiota, muuten kutsutaan yllä olevaa saveFileAs()-funktiota.

    Tiedoston avaaminen

    Lopulta olemme valmiita tarkastelemaan tiedoston avaamista levyltä. Kaikki koodi tätä tarkoitusta varten on sijoitettu MainWindow::openFile()-funktioon.

    Ensin meidän pitää kysyä käyttäjältä, mikä tiedosto halutaan avata. Tätä tarkoitusta varten käytetään toista KFileDialog-funktiota, getOpenFileName():

    QString fileNameFromDialog = KFileDialog::getOpenFileName();
    

    Seuraavaksi käytämme KIO-kirjastoa tiedoston avaamiseen. KIO-kirjaston avulla voimme avata tiedoston QFilen avulla, paikalliselta levyltä tai etäkoneelta, vaikkapa FTP:n yli. Tiedoston avaamiseksi käytämme NetAccess:n download()-funktiota:

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

    Ensimmäinen argumentti on avattavan tiedoston nimi. Toinen on QString, joka tiedoston hakemisen jälkeen sisältää väliaikaisen tiedoston sijainnin. Tämän tmpFile:n kanssa jatkamme tästä eteenpäin.

    Funktio palauttaa boolen arvon tosi tai epätosi, riippuen onnistuiko tiedoston siirto. Jos tiedoston haku epäonnistui, näytetään virheilmoitus:

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

    Muussa tapauksessa jatketaan tiedoston avaamista.

    Luomme uuden QFile:n välittämällä sen muodostajaan NetAccess::download():n luoman väliaikaistiedoston, minkä jälkeen avataan se kirjoitussuojattuna:

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

    Tuodaksemme avaamamme tiedoston sisällön ruudulle näkyviin, tarvitsee meidän käyttää QTextStream-luokkaa. QTextStream:n muodostajalle välitämme äsken avaamamme tiedoston (QFile file) ja kutsumme QFile:n readAll()-funktiota saadaksemme tekstin tiedostosta. Seuraavaksi välitämme tämän kaiken tekstialueen setPlainText()-funktiolle:

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

    Tallennetaan avatun tiedoston nimi(polkuineen):

    fileName = fileNameFromDialog;
    

    Ja lopuksi poistetaan NetAccess::download():n luoma väliaikaistiedosto:

    KIO::NetAccess::removeTempFile(tmpFile);
    

    Käännä, Asenna ja Suorita

    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)
    

    Koska otimme nyt KIO-kirjaston käyttöön, täytyy CMakelle kertoa, että linkittäjän tarvitsee ottaa myös tämä mukaan. Tätä varten lisäämme target_link_libraries()-funktioon tiedon ${KDE4_KIO_LIBS}.

    Komennot

    Itse käännös, asennus ja suoritus tehdään samoin kuin oppaassa 3.

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

    Seuraavaksi

    Seuraavana perehdytys: Komentoriviparametrit. (keskeneräinen, englanniksi)