Revisitando los Layouts

La mayoría de nosotros estaremos de acuerdo que el Layout es una de las funcionalidades más importantes provenientes de QT e incorporadas al diseño de formularios en vDevelop. Seguro también que todos usamos los layouts sin mayor problema y de forma automática.

En este tutorial sobre layouts quiero repasar algunos aspectos de su funcionamiento que todavía causan confusión, e intentar aclararlos con ejemplos de uso.

Pero primero os invito a repasar el Tutorial Layouts en Velneo V7, un objeto para dominarlos a todos del autor de este mismo blog y un video resumen de Jesús Arboleya. Ambos recursos dejan clara la función que tienen los layouts y cómo organizan los controles automáticamente en el formulario.

Solo recordar un par de cosas antes de ver con ejemplos algunas propiedades interesantes de los layouts.

  • Existen otros controles que también pueden funcionar como layouts, son el formulario, la caja de grupo y el dibujo.
    Los layouts solo son visibles en tiempo de diseño, pero podemos enumerarlos con el API mediante la clase VObjectInfo (Tipo de control 22).
  • Los layouts dentro del formulario solo funcionarán cuando el formulario es también un tipo de layout.
    Los layouts pueden anidarse unos dentro de otros, siendo el layout Principal el formulario que los engloba a todos.

Tipo de Layout

El Tipo de layout determina cómo se distribuyen los controles dentro del Layout. Hasta la versión 24 existían 3 tipos de layout, Horizontal, Vertical y Grid. En la versión 25 se han añadido 2 nuevos tipos, Flujo Horizontal y Flujo Vertical que nos van a permitir diseño Responsive en nuestros formularios. Disponemos también de espaciador Fijo y espaciador Expandible.

  • En el layout Horizontal los controles se distribuyen horizontalmente de izquierda a derecha ordenados por el valor de la propiedad Posición X. Si coincide el valor de X se toma primero el que tiene menor valor de Y.
  • En el layout Vertical los controles se distribuyen verticalmente de arriba a abajo ordenados por el valor de la propiedad Posición Y. Si coincide el valor de Y se toma primero el que tiene menor valor de X.
  • El layout Grid organiza los controles en una cuadrícula de filas y columnas, pero también nos servirá cuando tenemos que superponer uno o varios controles encima de otro. Si lo que vamos a superponer son imágenes, tendremos que usar el canal Alfa o de transparencia.

Velneo permite cargar en el proyecto imágenes con transparencia o crearlas con el editor de imágenes, sin embargo, no podemos guardarlas en un campo dibujo porque solo admite el formato jpeg. Si queremos trabajar con registros de una tabla que contengan imágenes con transparencia no tenemos más remedio que codificarlas en Base64 y usar el API para leer y guardar en un campo de Texto.

El formulario siguiente es de tipo Grid con 2 layouts: el layout Artículo con los campos y el layout Tampón con una imagen con fondo transparente.
Para atenuar los controles que hay detrás de la imagen se puede activar la propiedad «Fondo opaco» del layout Tampón y usar un «Color de fondo» con transparencia.

Superponiendo el layout Tampón un poco con el layout Artículo, el resultado es un Grid con una sola celda que contiene ambos controles.

Propiedad «Fondo opaco» del layout Tampón desactivada.

Propiedad «Fondo opaco» del layout Tampón activada para atenuar los controles del formulario.

Si no lo haces ya, ¡¡hazlo!!, activa la Rejilla de puntos en el editor de formularios. Los controles estarán siempre anclados en múltiplos de 10px. Te aseguro que una vez activado ya no sabrás trabajar sin él.

Ancho y Alto en layout

Las propiedades Ancho en layout y Alto en layout  determinan el tamaño de los controles dentro de un layout y pueden tener 3 valores:

  • Por defecto: el control se pintará en el formulario con el ancho y alto asignado por defecto.
  • Fijo: el control se pintará en el formulario con el ancho y alto especificado en las propiedades Ancho y Alto.
  • Proporcional: el control se pintará en el formulario ocupando el máximo espacio que le permita el layout y en el caso de que comparta el espacio con otro control, lo hará de forma proporcional al tamaño indicado en las propiedades Ancho y Alto.
    Este será el comportamiento habitual de la mayoría de controles, lo que les permite adaptarse a los diferentes tamaños de pantalla.
    Más adelante veremos qué implicaciones tiene esto.

Si no hemos aplicado CSS, siempre que sea necesario, el alto y ancho del layout del formulario crecerán para albergar todos los controles que contenga.

