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

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


{{TutorialBrowser_(es)|
{{TutorialBrowser_(es)|
Line 9: Line 9:
pre=[[Development/Tutorials/Using_KActions_(es)|Tutorial 3 - KActions]]|
pre=[[Development/Tutorials/Using_KActions_(es)|Tutorial 3 - KActions]]|


next=[[Development/Tutorials/KCmdLineArgs|Tutorial 5 - Using KCmdLineArgs]]|  
next=[[Development/Tutorials/KCmdLineArgs_(es)|Tutorial 5 - Usar KCmdLineArgs]]|  


reading=KIO::{{class|NetAccess}} {{qt|QFile}}
reading=KIO::{{class|NetAccess}} {{qt|QFile}}
Line 26: Line 26:


===main.cpp===
===main.cpp===
<code cppqt n>
<syntaxhighlight lang="cpp-qt" line>
#include <KApplication>
#include <KApplication>
#include <KAboutData>
#include <KAboutData>
Line 47: Line 47:
   return app.exec();
   return app.exec();
}
}
</code>
</syntaxhighlight>
<tt>main.cpp</tt> no ha cambiado desde el tutorial 3, excepto el cambio de cualquier referencia de tutorial 3 a tutorial 4.
<tt>main.cpp</tt> no ha cambiado desde el tutorial 3, excepto el cambio de cualquier referencia de tutorial 3 a tutorial 4.


===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 79: Line 79:


#endif
#endif
</code>
</syntaxhighlight>
Como queremos añadir la posibilidad de cargar y guardar archivos, debemos añadir las funciones que realizarán el trabajo. Ya que invocaremos a las funciones mediante el mecanismo de Qt [http://doc.trolltech.com/latest/signalsandslots.html signal/slot], debemos especificar que dichas funciones son slots, por lo que lo hacemos en la linea 19. Como estamos usando slots en el archivo cabecera, debemos también añadir la macro [http://doc.trolltech.com/latest/qobject.html#Q_OBJECT <tt>Q_OBJECT</tt>].
Como queremos añadir la posibilidad de cargar y guardar archivos, debemos añadir las funciones que realizarán el trabajo. Ya que invocaremos a las funciones mediante el mecanismo de Qt [http://doc.trolltech.com/latest/signalsandslots.html signal/slot], debemos especificar que dichas funciones son slots, por lo que lo hacemos en la linea 19. Como estamos usando slots en el archivo cabecera, debemos también añadir la macro [http://doc.trolltech.com/latest/qobject.html#Q_OBJECT <tt>Q_OBJECT</tt>].


Line 85: Line 85:


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


Line 195: Line 195:
}
}
}
}
</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="limpiar" />
    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 211: Line 212:
     </Menu>
     </Menu>
   </MenuBar>
   </MenuBar>
  <ToolBar name="mainToolBar" >
    <text>Main Toolbar</text>
    <Action name="limpiar" />
  </ToolBar>
</gui>
</gui>
</code>
</syntaxhighlight>
Es idéntico a <tt>tutorial3ui.rc</tt> del tutorial 3 excepto <tt>name</tt>, que ha cambiado a 'tutorial4'. No necesitamos añadir ninguna información de las <tt>KStandardAction</tt> ya que la posición de esas acciones las maneja KDE automaticamente.
Es idéntico a <tt>tutorial3ui.rc</tt> del tutorial 3 excepto <tt>name</tt>, que ha cambiado a 'tutorial4'. No necesitamos añadir ninguna información de las <tt>KStandardAction</tt> ya que la posición de esas acciones las maneja KDE automaticamente.


==Explicaciónn==
==Explicación==




Line 221: Line 228:


Lo primero que hacemos es añadir
Lo primero que hacemos es añadir
<code cppqt>
<syntaxhighlight lang="cpp-qt">
fileName(QString())
fileName(QString())
</code>
</syntaxhighlight>
al constructor de <tt>MainWindow</tt> en la linea 16. Esto asegura que <tt>fileName</tt> esté vacio desde el principio.
al constructor de <tt>MainWindow</tt> en la linea 16. Esto asegura que <tt>fileName</tt> esté vacio desde el principio.


