miércoles, 11 de febrero de 2009

Organización de widgets en wxPython (Parte VIII)

.

Si has llegado aquí mediante un buscador o por simple casualidad, te cuento que estas en la quinta parte de un tutorial que empieza AQUI.

Siguiendo con la idea de hacer “ventanas reales”, hoy será el turno de crear el cuadro de diálogo de “Ajustar balance de color” del programa Gimp (v. 2.6.1). Esta ventana tiene una organización de widgets mas compleja que la que vimos en el post anterior.

Empecemos!

La ventana “Ajustar balance de color” de Gimp

Al igual que hicimos con la ventana de gedit, lo primera será identificar cuales son los Sizers que debemos usar. Una “técnica” que puede servir para “ver” que Sizers existen en la ventana, es redimensionarla y observar el comportamiento de la misma, así que veamos ahora la ventana redimensionada:


Al redimensionar la ventana, se puede ver básicamente lo siguiente:

1- El ComboBox y los Sliders crecen a lo ancho.

2- La imagen que está en el margen superior derecho, siempre queda anclada en esa esquina.

3- Los botones “Reiniciar”, “Cancelar” y “Aceptar”, quedan “anclados” en la esquina inferior derecha; pero el botón “Ayuda” que esta en la misma fila, no cambia su posición.

4- Los dos CheckBox que están arriba del botón “Ayuda”, quedan siempre a la misma distancia de este botón.

5- Del botón “Reiniciar el rango” (inclusive) hacia arriba, la posición de los widgets no varía en forma vertical.

Para ayudarte un poco mas, y al igual que antes, te voy a mostrar la ventana original, pero con algunas pistas:


Con este análisis inicial, vamos a empezar a crear la ventana con wxFormBuilder.

Para no alargar esta parte mas de lo necesario, y ya que hemos visto como hacer muchas de las cosas que necesitaremos para realizar esta ventana (crear un Frame, un Button, un StaticText, usar Flags de alineación, etc.), voy a obviar algunas capturas y simplemente te diré el paso concreto y te mostrare la imagen con el resultado final; por ejemplo tal vez te diga: “crea dos botones, luego centralos y cambia sus textos a 'Aceptar' y 'Cancelar' respectivamente”. Para que te sirva de ayuda, también iré mostrándote el “Object Tree” en cada paso, para que puedes compararlo con lo que tu vayas haciendo. Dicho esto, veamos el desarrollo paso a paso:

Paso 1:

Para empezar crearemos un Frame, y luego como contenedor general usaremos un BoxSizer (Vertical), donde en cada fila iremos poniendo los widgets necesarios. Luego de crear el BoxSizer (Vertical); en la primera fila de éste, tenemos varias cosas por agregar. Debido a que debemos ubicar mas de un widget en esta fila, esta claro que no podemos simplemente agregarlos, ya que quedarían uno debajo del otro. Para cambiar este comportamiento, y dado que en realidad esta primera fila la podemos ver como una serie celdas una al lado de la otra, lo que agregaremos, será un BoxSizer (Horizontal) y dentro de éste, los siguientes widgets:

1- En primer lugar agregaremos la imagen del icono de la ventana: . Para esto creamos un StaticBitmap y establecemos la ruta a la imagen en file_path.


2- Al lado del icono de la ventana, tenemos que crear dos filas donde habrá un StaticText en cada una de ellas, así que vamos a usar un BoxSizer (Vertical), y de este modo, al agregar los dos StaticText, estos quedarán uno debajo del otro. El StaticText superior tendrá el texto "Ajustar el balance de colores" con una fuente de 16px y en negrita. El StaticText inferior tendrá el texto "Fondo-6 (Via.png)" con una fuente de 8px. Al agregar el BoxSizer (Vertical) y los dos StaticText con sus respectivos textos y formatos, el Editor quedaría así:


3- Al final de esta fila, debemos agregar un nuevo StaticBitmap, que representa la vista previa de la imagen a la que Gimp le ajustara el balance de colores: . El StaticBitmap se lo debemos agregar al BoxSizer (Horizontal) [bsizer2], ya que la imagen esta fuera de las dos filas de la columna del medio. Al igual que antes, una vez agregado el StaticBitmap, cambiamos la ruta (file_path) para que apunte a la imagen que queremos. Presta especial atención al “Object Tree” en la siguiente imagen, para que se entienda mejor lo que digo:


En esta imagen te remarque en rojo la relación entre el “Object Tree” y el Editor. Puedes ver que la Fila 0 del BoxSizer contenedor (bsizer1), es un BoxSizer (Horizontal), que dentro tiene 3 columnas, done la primera y la última son StaticBitmaps y la del medio, es un BoxSizer (Vertical) con dos filas, que tienen un StaticText cada una.

Por el momento terminamos con la primera fila, así que vamos al paso 2.

Paso 2:

Ya fuera del BoxSizer (Horizontal) [bsizer2], agregaremos una simple linea divisoria. Para agregarla, debes ir a la solapa “Common” y clickear sobre el botón “wxStaticLine”:


En la imagen anterior, pueden ver que en el “Object Tree”, contraje el BoxSizer que representa la fila 0, y de este modo podemos notar que el StaticLine esta por fuera del BoxSizer bSizer2. Esto de ir contrayendo los Sizers en el “Object Tree”, es una buena practica para lograr mayor claridad.

Paso 3:

La tercera fila del BoxSizer (Vertical) [bsizer1] debe contener cuatro widgets uno al lado del otro. Una vez mas, para lograr esta distribución, usaremos un BoxSizer (Horizontal) donde agregaremos los widgets que irán en cada columna. Los widgets que debemos agregar son los siguientes:

1- Un StaticText con el texto “Ajustes prefijados:”


2- Un ComboBox

Este widget lo encontramos en la solapa “Common”. Como vimos en el pequeño análisis del comportamiento de la ventana, al ser redimensionada, este ComboBox debe crecer a lo ancho, es por esto (y por estar contenido en un BoxSizer [Horizontal]) que debemos establecer la propiedad proportion en “1”.


3- Un BitmapButton

Para crear este widget lo hacemos desde la solapa “Common”. Una vez agregado; para establecer cual es la imagen que debe mostrar, lo hacemos igual que con el StaticBitmap, o sea en file_path. Un detalle que podemos notar, es que al crear el BitmapButton, este posee un borde que lo rodea dando la apariencia de ser un botón, pero en la ventana original, no existen bordes, sino que parecen simples imágenes. Para cambiar este aspecto, tenemos que establecer la propiedad window_style, con el valor wxNO_BORDER activado:

4- Otro BitmapButton con su respectiva imagen establecida en file_path, y sin el borde clásico de los botones:

Paso 4:

En este paso agregaremos cinco widgets directamente en el sizer que hace de contenedor principal (bSizer1). Los widgets que agregaremos serán los siguientes:

1- En primer lugar crearemos un StatiText con el texto “Seleccione el rango para ajustar”. Para que se asimile mas a la fuente original, ponemos el texto en negrita.


2- En segundo lugar, agregamos 3 widgets de la clase RadioButton desde la solapa “Common” . Para cambiar el texto que muestra cada uno de ellos, lo hacemos editando el valor de la propiedad label. El primer RadioButton tendrá el texto “Sombras”, el segundo dirá “Tonos medios” y el tercero “Puntos de luz”.


3- Para terminar este paso, agregamos otro StaticText con el texto “Ajustar los niveles de color”, y lo ponemos en negrita.


Paso 5:

En este paso usaremos un FlexGridSizer con cuatro columnas y tres filas. El FlexGridSizer lo agregaremos en el contenedor principal (bSizer1). Una vez creado, vamos a agregar los siguientes widgets:

1- Un StaticText con el texto “Cian”.


2- Un Slider (lo encuentras en la solapa “Common”).


3- Un StaticText con el texto “Rojo”.


4- Un SpinControl (lo encuentras en la solapa “Additional”).


5- Agregamos la misma secuencia de widgets anterior (de la 1 a la 4), pero con los textos “Magenta” y “Verde” para los StaticText.


6- Hacemos lo mismo que en el paso 5, pero con los textos “Amarillo” y “Azul” para los StaticText.


Como habrás podido notar, los sliders no crecen en forma horizontal como esperaríamos, así que para que esto suceda, y dado que están dentro de la columna 1 (la segunda) de un FlexGridSizer, lo que debemos hacer es especificar el valor “1” en la propiedad growablecols, y luego activar el flag wxEXPAND de los 3 Sliders.

Paso 6:

Agregamos un botón (fuera del FlexGridSizer) con el texto “Reiniciar el rango” y lo alineamos a la derecha.

Paso 7:

Agregamos dos CheckBox con los textos “Conservar la luminosidad” y “Vista previa” respectivamente (Para cambiar el texto de un checkbox, también lo hacemos desde la propiedad label).

Paso 8:

En este paso, vamos a crear la botonera de la parte inferior. Lo primero que haremos es crear un BoxSizer (Horizontal), al cual le cambiaremos su propiedad proportion a “0”. A este BoxSizer, le agregamos un botón con el texto “Ayuda”.


Si bien podríamos agregar los otros tres botones restantes (“Reinciar”, “Cancelar” y “Aceptar”) directamente dentro del BoxSizer (bSizer5), si miras nuevamente la ventana original redimensionada, estos tres botones se deben mover juntos hacia la derecha, dejando sólo al de “Ayuda” a la izquierda. Para lograr esto, agregaremos un BoxSizer (Vertical), que si bien sólo tendrá una fila, aprovecharemos la posibilidad de que podemos cambiar la alineación horizontal de los widgets que este contenga en su interior. Para mover los tres botones juntos dentro del BoxSizer (Vertical), los ubicaremos en un GridSizer de una sola fila y tres columnas. Para verlo mejor, presta atención al “Object Tree”:


Ahora desactivaremos el flag wxEXPAND del GridSizer que contiene los tres botones, y también alineamos el GridSizer a la derecha:

Paso 9:

Ya tenemos todos los widgets en su lugar, así que ahora haremos algunos últimos retoques.

1- Establecemos la propiedad proportion en “0” del StaticBox bSizer2, para que no crezca en su altura.

2- También establecemos la propiedad proportion en “0” del StaticBox bSizer4 y el del StaticBox bSizer5, para que tampoco crezcan en su altura.


Hasta aquí esta casi todo terminado excepto por un detalle: si en este momento ejecutamos la ventana (lo cual lo puedes hacer, pulsando F5 desde wxFormBuilder), y luego la redimensionamos, verás algo como esto:


Prestando atención, si comparas esta ventana con la original, notaras que hay un error: el botón “Reiniciar el rango” no debe bajar al agrandarse la ventana, sólo debe ubicarse a la derecha, pero no bajar.

Como una solución a este problema, ubicaremos el botón dentro de un nuevo BoxSizer (Vertical). La forma mas simple de hacer esto es seleccionar el botón (m_button1) en el “Object Tree”, pulsar el botón derecho del mouse, y elegir la opción “Move into a new wxBoxSizer”:



Por último, establecemos la propiedad proportion en “0” del FlexGridSizer.


Si ahora ejecutamos la ventana y la redimensionamos, vemos que se comporta como la ventana original:


Listo, hemos terminado con esta interface. Al igual que en la ventana que hicimos en el post anterior, he tratado de concentrarme mas en lo organización de los widgets que en su apariencia, así que puede que la “copia” no sea 100% exacta en lo que se ve. Para aproximarla mas aun a la original, puedes jugar con los “bordes” de los widgets y así modificar el espaciado entre estos.

A continuación les muestro el “Object Tree” completamente expandido:


IMPORTANTE:
Si ya has estado practicando todo lo que hemos visto, o bien todavía no lo hiciste pero lo quieres intentar, podrás notar que wxFormBuilder aún es un poco inestable, así que deberías ir guardando tu trabajo a cada paso importante que vas dando.

Conclusión

Creo que con estos dos ejemplos, tienen los lineamientos para poder crear la interface que te propongas. De este modo, doy por terminado este tutorial, pero dejo abierta la posibilidad de crear dos ventanas de algún programa que ustedes propongan. En otro tutorial (seguramente, de varias partes) veremos como guardar estas interfaces como un archivo XML, y como cargarlas desde una aplicación Python, también les mostrare como hacer interfaces con las que podamos interactuar, y otros temas relacionados.

Espero que esta información te haya sido de utilidad. Me gustaría que comentes tu experiencia, para poder mejorar los tutoriales futuros.

Hasta la próxima!

Editado
Ya esta disponible la primera parte del tutorial sobre como utilizar archivos de recursos [XRC] con wxPython

8 comments

Anónimo dijo...

Hola pablo, estoy intentando aprender a utilizar wxpython,y acabo de encontrar con tu blog. La verdad que me estas dando una mano terrible. Excelente trabajo.

Neonemo.

Pablo Tilli dijo...

Hola Neonemo, que bueno que el blog te este siendo de ayuda. Si te surge alguna duda, puedes preguntar e intentare ayudarte.

Saludos!

JrcsDev dijo...

Pablo, excelente tutorial, desde anoche hice todos los ejemplos que planteas. Estoy interesado en aprender más sobre estas implementaciones gráficas. Me gustaría tener tu permiso para hacer una recopilación de todos los capítulos y pasarlos a pdf para tenerlos a la mano. Eso, incluyendo todos los créditos y la dirección al sitio. Buen material. Saludos desde Venezuela

Anónimo dijo...

hola pablo tu blog EXELENTE realmente me has ayudado a comprender los sizer.

tengo un problema a ver si sabes como hacerlo. como bien vos dijiste los sizer sirven para que la interfaz grafica se vea bien sin importar la plataforma y el tamano o resolucion de pantalla. pero cuando agregas por ejemplo un textctrl fijas un tamano de letra que se ve bien en tu resolucion pero no se como hacer que dicha font se mantenga PROPORCIONAL a los cambios de pantalla, si lo hace la caja que contiene el texto pero si los cambios de resolucion son grandes las letras pueden quedar chicas dentro de la caja o no entrar

un saludo
ruben

Anónimo dijo...

Espectacular!!!
Has conseguido que no desista en mi empeño de utilizar modo grafico en python.
Teniendo en cuenta que vengo de VB, se me hacia un mundo, pero gracias a ti, ya lo veo de otra manera.

Mil gracias!!!

Anakin

Santi Calvo dijo...

Muchísimas gracias.
He realizado todos los ejemplos del tutorial y gracias a él ya me veo capacitado para crear interfaces decentes con wxPython.

Felicidades.

Unknown dijo...

Amigo tu información me esta ayudando muchísimo. Gracias!

Unknown dijo...

Pablo todo el tutorial está muy bueno, completo y al mismo tiempo funcional para crear interfaces gráficas. Se agradece tu aporte, creo que de no haber caído en este blog me hubiese sido difícil aprender a construir GUI para usar con Python. Saludos y felicitaciones!

Publicar un comentario