El tutorial del desarrollo visual

Antonio Larrosa Jiménez Traductor: Miguel Revilla Rodríguez 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 aplicación que nos permitirá abrir una imagen y teñirla de diferentes colores. Posteriormente cambiaremos el interfaz para permitir al usuario seleccionar también la intensidad de un canal alfa. Table of Contents 1. Introducción 2. Cómo descargar e iniciar KDevelop y Designer Requisitos Cómo ejecutar KDevelop 3. Creación de la aplicación Creación de un nuevo proyecto Cómo compilar lo generado por KDevelop Comprensión de la estructura básica 4. Primeros cambios al código fuente Eliminación de las partes que no utilizaremos 5. Uso del diseñador Cómo añadir un archivo .ui Uso del diseñador Integración del diseñador con KDevelop 6. Cómo hacer que el widget generado por el diseñador funcione Añadir nombres, slots y conexiones Implementación de los slots 7. Eliminación del parpadeo utilizando un temporizador 8. Carga y manipulación de un mapa de pixels Carga de un mapa de pixels Mezcla de colores 9. Modificación del interfaz de usuario existente Añadir el deslizador de alfa Añadir el botón de selección de color 10. Añadir las implementaciones de los nuevos métodos Soporte para el deslizador alfa Soporte para el botón de color

11. Algunos cambios aquí y allá Corrección del botón de color. Corrección de mapa de pixels inicial 12. Añadir más características Posibilidad de guardar el resultado Añadir soporte para arratrar y soltar A. Créditos y licencia

Chapter 1. Introducción
Visual Tutorial es un tutorial que mostrará al lector la forma de crear aplicaciones visualmente utilizando KDevelop y Qt Designer. Crearemos una aplicación que nos permitirá abrir una imagen y teñirla de diferentes colores. Posteriormente cambiaremos el interfaz para permitir al usuario seleccionar también la intensidad de un canal alfa. Este tutorial complementa mi otro trabajo sobre desarrollo en KDE, que puede encontrar en developer.kde.org. Además 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 deberían leer los tutoriales mencionados y estos otros, yo únicamente diré una cosa: cuantos más lea, mejor :), y si también consulta la documentación de las clases que utiliza, mejor que mejor. Si desea descargar el código de este tutorial junto a la documentación y demás elementos, puede hacerlo en la página web. Puede seguir este tutorial haciendolo todo usted mismo de la forma que se indica, o puede descargar cada paso del tutorial de esa página. 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. Cómo descargar e iniciar KDevelop y Designer
Requisitos
En este tutorial necesitaremos, además del compilador, alguna otra aplicación 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 también tenga que instalar esas. Puede descargar KDevelop de www.kdevelop.org y muy probablemente su distribución incluya Qt/Designer en algún 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 también le hará falta Qt 3.x. La última versión estable de KDevelop en el momento de redacción de este texto es la 2.1.5, mientras que la versión de desarrollo de KDevelop 3.0 (con nombre clave gideon) se encuentra en estado alpha3. Este documento trata mayormente sobre gideon, pero también se puede utilizar como referencia en el manejo de KDevelop 2.1.5.

Cómo 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 básicos (como la ubicación de la documentación, etc.). Para hacerlo, basta con que siga las instrucciones en pantalla. Una vez terminada la configuración, podrá ver una ventana similar a esta:

Ahora ya estamos listos para desarrollar una aplicación.

Chapter 3. Creación de la aplicación
Creación de un nuevo proyecto
Ahora vamos a crear un nuevo proyecto para nuestra aplicación. Para hacerlo, seleccionamos Proyecto/Nuevo proyecto. Se abrirá un asistente en el que seleccionaremos las opciones del proyecto.

Java. una extensión de Kate. una extensión del panel de navegación de Konqueror. puede generar (en el momento de redacción de este tutorial): una aplicación de KDE. varios tipos de aplicaciones Qt y una aplicación para terminal basada en C++. una aplicación GNOME (sí.Podemos utilizar KDevelop para desarrollar en C++. El asistente de "nuevo proyecto" se puede utilizar para crear una aplicación sencilla que sirve como estructura de base para la aplicación final. Si decide crear una aplicación en C. una extensión KHTMLPart. es posible utilizar KDevelop para crear aplicaciones para GNOME). KDevelop podrá generar la estructura para una aplicación para GameBoyAdvance. un módulo de KDevelop. Python o PHP. un salvapantallas. un módulo del Centro de control. o símplemente una aplicación para terminal basada en C. C. Si decide crear una aplicación C++. . una aplicación KDE sencilla. un applet de Kicker. un servicio DCOP. una aplicación de KDE que utilice KParts. un esclavo KIO (implementación de un protocolo). un módulo de KOffice.

