You are on page 1of 38

El tutorial del desarrollo visual

Antonio Larrosa Jimnez Traductor: Miguel Revilla Rodrguez Revision 1.00.01 (2003-12-27) Copyright 2003 Antonio Larrosa Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.1 or any later version published by the Free Software Foundation; with no Invariant Sections, with no Front-Cover Texts, and with no Back-Cover Texts. A copy of the license is included in the section entitled "GNU Free Documentation License". Visual Tutorial es un tutorial que mostrar al lector la forma de crear aplicaciones visualmente utilizando KDevelop y Qt Designer. Crearemos una aplicacin que nos permitir abrir una imagen y teirla de diferentes colores. Posteriormente cambiaremos el interfaz para permitir al usuario seleccionar tambin la intensidad de un canal alfa. Table of Contents 1. Introduccin 2. Cmo descargar e iniciar KDevelop y Designer Requisitos Cmo ejecutar KDevelop 3. Creacin de la aplicacin Creacin de un nuevo proyecto Cmo compilar lo generado por KDevelop Comprensin de la estructura bsica 4. Primeros cambios al cdigo fuente Eliminacin de las partes que no utilizaremos 5. Uso del diseador Cmo aadir un archivo .ui Uso del diseador Integracin del diseador con KDevelop 6. Cmo hacer que el widget generado por el diseador funcione Aadir nombres, slots y conexiones Implementacin de los slots 7. Eliminacin del parpadeo utilizando un temporizador 8. Carga y manipulacin de un mapa de pixels Carga de un mapa de pixels Mezcla de colores 9. Modificacin del interfaz de usuario existente Aadir el deslizador de alfa Aadir el botn de seleccin de color 10. Aadir las implementaciones de los nuevos mtodos Soporte para el deslizador alfa Soporte para el botn de color

11. Algunos cambios aqu y all Correccin del botn de color. Correccin de mapa de pixels inicial 12. Aadir ms caractersticas Posibilidad de guardar el resultado Aadir soporte para arratrar y soltar A. Crditos y licencia

Chapter 1. Introduccin
Visual Tutorial es un tutorial que mostrar al lector la forma de crear aplicaciones visualmente utilizando KDevelop y Qt Designer. Crearemos una aplicacin que nos permitir abrir una imagen y teirla de diferentes colores. Posteriormente cambiaremos el interfaz para permitir al usuario seleccionar tambin la intensidad de un canal alfa. Este tutorial complementa mi otro trabajo sobre desarrollo en KDE, que puede encontrar en developer.kde.org. Adems hay otro excelente tutorial sobre KDevelop y Designer, realizado por Anne-Marie Mahfouf que se encuentra en women.kde.org. Sobre el orden en el que se deberan leer los tutoriales mencionados y estos otros, yo nicamente dir una cosa: cuantos ms lea, mejor :), y si tambin consulta la documentacin de las clases que utiliza, mejor que mejor. Si desea descargar el cdigo de este tutorial junto a la documentacin y dems elementos, puede hacerlo en la pgina web. Puede seguir este tutorial haciendolo todo usted mismo de la forma que se indica, o puede descargar cada paso del tutorial de esa pgina. En el segundo caso, obtendr cada paso en un directorio diferente y tendr que abrir diferentes proyectos de KDevelop para cada uno de ellos. Listo? Vamos a empezar.

Chapter 2. Cmo descargar e iniciar KDevelop y Designer


Requisitos
En este tutorial necesitaremos, adems del compilador, alguna otra aplicacin como KDevelop o Qt/Designer. Se dar cuenta de que KDevelop tiene una lista de requisitos y/o aplicaciones recomendadas, as es que posible que tambin tenga que instalar esas. Puede descargar KDevelop de www.kdevelop.org y muy probablemente su distribucin incluya Qt/Designer en algn paquete, normalmente denominado qt-devel, qt3-devel o qt-designer. Si no lo encuentra, puede descargarlo de trolltech.com Necesitar KDE 3.x, lo que significa que tambin le har falta Qt 3.x. La ltima versin estable de KDevelop en el momento de redaccin de este texto es la 2.1.5, mientras que la versin de desarrollo de KDevelop 3.0 (con nombre clave gideon) se encuentra en estado alpha3. Este documento trata mayormente sobre gideon, pero tambin se puede utilizar como referencia en el manejo de KDevelop 2.1.5.

Cmo ejecutar KDevelop


Una vez que haya instalado todos los paquetes, puede iniciar gideon (kdevelop) desde el men K o desde una ventana de konsole. Es necesario configurar elementos bsicos (como la ubicacin de la documentacin, etc.). Para hacerlo, basta con que siga las instrucciones en pantalla. Una vez terminada la configuracin, podr ver una ventana similar a esta:

Ahora ya estamos listos para desarrollar una aplicacin.

Chapter 3. Creacin de la aplicacin


Creacin de un nuevo proyecto
Ahora vamos a crear un nuevo proyecto para nuestra aplicacin. Para hacerlo, seleccionamos Proyecto/Nuevo proyecto. Se abrir un asistente en el que seleccionaremos las opciones del proyecto.

Podemos utilizar KDevelop para desarrollar en C++, C, Java, Python o PHP. El asistente de "nuevo proyecto" se puede utilizar para crear una aplicacin sencilla que sirve como estructura de base para la aplicacin final. Si decide crear una aplicacin C++, puede generar (en el momento de redaccin de este tutorial): una aplicacin de KDE, una aplicacin de KDE que utilice KParts, un mdulo del Centro de control, un servicio DCOP, un salvapantallas, una extensin KHTMLPart, un esclavo KIO (implementacin de un protocolo), un mdulo de KOffice, una extensin de Kate, un applet de Kicker, una extensin del panel de navegacin de Konqueror, una aplicacin KDE sencilla, un mdulo de KDevelop, varios tipos de aplicaciones Qt y una aplicacin para terminal basada en C++. Si decide crear una aplicacin en C, KDevelop podr generar la estructura para una aplicacin para GameBoyAdvance, una aplicacin GNOME (s, es posible utilizar KDevelop para crear aplicaciones para GNOME), o smplemente una aplicacin para terminal basada en C.

