Siguiente Anterior Tabla de Contenidos
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" int main( int argc, char **argv ) { 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, 2001 Antonio Larrosa Jimenez","", "http://devel-home.kde.org/~larrosa/tutorial.html"); aboutdata->addAuthor("Antonio Larrosa Jimenez", I18N_NOOP("Original Developer/Mantainer"),"larrosa@kde.org", "http://devel-home.kde.org/~larrosa/index.html"); KCmdLineArgs::init(argc, argv, aboutdata); KApplication a; MainWindow *mywindow=new MainWindow( "Tutorial - p9" ); mywindow->resize( 300, 200 ); a.setMainWidget( mywindow ); mywindow->show(); return a.exec(); }
#include <dcopobject.h> class p9Iface : virtual public DCOPObject { K_DCOP k_dcop: virtual void setURL( const QString s )=0; };
#include "p9Iface.h" #include <kmainwindow.h> #include <kurl.h> #include <kparts/browserextension.h> #include <qvaluestack.h> class QLineEdit; class KHTMLPart; class MainWindow : public KMainWindow, virtual public p9Iface { Q_OBJECT public: MainWindow ( const char * titulo ); virtual void setURL ( const QString url ); 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; };
#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> MainWindow::MainWindow ( const char * name ) : KMainWindow ( 0L, name ), DCOPObject ( "browser" ) { KStdAction::quit(this, SLOT(close()), actionCollection()); (void)new KAction(i18n("&Set default page"), "gohome", 0, this, SLOT(fileSetDefaultPage()), actionCollection(), "set_default_page"); (void)new KAction(i18n("Add to Bookmarks"), "reload", 0, this, SLOT(bookLocation()), actionCollection(), "add_to_bookmarks"); (void)new KAction(i18n("Back to previous page"), "back", 0, this, SLOT(gotoPreviousPage()), actionCollection(), "back"); actionCollection()->action("back")->setEnabled(false); createGUI("p9ui.rc"); QVBox * vbox = new QVBox ( this ); location = new QLineEdit ( vbox ); KConfig *config=kapp->config(); config->setGroup("Settings"); location->setText( config->readEntry( "defaultPage", "http://localhost") ); connect( location , SIGNAL( returnPressed() ), this, SLOT( changeLocation() ) ); browser=new KHTMLPart( vbox ); browser->openURL( location->text() ); connect( browser->browserExtension(), SIGNAL( openURLRequest( const KURL &, const KParts::URLArgs & ) ), this, SLOT( openURLRequest(const KURL &, const KParts::URLArgs & ) ) ); setCentralWidget( vbox ); DCOPClient *client = kapp->dcopClient(); client->attach(); client->registerAs("p7"); } void MainWindow::changeLocation() { history.push( browser->url().url() ); actionCollection()->action("back")->setEnabled(true); browser->openURL( location->text() ); } void MainWindow::setURL( const QString url ) { location->setText( url ); changeLocation(); } void MainWindow::openURLRequest( const KURL &url, const KParts::URLArgs & ) { setURL( url.url() ); } void MainWindow::gotoPreviousPage() { location->setText( history.pop() ); if (history.isEmpty()) actionCollection()->action("back")->setEnabled(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"; } void MainWindow::fileSetDefaultPage() { KConfig *config=kapp->config(); config->setGroup("Settings"); config->writeEntry( "defaultPage", browser->url().url() ); }
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");
Cuando seguramente ya pensabas que no cambiariamos más el fichero main.cpp, aquí nos encontramos un cambio para generar el diálogo de "about" ("Sobre...") automaticamente.
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;
Aquí inicializamos los argumentos de la linea de comando, pasándoselos junto con el objeto aboutdata al método estático KCmdLineArgs::init.
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.
Ok, ahora podemos echar un vistazo a p9.cpp :
#define TOOLBAR_ID_ADDBOOKMARK 1 #define TOOLBAR_ID_BACK 2 #define TOOLBAR_ID_QUIT 3
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.
Vamos a usar objetos KAction . Esta es una característica radicalmente nueva y potente que simplifica la creación y mantenimiento de interfaces de usuario.
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().
Finalmente, ponemos el nombre de la acción (el nombre debe identificar a la acción, con lo que debe ser único).
Observa que no hemos guardado la acción en ninguna variable, ya que será gestionada por el objeto devuelto por actionCollection()
(void)new KAction(i18n("Add to Bookmarks"), "reload", 0, this, SLOT(bookLocation()), actionCollection(), "add_to_bookmarks"); (void)new KAction(i18n("Back to previous page"), "back", 0, this, SLOT(gotoPreviousPage()), actionCollection(), "back");
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);
Con esta llamada, deshabilitamos el botón de atrás ("back"). Primero cogemos la colección de acciones y le pedimos la acción con nombre "back", entonces llamamos a setEnabled(false) en esta acción para deshabilitarla.
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");
Llamando a createGUI, hacemos que las acciones se conecten a las barras de menú y de herramientas.
Ahora miramos el contenido de p9ui.rc :
<!DOCTYPE kpartgui> <kpartgui name="p9"> <MenuBar> <Menu name="file"><text>&File</text> <Action name="set_default_page"/> </Menu> </MenuBar> <ToolBar fullWidth="true" name="mainToolBar"> <Action name="add_to_bookmarks"/> <Action name="back"/> <Separator/> <Action name="file_quit"/> </ToolBar> </kpartgui>
Este es un fichero XML que contiene la definición del entorno de usuario para p9:
<kpartgui name="p9">
Esto especifica que estamos creando la interfaz de la aplicación p9.
<MenuBar> <Menu name="file"><text>&File</text> <Action name="set_default_page"/> </Menu> </MenuBar>
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>
Ahora, definimos la barra de herramientas (que llamaremos "mainToolBar" ya que podemos tener más de una barra de herramientas). Sólo escribimos las acciones que queremos que aparezcan en la barra de herramientas, con algunos separadores (opcional).
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.
Como una nota interesante, compararemos el número de lineas de p7.cpp con p9.cpp :
wc p7/p7.cpp p9/p9.cpp 124 324 3454 p7/p7.cpp 102 238 2687 p9/p9.cpp
En p9.cpp tenemos 22 lineas menos que en p7.cpp !!!
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 ! )
Si quieres saber más sobre KXMLGUI, no olvides leer el excelente tutorial de Kurt Granroth en http://developer.kde.org/documentation/tutorials/xmlui/preface.html .
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.
Gracias por leer este tutorial,
Siguiente Anterior Tabla de Contenidos