El formulario se expande en Horizontal y Vertical

Veamos un ejemplo con controles de tamaño Por defecto y cómo se distribuyen a lo ancho o a lo alto dentro del layout.

El formulario siguiente contiene en tiempo de diseño 6 textos estáticos con las letras de Velneo en 2 layouts, uno Horizontal y otro Vertical.
Se han fijado el Espaciado y los Márgenes a 0 en ambos layouts.

En ejecución los textos estáticos se distribuyen (horizontalmente o verticalmente) y expandirán el formulario hasta que se muestren todos los controles a su tamaño Por defecto.

Un efecto negativo de este comportamiento es que el usuario no puede redimensionar la ventana de la aplicación en el caso de que ésta desborde las medidas de la pantalla. Podemos solucionar este problema mediante el siguiente CSS:

QMainWindow
{
   min-width: 200px;
   min-height: 40px;
}

Estableciendo un valor mínimo al ancho y alto de la ventana principal los controles del formulario ya no intentarán redimensionarlo, pero surge otro problema cuando dichos controles desbordan el espacio disponible. En el siguiente formulario vemos lo que ocurre:

En la versión 23 se añadió un nuevo control de formulario área de scroll que soluciona éste y otros escenarios similares. Más información sobre el área de scroll en el siguiente artículo de este mismo blog.

Flujo Horizontal y Vertical para el diseño Responsive

El layout Flujo Horizontal distribuye los controles horizontalmente de izquierda a derecha ordenados por el valor de la propiedad Posición X siempre que haya espacio horizontal disponible. El layout Flujo Vertical distribuye verticalmente los controles de arriba a abajo ordenados por el valor de la propiedad Posición Y siempre que haya espacio vertical disponible.

Estos layouts de Flujo no modifican el tamaño del formulario, en su lugar distribuyen los controles creando un flujo horizontal (de izquierda a derecha y de arriba a abajo) o vertical (de arriba a abajo y de derecha a izquierda).

En el formulario siguiente tenemos 2 layouts de Flujo, uno de cada tipo. El layout de Flujo Horizontal contiene 2 layouts de ancho fijo y otros 2 proporcionales. En el layout de Flujo Vertical hay 2 layouts de alto fijo y otros 2 proporcionales.

En ejecución el formulario no se ha redimensionado para distribuir los elementos en los layouts de Flujo Horizontal (200px de ancho) y Flujo Vertical (200px de alto).

En el layout de Flujo Horizontal los layouts fijos naranja y verde caben en la primera fila porque miden 160px (70 + 90px) y todavía queda espacio para el layout proporcional amarillo. La segunda fila se ocupa proporcionalmente por el layout azul.

En el layout de Flujo Vertical el layout fijo naranja ocupa él solo la primera columna con 150px, el layout fijo amarillo de 70px pasa a la segunda columna porque ya no queda espacio disponible. Los layouts verde y azul ocupan proporcionalmente el espacio disponible de la segunda columna.

Cuando aumentamos el tamaño del formulario la distribución de los elementos del formulario cambia.

El layout de Flujo Horizontal ha aumentado a 300px de ancho y ya da cabida al layout azul que tendrá la mitad del ancho del layout amarillo por la proporción que tienen en diseño. El layout de Flujo Vertical aumenta a 220px de alto y mete en la primera columna el layout amarillo con sus 70px de alto. La segunda columna se reparte al 50% entre los layouts verde y azul que son iguales en diseño.

Los layouts de Flujo solucionan el problema de recolocar los controles cuando giramos las pantallas de los móviles y tablets.

En el siguiente formulario el layout de Flujo Horizontal de color verde colocará el Dibujo a la derecha o en la parte inferior según la orientación del formulario. Para determinar en qué momento el dibujo fluye de la posición horizontal a la vertical establecemos un control fijo de 140px que forzará el cambio cuando el formulario cambie de tamaño y ya no permita albergar a éste.

En ejecución con orientación horizontal del formulario.

En ejecución con orientación vertical del formulario.

Espaciado y Márgenes

Las propiedades Espaciado y Márgenes de los layouts tienen un valor por defecto de -1 que equivale a 5px y 10px respectivamente.

Usaremos estas propiedades para agrupar o separar visualmente los controles en un determinado formulario. Para aplicar estos valores de manera global usaremos CSS.

El valor «margin» del CSS de los controles se sumará al Espaciado del layout.