Si prefiere utilizar Java, KDevelop podr ayudarle con las estructuras de un proyecto de Ant, una aplicacin de KDE o una extensin de KDevelop, todas ellas con Java (incluso podr utilizar el depurador de aplicaciones Java integrado). Por ltimo, tambin puede utilizar el asistente para crear un simple guin de PHP, una aplicacin Python Qt o un sencillo guin de Python. Al pinchar en el botn Siguiente, se mostrarn las opciones sobre el sistema de control de versiones que utilizaremos en el proyecto. Para simplificar nuestro ejemplo, seleccionaremos Ninguno y continuaremos en las siguientes pginas, donde podremos cambiar la plantilla de la cabecera de los archivos .h y .cpp. Fjese en que KDevelop ha utilizado el ao actual, su nombre y su direccin de correo electrnico completos, por lo que probablemente no necesitar modificar nada. Al menos nosotros no modificaremos nada, as que bastar con pinchar en el botn Finalizar. Ahora KDevelop generar el proyecto, y estaremos listos para comenzar a desarrollar nuestra aplicacin sobre la estructura bsica. La siguiente instantnea muestra una ventana vaca de KDevelop con el interfaz IDEAL. Puede cambiar el interfaz de usuario y poner alguno de los modos clsicos a travs del men Preferencias/Configurar Gideon, en la seccin Interfaz de usuario, pero en este tutorial utilizaremos el interfaz IDEAL. Tenga en cuenta que las caractersticas de KDevelop son independientes del interfaz que se utilice, as que puede seleccionar aquel en el que se encuentre ms cmodo.

Cmo compilar lo generado por KDevelop


Ahora que ya est hecha una buena parte de la aplicacin, vamos a compilarla. Pinche en Construir/Construir proyecto, tras una corta espera (y en el caso de que no se produzca ningn error debido a la falta de alguno de los paquetes necesarios), obtendr una aplicacin compilada que podr ejecutar por medio de Construir/Ejecutar progama, de forma similar a lo mostrado en la siguiente instantnea.

La aplicacin tambin tiene los mens estndar, y un dalogo de preferencias preparado para que aada las opciones que considere oportuno. Algo parecido a esto:

Comprensin de la estructura bsica


Vamos a revisar ahora el cdigo utilizado por KDevelop. Pinche en la etiqueta Clases que podr encontrar a la izquierda para abrir la lista de clases de nuestro proyecto.

Puede ver que nuestro proyecto tiene 5 clases, TheTinter (que hereda de KMainWindow), TheTinterView (que es el widget central de la aplicacin donde se mostrarn nuestros documentos), TheTinterPreferences (el dilogo de preferencias), y TheTinterPrefPageOne y TheTinterPrefPageTwo (que son dos pginas de configuracin para diferentes opciones del dilogo de preferencias). En primer lugar, vamos a echar un vistazo a la clase TheTinter. Esta clase es la ventana principal de nuestra aplicacin y contiene el men, las barras de herramientas, etc. TheTinter contiene algunos mtodos interesantes que sin duda utilizaremos: load(const KURL &url) es el mtodo al que se llamar cuando el usuario quiera cargar un documento. Tenga en cuenta que en KDE se recomienda utilizar la clase KURL para almacenar la url de un documento. De esta forma, no nos preocupar si el documento es local, est en un servidor ftp, si se accede por ssh, etc. La biblioteca KIO se ocupar de todo. readProperties(KConfig *) y saveProperties(KConfig *) se utilizan para cargar y guardar el estado de la aplicacin (por ejemplo, los documentos cargados) cuando se abre o se cierra la sesin de KDE. De esa forma, los usuarios podrn cerrar la sesin y, cuando vuelvan a acceder al sistema, encontrarn la aplicacin en el mismo estado en que la dejaron. setupActions() se utiliza para crear las acciones y conectarlas con nuestros slots (si no le resultan familiares conceptos como seales o slots, puede consultar la documentacin de Qt o mi otro tutorial). Tenga en cuenta que la mayora de las acciones utilizadas en la aplicacin de plantilla son estndar, de forma que el mtodo contiene cdigo del tipo KStdAction::open(this, SLOT(fileOpen()), actionCollection()); con el fin de crear una accin estndar para la entrada de men Archivo/Abrir que, al ser activada, emite una seal que se conecta a nuestro slot fileOpen.

dragEnterEvent(QDragEnterEvent *event) se utiliza para indicarle al subsistema de arrastrar y soltar si aceptamos o no un evento de arrastrar y soltar que el usuario est arrastrando (todava no soltando) sobre nuestra ventana, mientras que se llama adropEvent(QDropEvent *event) cuando el usuario suelta algo sobre nuestra ventana, por lo que es ah donde debemos procesar el evento de soltar. TheTinterView es el widget central de nuestra aplicacin, es decir, el widget que se coloca debajo de la barra de herramientas y sobre la barra de estado. En la plantilla de KDevelop, esta clase crea un componente KPart capaz de leer documentos HTML, pero eliminaremos la mayor parte de esto en el siguiente paso del tutorial, ya que no nos resulta necesaria tanta complejidad.

Chapter 4. Primeros cambios al cdigo fuente


Eliminacin de las partes que no utilizaremos
Lo primero que haremos ser eliminar el cdigo que no resulte necesario para nuestra aplicacin. Por ejemplo, no utilizaremos el comando de impresin, con lo que podemos quitar la linea #include <kprinter.h> la inicializacin de m_printer(0) en el constructor de TheTinter, la linea KStdAction::print(this, SLOT(filePrint()), actionCollection()); que aada una accin estandar para imprimir, y la funcin TheTinter::filePrint, tanto del fichero .cpp como del .h as como la variable m_printer de la misma clase y la funcin TheTinterView::print . Tampoco abriremos pginas HTML, con lo que podemos eliminar el cdigo que busca una componente para tal fin y la utiliza. Este cdigo est en el constructor de TheTinterView, que ser suficiente con dejarlo con las tres primeras lineas: .
// setup our layout manager to automatically add our widgets QHBoxLayout *top_layout = new QHBoxLayout(this); top_layout->setAutoAdd(true);