obtendrá una aplicación compilada que podrá ejecutar por medio de Construir/Ejecutar progama. vamos a compilarla.Si prefiere utilizar Java. por lo que probablemente no necesitará modificar nada. donde podremos cambiar la plantilla de la cabecera de los archivos . Fíjese en que KDevelop ha utilizado el año actual. una aplicación Python Qt o un sencillo guión de Python.h y . su nombre y su dirección de correo electrónico completos. una aplicación de KDE o una extensión de KDevelop. Tenga en cuenta que las características de KDevelop son independientes del interfaz que se utilice. todas ellas con Java (incluso podrá utilizar el depurador de aplicaciones Java integrado). Por último. pero en este tutorial utilizaremos el interfaz IDEAL. en la sección Interfaz de usuario. Al pinchar en el botón Siguiente. Pinche en Construir/Construir proyecto. se mostrarán las opciones sobre el sistema de control de versiones que utilizaremos en el proyecto. Ahora KDevelop generará el proyecto. Para simplificar nuestro ejemplo. así que bastará con pinchar en el botón Finalizar.cpp. seleccionaremos Ninguno y continuaremos en las siguientes páginas. Al menos nosotros no modificaremos nada. así que puede seleccionar aquel en el que se encuentre más cómodo. Puede cambiar el interfaz de usuario y poner alguno de los modos clásicos a través del menú Preferencias/Configurar Gideon. KDevelop podrá ayudarle con las estructuras de un proyecto de Ant. Cómo compilar lo generado por KDevelop Ahora que ya está hecha una buena parte de la aplicación. . también puede utilizar el asistente para crear un simple guión de PHP. y estaremos listos para comenzar a desarrollar nuestra aplicación sobre la estructura básica. La siguiente instantánea muestra una ventana vacía de KDevelop con el interfaz IDEAL. tras una corta espera (y en el caso de que no se produzca ningún error debido a la falta de alguno de los paquetes necesarios). de forma similar a lo mostrado en la siguiente instantánea.

Algo parecido a esto: Comprensión de la estructura básica Vamos a revisar ahora el código utilizado por KDevelop. Pinche en la etiqueta Clases que podrá encontrar a la izquierda para abrir la lista de clases de nuestro proyecto.La aplicación también tiene los menús estándar. . y un díalogo de preferencias preparado para que añada las opciones que considere oportuno.