Line 233: Line 240:


La primera función que hemos definido es <tt>newFile()</tt>.
La primera función que hemos definido es <tt>newFile()</tt>.
<code cppqt>
<syntaxhighlight lang="cpp-qt">
void MainWindow::newFile()
void MainWindow::newFile()
{
{
Line 239: Line 246:
textArea->clear();
textArea->clear();
}
}
</code>
</syntaxhighlight>
Con <tt>fileName.clear()</tt> ponemos la QString como "vacia" para reflejar el hecho de que el documento aún no está presente en disco. A continuación, <tt>textArea->clear()</tt> limpia el area de texto usando la misma función que conectaba con la <tt>KAction</tt> <tt>clear</tt> en el tutorial 3.
Con <tt>fileName.clear()</tt> ponemos la QString como "vacia" para reflejar el hecho de que el documento aún no está presente en disco. A continuación, <tt>textArea->clear()</tt> limpia el area de texto usando la misma función que conectaba con la <tt>KAction</tt> <tt>clear</tt> en el tutorial 3.


Line 249: Line 256:


El prototipo de la función es:
El prototipo de la función es:
<code cppqt>
<syntaxhighlight lang="cpp-qt">
void MainWindow::saveFileAs(const QString &outputFileName)
void MainWindow::saveFileAs(const QString &outputFileName)
</code>
</syntaxhighlight>


A continuación creamos nuestro objeto <tt>KSaveFile</tt> y lo abrimos con:
A continuación creamos nuestro objeto <tt>KSaveFile</tt> y lo abrimos con:
<code cppqt>
<syntaxhighlight lang="cpp-qt">
KSaveFile file(outputFileName);
KSaveFile file(outputFileName);
file.open();
file.open();
</code>
</syntaxhighlight>


Ahora que tenemos nuestro "file" para escribir, necesitamos convertir el texto del area de texto a un formato que pueda ser escrito en el archivo. Para hacer esto, creamos un {{qt|QByteArray}} y lo rellenamos con la versión en texto plano de lo que haya en el area de texto:
Ahora que tenemos nuestro "file" para escribir, necesitamos convertir el texto del area de texto a un formato que pueda ser escrito en el archivo. Para hacer esto, creamos un {{qt|QByteArray}} y lo rellenamos con la versión en texto plano de lo que haya en el area de texto:
<code cppqt>
<syntaxhighlight lang="cpp-qt">
QByteArray outputByteArray;
QByteArray outputByteArray;
outputByteArray.append(textArea->toPlainText().toUtf8());
outputByteArray.append(textArea->toPlainText().toUtf8());
</code>
</syntaxhighlight>
Ahora que tenemos nuestro <tt>QByteArray</tt>, lo usamos para escribir el archivo con <tt>KSaveFile::write()</tt>. Si estuvieramos usando un <tt>QFile</tt>, los cambios se reflejarían inmediatamente. Sin embargo, si se ha producido un problema parcial en la escritura, el archivo podría estar corrupto. Por esta razón, <tt>KSaveFile</tt> escribe primero en un archivo temporal y luego, en la llamada <tt>KSaveFile::Finalize</tt>, refleja los cambios en el archivo actual.
Ahora que tenemos nuestro <tt>QByteArray</tt>, lo usamos para escribir el archivo con <tt>KSaveFile::write()</tt>. Si estuvieramos usando un <tt>QFile</tt>, los cambios se reflejarían inmediatamente. Sin embargo, si se ha producido un problema parcial en la escritura, el archivo podría estar corrupto. Por esta razón, <tt>KSaveFile</tt> escribe primero en un archivo temporal y luego, en la llamada <tt>KSaveFile::Finalize</tt>, refleja los cambios en el archivo actual.
<code cppqt>
<syntaxhighlight lang="cpp-qt">
file.write(outputByteArray);
file.write(outputByteArray);
file.finalize();
file.finalize();
file.close();
file.close();
</code>
</syntaxhighlight>
Por último, establecemos que <tt>fileName</tt> apunte al nombre del archivo que acabamos de guardar.
Por último, establecemos que <tt>fileName</tt> apunte al nombre del archivo que acabamos de guardar.
<code cppqt>
<syntaxhighlight lang="cpp-qt">
fileName = outputFileName;
fileName = outputFileName;
</code>
</syntaxhighlight>