Adems, podemos eliminar la variable m_html y algunas funciones que ya no usaremos de TheTinterView, como son signalChangeStatusbar, signalChangeCaption, slotOnURL y slotSetTitle (recuerda eliminarlas tanto del .h como del .cpp). Al eliminar la variable m_html, las funciones currentURL y openURL ya no compilarn, no te preocupes, pues las vamos a reimplementar dentro de poco, simplemente djalas vacas (o con un
return "";

en el caso de currentURL). Como hemos eliminado las seales de TheTinterView, podemos tambin eliminar el cdigo que las conectaba en el constructor de TheTinter:
// allow the view to change the statusbar and caption connect(m_view, SIGNAL(signalChangeStatusbar(const QString&)), this, SLOT(changeStatusbar(const QString&))); connect(m_view, SIGNAL(signalChangeCaption(const QString&)), this, SLOT(changeCaption(const QString&)));

Tampoco vamos a crear un nuevo documento, con lo que podemos eliminar la creacin de la accin estandar openNew con la llamada a KStdAction::openNew igual que hicimos con print, junto con la funcin fileNew tanto del .h como del .cpp .

Para terminar, podemos eliminar el siguiente trozo de cdigo ya que como el comentario que lo acompaa dice, no hace nada til y est slo para ilustrar como insertar un men propio de nuestra aplicacin.
// this doesnt do anything useful. its just here to illustrate // how to insert a custom menu and menu item KAction *custom = new KAction(i18n("Cus&tom Menuitem"), 0, this, SLOT(optionsPreferences()), actionCollection(), "custom_action");

El resultado es el paso 1, s1, del tutorial. En primer lugar, hemos eliminado la mayor parte del constructor de TheTinterView, ya que no lo vamos a utilizar para ver HTML. Adems, hemos eliminado los mtodos innecesarios de esa clase, junto con las seales y los slots. Si posteriormente necesitamos ms seales o slots, los aadiremos individualmente.

Chapter 5. Uso del diseador


Cmo aadir un archivo .ui
En primer lugar, aadiremos un nuevo archivo .ui (interfaz de usuario) a nuestro proyecto. Los archivos .ui se crean con el diseador de Trolltech para generar interfaces de usuario para nuestra aplicacin. Para hacerlo, abra la pestaa Nuevo archivo en la parte izquierda, y seleccione Un nuevo widget. El nombre del archivo que aadiremos es centralviewbase.ui. Se le mostrar un dilogo en el que configurar el nuevo archivo que se ha aadido al proyecto (utilizando el Gestor de automake). Seleccionaremos Aadir nuevos archivos a nuestro destino activo, y a continuacin marcaremos la casilla de la parte inferior para utilizar siempre el destino activo sin necesidad de ms preguntas.

Esto abrir una ventana del diseador con un widget vaco, dispuesto para comenzar el diseo.

Uso del diseador


En este punto tenemos que disear el aspecto de nuestra aplicacin. Haremos que la imagen aparezca en la parte superior de la ventana, y habr tres deslizadores en la parte inferior, para seleccionar los componentes rojo, verde y azul del color con el que teiremos el mapa de pixels. As que nuestro primer paso ser seleccionar un Pixmap label (de la caja de herramientas Display) y crearlo de forma que cubra la parte superior de nuestra ventana.

A continuacin, aadiremos, en la parte de abajo, tres deslizadores y sus correspondientes etiquetas.

Las etiquetas contendrn el texto Rojo:, Verde: y Azul:.

En el caso de que se est preguntando por qu cada etiqueta aparece en el color indicado por su texto, de dir que (obviamente) no es algo automtico. Dejo como ejercicio para lector la bsqueda en los mens del dilogo Edit Text de la forma de cambiar el color. Adems, con la intencin de embellecer un poco el widget, he aadido un Marco (en la pestaa Containers) que contiene los deslizadores y las etiquetas de texto.

Como ya habr notado, los elementos no han quedado correctamente alineados. Aunque usted crea que puede hacerlo mejor que yo, compruebe el aspecto del resultado mediante la opcin Preview Form en el men Preview y redimensione la ventana. Los elementos dentro de nuestro widget estn colocados en una posicin fija y con un tamao fijo. Est claro que esa no es nuestra intencin, as que aadiremos una disposicin que se ocupar de la ubicacin y el tamao correctos de los elementos. En primer lugar, pinche en el marco que contiene los deslizadores y las etiquetas, y pinche en el botn Lay out in a grid (el que tiene una rejilla verde de 3x3). De esta forma se crear una disposicin de rejilla para el contenedor seleccionado actualmente. El resultado es:

Intente redimensionar el marco contenedor y comprobar que ahora los elementos tienen siempre su posicin y tamao correctos. Bien, la parte de los botones ya est prcticamente acabada, ahora debemos darnos cuenta de que en el nivel ms alto slo hay dos elementos, el widget grfico y el marco contenedor. Estos dos elementos deben estar dispuestos verticalmente, as que pincharemos en una zona vaca del widget principal, y seleccionaremos Lay out vertically en el men (o el botn con tres rectngulos verdes apilados en la barra de herramientas). El resultado es:

Esta vez seguro que pensar que el resultado es extrao, verdad? Bueno, en realidad no lo es. Ambos elementos deben estar uno sobre el otro, pero el subsistema de disposicin no tiene forma de saber cul de los elementos debe ocupar ms espacio que el otro, as que los distribuye de forma proporcional. Para solucionar esto, cambiaremos la poltica de tamao del elemento grfico, de forma que su tipo de tamao vertical sea MinimumExpanding. Vaya a la ventana del editor de propiedades, pinche en el botn "ms" a la izquierda de la propiedad sizePolicy y cambie la opcin vSizeType a MinimumExpanding. El resulta ahora es exactamente lo que buscbamos:

Ya estamos preparados para volver a KDevelop y comerzar a utilizar esta clase. Pero, primero, debemos darle un nombre. Pinche en Object Explorer del widget principal o en cualquier parte vaca del mismo (cerca de los bordes suele haber un pequeo margen que puede utilizar). Despus edite la propiedad name y utilice algo como CentralViewBase. El diseador se utiliza normalmente para crear un widget que es la clase base de otra clase en la que se sobrecargan los mtodos importantes. Normalmente se utilizan dos esquemas de nomenclatura, llamar a la clase base XXXBase y a la clase de implementacin XXX o llamar a la clase base XXX y a la clase de implementacin XXXImpl. Prefiero la primera forma, as que esa es la que utilizaremos.

Integracin del diseador con KDevelop


KDevelop generar automticamente las opciones necesarias del Makefile para que uic (compilador de interfaz de usuario) produzca los archivos centralviewbase.h y centralviewbase.cpp con el cdigo fuente necesario. Todo lo que hay que hacer ahora es aadir una nueva clase (Proyecto | Nueva clase...). En el dilogo que aparecer, el nombre de la nueva clase ser CentralView y activaremos la casilla Generar clase hija de QWidget, como la clase heredar (indirectamente) de QWidget, de hecho, vamos a cambiar QWidget (que KDevelop haba aadido como Clase base) a CentralViewBase.

Dependiendo de la versin de Gideon que est utilizando, es posible que necesite ir a la pestaa Informacin avanzada y modificar manualmente el constructor para que la llamada sea a CentralViewBase en vez de a QWidget (sustituyendo el uno por el otro). Ahora aadiremos una variable protegida class CentralView * m_view; a TheTinterView, y despus una lnea m_view=new CentralView(this, "centralview"); al final del constructor, para tener un widget CentralView como nico hijo de TheTinterView. El primer parmetro (this) significa que el widget CentralView ser un hijo del widget this, lo que significa que aparecer en la pantalla dentro del widget this. El segundo parmetro es un nombre de metadatos para el objeto, y es posible utilizar el nombre que se quiera. En este momento estamos preparados para compilar s2 y obtener una aplicacin que utilice el widget que hemos creado en el diseador.

Chapter 6. Cmo hacer que el widget generado por el diseador funcione


Como ya habr notado, el widget se muestra pero no hace nada, as que le haremos trabajar un poco.

Aadir nombres, slots y conexiones


En primer lugar, abra con el diseador el archivo centralviewbase.ui, utilizando la pestaa Grupos de archivos (a la izquierda), dentro del bloque Interfaz de usuario. Dentro del diseador, seleccione cada uno de los widgets que se utilizarn desde la aplicacin, y deles un nombre. Yo he llamado m_label al elemento grfico, m_red al deslizador del componente rojo, y m_green y m_blue a los otros dos, respectivamente. En caso de que usted no est familiarizado con el sistema de nomenclatura m_nombre, puedo decirle que este sistema es el mejor invento desde el pan. Hace que el cdigo sea mucho ms sencillo de leer y mucho ms comprensible por otros desarrolladores. A modo de normas generales del esquema de nomenclatura que utilizo, cito algunas reglas: 1. Siempre se nombra a los componentes en la forma ?maximumSize?, y no como ?MaximumSize? o ?maximum_size?. La nica excepcin es en los nombres de las clases, que deben empezar por mayscula (?MaximumSize?). 2. Siempre se coloca "m_" como prefijo de los nombres de las variables miembro. De esta forma siempre se sabe si una variable es local a un mtodo o si depende de clase en la que se encuentra. 3. No utilice nunca getXXX() como nombre para acceder a un mtodo de una clase (lo mtodos normales que nicamente hacen { return m_XXX; };). Es mucho mejor utilizar siempre el nombre de la propiedad, algo como QFont font() const { return m_font; };. De esta forma, el mtodo set correspondiente, debera ir precedido por ?set?, de forma que la pareja

get/set de una propiedad quede en la forma font() y setFont(const QFont &) en nuestro ejemplo. 4. Las variables booleanas deben tener siempre nombres positivos, en vez de cosas como bool m_noBackground. Es mucho ms fcil entender el uso de la variable si se llama m_background y despus utilizar if (m_background) ... en vez de if (!m_noBackground) .... Bien, ahora nuestro widget tiene nombres que podremos utilizar en la implementacin de CentralView (ya que CentralView hereda de CentralViewBase), pero an no es suficiente. Tambin debemos aadir slots a CentralViewBase, y, como son mtodos virtuales, los sobrecargaremos en CentralView. Vamos a hacerlo para que lo entienda mejor. En primer lugar, abra el dilogo Slots del men Edit o pulsando el botn derecho del ratn en el widget principal. Pinche en New Function y dele el nombre setRed(int v), cree otros dos slots llamados setGreen(int v) y setBlue(int v). Haremos que se llame a estos slots cada vez que el usuario modifique los deslizadores rojo, verde o azul. Para hacerlo, salga del dilogo y selecione Connect Signals/Slots en el men Tools, en el icono de la flecha de la barra de herramientas, o pulsando F3. Despus pinche con el botn izquierdo del ratn sobre el deslizador rojo y, sin soltarlo, muvalo hacia el widget principal (por ejemplo hacia uno de los bordes). Hay un rectngulo magenta que muestra los widgets que sern conectados. Una vez que se ha determinado el deslizador rojo como origen y el widget principal como destino, suelte el botn y se le mostrar un dilogo en el que configurar qu seal del elemento de origen se conectar con qu slot del widget de destino. En el dilogo, pinche en el campo No Signal y seleccione la seal valueChanged(int). Despus, seleccione el slot setRed(int). Conecte tambin las seales correspondientes de m_green y m_blue con setGreen(int) y setBlue(int).