está en un servidor ftp. setupActions() se utiliza para crear las acciones y conectarlas con nuestros slots (si no le resultan familiares conceptos como señales o slots. readProperties(KConfig *) y saveProperties(KConfig *) se utilizan para cargar y guardar el estado de la aplicación (por ejemplo. cuando vuelvan a acceder al sistema. etc. vamos a echar un vistazo a la clase TheTinter. si se accede por ssh. En primer lugar. La biblioteca KIO se ocupará de todo. los documentos cargados) cuando se abre o se cierra la sesión de KDE. encontrarán la aplicación en el mismo estado en que la dejaron. y TheTinterPrefPageOne y TheTinterPrefPageTwo (que son dos páginas de configuración para diferentes opciones del diálogo de preferencias). puede consultar la documentación de Qt o mi otro tutorial). actionCollection()). al ser activada. no nos preocupará si el documento es local. con el fin de crear una acción estándar para la entrada de menú Archivo/Abrir que. los usuarios podrán cerrar la sesión y. SLOT(fileOpen()). . TheTinter contiene algunos métodos interesantes que sin duda utilizaremos: load(const KURL &url) es el método al que se llamará cuando el usuario quiera cargar un documento. Tenga en cuenta que la mayoría de las acciones utilizadas en la aplicación de plantilla son estándar. Tenga en cuenta que en KDE se recomienda utilizar la clase KURL para almacenar la url de un documento. Esta clase es la ventana principal de nuestra aplicación y contiene el menú. TheTinterPreferences (el diálogo de preferencias). las barras de herramientas. de forma que el método contiene código del tipo KStdAction::open(this. TheTinterView (que es el widget central de la aplicación donde se mostrarán nuestros documentos).Puede ver que nuestro proyecto tiene 5 clases. De esa forma. TheTinter (que hereda de KMainWindow). De esta forma. emite una señal que se conecta a nuestro slot fileOpen. etc.

Este código está en el constructor de TheTinterView. con lo que podemos quitar la linea #include <kprinter. this. que será suficiente con dejarlo con las tres primeras lineas: . Por ejemplo. las funciones currentURL y openURL ya no compilarán.cpp). tanto del fichero . TheTinterView es el widget central de nuestra aplicación. no te preocupes. top_layout->setAutoAdd(true). como son signalChangeStatusbar. con lo que podemos eliminar el código que busca una componente para tal fin y la utiliza. podemos eliminar la variable m_html y algunas funciones que ya no usaremos de TheTinterView. Además. es decir. ya que no nos resulta necesaria tanta complejidad. por lo que es ahí donde debemos procesar el evento de soltar. con lo que podemos eliminar la creación de la acción estandar openNew con la llamada a KStdAction::openNew igual que hicimos con print. Tampoco abriremos páginas HTML. this. actionCollection()). SIGNAL(signalChangeCaption(const QString&)). SLOT(changeStatusbar(const QString&))). Como hemos eliminado las señales de TheTinterView. Chapter 4. SIGNAL(signalChangeStatusbar(const QString&)). podemos también eliminar el código que las conectaba en el constructor de TheTinter: // allow the view to change the statusbar and caption connect(m_view. pero eliminaremos la mayor parte de esto en el siguiente paso del tutorial. SLOT(filePrint()). junto con la función fileNew tanto del . no utilizaremos el comando de impresión. la linea KStdAction::print(this. connect(m_view.h así como la variable m_printer de la misma clase y la función TheTinterView::print .h> la inicialización de m_printer(0) en el constructor de TheTinter.h como del . . SLOT(changeCaption(const QString&))). Tampoco vamos a crear un nuevo documento.cpp como del . En la plantilla de KDevelop. y la función TheTinter::filePrint. pues las vamos a reimplementar dentro de poco. Al eliminar la variable m_html. que añadía una acción estandar para imprimir. signalChangeCaption. en el caso de currentURL).h como del . simplemente déjalas vacías (o con un return "". slotOnURL y slotSetTitle (recuerda eliminarlas tanto del . el widget que se coloca debajo de la barra de herramientas y sobre la barra de estado. // setup our layout manager to automatically add our widgets QHBoxLayout *top_layout = new QHBoxLayout(this).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 (todavía no soltando) sobre nuestra ventana.cpp . Primeros cambios al código fuente Eliminación de las partes que no utilizaremos Lo primero que haremos será eliminar el código que no resulte necesario para nuestra aplicación. mientras que se llama adropEvent(QDropEvent *event) cuando el usuario suelta algo sobre nuestra ventana. esta clase crea un componente KPart capaz de leer documentos HTML.

this.ui. y a continuación marcaremos la casilla de la parte inferior para utilizar siempre el destino activo sin necesidad de más preguntas. s1. hemos eliminado la mayor parte del constructor de TheTinterView. Si posteriormente necesitamos más señales o slots. del tutorial. El resultado es el paso 1. Chapter 5. podemos eliminar el siguiente trozo de código ya que como el comentario que lo acompaña dice. En primer lugar. it’s just here to illustrate // how to insert a custom menu and menu item KAction *custom = new KAction(i18n("Cus&tom Menuitem"). no hace nada útil y está sólo para ilustrar como insertar un menú propio de nuestra aplicación. El nombre del archivo que añadiremos es centralviewbase.ui se crean con el diseñador de Trolltech para generar interfaces de usuario para nuestra aplicación. Se le mostrará un diálogo en el que configurar el nuevo archivo que se ha añadido al proyecto (utilizando el Gestor de automake). Los archivos . ya que no lo vamos a utilizar para ver HTML. actionCollection(). 0. Uso del diseñador Cómo añadir un archivo . hemos eliminado los métodos innecesarios de esa clase.Para terminar. "custom_action"). // this doesn’t do anything useful. abra la pestaña Nuevo archivo en la parte izquierda. y seleccione Un nuevo widget. los añadiremos individualmente. SLOT(optionsPreferences()). Además. Seleccionaremos Añadir nuevos archivos a nuestro destino activo.ui (interfaz de usuario) a nuestro proyecto. junto con las señales y los slots. . Para hacerlo. añadiremos un nuevo archivo .ui En primer lugar.

dispuesto para comenzar el diseño.Esto abrirá una ventana del diseñador con un widget vacío. .

verde y azul del color con el que teñiremos el mapa de pixels.Uso del diseñador En este punto tenemos que diseñar el aspecto de nuestra aplicación. para seleccionar los componentes rojo. A continuación. Haremos que la imagen aparezca en la parte superior de la ventana. 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. . en la parte de abajo. y habrá tres deslizadores en la parte inferior. tres deslizadores y sus correspondientes etiquetas. añadiremos.

Además. Verde: y Azul:. de diré que (obviamente) no es algo automático. he añadido un Marco (en la pestaña Containers) que contiene los deslizadores y las etiquetas de texto. Dejo como ejercicio para lector la búsqueda en los menús del diálogo Edit Text de la forma de cambiar el color. .Las etiquetas contendrán el texto Rojo:. En el caso de que se esté preguntando por qué cada etiqueta aparece en el color indicado por su texto. con la intención de embellecer un poco el widget.

Estos dos elementos deben estar dispuestos verticalmente. ahora debemos darnos cuenta de que en el nivel más alto sólo hay dos elementos. el widget gráfico y el marco contenedor. así que añadiremos una disposición que se ocupará de la ubicación y el tamaño correctos de los elementos. De esta forma se creará una disposición de rejilla para el contenedor seleccionado actualmente. los elementos no han quedado correctamente alineados. El resultado es: . y pinche en el botón Lay out in a grid (el que tiene una rejilla verde de 3x3). Bien. Está claro que esa no es nuestra intención. El resultado es: Intente redimensionar el marco contenedor y comprobará que ahora los elementos tienen siempre su posición y tamaño correctos. En primer lugar. y seleccionaremos Lay out vertically en el menú (o el botón con tres rectángulos verdes apilados en la barra de herramientas). pinche en el marco que contiene los deslizadores y las etiquetas. así que pincharemos en una zona vacía del widget principal. la parte de los botones ya está prácticamente acabada. compruebe el aspecto del resultado mediante la opción Preview Form en el menú Preview y redimensione la ventana. Los elementos dentro de nuestro widget están colocados en una posición fija y con un tamaño fijo. Aunque usted crea que puede hacerlo mejor que yo.Como ya habrá notado.

Esta vez seguro que pensará que el resultado es extraño. así que los distribuye de forma proporcional. pero el subsistema de disposición no tiene forma de saber cuál de los elementos debe ocupar más espacio que el otro. pinche en el botón "más" a la izquierda de la propiedad sizePolicy y cambie la opción vSizeType a MinimumExpanding. de forma que su tipo de tamaño vertical sea MinimumExpanding. ¿verdad? Bueno. Vaya a la ventana del editor de propiedades. cambiaremos la política de tamaño del elemento gráfico. Para solucionar esto. en realidad no lo es. Ambos elementos deben estar uno sobre el otro. El resulta ahora es exactamente lo que buscábamos: .

vamos a cambiar QWidget (que KDevelop había añadido como Clase base) a CentralViewBase. El diseñador se utiliza normalmente para crear un widget que es la clase base de otra clase en la que se sobrecargan los métodos importantes. En el diálogo que aparecerá. el nombre de la nueva clase será CentralView y activaremos la casilla Generar clase hija de QWidget.. Pero. Pinche en Object Explorer del widget principal o en cualquier parte vacía del mismo (cerca de los bordes suele haber un pequeño margen que puede utilizar). Integración del diseñador con KDevelop KDevelop generará automáticamente las opciones necesarias del Makefile para que uic (compilador de interfaz de usuario) produzca los archivos centralviewbase. Después edite la propiedad name y utilice algo como CentralViewBase.h y centralviewbase. .Ya estamos preparados para volver a KDevelop y comerzar a utilizar esta clase.). Normalmente se utilizan dos esquemas de nomenclatura.cpp con el código fuente necesario. Prefiero la primera forma. llamar a la clase base XXXBase y a la clase de implementación XXX o llamar a la clase base XXX y a la clase de implementación XXXImpl.. primero. Todo lo que hay que hacer ahora es añadir una nueva clase (Proyecto | Nueva clase. como la clase heredará (indirectamente) de QWidget. de hecho. debemos darle un nombre. así que esa es la que utilizaremos.

al final del constructor. lo que significa que aparecerá en la pantalla dentro del widget this. Ahora añadiremos una variable protegida class CentralView * m_view. En este momento estamos preparados para compilar s2 y obtener una aplicación que utilice el widget que hemos creado en el diseñador. .Dependiendo de la versión de Gideon que esté utilizando. para tener un widget CentralView como único hijo de TheTinterView. y después una línea m_view=new CentralView(this. El segundo parámetro es un nombre de metadatos para el objeto. El primer parámetro (this) significa que el widget CentralView será un hijo del widget this. y es posible utilizar el nombre que se quiera. es posible que necesite ir a la pestaña Información avanzada y modificar manualmente el constructor para que la llamada sea a CentralViewBase en vez de a QWidget (sustituyendo el uno por el otro). a TheTinterView. "centralview").

algo como QFont font() const { return m_font.). el widget se muestra pero no hace nada. 2. 3. así que le haremos trabajar un poco. De esta forma siempre se sabe si una variable es local a un método o si depende de clase en la que se encuentra. y deles un nombre. Siempre se coloca "m_" como prefijo de los nombres de las variables miembro. que deben empezar por mayúscula (?MaximumSize?). De esta forma. seleccione cada uno de los widgets que se utilizarán desde la aplicación. Añadir nombres. A modo de normas generales del esquema de nomenclatura que utilizo. No utilice nunca getXXX() como nombre para acceder a un método de una clase (lo métodos normales que únicamente hacen { return m_XXX. abra con el diseñador el archivo centralviewbase. Dentro del diseñador. Siempre se nombra a los componentes en la forma ?maximumSize?. respectivamente. }. En caso de que usted no esté familiarizado con el sistema de nomenclatura m_nombre. el método set correspondiente.. Cómo hacer que el widget generado por el diseñador funcione Como ya habrá notado. y m_green y m_blue a los otros dos. cito algunas reglas: 1. y no como ?MaximumSize? o ?maximum_size?.ui. Hace que el código sea mucho más sencillo de leer y mucho más comprensible por otros desarrolladores. puedo decirle que este sistema es el mejor invento desde el pan. Yo he llamado m_label al elemento gráfico. m_red al deslizador del componente rojo. slots y conexiones En primer lugar. Es mucho mejor utilizar siempre el nombre de la propiedad. de forma que la pareja . dentro del bloque Interfaz de usuario. debería ir precedido por ?set?. La única excepción es en los nombres de las clases. utilizando la pestaña Grupos de archivos (a la izquierda). }.Chapter 6.

muévalo hacia el widget principal (por ejemplo hacia uno de los bordes).. y. cree otros dos slots llamados setGreen(int v) y setBlue(int v). salga del diálogo y selecione Connect Signals/Slots en el menú Tools. .. pinche en el campo No Signal y seleccione la señal valueChanged(int). Para hacerlo. los sobrecargaremos en CentralView. Es mucho más fácil entender el uso de la variable si se llama m_background y después utilizar if (m_background) . Conecte también las señales correspondientes de m_green y m_blue con setGreen(int) y setBlue(int). Haremos que se llame a estos slots cada vez que el usuario modifique los deslizadores rojo. Después. Las variables booleanas deben tener siempre nombres positivos. ahora nuestro widget tiene nombres que podremos utilizar en la implementación de CentralView (ya que CentralView hereda de CentralViewBase). Para hacerlo. suelte el botón y se le mostrará un diálogo en el que configurar qué señal del elemento de origen se conectará con qué slot del widget de destino. conviene recordar que los componentes de color van normalmente de 0 a 255 (y eso es lo que dice la documentación de QColor). Vamos a hacerlo para que lo entienda mejor.. Una vez que se ha determinado el deslizador rojo como origen y el widget principal como destino. Hay un rectángulo magenta que muestra los widgets que serán conectados. Ahora. en vez de cosas como bool m_noBackground. sin soltarlo. verde o azul. 4..get/set de una propiedad quede en la forma font() y setFont(const QFont &) en nuestro ejemplo. en el icono de la flecha de la barra de herramientas. Pinche en New Function y dele el nombre setRed(int v). También debemos añadir slots a CentralViewBase. Después pinche con el botón izquierdo del ratón sobre el deslizador rojo y. como son métodos virtuales. o pulsando F3. seleccionamos cada deslizador y fijamos su propiedad maxValue (en el editor de propiedades) a 255. abra el diálogo Slots del menú Edit o pulsando el botón derecho del ratón en el widget principal. En el diálogo. seleccione el slot setRed(int). así que lo propio sería que el deslizador nos proporcionase valores entre 0 y 255.. Bien. En primer lugar. pero aún no es suficiente. en vez de if (!m_noBackground) .

Guarde el interfaz de usuario y cierre el diseñador para volver a KDevelop. también la haremos protegida. Aquí vamos a reimplementar setRed. Utilice como tipo QColor y como nombre m_color. y que tendrá tipo QColor. Para hacerlo. y le incluimos un parámetro de tipo int. el tipo es void (hace referencia al tipo del valor devuelto). Tendremos que realizar la misma operación otras dos veces. abra la pestaña Clases. de forma que se utilice este en vez del que aparece en CentralViewBase. Implementación de los slots De vuelta en KDevelop. pinche con el botón derecho sobre la clase CentralView y seleccione Nuevo método en el menú emergente. Ahora vamos a añadir una nueva variable. Así que el nombre es setRed. que será el color utilizado en TheTinter. para crear los slots de setGreen y setBlue. pinche nuevamente con el botón derecho en el nombre de la clase en la vista de árbol y seleccione Añadir atributo. Para ser correctos. . para hacerlo coincidir con el slot que hemos creado en el diseñador.

Ahora vamos a añadir el método y a implementarlo.blue() ). void CentralView::updatePixmap() { QPixmap pixmap(128. m_label->setPixmap(pixmap). pixmap. Este es nuestro código fuente para s3. updatePixmap().fill(m_color). } La primera línea crea un mapa de pixels de 128x128. pero. También llamamos al método updatePixmap() que actualizará el mapa de pixels con el color m_color.Ahora vamos a implementar los slots que acabamos de añadir para modificar el valor de m_color. en vez de que únicamente aparezca este rellenando un rectángulo.green(). La tercera línea coloca el mapa de pixels en el elemento de imagen. en las propiedades). si lo desea. El tamaño 128x128 es un tanto arbitrario. ya que el elemento gráfico lo redimensionará para que llene todo el área (puede cambiar esto. se produce mucho parpadeo). y en la segunda línea se rellena del color. . m_color. Utilizaremos el siguiente código: void CentralView::setRed(int v) { m_color.128). antes. Ese será nuestro siguiente objetivo.setRgb( v. m_color. trataremos de hacer que no parpadee (fíjese en que si mueve los deslizadores muy rápido. pronto haremos que cargue una imagen y la tiña con el color. } que utiliza el nuevo valor del componente rojo y deja los otros dos sin cambios.

this. setGreen y setBlue para que inicien el temporizador en vez de que llamen directamente a updatePixmap. Eliminación del parpadeo utilizando un temporizador Los objetos QTimer se utilizan cuando se desea hacer algo en un momento determinado. puede iniciar un temporizador que emita una señal en un momento concreto.Chapter 7. Chapter 8. se detendrá el más antiguo. SIGNAL(timeout()).. además de un método setPixmap que únicamente hará: void CentralView::setPixmap(const QPixmap &pixmap) { m_pixmap=pixmap. SLOT(updatePixmap())). utilizaremos connect(&m_timer. En primer lugar. } Ahora vamos a implementar el método que KDevelop creó como futura ubicación de un método de carga de un documento en TheTinterView. podemos buscar experiencias más avanzadas. conectamos la señal timeout() que emite m_timer al slot updatePixmap() que hemos creado en s3. ya que su corrección completa no entra dentro del objetivo de este tutorial). Cada vez que iniciemos un temporizador mientras haya otro funcionando (iniciamos el segundo temporizador antes de que hayan pasado los 200 ms). . Eso significa que si el usuario mueve el deslizador muy rápido. En el constructor de CentralView. updatePixmap(). en vez de producirse muchas llamadas a updatePixmap().true). Para hacerlo. Ahora que hemos corregido el parpadeo (al menos en gran parte. esperaremos a que el usuario deje de modificar los valores durante 200 ms antes de hacer el cambio real. añadiremos una variable miembro de QPixmap llamada m_pixmap a CentralView. inicia un temporizador de un único ciclo (que únicamente emitirá una señal y se detendrá) con un tiempo de espera de 200 ms. Después conecta esa señal a un slot para que realice el trabajo necesario. Ahora cambiamos los tres slots. o cada x milisegundos.h> correspondiente). añadimos una variable protegida QTimer m_timer a CentralView (no olvide la línea de inclusión #include <qtimer. m_timer. Carga y manipulación de un mapa de pixels Carga de un mapa de pixels En primer lugar. Para hacerlo. Eso nos lleva a s4.start(200. setRed.

Cambiaremos la implementación de updatePixmap a: void CentralView::updatePixmap() { QImage image= m_pixmap. Mezcla de colores Ahora vamos a hacer que CentralView realice operaciones sobre el mapa de pixels. KIO::NetAccess::removeTempFile(tmpFile).5). tmpFile)) { // cargar el archivo (el destino es siempre local) m_view->setPixmap( tmpFile ).por lo que es seguro llamarlo aunque el archivo no sea temporal. Conviene reseñar que en caso de que esté tratando de acceder a un archivo local. de forma que habrá que utilizar uno un otro dependiendo del objetivo que se quiera lograr. En este caso resultará más sencillo teñir una imagen.convertToImage(). } } KIO::NetAccess::download se utiliza para descargar la URL url y guardarla en un archivo temporal local cuya ruta completa se almacena en tmpFile. image. KIO::NetAccess::download devuelve el archivo real en vez de copiarlo a una ubicación temporal (lo que sería absurdo) y KIO::NetAccess::removeTempFile no hace nada.void TheTinterView::openURL(const KURL& url) { QString tmpFile. pixmap. hace algo muy interesante. sabrá que QPixmap tiene un constructor que admite un único parámetro QString. devolverá true y el documento será cargado. 0. m_label->setPixmap(pixmap). Si tiene éxito. carga el mapa de pixels indicado en dicho parámetro. QPixmap pixmap. mientras que QPixmap lo hace en el servidor X. que es otra clase que puede albergar imágenes. KImageEffect::blend(m_color. mientras que nosotros hemos utilizado como parámetro aquí un QString. Por último. . // y eliminar el archivo temporal KIO::NetAccess::removeTempFile(tmpFile). if (KIO::NetAccess::download(url. y. Elimina el archivo tmpFile únicamente si se trata realmente de un archivo temporal. esa línea es equivalente a: QPixmap pixmap( tmpFile ). ¿por qué? Si tiene experiencia en C++ y ha leido un poco la documentación de QPixmap. por lo tanto. m_view->setPixmap( pixmap ). Habrá notado que CentralView::setPixmap admite un parámetro QPixmap.convertFromImage(image). La diferencia es que QImage almacena las imágenes localmente. 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. He olvidado mencionar que el constructor de QPixmap que obtiene un parámetro QString. } La primera línea convierte el mapa de pixels a una imagen.

Modificación del interfaz de usuario existente Ahora vamos a cambiar el interfaz de nuestra aplicación para añadirle un nuevo deslizador que nos permita modificar el componente de opacidad (o alfa) del tinte. en el elemento gráfico. image. KImageEffect es una clase que contiene métodos muy útiles para aplicar efectos a imágenes. 0. y ya podemos abrir imágenes y teñirlas. Chapter 9. al igual que antes.KImageEffect::blend(m_color. Continuamos volviendo a convertir la imagen a un mapa de pixels y colocándola. Esto significa que hemos terminado con s5. El primer parámetro es el color. el segundo la imagen (que será modificada por este método) y el tercero es la intensidad de opacidad. utiliza KImageEffect (de kdelibs/kdefx) para mezclar un color con una imagen.5). pero aún no hemos acabado. .

seleccionamos el archivo centralviewbase. Usted se preguntará.Añadir el deslizador de alfa En primer lugar. Si intenta añadir un elemento dentro de otro que tiene una disposición. maneja las etiquetas y los deslizadores. La disposición del widget principal es una disposición vertical. . Debe darse cuenta de que las dos disposiciones son independientes entre sí. y la disposición de dicha caja. se le preguntará si desea romper antes la disposición para poder añadirlo. eso se hace para poder decir: "Deja de manejar las posiciones actuales de los elementos. Pinche para seleccionar el widget principal. y KDevelop abrirá el diseñador.ui. Ahora seleccione la caja de agrupación que contiene los deslizadores y rompa también la disposición de estos. Pero eso no debería ocurrir ahora puesto que hemos roto las disposiciones manualmente :). que maneja m_label y la caja de agrupación. Lo primero que haremos es romper las disposiciones. ¿romper las disposiciones? Sí. porque vamos a añadir otros nuevos". en la pestaña Grupos de archivos. y después pinche en el icono de la barra de herramientas Break Layout (también disponible en el menú Layout).

Seleccione la caja de agrupación . . . puede mover hacia arriba los deslizadores y las etiquetas para dejar espacio para el nuevo deslizador que añadiremos.Ahora que hemos roto la disposición. Añada 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 agrupación... y pinche en la barra de herramientas para añadir una disposición de rejilla. Cambie sus propiedades para que el rango de valores de m_alpha vaya de 0 a 100...

. podemos añadir un botón KColorButton que utilizaremos como forma alternativa de selección del color mediante un diálogo de color estándar.Ahora puede seleccionar el widget principal y añadir la disposición vertical. Añadir el botón de selección de color Si queremos mejor todavía más el aspecto de nuestra aplicacióm.

Seleccionaremos romper la disposición y a continuación la volveremos a crear.Selecciónelo y añádalo en la parte derecha de la caja de agrupación (pero dentro de ella). ¿Reconoce ese diálogo? Sí. . está preguntando si se debe romper la disposición o cancelar la operación.

. de esa forma quedarán seleccionados. y el diseñador generará una disposición de rejilla para eso elementos. pero vamos a utilizar otra técnica con la intención de aprender algo más. Deseleccione cualquier widget que pudiera estar seleccionado. Ahora esta disposición maneja a sus hijos. así que resulta un tanto inútil. y el diseñador lo colocará correctamente de forma automática.Podemos añadir una disposición de rejilla a la caja de agrupación tal y como está. pinche en la disposición de rejilla. y pulsando la tecla ?Ctrl? en el teclado. Una vez que tenga los 8 elementos seleccionados. ni el KColorButton). dibuje un rectángulo que cubra las etiquetas y los deslizadores (pero no la caja de agrupación entera. pero (aún) no está manejada por nadie.

Esto podría ser suficiente para la mayor parte de la gente. Seleccione m_colorButton y en el editor de propiedades. . de forma que administrará las políticas de tamaño y (directa o indirectamente) a todos sus hijos. que es la forma correcta de actuación (quizá debería decir que esta debe ser la forma correcta de actuación. Esta nueva disposición manejará la disposición de rejilla y el widget KColorButton. ahora seleccionaremos el widget principal y añadiremos una disposición vertical y. de esta forma. añadiremos una disposición horizontal independiente. le cambiaremos la política de tamaño. Queremos que el widget KColorButton (al que llamaremos m_colorButton) aumente hasta ocupar todo el espacio de la caja de agrupación. para resaltar que es como hay que hacerlo para que las aplicaciones funcionen correctamente). Para ello. abra la sección sizePolicy y cambie vSizeType a Expanding.Para solucionarlo. seleccionaremos la caja de agrupación y añadiremos una disposición horizontal. la disposición pertenecerá a la caja de agrupación. En el segundo caso. Bien. pero no para nosotros. En el primer caso. hemos hecho que todos los elementos estén manejados por una disposición. Puede realizar pruebas de estas dos formas de actuar y utilizar la opción Preview Form para comprobar las diferencias que se produce al redimensionar la ventana. Note la diferencia existente entre seleccionar la caja de agrupación y añadir una disposición horizontal y seleccionar la disposición de rejilla y el KColorButton y añadir entonces esa disposición horizontal. colocándolos uno junto al otro.

así que el sistema de disposición trata de darle a cada uno la mitad del espacio disponible. Hemos observado anteriormente que m_label tenía una política de tamaño de expansión (MinimumExpanding para ser precisos) mientras que la caja de agrupación no nos importaba (únicamente tenía una política Preferred). Ahora la caja de agrupación también se ha expandido porque la política de m_colorButton es Expanding (por lo que trata de ocupar el mayor espacio vertical posible). y m_label también.Vaya. probablemente no era esto lo que queríamos. y eso no es lo que buscamos. Esto se hace gracias a la propiedad verticalStretch. En casos como este es posible darle a los elementos involucrados distintos factores de expansión para que a uno de ellos se le asigne más espacio que al otro en caso de que ambos quieran la mayor cantidad de espacio posible. Así que incrementaremos el valor de la propiedad verticalStretch de m_label para que éste sea más grande y se solucione el problema. . ¿Hay una solución? Desde luego.

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

ui. Añadiremos el código del nuevo interfaz en el siguiente paso. cerraremos el diseñador. para iniciar la actualización del mapa de pixels. en este caso. de momento ya es suficiente. únicamente hemos modificado el interfaz de usuario con el diseñador y el hecho de compilar sin haber cambiado nada nos da el nuevo interfaz de usuario (con el código anterior funcionando correctamente). añadiremos un slot público a CentralView llamado setAlpha(int v). no hemos hecho nada con KDevelop. .Bueno. La implementación de este método únicamente llamará a m_timer. Añadir las implementaciones de los nuevos métodos Soporte para el deslizador alfa En primer lugar.true). compilaremos el resultado y lo llamaremos s6. Chapter 10. así que guardaremos el archivo . Fíjese que.start(200.

m_blue->setValue(color. a la que llamaremos s7. m_alpha->value()/100. Con este cambio tenemos una aplicación completamente operativa. emitiendo las tres respectivas señales y llamando a setRed. De forma que el valor resultante esté en el intervalo [0. por lo tanto.blue()). ya que fija el valor de los tres componentes.5 ). no parpadeará puesto que se llama al temporizador cada vez que cambia un valor (gracias a las señales que emiten los deslizadores cada vez que cambia su valor) y. además. Cambiamos KImageEffect::blend(m_color. void CentralView::setColor(const QColor &color) { m_red->setValue(color. } Con esto es suficiente para establecer el color. Soporte para el botón de color Vamos a añadir un nuevo método a CentralView llamado setColor. y. setGreen y setBlue. .1] (ya que en el diseñador hemos establecido en 100 en valor máximo de m_alpha). image.red()). updatePixmap recibirá una única llamada. por KImageEffect::blend(m_color.Entonces cambiaremos el método updatePixmap para que utilice el valor del deslizador alfa.green()). 0. m_green->setValue(color. image.0 ).

los deslizadores cambian sus valores. Algunos cambios aquí y allá Corrección del botón de color. cuando el usuario cambia el color en el botón de color. Como habrá notado. Vamos a arreglarlo. . pero esto no ocurre en la situación inversa.Chapter 11.

pero si lo hace. pixmap.ui en el diseñador. } De esta forma. QPixmap pixmap. a CentralView::updatePixmap y así haremos que m_colorButton actualice su valor al mismo tiempo que se actualiza el mapa de pixels. en ese caso.isNull()) { QImage image= m_pixmap. en primer lugar comprobamos si m_pixmap es válido (no es nulo) y. queremos que haya algo dibujado al comenzar. Vamos a modificar la función updatePixmap para: void CentralView::updatePixmap() { m_colorButton->setColor(m_color). Aun así. KImageEffect::blend(m_color. y seguimos queriendo que aparezca algo. podría tener problemas y posiblemente tendría que utilizar QObject::blockSignals para bloquear las señales de los deslizadores o de m_colorButton. image. así que lo actualizaremos en updatePixmap para estar seguros del correcto funcionamiento. Quizá quiera actualizar m_colorButton cada vez que cambie uno de los deslizadores. En cualquier caso. abra centralviewbase. continuamos con el funcionamiento normal. pixmap. Para hacerlo. En caso de que m_pixmap no haya sido inicializado. } m_label->setPixmap(pixmap). en el constructor de CentralView. } else { pixmap = QPixmap(128. Corrección de mapa de pixels inicial En primer lugar.Añadiremos m_colorButton->setColor(m_color). m_alpha->value()/100. m_pixmap está vacío al iniciar TheTinter. Así que añadiremos una llamada a updatePixmap(). if (!m_pixmap.convertFromImage(image).fill(m_color). haremos lo que se vio en los primeros pasos del tutorial y pintaremos un color. seleccione la etiqueta y elimine el contenido de la propiedad pixmap pinchando en la pequeña flecha roja (aparece al seleccionar la propiedad). vamos a eliminar el logotipo de Qt que utiliza m_label de forma predeterminada. .0 ).convertToImage().128).

} } En caso de que la URL sea un archivo local. KTempFile *temp=0. de momento. ¿Qué podemos hacer? En primer lugar añadiremos un método de acceso a CentralView que devuelva el mapa de pixels teñido (el mapa de pixels que se encuentra en m_label): QPixmap *tintedPixmap() const { return m_label->pixmap(). ya que KDevelop añadió automáticamente desde el principio el código necesario para aceptar operaciones de arrastrar y soltar con URIs (equivalentes. }. delete temp.isLocalFile()) tmpFile=url. y como hemos utilizado KIO para cargar nuestras imágenes. cargaremos el archivo temporal en la URL especificada por el usuario. if (temp) { KIO::NetAccess::upload(tmpFile. "PNG"). añadiremos soporte para imágenes. Añadir más características Posibilidad de guardar el resultado Ahora queremos guardar la imagen teñida en un archivo. La diferencia con las URIs es que los eventos de arrastrar y soltar de una imagen contienen la información de la propia imagen. temp->unlink(). eliminaremos el archivo temporal y destruiremos el objeto que habíamos creado para ello. Añadir soporte para arratrar y soltar Aquí no hay mucho que hacer. tmpFile=temp->name().Chapter 12.path(). . Para desarrollar algo sobre arrastrar y soltar. ¿adivina el nombre? Correcto. En TheTinterView. a URLs). } m_view->tintedPixmap()->save( tmpFile. a continuación. Si no es un archivo local. saveURL(const KURL &url) :) Este método hace: void TheTinterView::saveURL(const KURL &url) { QString tmpFile. crearemos un archivo temporal y guardaremos ahí la imagen en formato PNG y. mientras que en el caso de las URLs. lo utilizaremos como archivo en el que guardar el mapa de pixels teñido en formato PNG. if (url. estas únicamente contienen la dirección de la imagen. url. añadiremos un nuevo método junto a openURL(const KURL &url). else { temp=new KTempFile. ya es posible arrastrar una imagen desde Konqueror a TheTinter y será descargada. this).

tenemos una URI. por favor. así que. nuestra aplicación final. target. el código de arrastrar y soltar de Qt puede hacer // *mucho* más. return. // cargamos el archivo load(KURL(url)).first(). ignorarla if (QUriDrag::decode(event. si no. Le dice al subsistema de arrastrar y soltar si se debe aceptar o rechazar la operación. uri)) { // correcto.El código que utilizaremos es: void TheTinter::dragEnterEvent(QDragEnterEvent *event) { // accept uri drops only event->accept(QUriDrag::canDecode(event) || QImageDrag::canDecode(event)). Espero que haya disfrutado del tutorial y haya aprendido con él. } Esto será llamado cada vez que recibamos un evento de arrastrar y soltar en nuestra aplicación. } QPixmap pixmap. tratamos de decodificarlo como un evento de imagen (utilizando QImageDrag::decode). Un saludo. void TheTinter::dropEvent(QDropEvent *event) { // esta es una implementación muy simple de un evento de soltar. consulte la documentación QStrList uri. . y ejecutamos esa URL. pixmap)) { m_view->openPixmap(pixmap). Si es correcto. Hay que tener en cuenta que TheTinterView::openPixmap todavía no existe. abrimos esa imagen. únicamente // aceptaremos una URL. if (QImageDrag::decode(event. extraemos la primera URL de la (posible) lista de URIs que contiene el evento. También deseo poder ver publicadas muy pronto sus aplicaciones de KDE :). En primero lugar tratamos de comprobar si el evento es un evento URI. Si la primera parte tiene éxito. } } Este código se ejecutará cada vez que el usuario suelte sobre nuestra ventana cualquier cosa que esté arrastrando. así que añadimos ese método y lo implementamos de esta forma: void TheTinterView::openPixmap(const QPixmap& pixmap) { m_view->setPixmap( pixmap ). } Y esto es s9. // comprobar si se puede decodificar la URI. Si no se trataba de un evento URI. la procesamos QString url. url = uri.

que podrá encontrar aquí. Me gustaría agradecer a las siguientes personas su ayuda en la creación de este tutorial: A Lauri Watts por explicar el docbook y crear un marco de trabajo excepcional para la documentación de KDE (y por responder a mis preguntas sobre docbook hasta altas horas en el IRC :) ).org> Appendix A. A Harald Fernengel y Roberto Raggi del equipo de KDevelop por solucionar rápidamente algunos problemas que he encontrado en Gideon al hacer este tutorial. por traducir este tutorial en menos un día para que me diera tiempo a usar la versión en español en Imaginática. A Anne-Marie Mahfouf por explicarme algunas etiquetas del docbook y por escribir otro excelente tutorial sobre KDevelop y el diseñador. A Miguel Revilla.org> This documentation is licensed under the terms of the GNU Free Documentation License.Antonio Larrosa Jiménez <larrosa@kde. TheTinter está publicado bajo los términos de la Licencia pública general GNU. Créditos y licencia El tutorial de desarrollo visual en KDE y TheTinter tienen Copyright (c) 2003 Antonio Larrosa Jiménez <larrosa@kde. . del equipo de traducción de KDE.

Sign up to vote on this title
UsefulNot useful