El formulario siguiente tiene un margen izquierdo y superior a un valor personalizado de 50px. Los 4 layouts verdes se distribuyen dentro del layout principal con un espaciado de 10px y márgenes personalizados de 20,30,20,30 (sup, der, inf, izq).

En este otro formulario los márgenes se han fijado a 50px y el espaciado entre controles a 30px. Dentro de cada Layout se han definido espaciados y márgenes personalizados.

Ancho y Alto proporcional

El diseño de la mayoría de los formularios se resuelve dejando los valores Por defecto en las propiedades Ancho en layout y Alto en layout.
En la tabla siguiente se listan algunos controles y los valores Por defecto que tendrán el Ancho y Alto dentro de un Layout.

Observamos que el valor más común es Proporcional, pero ¿qué tamaño efectivo tomará el control en tiempo de ejecución?

Rellenar proporcionalmente el espacio disponible

En el formulario siguiente hemos colocado 2 layouts con la propiedad Alto en layout a valor Por defecto. El layout «cabecera» mide en diseño 240px de alto y el layout «botones» 80px.

En ejecución los layouts se adaptan por defecto a la suma del tamaño de los controles que contienen. El layout «cabecera» mide en pantalla unos 220px de alto y el layout «botones» unos 65px.

Cambiemos en diseño los valores de Alto en layout de ambos layouts a Proporcional. Vemos que ahora la Altura de los layouts es 300px y 100px respectivamente. Los valores se han repartido de arriba a abajo del formulario (400px) de manera Proporcional, según la relación (3/1 = 240px/80px ) que tienen en diseño.

Finalmente pongamos el Alto del layout «botones» al valor Por defecto.
En este caso el layout «botones» ocupa el espacio mínimo necesario, unos 65px, y el layout «cabecera» ocupará el resto del formulario.

Resumiendo, cuando establecemos el tamaño de un control en diseño al valor Proporcional, tendremos 2 casos en tiempo de ejecución:

  1. Si el control es el único Proporcional dentro del layout, entonces se expandirá para ocupar todo el ancho o alto disponible en el layout.
  2. Si comparte layout con otros controles tendrá que adaptar su tamaño proporcionalmente al de dichos controles. Los tamaños en tiempo de ejecución estarán siempre referenciados proporcionalmente al tamaño que tenían en tiempo de diseño.

Mantener Proporción en horizontal y vertical

Compliquemos un poco el diseño de nuestro formulario para jugar con la Proporción en horizontal y vertical. En este caso tenemos 3 layouts que deben tener cada uno la mitad del tamaño que el siguiente.

El formulario es un layout vertical. En diseño creamos 3 layouts Horizontales (azul, naranja y verde) de tamaños proporcionales de 30, 60 y 120px de lado respectivamente. Los 2 layouts Horizontales amarillos son de igual ancho que el layout verde (120px). Los espaciadores Expansibles mantienen la proporción en sentido horizontal.

El layout azul siempre ocupará la cuarta parte del ancho del formulario, el naranja la mitad y el verde el total.

En ejecución, la relación del área entre los layouts azul, naranja y verde siempre es constante e igual a 1,4,8.

Los espaciadores expansibles son proporcionales

Los espaciadores Expansibles consiguen anclar controles en coordenadas exactas y se adaptarán proporcionalmente al tamaño de la pantalla.

En el siguiente formulario queremos que el marge derecho sea siempre la mitad que el margen izquierdo y el margen superior igual al inferior. En diseño colocamos los espaciadores expansibles y el control con los tamaños proporcionales planteados.

En ejecución el formulario mantiene las mismas proporciones cuando ha duplicado el tamaño respecto al que tenía en diseño.

Superposición parcial de controles

Ya sabemos que para superponer 2 elementos en el formulario necesitamos un layout Grid. Pero necesito ahora superponer elementos de forma parcial, como cuando montamos un puzzle.

Planteamos el siguiente Puzzle con 4 piezas que tienen una parte de solapamiento:

La 4 piezas en diseño las colocamos en un layout Grid sin solapamiento. En este caso no encajan.

Si en diseño solapamos las 4 piezas tampoco se obtiene el resultado deseado.

Hagamos uso de los layouts y espaciadores proporcionales. Planteamos un diseño con 2 layouts proporcionales, uno Horizontal y otro Vertical y 2 espaciadores. El layout Vertical es el que engloba el resto de controles. La pieza es un dibujo proporcional con la propiedad Aspecto en Estirar / Encoger.

