Archive:Development/Tutorials/Saving and loading (zh CN)

From KDE TechBase
The printable version is no longer supported and may have rendering errors. Please update your browser bookmarks and please use the default browser print function instead.


Template:TutorialBrowser (zh CN)

摘要

现在我们拥有了一个基本的文本编辑器的界面,已经到了做一些有用的事情的时候了。从最根本的来说,一个文本编辑器需要能够从磁盘中装载文件,并且能够创建新文件并保存你创建/编辑过的文件。

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中的相比没什么变化,除了说明参数从教程3变为了教程4。

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <KXmlGuiWindow>
#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); //新增加的
};

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

这和教学 3 的 tutorial3ui.rc 相同,除了 name 改为 'tutorial4' 以外。我们不需要加入任何 KStandardAction 的信息来安置这些动作,KDE 会自动处理。

解释

Okay,现在实现的程序代码将执行加载和储存。这都实做在 mainwindow.cpp

首先我们加入

fileName(QString())

MainWindow 建构子列表的第16行。这可以确保 fileName一开始是空的。

加入动作

我们首先要做的是为用户提供操作接口,使他们能够告诉应用程序加载和储存。类似教学3的 quit 动作,我们将使用 KStandardActions。在37至47行,我们用和 quit 动作同样的方式加入动作。我们将每一项连接到适当的槽,并宣告在头文件中。

创建新文件

我们第一个创建的函数是 newFile()

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

fileName.clear() 会设定 fileName QString 是空的,以反映该文件还没有存在于硬盘的事实。textArea->clear() 会清除中央的文本块,使用在教学 3我们连接 clear KAction 的相同功能函式。

储存档案

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

最后,我们设定 MainWindowsfileName 成员指向我们刚刚储存的文件名。

fileName = outputFileName;

saveFileAs()

这是一个连接 saveAs 槽的函数。它只是呼叫一般的 saveFileAs(QString) 函数并传递KFileDialog::getSaveFileName()返回的文件名。

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

这是我们第一次实际使用 KIO 函式库。KFileDialog 提供了一些显示常见的档案对话框的静态函数。呼叫 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 打开档案,即使它储存在远程例如 FTP 网站。我们呼叫 NetAccessdownload() 函数

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

第一个参数是您要下载文件的名称。第二个是仅次于下载完成后的 QString,将包含档案暂时备份的位置。我们将从现在开始工作这个 tmpFile

该函数返回truefalse取决于转换是否成功。如果失败,我们显示一个讯息框提供错误:

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

否则,我们继续打开档案。

我们传递NetAccess::download()创建的临时档案,给新创建 QFile 的建构子,然后在只读模式打开它

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

为了显示档案的内容,我们必须使用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


继续前进

现在你可以开始学习下一课:KCmdLineArgs 教学。