====saveFileAs()====
====saveFileAs()====
Line 279: Line 286:
Esta es la función a la que está conectado el slot <tt>saveAs</tt>. Simplemente llama a la función genérica <tt>saveFileAs(QString)</tt> pasandole el nombre del fichero devuelto por <tt>{{class|KFileDialog}}::[http://api.kde.org/4.0-api/kdelibs-apidocs/kio/html/classKFileDialog.html#8891356c249c5911e1ab15cc2739a89b getSaveFileName()]</tt>.
Esta es la función a la que está conectado el slot <tt>saveAs</tt>. Simplemente llama a la función genérica <tt>saveFileAs(QString)</tt> pasandole el nombre del fichero devuelto por <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>


Este es nuestro primer uso de la biblioteca KIO. La clase {{class|KFileDialog}} proporciona diferentes funciones estáticas para mostrar cuadros de dialgo estandar, usados en todas las aplicaciones de KDE. La llamada a <tt>KFileDialog::getSaveFileName()</tt> mostrará un diálogo donde el usuario puede seleccionar en nombre del archivo a guardar o elegir un nuevo nombre. La función devuelve el nombre del archivo, con el cual luego llamamos a <tt>saveFileAs(QString)</tt>.
Este es nuestro primer uso de la biblioteca KIO. La clase {{class|KFileDialog}} proporciona diferentes funciones estáticas para mostrar cuadros de dialgo estandar, usados en todas las aplicaciones de KDE. La llamada a <tt>KFileDialog::getSaveFileName()</tt> mostrará un diálogo donde el usuario puede seleccionar en nombre del archivo a guardar o elegir un nuevo nombre. La función devuelve el nombre del archivo, con el cual luego llamamos a <tt>saveFileAs(QString)</tt>.
Line 290: Line 297:
====saveFile()====
====saveFile()====


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


No hay nada destacable o nuevo en esta función, solamente la lógica para decidir si se muestra el dialogo para guardar el archivo. Si <tt>fileName</tt> no está vacio, el archivo es guardado como <tt>fileName</tt>. Si lo está, se muestra el diálogo para permitir al usuario elegir el nombre del archivo.
No hay nada destacable o nuevo en esta función, solamente la lógica para decidir si se muestra el dialogo para guardar el archivo. Si <tt>fileName</tt> no está vacio, el archivo es guardado como <tt>fileName</tt>. Si lo está, se muestra el diálogo para permitir al usuario elegir el nombre del archivo.


===Loading a file===
===Cargar un archivo===


Por último, el tutorial 4 debe ser capaz de cargar archivos desde disco. El código para llevarlo a cabo se encuentra en la función <tt>MainWindow::openFile()</tt>.
Por último, el tutorial 4 debe ser capaz de cargar archivos desde disco. El código para llevarlo a cabo se encuentra en la función <tt>MainWindow::openFile()</tt>.


Primero debemos preguntar al usuario por en nombre del archivo que desea abrir. Esto lo hacemos usando otra de las funciones de <tt>KFileDialog</tt>, <tt>getOpenFileName()</tt>:
Primero debemos preguntar al usuario por en nombre del archivo que desea abrir. Esto lo hacemos usando otra de las funciones de <tt>KFileDialog</tt>, <tt>getOpenFileName()</tt>:
<code cppqt>
<syntaxhighlight lang="cpp-qt">
QString fileNameFromDialog = KFileDialog::getOpenFileName();
QString fileNameFromDialog = KFileDialog::getOpenFileName();
</code>
</syntaxhighlight>


Luego usamos la biblioteca KIO para recuperar nuestro archivo. Esto nos permite abrir el archivo con QFile incluso si está almacenado en una ubicación remota, como un sitio FTP. Hacemos la siguiente llamada a la función <tt>download()</tt> de la clase {{class|NetAccess}}:
Luego usamos la biblioteca KIO para recuperar nuestro archivo. Esto nos permite abrir el archivo con QFile incluso si está almacenado en una ubicación remota, como un sitio FTP. Hacemos la siguiente llamada a la función <tt>download()</tt> de la clase {{class|NetAccess}}:
<code cppqt>
<syntaxhighlight lang="cpp-qt">
KIO::NetAccess::download(fileNameFromDialog, tmpFile, this)
KIO::NetAccess::download(fileNameFromDialog, tmpFile, this)
</code>
</syntaxhighlight>
El primer argumento es el nombre del archivo que queremos descargar. El segundo es un QString el cual, una vez se haya completado la descarga, contendrá la localización de la copia temporal del archivo. Es con <tt>tmpFile</tt> con quien trabajaremos a partir de ahora.
El primer argumento es el nombre del archivo que queremos descargar. El segundo es un QString el cual, una vez se haya completado la descarga, contendrá la localización de la copia temporal del archivo. Es con <tt>tmpFile</tt> con quien trabajaremos a partir de ahora.


La función devuelve <tt>true</tt> o <tt>false</tt> dependiendo si la transferencia tuvo exito. Si falló, mostraremos un mensaje informando del error:
La función devuelve <tt>true</tt> o <tt>false</tt> dependiendo si la transferencia tuvo exito. Si falló, mostraremos un mensaje informando del error:
<code cppqt>
<syntaxhighlight lang="cpp-qt">
KMessageBox::error(this, KIO::NetAccess::lastErrorString());
KMessageBox::error(this, KIO::NetAccess::lastErrorString());
</code>
</syntaxhighlight>


En caso contrario, continuamos con la apertura del archivo.
En caso contrario, continuamos con la apertura del archivo.


Creamos un QFile pasando a su constructor el archivo temporal creado por <tt>NetAccess::download()</tt> y lo abrirmos en modo de solo lectura:
Creamos un QFile pasando a su constructor el archivo temporal creado por <tt>NetAccess::download()</tt> y lo abrirmos en modo de solo lectura:
<code cppqt>
<syntaxhighlight lang="cpp-qt">
QFile file(tmpFile);
QFile file(tmpFile);
file.open(QIODevice::ReadOnly);
file.open(QIODevice::ReadOnly);
</code>
</syntaxhighlight>


Para mostrar el contenido del archivo debemos usar un {{qt|QTextStream}}. Creamos uno pasandole el contenido de nuestro archivo a su constructor, y llamamos a la función de QFile <tt>readAll()</tt> para obtener el texto del archivo. Luego este texto se le pasa a la función <tt>setPlainText()</tt> de nuestra área de texto.
Para mostrar el contenido del archivo debemos usar un {{qt|QTextStream}}. Creamos uno pasandole el contenido de nuestro archivo a su constructor, y llamamos a la función de QFile <tt>readAll()</tt> para obtener el texto del archivo. Luego este texto se le pasa a la función <tt>setPlainText()</tt> de nuestra área de texto.


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


A continuación almacenamos la ruta de acceso del archivo que acabamos de abrir:
A continuación almacenamos la ruta de acceso del archivo que acabamos de abrir:


<code cppqt>
<syntaxhighlight lang="cpp-qt">
fileName = fileNameFromDialog;
fileName = fileNameFromDialog;
</code>
</syntaxhighlight>
y por último, eliminamos el archivo temporal que fué creado por <tt>NetAccess::download()</tt>:
y por último, eliminamos el archivo temporal que fué creado por <tt>NetAccess::download()</tt>:
<code cppqt>
<syntaxhighlight lang="cpp-qt">
KIO::NetAccess::removeTempFile(tmpFile);
KIO::NetAccess::removeTempFile(tmpFile);
</code>
</syntaxhighlight>


==Make, instalar  y ejecutar==
==Make, instalar  y ejecutar==


===CMakeLists.txt===
===CMakeLists.txt===
<code ini n>
<syntaxhighlight lang="ini" line>
project(tutorial4)
project(tutorial4)
   
   
Line 372: Line 379:
install(FILES tutorial4ui.rc  
install(FILES tutorial4ui.rc  
         DESTINATION ${DATA_INSTALL_DIR}/tutorial4)
         DESTINATION ${DATA_INSTALL_DIR}/tutorial4)
</code>
</syntaxhighlight>
Como ahora estamos usando la biblioteca KIO, debemos decirselo a CMAKE para que la enlace. Hacemos esto pasándole <tt>${KDE4_KIO_LIBS}</tt> a la función <tt>target_link_libraries()</tt>
Como ahora estamos usando la biblioteca KIO, debemos decirselo a CMAKE para que la enlace. Hacemos esto pasándole <tt>${KDE4_KIO_LIBS}</tt> a la función <tt>target_link_libraries()</tt>


Line 382: Line 389:
  $HOME/bin/tutorial4
  $HOME/bin/tutorial4


==Moving On==
==Avanzando==
Ahora puedes continuar con el [[Development/Tutorials/KCmdLineArgs|Tutorial 5 - KCmdLineArgs]]
Ahora puedes continuar con el [[Development/Tutorials/KCmdLineArgs_(es)|Tutorial 5 - KCmdLineArgs]]


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

Latest revision as of 18:30, 15 July 2012


Cargar y guardar archivos
Serie   Tutorial para principiantes
Requisitos previos   Tutorial 3 - KActions
Siguiente   Tutorial 5 - Usar KCmdLineArgs
Lectura avanzada   KIO::NetAccess QFile

Resumen

Ahora que tenemos una interfaz básica del editor de textos, es hora de añadirle funcionalidad. Básicamente, un editor de textos debe abrir archivos desde disco, guardar archivos que has creado/editado y crear nuevos archivos.

KDE proporciona algunas clases para trabajar con archivos que hacen la vida mucho mas fácil a los desarrolladores. La biblioteca KIO te permite fácilmente tener acceso a archivos a través de protocolos de red transparentes, así como mediante cuadros de dialogo estándar.

El código

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) 2008 Developer") );
  KCmdLineArgs::init( argc, argv, &aboutData );
  KApplication app;
 
  MainWindow* window = new MainWindow();
  window->show();
  return app.exec();
}