Manteniendo en diseño las adecuadas proporciones obtenemos en ejecución la pieza ocupando la cuarta parte del formulario. No olvidemos poner a 0 todos los espaciados y márgenes de los layouts.

Haciendo lo mismo con el resto de piezas y solapando los cuatro layouts Verticales obtenemos el resultado deseado.

Ya tenemos un marco que pueda encuadrar, por ejemplo, un gráfico de tipo Tarta.

Como siempre encuadramos el gráfico en un layout proporcional estableciendo los márgenes con espaciadores proporcionales. El gráfico deberá tener los márgenes a cero y el fondo transparente para que funcione correctamente el solapamiento con otros layouts del Grid.

Juntamos todo añadiendo leyendas y un título y este es el resultado final.

Con semejante cantidad de controles solapados entenderemos la importancia de disponer en tiempo de diseño de eficaces herramientas de edición.
En la versión 24 se han incorporado dos nuevas funcionalidades, la selección de layouts solapados y la posibilidad de desactivarlos. Por supuesto, poder agrupar los controles y desactivarlos facilitaría enormemente el trabajo de diseño y edición de formularios complejos.

Conclusiones

Los layouts y espaciadores expansibles son elementos básicos en el diseño y ejecución de nuestros formularios.

Hemos hecho un repaso, quizás algo peculiar, de algunas aplicaciones de los layouts que considero interesantes. Espero que se haya entendido el significado de la propiedad «Ancho en layout Proporcional» para que a partir de ahora no sea un problema colocar y dimensionar los controles del formulario exactamente dónde y cómo queremos.

Algo de código para terminar

Los layouts son controles cuya propiedad Tipo de control es igual a 22. Los espaciadores expansibles son Tipo de control igual a 23.

Con este código se puede obtener el Id y el tipo de control de todos los elementos de un formulario.

// Obtiene todos los controles del Formulario y Separadores de Formularios
 var oForm = theRoot.dataView()
 var oFormInfo = theRoot.objectInfo()
 var nNumObj = oFormInfo.subObjectCount(VObjectInfo.TypeControl)
 var oListaCtr = [], cCtr = ""
// Los objetos se recorren con subObjectInfo que devuelve otro VObjectInfo
 for ( var numControl = 0; numControl < nNumObj ; numControl++ ) {
     objInfo = oFormInfo.subObjectInfo(VObjectInfo.TypeControl, numControl)
     // La función objInfo.id() devuelve el nombre del Control
     // La función objInfo.propertyData(0) devuelve el Tipo de Control
     cCtr = objInfo.propertyData(0) + " - " + objInfo.id()
     oListaCtr.push(cCtr)
     // Comprobamos el Separador de formularios
     if (objInfo.propertyData(0) == 13) {
         var oSep = oForm.control(objInfo.id())
         // Recorremos los Separadores de formularios
         for (var numSep = 0; numSep < oSep.count ; numSep++ ) {
             var oFormSep = oSep.form(numSep)
             var oFormSepInfo = oFormSep.objectInfo()
             var nNumObjSub = oFormSep.controlCount()
             cCtr = "SubForm - " + oFormSepInfo.id()
             oListaCtr.push(cCtr)
             for ( var nCtrl = 0; nCtrl < nNumObjSub ; nCtrl++ ) {
                 objetoSub = oFormSepInfo.
                     subObjectInfo(VObjectInfo.TypeControl, nCtrl)
                 cCtr = "SubCtrl " + 
                     objetoSub.propertyData(0) + " - " + objetoSub.id()
                 oListaCtr.push(cCtr)
             }
         }
     }
 }
 alert (JSON.stringify(oListaCtr))

Espero que después de estar «master-class» sobre los layouts del maestro Satué no quede ninguna duda al respecto…

Espero tus comentarios mas abajo

Paco Satué
correo@pacosatu.es

Es un Ingeniero de Telecomunicación de carrera y un Informático-programador por vocación, que empezó en los 80 con DBase, Clipper y FoxBase. Hasta el año 2012 utilizó Visual Foxpro, pero debido a su abandono por parte de Microsoft tuvo que buscar una herramienta de desarrollo alternativa. La elección fue Velneo porque principalmente es una empresa española con soporte cercano, aparte de un producto moderno y adaptado a las últimas tecnologías Cliente-Servidor en Cloud.