Ahora, conviene recordar que los componentes de color van normalmente de 0 a 255 (y eso es lo que dice la documentacin de QColor), as que lo propio sera que el deslizador nos proporcionase valores entre 0 y 255. Para hacerlo, seleccionamos cada deslizador y fijamos su propiedad maxValue (en el editor de propiedades) a 255.

Guarde el interfaz de usuario y cierre el diseador para volver a KDevelop.

Implementacin de los slots


De vuelta en KDevelop, abra la pestaa Clases, pinche con el botn derecho sobre la clase CentralView y seleccione Nuevo mtodo en el men emergente. Aqu vamos a reimplementar setRed, de forma que se utilice este en vez del que aparece en CentralViewBase. As que el nombre es setRed, el tipo es void (hace referencia al tipo del valor devuelto), y le incluimos un parmetro de tipo int, para hacerlo coincidir con el slot que hemos creado en el diseador. Tendremos que realizar la misma operacin otras dos veces, para crear los slots de setGreen y setBlue.

Ahora vamos a aadir una nueva variable, que ser el color utilizado en TheTinter, y que tendr tipo QColor. Para hacerlo, pinche nuevamente con el botn derecho en el nombre de la clase en la vista de rbol y seleccione Aadir atributo. Utilice como tipo QColor y como nombre m_color. Para ser correctos, tambin la haremos protegida.

Ahora vamos a implementar los slots que acabamos de aadir para modificar el valor de m_color. Utilizaremos el siguiente cdigo:
void CentralView::setRed(int v) { m_color.setRgb( v, m_color.green(), m_color.blue() ); updatePixmap(); }

que utiliza el nuevo valor del componente rojo y deja los otros dos sin cambios. Tambin llamamos al mtodo updatePixmap() que actualizar el mapa de pixels con el color m_color. Ahora vamos a aadir el mtodo y a implementarlo.
void CentralView::updatePixmap() { QPixmap pixmap(128,128); pixmap.fill(m_color); m_label->setPixmap(pixmap); }

La primera lnea crea un mapa de pixels de 128x128, y en la segunda lnea se rellena del color. La tercera lnea coloca el mapa de pixels en el elemento de imagen. El tamao 128x128 es un tanto arbitrario, ya que el elemento grfico lo redimensionar para que llene todo el rea (puede cambiar esto, si lo desea, en las propiedades). Este es nuestro cdigo fuente para s3, pronto haremos que cargue una imagen y la tia con el color, en vez de que nicamente aparezca este rellenando un rectngulo, pero, antes, trataremos de hacer que no parpadee (fjese en que si mueve los deslizadores muy rpido, se produce mucho parpadeo). Ese ser nuestro siguiente objetivo.

Chapter 7. Eliminacin del parpadeo utilizando un temporizador