main.cpp no ha cambiado desde el tutorial 3, excepto el cambio de cualquier referencia de tutorial 3 a tutorial 4.

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <KXmlGuiWindow>
#include <KTextEdit>

class MainWindow : public KXmlGuiWindow
{
	Q_OBJECT //nuevo respecto al tutorial3

	public:
		MainWindow(QWidget *parent=0);

	private:
		KTextEdit* textArea;
		void setupActions();
		QString fileName; //nuevo

	private slots: //nuevo
		void newFile(); //nuevo
		void openFile(); //nuevo
		void saveFile(); //nuevo
		void saveFileAs(); //nuevo
		void saveFileAs(const QString &outputFileName); //nuevo 
};

#endif

Como queremos añadir la posibilidad de cargar y guardar archivos, debemos añadir las funciones que realizarán el trabajo. Ya que invocaremos a las funciones mediante el mecanismo de Qt signal/slot, debemos especificar que dichas funciones son slots, por lo que lo hacemos en la linea 19. Como estamos usando slots en el archivo cabecera, debemos también añadir la macro Q_OBJECT.

También queremos llevar un registro del nombre del archivo abierto actual, por lo que declaramos QString fileName.

mainwindow.cpp

#include "mainwindow.h"

#include <KApplication>
#include <KAction>
#include <KLocale>
#include <KActionCollection>
#include <KStandardAction>
#include <KFileDialog> //nuevo
#include <KMessageBox> //nuevo
#include <KIO/NetAccess> //nuevo
#include <KSaveFile> //nuevo
#include <QTextStream> //nuevo
 
