Archive:Development/Tutorials/Saving and loading (zh TW): Difference between revisions

From KDE TechBase
(Created page with '{{Template:I18n/Language Navigation Bar|Development/Tutorials/Saving_and_loading}} {{TutorialBrowser (zh TW)| series=初學者教學| name=儲存與載入檔案| pre=[[Develo...')
 
No edit summary
Line 16: Line 16:
==摘要==
==摘要==


Now that we have a basic text editor interface, it's time to make it do something useful. At the most basic, a text editor needs to be able to load files from disc, save files that you've created/edited and create new files.
現在我們擁有了一個基本的文字編輯器界面,開始要讓他做一些有用的事情了。最根本的來說,一個文字編輯器需要能夠從硬碟中載入檔案,儲存你新增或編輯過的檔案。


KDE provides a number of classes for working with files which make life a lot easier for developers. The KIO library allows you to easily access files through network-transparent protocols as well as providing standard file dialogs.
KDE 提供了許多讓開發者能輕鬆操作檔案的類別。KIO 函式庫讓你可以十分容易地透過網路協定訪問檔案,而且提供標準的檔案對話框。


[[image:introtokdetutorial4.png|frame|center]]
[[image:introtokdetutorial4.png|frame|center]]
Line 47: Line 47:
}
}
</code>
</code>
<tt>main.cpp</tt> hasn't changed from tutorial 3 except to change any reference to tutorial 3 to tutorial 4.
<tt>main.cpp</tt> 與教學 3中的相比沒什麼變化,除了說明參數從 tutorial 3 變為了 tutorial 4。


===mainwindow.h===
===mainwindow.h===
Line 59: Line 59:
class MainWindow : public KXmlGuiWindow
class MainWindow : public KXmlGuiWindow
{
{
   Q_OBJECT //new from tutorial3
   Q_OBJECT //與教學 3相比,新增加的
    
    
   public:
   public:
Line 67: Line 67:
     KTextEdit* textArea;
     KTextEdit* textArea;
     void setupActions();
     void setupActions();
     QString fileName; //new
     QString fileName; //新增的


   private slots: //new
   private slots: //新增的
     void newFile(); //new
     void newFile(); //新增的
     void openFile(); //new
     void openFile(); //新增的
     void saveFile(); //new
     void saveFile(); //新增的
     void saveFileAs(); //new
     void saveFileAs(); //新增的
     void saveFileAs(const QString &outputFileName); //new
     void saveFileAs(const QString &outputFileName); //新增的
};
};


