Beruflich Dokumente
Kultur Dokumente
a.setMainWidget( hello );
hello->show();
return a.exec();
}
main.cpp
Un objeto no tiene por qué saber si un slot está conectado a múltiples señales, e
incluso se puede conectar una señal a múltiples slots o múltiples señales a un solo
slot. Cualquier combinación es válida. Esto permite crear componentes
verdaderamente reusables en el desarrollo de una aplicación.
El código que se necesita para llamar a los slots es generado por moc (meta
object compiler) junto a otro código que inicializa la información de los
metaobjetos. Veremos posteriormente como usar moc, pero como por ahora nos
centramos en el ejemplo 1, y no hace falta, no usaremos moc todavia.
Finalmente, el código de arriba se usa para conectar la señal de " clicked()" del
objeto hello al slot "quit()" de la aplicación (a) que cuando se ejecuta, cierra la
aplicación.
a.setMainWidget( hello );
hello->show();
Este código hace que hello sea el widget principal (main) de la aplicación (el que
cerrará la aplicación cuando sea destruido), y lo muestra en la pantalla.
return a.exec();
¿ No es fácil ? :-)
Ahora haremos p2, que es el ejemplo análogo a p1, pero usando las librerías de
KDE, todo el código esta de nuevo en main.cpp:
#include <kapp.h>
#include <klocale.h>
#include <qpushbutton.h>
a.setMainWidget( hello );
hello->show();
return a.exec();
}
main.cpp
Analicémoslo.
KApplication a( argc, argv , "p2");
Este código crea un objeto KApplication. KApplication es una clase que hereda
de QApplication para añadirle características específicas de las librerías de KDE,
como internacionalización, configuración de las aplicaciones, etc.
QPushButton *hello=new QPushButton( i18n("Hello World !"), 0 );
hello->setAutoResize( TRUE );
Debido a esto, no podemos usar un tamño fijo para los botones, ya que no
podemos conocer a priori la longitud de la cadena tras hacer la traducción, y no
estaría bien si no mostramos la cadena completa porque el botón sea muy
pequeño. Usando el flag de autoResize, los botones cambiarán su tamaño
automáticamente siempre que su contenido cambie, con lo que estaremos seguros
de que todo el texto se muestre siempre correctamente.
Ya hemos creado una aplicación simple de KDE en p2, pero en la mayoría de los
casos habrá que usar un interfaz mas complicado que un simple botón :-), asi que
veamos como añadir un widget principal standard con una barra de menú.
#include <kapp.h>
#include "p3.h"
a.setMainWidget( window );
window->show();
return a.exec();
}
main.cpp
#include <kmainwindow.h>
public slots:
void fileOpen();
void fileSave();
};
p3.h
#include "p3.h"
#include <kfiledialog.h>
#include <kapp.h>
#include <kmenubar.h>
#include <klocale.h>
#include <kmessagebox.h>
#include <kpopupmenu.h>
#include <qtextview.h>
MainWindow::MainWindow ( const char * name ) : KMainWindow ( 0L, name )
{
setCaption("KDE Tutorial - p3");
void MainWindow::fileOpen()
{
KURL filename = KFileDialog::getOpenURL( QString::null, "*", this );
QString msg = QString( i18n("Now this app should open the url
%1 .") ).arg(filename.url());
KMessageBox::information( 0, msg, i18n( "Information" ),
"fileOpenInformationDialog" );
}
void MainWindow::fileSave()
{
KURL filename=KFileDialog::getSaveURL( QString::null, "*", this );
}
p3.cpp
Este código parece mucho mas complejo, pero no lo es, echemos un vistazo.
El código en main.cpp no ha cambiado mucho desde p1 o p2. La única diferencia
es que ahora creamos un objeto MainWindow en vez de un QPushButton, y que
ahora no especificamos un puntero NULL en el widget padre, ya que se toma por
defecto.
public slots:
void fileOpen();
void fileSave();
Como aprendimos en p1, los slots son métodos normales, así que pueden ser
públicos, protegidos o privados (public:, protected: o private:) como cualquier
otro. La única diferencia sera que debemos especificar public slots:,protected
slots: o private slots:, para que moc sepa que son slots.
Cuando el usuario selecciona un elemento, el menú emite una señal que puede
(¡ y debe !) ser conectada a una función para hacer algo. Por conveniencia, en vez
de usar connect, podemos conectar esta señal directamente cuando definimos la
entrada del menú. Ésto es lo que hacemos en el segundo y tercer parámetros.
Conectamos el elemento de menú Open al slot fileOpen() en este (this) objeto, la
entrada Save al slot fileSave() en este (this) objeto, y la entrada Quit al
slot quit() de la aplicación (kapp), tal como hicimos en p1 y p2.
Fijémonos en que kapp es una función de conveniencia que puede ser usada para
acceder a la instancia actual de KApplication.
QString about =
i18n("p3 1.0\n\n"
"(C) 1999-2002 Antonio Larrosa Jimenez\n"
"larrosa@kde.org\t\tantlarr@supercable.es\n"
"Malaga (Spain)\n\n"
"Simple KDE Tutorial\n"
"This tutorial comes with ABSOLUTELY NO WARRANTY\n"
"This is free software, and you are welcome to
redistribute it\n"
"under certain conditions\n");
Primero ponemos esa cadena dentro del objeto QString. QString es una clase de
cadena de texto de propósito general que se usa para manipular texto de muy
diversas formas. Uno de los principales beneficios de QString es el poder usar
Unicode en la aplicación de forma automática, sin tener que preocuparse de ello.
Ademas provée un mecanismo implícito de compartición de las cadenas que
incrementa su eficiencia, ya que cuando se crea una copia de un QString no se
copia realmente su contenido hasta que uno de ellos cambia.
Quería que esta aplicación hiciera algo bonito y simple, asi que le vamos a añadir
un widget QTextView. Este widget es un visor bastante útil de texto enriquecido
(Rich Text), donde podemos especificar el tipo de fuente y color entre otras
cosas. Despues de crearlo con el texto anterior lo ponemos como el widget
central (centralWidget ) de esta ventana.
Los tres parámetros indican el directorio en el que se comienza a buscar (no nos
importa en cual comienza, asi que dejamos el actual por defecto), los filtros (para
ver solo los archivos que cumplen una determinada expresión regular) y el objeto
padre.
Ahora tenemos el url que el usuario quiere abrir almacenado en filename.
KIO::NetAccess::removeTempFile( tmpFile );
}
Pero esto está fuera del ámbito de este tutorial, aunque puede que lo incremente
en el futuro con un ejemplo de la libreria KIO, libkio.
Ahora que sabemos crear un menú haremos una aplicación casi real en p4.
Gracias al trabajo de Lars Knoll, Dirk Mueller, Antti Koivisto, Waldo Bastian y
muchos otros, podemos usar un objeto KHTML, un widget que es capaz de
mostrar una página HTML, y que usando la libreria KIO es capaz de traer esa
página de la red. Veamos como funciona.
#include <kapp.h>
#include "p4.h"
a.setMainWidget( window );
window->show();
return a.exec();
}
main.cpp
#include <kmainwindow.h>
#include <kurl.h>
#include <kparts/browserextension.h>
class QLineEdit;
class KHTMLPart;
public:
MainWindow ( const char * name );
public slots:
void changeLocation();
void openURLRequest(const KURL &url, const KParts::URLArgs & );
private:
QLineEdit *location;
KHTMLPart *browser;
};
p4.h
#include "p4.h"
#include <qvbox.h>
#include <qlineedit.h>
#include <kapp.h>
#include <kmenubar.h>
#include <klocale.h>
#include <kpopupmenu.h>
#include <khtml_part.h>
connect( browser->browserExtension(),
SIGNAL( openURLRequest( const KURL &, const KParts::URLArgs & ) ),
this, SLOT( openURLRequest(const KURL &, const KParts::URLArgs & )
) );
setCentralWidget( vbox );
}
void MainWindow::changeLocation()
{
browser->openURL( location->text() );
}
p4.cpp
La primera diferencia que vemos con p3 esta en p4.h . Solo vamos a usar los
menús para salir de la aplicación, así que hemos quitado los dos slots que
usabamos para los diálogos de Open y Save.
public slots:
void changeLocation();
void openURLRequest(const KURL &url, const KParts::URLArgs & );
private:
QLineEdit *location;
KHTMLPart *browser;
Ahora se puede navegar con internet con esta aplicación, pero como muchas
personas solamente lo podran testear con su servidor local web, por estar
realizando las pruebas sin conexión a internet, es el que hemos puesto por
defecto. Asegurate de estar ejecutando apache en tu máquina para que esta
aplicación (¡ y las siguientes !) funcionen.
browser=new KHTMLPart( vbox );
browser->openURL( location->text() );
Con este código creamos el objeto navegador como un hijo de vbox, con lo que
será añadido debajo de location (la barra que contiene la dirección).
Por cierto, el segundo parámetro no lo usamos, así que vamos a ignorarlo pues
sólo se usa en ocasiones muy especiales.
setCentralWidget( vbox );
Éste es el código que se llama cuando el navegador quiere abrir una nueva
dirección. Como recordará, el método url() de la clase KURL nos devuelve la url
completa, así que la escribimos en la barra de dirección. Entonces llamamos a
changeLocation(), para abrir la url que hay en la barra de dirección dentro de la
"parte" html.
Piense en todo lo que hemos hecho con sólo 96 lineas. ¡Imagine lo que se puede
conseguir con 1000! :-)
a.setMainWidget( mywindow );
mywindow->show();
return a.exec();
}
main.cpp
#include <kmainwindow.h>
#include <kurl.h>
#include <kparts/browserextension.h>
class QLineEdit;
class KHTMLPart;
class QSplitter;
class QPushButton;
public slots:
void changeLocation();
void openURLRequest(const KURL &url, const KParts::URLArgs & );
void bookLocation();
private:
QLineEdit *location;
KHTMLPart *browser;
QPushButton *bookmark;
QSplitter *split;
};
p5.h
#include "p5.h"
#include <qvbox.h>
#include <qlineedit.h>
#include <qpushbutton.h>
#include <qsplitter.h>
#include <dcopclient.h>
#include <kapp.h>
#include <kmenubar.h>
#include <klocale.h>
#include <kpopupmenu.h>
#include <khtml_part.h>
#include <kdebug.h>
connect( browser->browserExtension(),
SIGNAL( openURLRequest( const KURL &, const KParts::URLArgs & ) ),
this, SLOT( openURLRequest(const KURL &, const KParts::URLArgs & )
) );
setCentralWidget( vbox );
void MainWindow::changeLocation()
{
browser->openURL( location->text() );
}
void MainWindow::bookLocation()
{
DCOPClient *client=kapp->dcopClient();
QByteArray params;
QDataStream stream(params, IO_WriteOnly);
stream << location->text();
if (!client->send("p6-*", "bookmarkList", "add(QString)", params))
kdDebug() << "Error with DCOP\n";
}
p5.cpp
private:
QLineEdit *location;
KHTMLPart *browser;
QPushButton *bookmark;
QSplitter *split;
Vamos a usar un nuevo slot que sera llamado cuando el usuario quiera almacenar
una dirección en su aplicación de marcadores. Ademas hay un par de nuevos
widgets que necesitaremos.
split = new QSplitter ( vbox );
split->setOpaqueResize();
Cuando el cliente pulsa el botón para añadir el url actual a la lista de bookmarks
este slot es llamado. Primero cogemos el objeto DCOPClient de esta aplicación y
despues inicializamos un objeto QDataStream.
QDataStream es una clase que se usa para serializar datos de cualquier tipo. Entre
sus características se encuentran las de ser completamente independiente del
equipo anfitrion (orden de byte, CPU, etc.). De esta forma se puede crear un
stream (caudal, conjunto de datos que se envían uno tras otro) en un sistema big-
endian y leerlo en una little-endian sin ningún problema.
stream << location->text();
kdDebug() devuelve un stream que puedes usar como salida para tus cadenas de
depuración. Hay varios niveles de logs : kdDebug, kdWarning, kdError y
kdFatal. Con estos indicas la seriedad del mensaje. También se puede especificar
un número de area para especificar la zona del programa que emitió el mensaje y
activar/desactivar areas para depurar partes específicas de tu aplicación.
Aqui explicaremos p6, que es una aplicación muy simple que simplemente muestra una
lista con bookmarks.
#include <kapp.h>
#include "p6.h"
a.setMainWidget( mylist );
mylist->show();
return a.exec();
}
main.cpp
#ifndef __P6IFACE_H__
#define __P6IFACE_H__
#include <dcopobject.h>
#include <qstring.h>
k_dcop:
virtual void add( const QString s ) = 0;
};
#endif
p6Iface.h
#include "p6Iface.h"
#include <qlistview.h>
public:
MainList();
};
p6.h
#include "p6.h"
#include <klocale.h>
#include <kapp.h>
#include <dcopclient.h>
DCOPClient *client=kapp->dcopClient();
client->attach();
client->registerAs("p6");
};
p6.cpp
En vez de usar un KTMainWindow y para conseguir claridad en el código fuente,
usaremos directamente un widget QListView a costa del diseño del interfaz
gráfico.
Con QListView se puede crear un widget con una lista de elementos (además con
diferentes columnas para mostrar las diferentes propiedades de cada elemento).
Es además posible, y rápido, el crear un árbol de elementos para tenerlo todo
mejor organizado. En este pequeño ejemplo, lo usaremos como una vista de una
lista compuesta por una sola columna y con capacidades de ordenación. ( por esto
es por lo que no estamos usando un QListBox, que satisfacería nuestros
propósitos con un API mas simple).
Hay una gran diferencia con los ejemplos previos, tenemos un fichero llamado
p6Iface.h, veamos ahora que es y para que sirve.
class p6Iface : virtual public DCOPObject
{
K_DCOP
public:
k_dcop:
virtual void add( const QString s ) = 0;
};
Con este dichero definimos el interface que p6 exportará a otras aplicaciones para
que lo usen. Usando la marca especial k_dcop ( que sera eliminada por el
preprocesador antes de llegar al compilador ), definimos los miembros del objeto
que otros podran llamar remotamente usando DCOP, en este caso add.
Hay que fijarse en que definimos esta función como pure virtual, con lo que no
esta implementada en la clase p6Iface. El truco aquí es usar dcopidl, una
herramienta que traduce nuestra definición parecida a un fichero .h a un lenguaje
de definición de interfaces común (IDL, Interface Definition Language). Esto le
permite a otras aplicaciones, como a dcopidl2cpp, el escribir por nosotros una
plantilla de la implementación, que se usa internamente por DCOPObject para
saber que métodos estan disponibles en este objeto.
class MainList : public QListView, virtual public p6Iface
Añadimos una columna ( al menos debe haber una para que QListView sea útil).
DCOPClient *client=kapp->dcopClient();
client->attach();
client->registerAs("p6");
Ahora esta aplicación está unida (attached) al servidor DCOP, tal como hicimos
en p5, pero de una nueva forma. Como en p5 no ibamos a recivir ninguna
llamada usamos una conexión anónima. En este caso queremos que otras
aplicaciones se conecten a esta, con lo que debemos registrarnos con un nombre,
que es lo que hace registerAs.
Obviamente, ese es el nombre que otros usaran cuando hagan una llamada a esta
aplicación usando DCOP.
insertItem ( new QListViewItem ( this , s ) );
Finalmente el método add toma el parámetro QString, construye un QListViewItem con el
y añade el elemento (item) a QListView.
Quizá te preguntes porque no hay un método mas simple para insertar un QString
en un QListView. La razón es simple: Un objeto QListView puede contener
cosas mucho mas complicadas que cadenas, por ejemplo, se pueden tener
múltiples columnas, donde algunas de ellas son objetos QPixmap (imágenes),
etc.
Scripting (guiones)
Uno de los múltiples beneficios de usar DCOP es su habilidad para facilmente permitir la
creación de scripts para cualquier aplicación que use este interface. Como ejemplo de esto,
mostraremos un script en Python que añade un url a la lista de marcadores.
Para poder usarlo sera necesaria la librería xmlrpc para Python. Esta disponible
en http://www.pythonware.com/downloads/xmlrpc-0.9.8--990621.zip, solo hace
falta descomprimirla en el directorio de librerías de Python (usualmente
/usr/lib/python1.5). Además hará falta ejecutar kxmlrpcd, que es un demonio de
xmlrpc que funciona como puente entre el protocolo xmlrpc y el protocolo
DCOP.
#!/usr/bin/python
server.bookmarkList.add(auth, "http://www.kde.org")
addurl.py
Quisiera aquí darle las gracias a Kurt Granroth por darme el código fuente de este
script, ya que mi conocimiento de Python es nulo.
¡ Incluso se puede hacer un script para una aplicación de KDE usando un script
en bash !
#include <kapp.h>
#include "p7.h"
a.setMainWidget( mywindow );
mywindow->show();
return a.exec();
}
main.cpp
#include <dcopobject.h>
k_dcop:
virtual void setURL( QString s )=0;
};
p7Iface.h
#include "p7Iface.h"
#include <kmainwindow.h>
#include <kurl.h>
#include <kparts/browserextension.h>
#include <qvaluestack.h>
class QLineEdit;
class KHTMLPart;
public slots:
void fileSetDefaultPage();
void changeLocation();
void bookLocation();
void gotoPreviousPage();
void openURLRequest(const KURL &url, const KParts::URLArgs & );
private:
QLineEdit *location;
KHTMLPart *browser;
QValueStack <QString> history;
};
p7.h
#include "p7.h"
#include <qvbox.h>
#include <qlineedit.h>
#include <dcopclient.h>
#include <kfiledialog.h>
#include <kapp.h>
#include <kmenubar.h>
#include <ktoolbar.h>
#include <klocale.h>
#include <kpopupmenu.h>
#include <khtml_part.h>
#include <kdebug.h>
#include <kconfig.h>
#include <kiconloader.h>
#define TOOLBAR_ID_ADDBOOKMARK 1
#define TOOLBAR_ID_BACK 2
#define TOOLBAR_ID_QUIT 3
toolbar->insertButton(BarIcon("reload"), TOOLBAR_ID_ADDBOOKMARK,
SIGNAL(clicked(int)),this,SLOT(bookLocation()),TRUE,
i18n("Add to Bookmarks"));
toolbar->insertButton(BarIcon("back"), TOOLBAR_ID_BACK,
SIGNAL(clicked(int)),this,SLOT(gotoPreviousPage()),
FALSE, i18n("Back to previous page"));
toolbar->insertButton(BarIcon("exit"), TOOLBAR_ID_QUIT,
SIGNAL(clicked(int)),kapp,SLOT(quit()),TRUE,
i18n("Quit the application"));
addToolBar(toolbar);
KConfig *config=kapp->config();
config->setGroup("Settings");
location->setText( config->readEntry( "defaultPage",
"http://localhost") );
connect( browser->browserExtension(),
SIGNAL( openURLRequest( const KURL &, const KParts::URLArgs & ) ),
this, SLOT( openURLRequest(const KURL &, const KParts::URLArgs & )
) );
setCentralWidget( vbox );
void MainWindow::changeLocation()
{
history.push( browser->url().url() );
toolBar()->setItemEnabled( TOOLBAR_ID_BACK, TRUE);
browser->openURL( location->text() );
}
void MainWindow::gotoPreviousPage()
{
location->setText( history.pop() );
if (history.isEmpty()) toolBar()->setItemEnabled( TOOLBAR_ID_BACK,
FALSE);
browser->openURL( location->text() );
}
void MainWindow::bookLocation()
{
DCOPClient *client=kapp->dcopClient();
QByteArray params;
QDataStream stream(params, IO_WriteOnly);
stream << location->text();
if (!client->send("p8-*", "bookmarkList", "add(QString)", params))
kdDebug << "Error with DCOP\n";
void MainWindow::fileSetDefaultPage()
{
KConfig *config=kapp->config();
config->setGroup("Settings");
config->writeEntry( "defaultPage", browser->url().url() );
}
p7.cpp
La clase MainWindow ahora hereda además de p7Iface, asi que tenemos que
llamar al constructor DCOPObject. Vamos a llamar a este objeto browser. Ahora
hay otro elemento en el menú de fichero que será usado para que la página actual
sea la se cargue por defecto cuando se ejecute la aplicación. Así mostraremos
como almacenar una variable de configuración.
KToolBar *toolbar=new KToolBar(this);
SIGNAL(clicked(int)),this,SLOT(bookLocation()),TRUE,
i18n("Add to Bookmarks"));
toolbar->insertButton(BarIcon("back"), TOOLBAR_ID_BACK,
SIGNAL(clicked(int)),this,SLOT(gotoPreviousPage()),
FALSE, i18n("Back to previous page"));
toolbar->insertButton(BarIcon("exit"), TOOLBAR_ID_QUIT,
SIGNAL(clicked(int)),kapp,SLOT(quit()),TRUE,
i18n("Quit the application"));
Este miembro es ahora usado para poner de forma genérica un url. Actualmente
esta función puede ser llamada por el interface DCOP o cuando el usuario pulsa
sobre un enlace. Primero hacemos que el texto de la dirección contenga el url, y
entonces llamamos a la función changeLocation.
void MainWindow::changeLocation()
{
history.push( browser->url() );
toolBar()->setItemEnabled( TOOLBAR_ID_BACK, TRUE);
browser->openURL( location->text() );
}
Nos fijamos ahora en que definimos un objeto historia ( history ) en p7.h del tipo
QValueStack <QString> . QValueStack es una clase de Qt que implementa una
pila usando un template (plantilla) como tipo de datos para sus elementos. La
diferencia entre QValueStack y QStack es que el primero almacena el valor de
los elementos que se añaden, creando una copia de cada elemento (debe de haber
un constructor de copia para eso), mientras el segundo simplemente almacena
punteros a los elementos que se añaden, asi que habría que tener cuidado de no
borrarlos mientras permanecen en la pila.
Como la clase QString comparte los datos entre todas las copias de la cadena
hasta que se realiza un cambio a una de ellas, no hay apenas penalización por
crear copias de los urls.
Cuando el usuario quiera volver hacia atra a una página anterior simplemente
tomaremos el último elemento insertado en la pila del historial ( con un pop, ya
que es una pila después de todo :-) ), y lo ponemos en la barra de direcciones. La
cadena es eliminada de la pila, con lo que la próxima vez que se haga un pop
tomará el siguiente y así sucesivamente hasta que se vacíe la pila.
if (history.isEmpty()) toolBar()->setItemEnabled( TOOLBAR_ID_BACK,
FALSE);
browser->openURL( location->text() );
}
Si la pila está vacía, mejor deshabilitamos el botón back, una vez que añadimos
un elemento el botón es habilitado de nuevo en changeLocation(). Entonces
abrimos el url que sacamos (popped) de la pila en el browser.
void MainWindow::fileSetDefaultPage()
{
KConfig *config=kapp->config();
config->setGroup("Settings");
config->writeEntry( "defaultPage", browser->url() );
}
Ahora estamos llegando al final del tutorial, pero primero tenemos que hacerle un
pequeño cambio a p6 para que se pueda comunicar con p7 para poner la página
actual cuando se pulse sobre un url en la lista de marcadores, ya que en otro caso
no tendría mucho sentido tener una lista de marcadores :-).
Este programa, p8, es una versión actualizada de p6 con la habilidad de enviarle a
p7 el url que se seleccione de la lista. Esto permitirá a p7 el abrir nuestros urls
favoritos.
#include <kapp.h>
#include "p8.h"
a.setMainWidget( mylist );
mylist->show();
return a.exec();
}
main.cpp
#include <dcopobject.h>
#include <qstring.h>
k_dcop:
virtual void add( const QString s ) = 0;
};
p8Iface.h
#include "p8Iface.h"
#include <qlistview.h>
public:
MainList();
};
p8.h
#include "p8.h"
#include <klocale.h>
#include <kapp.h>
#include <dcopclient.h>
#include <kdebug.h>
DCOPClient *client=kapp->dcopClient();
client->attach();
client->registerAs("p8");
}
p8.cpp
Tanto main.cpp, como p8Iface.h son muy similares a los ficheros respectivos en
p6, asi que no los comentare.
connect( this, SIGNAL(clicked(QListViewItem *)),
this, SLOT(setURLInBrowser(QListViewItem *)));
Si tenemos un url para enviar, cogemos el objeto DCOPCLient tal como hicimos
en p5 o p7 para enviar un mensaje DCOP y entonces preparamos el flujo de datos
(data stream).
Ahora estamos alcanzando el final del tutorial, pero primero, vamos a hacer
algunas modificaciones para simplificar el código fuente y usar las últimas
tecnicas disponibles.
Ahora vamos a simplificar p7 a la vez que vamos a maximizar la
configurabilidad del programa usando la arquitectura XMLGUI para construir el
interfaz de usuario.
#include <kapp.h>
#include <kcmdlineargs.h>
#include <klocale.h>
#include <kaboutdata.h>
#include "p9.h"
KApplication a;
a.setMainWidget( mywindow );
mywindow->show();
return a.exec();
main.cpp
#include <dcopobject.h>
k_dcop:
virtual void setURL( const QString s )=0;
};
p9Iface.h
#include "p9Iface.h"
#include <kmainwindow.h>
#include <kurl.h>
#include <kparts/browserextension.h>
#include <qvaluestack.h>
class QLineEdit;
class KHTMLPart;
public slots:
void fileSetDefaultPage();
void changeLocation();
void bookLocation();
void gotoPreviousPage();
void openURLRequest(const KURL &url, const KParts::URLArgs & );
private:
QLineEdit *location;
KHTMLPart *browser;
QValueStack <QString> history;
};
p9.h
#include "p9.h"
#include <qvbox.h>
#include <qlineedit.h>
#include <dcopclient.h>
#include <kfiledialog.h>
#include <kapp.h>
#include <kaction.h>
#include <klocale.h>
#include <khtml_part.h>
#include <kdebug.h>
#include <kconfig.h>
#include <kstdaction.h>
actionCollection()->action("back")->setEnabled(false);
createGUI("p9ui.rc");
KConfig *config=kapp->config();
config->setGroup("Settings");
location->setText( config->readEntry( "defaultPage",
"http://localhost") );
connect( browser->browserExtension(),
SIGNAL( openURLRequest( const KURL &, const KParts::URLArgs & ) ),
this, SLOT( openURLRequest(const KURL &, const KParts::URLArgs
& ) ) );
setCentralWidget( vbox );
void MainWindow::changeLocation()
{
history.push( browser->url().url() );
actionCollection()->action("back")->setEnabled(true);
browser->openURL( location->text() );
}
void MainWindow::bookLocation()
{
DCOPClient *client=kapp->dcopClient();
QByteArray params;
QDataStream stream(params, IO_WriteOnly);
stream << location->text();
if (!client->send("p8-*", "bookmarkList", "add(QString)", params))
kdDebug << "Error with DCOP";
}
void MainWindow::fileSetDefaultPage()
{
KConfig *config=kapp->config();
config->setGroup("Settings");
config->writeEntry( "defaultPage", browser->url().url() );
}
p9.cpp
KAboutData *aboutdata = new KAboutData("p9", I18N_NOOP("KDE Tutorial -
p9"),
"1.0", I18N_NOOP("Step 9 of a simple tutorial"),
KAboutData::License_GPL,
"(C) 2000, Antonio Larrosa Jimenez","",
"http://devel-home.kde.org/~larrosa/tutorial.html");
La clase KAboutData se usa para guardar los datos para el diálogo de "about"
(¿no era obvio? :-) ) . Primero pasamos el nombre interno de la aplicación,
después el nombre "real" (el que aparece en la pantalla), la versión, una
descripción corta, la licencia, la nota de copyright, un texto libre (nada en este
caso) y la página web de la aplicación.
Observamos que en lugar de la típica función i18n, ahora estamos usando una
macro llamada I18N_NOOP, esto lo hacemos así porque no podemos usar i18n
antes de crear un objeto KApplication, con lo que usamos I18N_NOOP, que
"marca" el texto para traducirlo más tarde.
aboutdata->addAuthor("Antonio Larrosa Jimenez",
I18N_NOOP("Original Developer/Mantainer"),"larrosa@kde.org",
"http://devel-home.kde.org/~larrosa/index.html");
Ahora, añadimos la información de un autor (podemos añadir información de
tantos autores como queramos). Primero, el nombre (no, no lo marcamos para
traducción ;-) ), después, el cargo dentro de la aplicación, la dirección de correo
electrónico, y su página web personal.
KCmdLineArgs::init(argc, argv, aboutdata);
KApplication a;
Observa que ahora podemos simplemente usar los parámetros por defecto para el
constructor de KApplication, ya que cogerá toda la información necesaria del
objeto aboutdata.
Primero notamos que estas lineas ya no existen. Vamos a usar otro mecanismo
que es mucho más intuitivo y por tanto nos permite simplificar el código.
Crearemos un objeto KAction para cada acción que el usuario pueda hacer. Hay
dos tipos de acciones, las estándares (como abrir un archivo, guardar, deshacer,
salir, etc.) y las propias (las específicas de cada aplicación).
KStdAction::quit(this, SLOT(close()), actionCollection());
Primero, creamos una acción estandar "quit" (salir), que está conectada al
objeto this y al slot close(). El parámetro actionCollection() es sólo el objeto
que almacena todas las acciones de la aplicación.
(void)new KAction(i18n("&Set default page"), "gohome", 0, this,
SLOT(fileSetDefaultPage()), actionCollection(),
"set_default_page")
Ahora creamos una nueva acción. El primer parámetro es el texto que queremos
mostrar cuando esta acción está en una barra de menú, el segundo parámetro es el
nombre del icono que será mostrado cuando esta acción esté en una barra de
herramientas o de menú. Observa que sólo especificamos el nombre del icono en
vez del propio icono. Esto permite a las librerías hacer algunas cosas interesantes,
como aplicar efectos y elegir el tamaño adecuado del icono para las barras de
menú o de herramientas. El tercer parámetro es la tecla "atajo" que el usuario
puede pulsar para activar la acción (en este caso, no tenemos ningún atajo).
Después, especificamos el objeto y slot que se llamarán cuando esta acción se
active y el objeto actionCollection().
Aquí creamos un par de acciones más, para añadir la página actual a la lista de
marcadores y para ir atrás a la página anterior.
actionCollection()->action("back")->setEnabled(false);
El gran beneficio de esto es que no tenemos que llevar los elementos del menú y
de la barra de herramientas por separado, simplemente dehabilitamos la acción y
las correspondientes entradas de menú y botones de la barra de herramientas se
deshabilitan automaticamente. No hay que usar nunca más millones de
definiciones para cada ID de elementos de menú e IDs para botones de la barra
de herramientas, ¿ no es fantástico ? :-)
createGUI("p9ui.rc");
Este es un fichero XML que contiene la definición del entorno de usuario para
p9:
<kpartgui name="p9">
Primero, definimos una barra de menú en la sección MenuBar. Para cada menú,
tenemos una sección "Menu" con su nombre (en este caso sólo tenemos un menú
"file" ya que el menú de ayuda se añade automáticamente), y en cada sección
"Menu", ponemos las acciones que queremos que aparezcan en ese menú.
<ToolBar fullWidth="true" name="mainToolBar">
<Action name="add_to_bookmarks"/>
<Action name="back"/>
<Separator/>
<Action name="file_quit"/>
</ToolBar>
Por cierto, la misma sintaxis para los separadores se puede usar para insertar
separadores en los menús.
Volviendo a p9.cpp, observamos que estamos registrándolo como "p7" al
servidor dcop. Hacemos esto sólo para poder usar p8 con p9 tal y como hicimos
con p7.
Así que a la vez que hemos eliminado el 17.7 por ciento del código fuente,
hemos permitido la posibilidad de que el usuario configure el interfaz de la
aplicación (sólo tiene que modificar el archivo XML y volver a ejecutar la
aplicación, sin necesidad de recompilar ! )
Este es el último paso del tutorial. Espero que hayas aprendido muchas cosas, y
decidas comenzar tu propia aplicación con KDE. Si es así, hecha un vistazo a las
sugerencias en la Introducción para ver como resolver las dudas que vayan
surgiendo y leer algunas ideas sobre como comenzar a escribir la aplicación.
Antonio Larrosa