MainWindow::MainWindow(QWidget *parent)
	: KXmlGuiWindow(parent),
		fileName(QString()) //nuevo
{
	textArea = new KTextEdit;
	setCentralWidget(textArea);
 
	setupActions();
}
 
void MainWindow::setupActions()
{
	KAction* clearAction = new KAction(this);
	clearAction->setText(i18n("Limpiar"));
	clearAction->setIcon(KIcon("document-new"));
	clearAction->setShortcut(Qt::CTRL + Qt::Key_W);
	actionCollection()->addAction("limpiar", clearAction);
	connect(clearAction, SIGNAL(triggered(bool)), 
			textArea, SLOT(clear()));

	KStandardAction::quit(kapp, SLOT(quit()), 
						actionCollection());

	KStandardAction::open(this, SLOT(openFile()),
						actionCollection()); //nuevo

	KStandardAction::save(this, SLOT(saveFile()),
						actionCollection()); //nuevo

	KStandardAction::saveAs(this, SLOT(saveFileAs()),
						actionCollection()); //nuevo

	KStandardAction::openNew(this, SLOT(newFile()),
						actionCollection()); //nuevo

	setupGUI();
}

//Nuevo de aqui en adelante

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="limpiar" />
    </Menu>
  </MenuBar>

  <ToolBar name="mainToolBar" >
    <text>Main Toolbar</text>
    <Action name="limpiar" />
  </ToolBar>