#endif
#endif
</code>
</code>
Since we want to add the ability to load and save files, we must add the functions which will do the work. Since the functions will be called through Qt's [http://doc.trolltech.com/latest/signalsandslots.html signal/slot] mechanism we must specify that these functions are slots as we do on line 19. Since we are using slots in this header file, we must also add the [http://doc.trolltech.com/latest/qobject.html#Q_OBJECT <tt>Q_OBJECT</tt>] macro.
由於我們想要加上載入和儲存檔案的能力,所以我們必須加入用來完成這些工作的函數。由於這些函數將會透過Qt的[http://doc.qt.nokia.com/latest/signalsandslots.html 訊號/槽(signal/slot)] 機制被呼叫,所以我們必須註明這些函數是槽(slots),就像我們在第19行做的那樣。由於我們在標頭檔中使用槽,所以我們同樣必須加入[http://doc.qt.nokia.com/latest/qobject.html#Q_OBJECT <tt>Q_OBJECT</tt>] 巨集。


We also want to keep track of the filename of the currently opened file so we declare a <tt>{{qt|QString}} fileName</tt>.
我們同樣想要跟踪當前打開檔案的名稱,所以我們宣告了一個{{qt|QString}} fileName


===mainwindow.cpp===
===mainwindow.cpp===
Line 92: Line 92:
#include <KActionCollection>
#include <KActionCollection>
#include <KStandardAction>
#include <KStandardAction>
#include <KFileDialog> //new
#include <KFileDialog> //新增的
#include <KMessageBox> //new
#include <KMessageBox> //新增的
#include <KIO/NetAccess> //new
#include <KIO/NetAccess> //新增的
#include <KSaveFile> //new
#include <KSaveFile> //新增的
#include <QTextStream> //new
#include <QTextStream> //新增的
   
   
MainWindow::MainWindow(QWidget *parent)
MainWindow::MainWindow(QWidget *parent)
     : KXmlGuiWindow(parent),
     : KXmlGuiWindow(parent),
       fileName(QString()) //new
       fileName(QString()) //新增的
{
{
   textArea = new KTextEdit;
   textArea = new KTextEdit;
Line 122: Line 122:
   
   
   KStandardAction::open(this, SLOT(openFile()),
   KStandardAction::open(this, SLOT(openFile()),
                         actionCollection()); //new
                         actionCollection()); //新增的
   
   
   KStandardAction::save(this, SLOT(saveFile()),
   KStandardAction::save(this, SLOT(saveFile()),
                         actionCollection()); //new
                         actionCollection()); //新增的
   
   
   KStandardAction::saveAs(this, SLOT(saveFileAs()),
   KStandardAction::saveAs(this, SLOT(saveFileAs()),
                         actionCollection()); //new
                         actionCollection()); //新增的
   
   
   KStandardAction::openNew(this, SLOT(newFile()),
   KStandardAction::openNew(this, SLOT(newFile()),
                         actionCollection()); //new
                         actionCollection()); //新增的
   
   
   setupGUI();
   setupGUI();
}
}


//New from here on
//從這裡開始都是新增加的


void MainWindow::newFile()
void MainWindow::newFile()
Line 233: Line 233:
to the <tt>MainWindow</tt> constructor list on line 16. This makes sure that <tt>fileName</tt> is empty right from the beginning.
to the <tt>MainWindow</tt> constructor list on line 16. This makes sure that <tt>fileName</tt> 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 <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.
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.
Line 249: Line 249:
<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> 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.


===Saving a file===
===儲存檔案===


====saveFileAs(QString)====
====saveFileAs(QString)====
Line 313: Line 313:
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.
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.


===Loading a file===
===載入檔案===


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>.
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>.

Revision as of 05:24, 29 September 2009


Development/Tutorials/Saving_and_loading


Template:TutorialBrowser (zh TW)

摘要

現在我們擁有了一個基本的文字編輯器界面,開始要讓他做一些有用的事情了。最根本的來說,一個文字編輯器需要能夠從硬碟中載入檔案,儲存你新增或編輯過的檔案。

KDE 提供了許多讓開發者能輕鬆操作檔案的類別。KIO 函式庫讓你可以十分容易地透過網路協定訪問檔案,而且提供標準的檔案對話框。

程式碼

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 與教學 3中的相比沒什麼變化,除了說明參數從 tutorial 3 變為了 tutorial 4。

mainwindow.h

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

class MainWindow : public KXmlGuiWindow {

 Q_OBJECT //與教學 3相比,新增加的
 
 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的訊號/槽(signal/slot) 機制被呼叫,所以我們必須註明這些函數是槽(slots),就像我們在第19行做的那樣。由於我們在標頭檔中使用槽,所以我們同樣必須加入Q_OBJECT 巨集。

我們同樣想要跟踪當前打開檔案的名稱,所以我們宣告了一個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> //新增的
  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> This is identical to tutorial3ui.rc from tutorial 3 except the name has changed to 'tutorial4'. We do not need to add any information about any of the KStandardActions since the placement of those actions is handled automatically by KDE.

解釋

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.

加入動作

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.

儲存檔案

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().toUtf8()); 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 is 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.

載入檔案

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);

編譯、安裝與執行

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 be 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

Note
Changed settings are saved in your KDE directory, in this case into $HOME/.kde/share/apps/tutorial4.


繼續前進

現在你可以開始學習下一課:KCmdLineArgs 教學。