Los objetos QTimer se utilizan cuando se desea hacer algo en un momento determinado, o cada x milisegundos. Para hacerlo, puede iniciar un temporizador que emita una seal en un momento concreto. Despus conecta esa seal a un slot para que realice el trabajo necesario. En primer lugar, aadimos una variable protegida QTimer m_timer a CentralView (no olvide la lnea de inclusin #include <qtimer.h> correspondiente). En el constructor de CentralView, conectamos la seal timeout() que emite m_timer al slot updatePixmap() que hemos creado en s3. Para hacerlo, utilizaremos connect(&m_timer, SIGNAL(timeout()), this, SLOT(updatePixmap()));. Ahora cambiamos los tres slots, setRed, setGreen y setBlue para que inicien el temporizador en vez de que llamen directamente a updatePixmap. m_timer.start(200,true); inicia un temporizador de un nico ciclo (que nicamente emitir una seal y se detendr) con un tiempo de espera de 200 ms. Cada vez que iniciemos un temporizador mientras haya otro funcionando (iniciamos el segundo temporizador antes de que hayan pasado los 200 ms), se detendr el ms antiguo. Eso significa que si el usuario mueve el deslizador muy rpido, en vez de producirse muchas llamadas a updatePixmap(), esperaremos a que el usuario deje de modificar los valores durante 200 ms antes de hacer el cambio real. Eso nos lleva a s4. Ahora que hemos corregido el parpadeo (al menos en gran parte, ya que su correccin completa no entra dentro del objetivo de este tutorial), podemos buscar experiencias ms avanzadas.

Chapter 8. Carga y manipulacin de un mapa de pixels


Carga de un mapa de pixels
En primer lugar, aadiremos una variable miembro de QPixmap llamada m_pixmap a CentralView, adems de un mtodo setPixmap que nicamente har:
void CentralView::setPixmap(const QPixmap &pixmap) { m_pixmap=pixmap; updatePixmap(); }

Ahora vamos a implementar el mtodo que KDevelop cre como futura ubicacin de un mtodo de carga de un documento en TheTinterView.

void TheTinterView::openURL(const KURL& url) { QString tmpFile; if (KIO::NetAccess::download(url, tmpFile)) { // cargar el archivo (el destino es siempre local) m_view->setPixmap( tmpFile ); // y eliminar el archivo temporal KIO::NetAccess::removeTempFile(tmpFile); } }

KIO::NetAccess::download se utiliza para descargar la URL url y guardarla en un archivo temporal local cuya ruta completa se almacena en tmpFile. Si tiene xito, devolver true y el documento ser cargado. Habr notado que CentralView::setPixmap admite un parmetro QPixmap, mientras que nosotros hemos utilizado como parmetro aqu un QString, por qu? Si tiene experiencia en C++ y ha leido un poco la documentacin de QPixmap, sabr que QPixmap tiene un constructor que admite un nico parmetro QString, y, por lo tanto, esa lnea es equivalente a:
QPixmap pixmap( tmpFile ); m_view->setPixmap( pixmap );

He olvidado mencionar que el constructor de QPixmap que obtiene un parmetro QString, carga el mapa de pixels indicado en dicho parmetro. Por ltimo, KIO::NetAccess::removeTempFile(tmpFile); hace algo muy interesante. Elimina el archivo tmpFile nicamente si se trata realmente de un archivo temporal. Conviene resear que en caso de que est tratando de acceder a un archivo local, KIO::NetAccess::download devuelve el archivo real en vez de copiarlo a una ubicacin temporal (lo que sera absurdo) y KIO::NetAccess::removeTempFile no hace nada,por lo que es seguro llamarlo aunque el archivo no sea temporal, porque KIO::NetAccess guarda una lista de los archivos que se han descargado (y son realmente temporales) y los archivos reales que no deben ser eliminados.

Mezcla de colores
Ahora vamos a hacer que CentralView realice operaciones sobre el mapa de pixels. Cambiaremos la implementacin de updatePixmap a:
void CentralView::updatePixmap() { QImage image= m_pixmap.convertToImage(); KImageEffect::blend(m_color, image, 0.5); QPixmap pixmap; pixmap.convertFromImage(image); m_label->setPixmap(pixmap); }

La primera lnea convierte el mapa de pixels a una imagen, que es otra clase que puede albergar imgenes. La diferencia es que QImage almacena las imgenes localmente, mientras que QPixmap lo hace en el servidor X, de forma que habr que utilizar uno un otro dependiendo del objetivo que se quiera lograr. En este caso resultar ms sencillo teir una imagen.

KImageEffect::blend(m_color, image, 0.5); utiliza KImageEffect (de kdelibs/kdefx) para mezclar un color con una imagen. KImageEffect es una clase que contiene mtodos muy tiles para aplicar efectos a imgenes. El primer parmetro es el color, el segundo la imagen (que ser modificada por este mtodo) y el tercero es la intensidad de opacidad. Continuamos volviendo a convertir la imagen a un mapa de pixels y colocndola, al igual que antes, en el elemento grfico.

Esto significa que hemos terminado con s5, y ya podemos abrir imgenes y teirlas, pero an no hemos acabado.

Chapter 9. Modificacin del interfaz de usuario existente


Ahora vamos a cambiar el interfaz de nuestra aplicacin para aadirle un nuevo deslizador que nos permita modificar el componente de opacidad (o alfa) del tinte.

Aadir el deslizador de alfa


En primer lugar, en la pestaa Grupos de archivos, seleccionamos el archivo centralviewbase.ui, y KDevelop abrir el diseador. Lo primero que haremos es romper las disposiciones. Usted se preguntar, romper las disposiciones? S, eso se hace para poder decir: "Deja de manejar las posiciones actuales de los elementos, porque vamos a aadir otros nuevos". Pinche para seleccionar el widget principal, y despus pinche en el icono de la barra de herramientas Break Layout (tambin disponible en el men Layout). Ahora seleccione la caja de agrupacin que contiene los deslizadores y rompa tambin la disposicin de estos. Debe darse cuenta de que las dos disposiciones son independientes entre s. La disposicin del widget principal es una disposicin vertical, que maneja m_label y la caja de agrupacin, y la disposicin de dicha caja, maneja las etiquetas y los deslizadores.

Si intenta aadir un elemento dentro de otro que tiene una disposicin, se le preguntar si desea romper antes la disposicin para poder aadirlo. Pero eso no debera ocurrir ahora puesto que hemos roto las disposiciones manualmente :).

Ahora que hemos roto la disposicin, puede mover hacia arriba los deslizadores y las etiquetas para dejar espacio para el nuevo deslizador que aadiremos. Aada una etiqueta de texto Alfa: y un deslizador al que llamaremos m_alpha (que el usuario podr utilizar para establecer el alfa u opacidad de la mezcla de colores) en la parte de abajo de la caja de agrupacin. Cambie sus propiedades para que el rango de valores de m_alpha vaya de 0 a 100. Seleccione la caja de agrupacin ...

... y pinche en la barra de herramientas para aadir una disposicin de rejilla.

Ahora puede seleccionar el widget principal y aadir la disposicin vertical.

Aadir el botn de seleccin de color


Si queremos mejor todava ms el aspecto de nuestra aplicacim, podemos aadir un botn KColorButton que utilizaremos como forma alternativa de seleccin del color mediante un dilogo de color estndar.

Seleccinelo y adalo en la parte derecha de la caja de agrupacin (pero dentro de ella). Reconoce ese dilogo? S, est preguntando si se debe romper la disposicin o cancelar la operacin. Seleccionaremos romper la disposicin y a continuacin la volveremos a crear.

Podemos aadir una disposicin de rejilla a la caja de agrupacin tal y como est, y el diseador lo colocar correctamente de forma automtica, pero vamos a utilizar otra tcnica con la intencin de aprender algo ms. Deseleccione cualquier widget que pudiera estar seleccionado, y pulsando la tecla ?Ctrl? en el teclado, dibuje un rectngulo que cubra las etiquetas y los deslizadores (pero no la caja de agrupacin entera, ni el KColorButton), de esa forma quedarn seleccionados. Una vez que tenga los 8 elementos seleccionados, pinche en la disposicin de rejilla, y el diseador generar una disposicin de rejilla para eso elementos. Ahora esta disposicin maneja a sus hijos, pero (an) no est manejada por nadie, as que resulta un tanto intil.

Para solucionarlo, seleccionaremos la caja de agrupacin y aadiremos una disposicin horizontal. Esta nueva disposicin manejar la disposicin de rejilla y el widget KColorButton, colocndolos uno junto al otro.

Note la diferencia existente entre seleccionar la caja de agrupacin y aadir una disposicin horizontal y seleccionar la disposicin de rejilla y el KColorButton y aadir entonces esa disposicin horizontal. En el primer caso, la disposicin pertenecer a la caja de agrupacin, de forma que administrar las polticas de tamao y (directa o indirectamente) a todos sus hijos. En el segundo caso, aadiremos una disposicin horizontal independiente. Puede realizar pruebas de estas dos formas de actuar y utilizar la opcin Preview Form para comprobar las diferencias que se produce al redimensionar la ventana. Bien, ahora seleccionaremos el widget principal y aadiremos una disposicin vertical y, de esta forma, hemos hecho que todos los elementos estn manejados por una disposicin, que es la forma correcta de actuacin (quiz debera decir que esta debe ser la forma correcta de actuacin, para resaltar que es como hay que hacerlo para que las aplicaciones funcionen correctamente).

Esto podra ser suficiente para la mayor parte de la gente, pero no para nosotros. Queremos que el widget KColorButton (al que llamaremos m_colorButton) aumente hasta ocupar todo el espacio de la caja de agrupacin. Para ello, le cambiaremos la poltica de tamao. Seleccione m_colorButton y en el editor de propiedades, abra la seccin sizePolicy y cambie vSizeType a Expanding.

Vaya, probablemente no era esto lo que queramos. Hemos observado anteriormente que m_label tena una poltica de tamao de expansin (MinimumExpanding para ser precisos) mientras que la caja de agrupacin no nos importaba (nicamente tena una poltica Preferred). Ahora la caja de agrupacin tambin se ha expandido porque la poltica de m_colorButton es Expanding (por lo que trata de ocupar el mayor espacio vertical posible), y m_label tambin, as que el sistema de disposicin trata de darle a cada uno la mitad del espacio disponible, y eso no es lo que buscamos. Hay una solucin? Desde luego. En casos como este es posible darle a los elementos involucrados distintos factores de expansin para que a uno de ellos se le asigne ms espacio que al otro en caso de que ambos quieran la mayor cantidad de espacio posible. Esto se hace gracias a la propiedad verticalStretch. As que incrementaremos el valor de la propiedad verticalStretch de m_label para que ste sea ms grande y se solucione el problema.

Bien, ahora que el aspecto vuelve a ser el correcto, podemos abrir el editor de slots y aadir dos nuevos, uno para el deslizador alfa (llamado setAlpha(int)) y otro al que se llamar cuando el usuario modifique el color en m_colorButton, al que llamaremos setColor(const QColor &). Es hora de hacer las conexiones. No voy a volver a explicar cmo se hace, puesto que es muy sencillo. Tiene que conectar la seal valueChanged(int) de m_alpha con el slot setAlpha(int) del widget principal. No olvide conectar tambin la seal changed(const QColor &) de m_colorButton con el slot setColor(const QColor &) del widget principal.

Bueno, de momento ya es suficiente, as que guardaremos el archivo .ui, cerraremos el diseador, compilaremos el resultado y lo llamaremos s6. Fjese que, en este caso, no hemos hecho nada con KDevelop, nicamente hemos modificado el interfaz de usuario con el diseador y el hecho de compilar sin haber cambiado nada nos da el nuevo interfaz de usuario (con el cdigo anterior funcionando correctamente). Aadiremos el cdigo del nuevo interfaz en el siguiente paso.

Chapter 10. Aadir las implementaciones de los nuevos mtodos


Soporte para el deslizador alfa
En primer lugar, aadiremos un slot pblico a CentralView llamado setAlpha(int v). La implementacin de este mtodo nicamente llamar a m_timer.start(200,true); para iniciar la actualizacin del mapa de pixels.

Entonces cambiaremos el mtodo updatePixmap para que utilice el valor del deslizador alfa. Cambiamos
KImageEffect::blend(m_color, image, 0.5 );

por
KImageEffect::blend(m_color, image, m_alpha->value()/100.0 );

De forma que el valor resultante est en el intervalo [0,1] (ya que en el diseador hemos establecido en 100 en valor mximo de m_alpha).

Soporte para el botn de color


Vamos a aadir un nuevo mtodo a CentralView llamado setColor.
void CentralView::setColor(const QColor &color) { m_red->setValue(color.red()); m_green->setValue(color.green()); m_blue->setValue(color.blue()); }

Con esto es suficiente para establecer el color, ya que fija el valor de los tres componentes, emitiendo las tres respectivas seales y llamando a setRed, setGreen y setBlue, y, adems, no parpadear puesto que se llama al temporizador cada vez que cambia un valor (gracias a las seales que emiten los deslizadores cada vez que cambia su valor) y, por lo tanto, updatePixmap recibir una nica llamada. Con este cambio tenemos una aplicacin completamente operativa, a la que llamaremos s7.

Chapter 11. Algunos cambios aqu y all


Correccin del botn de color.
Como habr notado, cuando el usuario cambia el color en el botn de color, los deslizadores cambian sus valores, pero esto no ocurre en la situacin inversa. Vamos a arreglarlo.

Aadiremos m_colorButton->setColor(m_color); a CentralView::updatePixmap y as haremos que m_colorButton actualice su valor al mismo tiempo que se actualiza el mapa de pixels. Quiz quiera actualizar m_colorButton cada vez que cambie uno de los deslizadores, pero si lo hace, podra tener problemas y posiblemente tendra que utilizar QObject::blockSignals para bloquear las seales de los deslizadores o de m_colorButton, as que lo actualizaremos en updatePixmap para estar seguros del correcto funcionamiento.

Correccin de mapa de pixels inicial


En primer lugar, vamos a eliminar el logotipo de Qt que utiliza m_label de forma predeterminada. Para hacerlo, abra centralviewbase.ui en el diseador, seleccione la etiqueta y elimine el contenido de la propiedad pixmap pinchando en la pequea flecha roja (aparece al seleccionar la propiedad).

En cualquier caso, queremos que haya algo dibujado al comenzar. As que aadiremos una llamada a updatePixmap(); en el constructor de CentralView. Aun as, m_pixmap est vaco al iniciar TheTinter, y seguimos queriendo que aparezca algo. Vamos a modificar la funcin updatePixmap para:
void CentralView::updatePixmap() { m_colorButton->setColor(m_color); QPixmap pixmap; if (!m_pixmap.isNull()) { QImage image= m_pixmap.convertToImage(); KImageEffect::blend(m_color, image, m_alpha->value()/100.0 ); pixmap.convertFromImage(image); } else { pixmap = QPixmap(128,128); pixmap.fill(m_color); } m_label->setPixmap(pixmap); }

De esta forma, en primer lugar comprobamos si m_pixmap es vlido (no es nulo) y, en ese caso, continuamos con el funcionamiento normal. En caso de que m_pixmap no haya sido inicializado, haremos lo que se vio en los primeros pasos del tutorial y pintaremos un color.

Chapter 12. Aadir ms caractersticas


Posibilidad de guardar el resultado
Ahora queremos guardar la imagen teida en un archivo. Qu podemos hacer? En primer lugar aadiremos un mtodo de acceso a CentralView que devuelva el mapa de pixels teido (el mapa de pixels que se encuentra en m_label):
QPixmap *tintedPixmap() const { return m_label->pixmap(); };

En TheTinterView, aadiremos un nuevo mtodo junto a openURL(const KURL &url), adivina el nombre? Correcto, saveURL(const KURL &url) :) Este mtodo hace:
void TheTinterView::saveURL(const KURL &url) { QString tmpFile; KTempFile *temp=0; if (url.isLocalFile()) tmpFile=url.path(); else { temp=new KTempFile; tmpFile=temp->name(); } m_view->tintedPixmap()->save( tmpFile, "PNG"); if (temp) { KIO::NetAccess::upload(tmpFile, url, this); temp->unlink(); delete temp; } }

En caso de que la URL sea un archivo local, lo utilizaremos como archivo en el que guardar el mapa de pixels teido en formato PNG. Si no es un archivo local, crearemos un archivo temporal y guardaremos ah la imagen en formato PNG y, a continuacin, cargaremos el archivo temporal en la URL especificada por el usuario, eliminaremos el archivo temporal y destruiremos el objeto que habamos creado para ello.

Aadir soporte para arratrar y soltar


Aqu no hay mucho que hacer, ya que KDevelop aadi automticamente desde el principio el cdigo necesario para aceptar operaciones de arrastrar y soltar con URIs (equivalentes, de momento, a URLs), y como hemos utilizado KIO para cargar nuestras imgenes, ya es posible arrastrar una imagen desde Konqueror a TheTinter y ser descargada. Para desarrollar algo sobre arrastrar y soltar, aadiremos soporte para imgenes. La diferencia con las URIs es que los eventos de arrastrar y soltar de una imagen contienen la informacin de la propia imagen, mientras que en el caso de las URLs, estas nicamente contienen la direccin de la imagen.

El cdigo que utilizaremos es:


void TheTinter::dragEnterEvent(QDragEnterEvent *event) { // accept uri drops only event->accept(QUriDrag::canDecode(event) || QImageDrag::canDecode(event)); }

Esto ser llamado cada vez que recibamos un evento de arrastrar y soltar en nuestra aplicacin. Le dice al subsistema de arrastrar y soltar si se debe aceptar o rechazar la operacin.
void TheTinter::dropEvent(QDropEvent *event) { // esta es una implementacin muy simple de un evento de soltar. nicamente // aceptaremos una URL. el cdigo de arrastrar y soltar de Qt puede hacer // *mucho* ms, as que, por favor, consulte la documentacin QStrList uri; // comprobar si se puede decodificar la URI. si no, ignorarla if (QUriDrag::decode(event, uri)) { // correcto, tenemos una URI. la procesamos QString url, target; url = uri.first(); // cargamos el archivo load(KURL(url)); return; } QPixmap pixmap; if (QImageDrag::decode(event, pixmap)) { m_view->openPixmap(pixmap); } }

Este cdigo se ejecutar cada vez que el usuario suelte sobre nuestra ventana cualquier cosa que est arrastrando. En primero lugar tratamos de comprobar si el evento es un evento URI. Si la primera parte tiene xito, extraemos la primera URL de la (posible) lista de URIs que contiene el evento, y ejecutamos esa URL. Si no se trataba de un evento URI, tratamos de decodificarlo como un evento de imagen (utilizando QImageDrag::decode). Si es correcto, abrimos esa imagen. Hay que tener en cuenta que TheTinterView::openPixmap todava no existe, as que aadimos ese mtodo y lo implementamos de esta forma:
void TheTinterView::openPixmap(const QPixmap& pixmap) { m_view->setPixmap( pixmap ); }

Y esto es s9, nuestra aplicacin final. Espero que haya disfrutado del tutorial y haya aprendido con l. Tambin deseo poder ver publicadas muy pronto sus aplicaciones de KDE :). Un saludo,

Antonio Larrosa Jimnez <larrosa@kde.org>

Appendix A. Crditos y licencia


El tutorial de desarrollo visual en KDE y TheTinter tienen Copyright (c) 2003 Antonio Larrosa Jimnez <larrosa@kde.org> This documentation is licensed under the terms of the GNU Free Documentation License. TheTinter est publicado bajo los trminos de la Licencia pblica general GNU. Me gustara agradecer a las siguientes personas su ayuda en la creacin de este tutorial: A Lauri Watts por explicar el docbook y crear un marco de trabajo excepcional para la documentacin de KDE (y por responder a mis preguntas sobre docbook hasta altas horas en el IRC :) ). A Anne-Marie Mahfouf por explicarme algunas etiquetas del docbook y por escribir otro excelente tutorial sobre KDevelop y el diseador, que podr encontrar aqu. A Harald Fernengel y Roberto Raggi del equipo de KDevelop por solucionar rpidamente algunos problemas que he encontrado en Gideon al hacer este tutorial. A Miguel Revilla, del equipo de traduccin de KDE, por traducir este tutorial en menos un da para que me diera tiempo a usar la versin en espaol en Imagintica.

You might also like