</gui>

Es idéntico a tutorial3ui.rc del tutorial 3 excepto name, que ha cambiado a 'tutorial4'. No necesitamos añadir ninguna información de las KStandardAction ya que la posición de esas acciones las maneja KDE automaticamente.

Explicación

Bien, ahora vamos a implementar el código que realizará el trabajo de cargar y guardar. Esto se lleva a cabo en mainwindow.cpp

Lo primero que hacemos es añadir

fileName(QString())

al constructor de MainWindow en la linea 16. Esto asegura que fileName esté vacio desde el principio.

Añadir las acciones

La primera cosa que vamos a hacer es proporcionar al usuario la interfaz para decirle a la aplicación que cargue o guarde un archivo. Como ya hicimos con la acción quit en el tutorial 3, usaremos KStandardActions. En las lineas 37-47 añadimos las acciones de la misma manera que la acción quit. Para cada una de ellas, la conectamos que el slot apropiado que hemos declarado en el archivo cabecera.

Crear un nuevo documento

La primera función que hemos definido es newFile().

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

Con fileName.clear() ponemos la QString como "vacia" para reflejar el hecho de que el documento aún no está presente en disco. A continuación, textArea->clear() limpia el area de texto usando la misma función que conectaba con la KAction clear en el tutorial 3.

Guardar un archivo

saveFileAs(QString)

Ahora vamos a escribir nuestro primer código que maneje archivos. Vamos a implementar una función que guardará el contenido del área de texto en un archivo con un nombre pasado como parámetro. KDE proporciona una clase llamada KSaveFile para guardar archivos de manera segura, que hereda de la clase de Qt QFile.

El prototipo de la función es:

void MainWindow::saveFileAs(const QString &outputFileName)

A continuación creamos nuestro objeto KSaveFile y lo abrimos con:

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

Ahora que tenemos nuestro "file" para escribir, necesitamos convertir el texto del area de texto a un formato que pueda ser escrito en el archivo. Para hacer esto, creamos un QByteArray y lo rellenamos con la versión en texto plano de lo que haya en el area de texto:

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