16 Comments
  • Fran Varona
    Posted at 12:20h, 11 junio Responder

    Gracias, Paco. Velneo debería contratarte (y no como la Universidad española, en lo que se conoce como «a coste cero»), para que les enseñes a elaborar documentación. Gracias por dedicarnos tu tiempo. Es una gozada leerte.

    • Francisco José Vila Martín
      Posted at 12:38h, 11 junio Responder

      No des ideas… que me quedo sin «Becario».

      Pedazo de trabajo que realiza de forma altruista. Ya no se ni las cervezas que le debo.

  • Fernando
    Posted at 12:30h, 11 junio Responder

    Pedazo de post, Paco. Master Class sobre layout.
    Llevamos usándolos años y en algún caso sin conocer en profundidad su funcionamiento.

    Gracias y te animo a seguir escribiendo y descubriendo Velneo.

  • MANUEL BARRERA
    Posted at 12:34h, 11 junio Responder

    Gracias Paco . Como siempre muy instructivo.. Se agradece el esfuerzo , no es facil sacar unos minutos para hacer un post que tiene la clara intencion de ayudar a los demas.
    Eso se perdio en v7 …

    Un melancolico del foro de V6X…

  • Ramon Denuc
    Posted at 13:29h, 11 junio Responder

    Muchísimas gracias Paco por esta sensacional clase sobre los Layaouts,

    No voy a sumar más calificativos, porque para mi eres como Messi, es mejor.

  • Veldevelop
    Posted at 15:06h, 11 junio Responder

    Es increíble como una explicación tan simple pero a la vez tan amplia es capaz de abrir los ojos de forma monumental. Gracias Maestro

  • ANTONIO OSORIO LEON
    Posted at 15:13h, 11 junio Responder

    Gracias por por el artículo, ha sido muy instructivo.

    hace muchos años en una aplicación de gestión, vi como ponían una especie de sello en las facturas para indicar si estaban contabilizadas o cobradas, etc.

    Desde entonces eso se me quedó ahí grabado, como una cosa ha hacer, porque me parecía una pasada.

    Ahora, gracias a tu artículo, se puede decir que voy a cumplir un pequeño sueño.

    Gracias Paco, eres un crack.

    • José Simon
      Posted at 12:24h, 13 junio Responder

      Paco yo me voy a desmarcar de los agradecimientos, tienes que colaborar mas con esta web… ;P .

      A parte de las bromas, se hecha de menos la didatecnia de tus explicaciones en la documentación oficial, sobre todo a la hora de crear ejemplos prácticos con la casuistica del tema a tratar…

  • Cristian vasquez
    Posted at 15:54h, 11 junio Responder

    Más que un post parece documentacion, un gran esfuerzo.

  • Pablo Navarrete
    Posted at 16:10h, 11 junio Responder

    Exelente articulo Paco, este tipo de contenido fuera de velneo.es es lo que da vida y valor a la comunidad.
    Gracias por el tiempo dedicado.

  • JOSE ANTONIO LOPEZ MARTINEZ
    Posted at 16:18h, 11 junio Responder

    Magníficamente explicado

  • Francisco Clemente
    Posted at 15:03h, 12 junio Responder

    Excelente aportacion, Gracias Paco siempre agradecido con tus aportaciones a la comunidad

  • Luis Miguel Domínguez
    Posted at 14:07h, 13 junio Responder

    Muchas gracias Paco por un artículo tan bueno y a Fran por ofrecer la plataforma y otros artículos igual de interesantes.

    Saludos.

  • carlos moreno
    Posted at 17:13h, 13 junio Responder

    Pues muy buen artículo, que, me ayudará a mejorar los formularios que tengo algo oxidados. Utilizo los layouts de forma exhaustiva, pero te puedo garantizar que, ahora más.
    Habrá que guardar una copia no vaya a ser que al autor del blog le dé por desaparecer…

  • Juan Figueroa
    Posted at 19:14h, 17 junio Responder

    Siempre hay algo que aprender de los maestros.
    Perfecto y completo. Como siempre
    Gracias

  • Maltrana
    Posted at 08:24h, 24 junio Responder

    Guuuuala. Pedazo Súper Post

    Grande Paco

Post A Comment

Información básica sobre Protección de Datos: Responsable: Francisco José Vila Martín. Finalidad: Gestionar y moderar los comentarios. Legitimación: Tu consentimiento. Destinatarios: Tus datos se alojarán en los servidores de Web Empresa S.L. (UE). Derechos: Tienes derecho a acceder, rectificar, limitar y suprimir los datos, así como otros derechos, como se explica en la información adicional. Información adicional: Puedes consultar la información adicional y detallada sobre protección de datos personales en mi Política de Privacidad.

Pin It on Pinterest