Ahora que tenemos nuestro QByteArray, lo usamos para escribir el archivo con KSaveFile::write(). Si estuvieramos usando un QFile, los cambios se reflejarían inmediatamente. Sin embargo, si se ha producido un problema parcial en la escritura, el archivo podría estar corrupto. Por esta razón, KSaveFile escribe primero en un archivo temporal y luego, en la llamada KSaveFile::Finalize, refleja los cambios en el archivo actual.

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

Por último, establecemos que fileName apunte al nombre del archivo que acabamos de guardar.

fileName = outputFileName;

saveFileAs()

Esta es la función a la que está conectado el slot saveAs. Simplemente llama a la función genérica saveFileAs(QString) pasandole el nombre del fichero devuelto por KFileDialog::getSaveFileName().

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

Este es nuestro primer uso de la biblioteca KIO. La clase KFileDialog proporciona diferentes funciones estáticas para mostrar cuadros de dialgo estandar, usados en todas las aplicaciones de KDE. La llamada a KFileDialog::getSaveFileName() mostrará un diálogo donde el usuario puede seleccionar en nombre del archivo a guardar o elegir un nuevo nombre. La función devuelve el nombre del archivo, con el cual luego llamamos a saveFileAs(QString).

saveFile()

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

No hay nada destacable o nuevo en esta función, solamente la lógica para decidir si se muestra el dialogo para guardar el archivo. Si fileName no está vacio, el archivo es guardado como fileName. Si lo está, se muestra el diálogo para permitir al usuario elegir el nombre del archivo.

Cargar un archivo

Por último, el tutorial 4 debe ser capaz de cargar archivos desde disco. El código para llevarlo a cabo se encuentra en la función MainWindow::openFile().

Primero debemos preguntar al usuario por en nombre del archivo que desea abrir. Esto lo hacemos usando otra de las funciones de KFileDialog, getOpenFileName():

QString fileNameFromDialog = KFileDialog::getOpenFileName();

Luego usamos la biblioteca KIO para recuperar nuestro archivo. Esto nos permite abrir el archivo con QFile incluso si está almacenado en una ubicación remota, como un sitio FTP. Hacemos la siguiente llamada a la función download() de la clase NetAccess:

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

El primer argumento es el nombre del archivo que queremos descargar. El segundo es un QString el cual, una vez se haya completado la descarga, contendrá la localización de la copia temporal del archivo. Es con tmpFile con quien trabajaremos a partir de ahora.

La función devuelve true o false dependiendo si la transferencia tuvo exito. Si falló, mostraremos un mensaje informando del error:

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

En caso contrario, continuamos con la apertura del archivo.

Creamos un QFile pasando a su constructor el archivo temporal creado por NetAccess::download() y lo abrirmos en modo de solo lectura:

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

Para mostrar el contenido del archivo debemos usar un QTextStream. Creamos uno pasandole el contenido de nuestro archivo a su constructor, y llamamos a la función de QFile readAll() para obtener el texto del archivo. Luego este texto se le pasa a la función setPlainText() de nuestra área de texto.

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

A continuación almacenamos la ruta de acceso del archivo que acabamos de abrir:

fileName = fileNameFromDialog;

y por último, eliminamos el archivo temporal que fué creado por NetAccess::download():

KIO::NetAccess::removeTempFile(tmpFile);

Make, instalar y ejecutar

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)

Como ahora estamos usando la biblioteca KIO, debemos decirselo a CMAKE para que la enlace. Hacemos esto pasándole ${KDE4_KIO_LIBS} a la función target_link_libraries()

Con este archivo, puede construirse y ejecutar el tutorial de la misma forma que el tutorial 3. Para mas información, ve al tutorial 3.

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

Avanzando

Ahora puedes continuar con el Tutorial 5 - KCmdLineArgs