4to paso: diseñar una interfaz (parte IX)

Viendo a Pier pincelado por las últimas luces del crepúsculo, me resulta aún más evidente su transformación desde aquel frágil saltimbanqui del comienzo a este comprometido actor decidido a entregar lo mejor de sí en el escenario. Y necesitaremos también de su temple para superar esta última etapa, posiblemente la de desarrollo más tortuoso de todo este extenso tutorial… ya puede olerse algo de épico en la atmósfera.
Pero no es momento de dar un paso atrás: metámonos de lleno en el análisis y resolución de esta etapa…

Etapa 4: un gato considerado

Es momento de recopilar aquellas palabras con que fueron descriptas tanto la función que le atañe a esta etapa 4 —la etapa final de nuestro proyecto— como sus metas puntuales de funcionamiento:
  • Una vez terminado su dibujo, Pier se correrá hacia el centro del escenario, recobrará su tamaño original y ofrecerá al usuario (por un lapso de 10 segundos) la opción de peticionar un nuevo dibujo: si pasado este tiempo no lo hace, nuestro amigo gatuno se despedirá con un saludo y se producirá la detención automática del programa.
De las múltiples maneras de "cerrar" un proyecto, podríamos calificar a esta como de considerada al momento de pensar en el usuario¿que complicación nos implicaría que Pier sólo se despidiese al terminar su 1er dibujo y se cerrara el programa? ¡Ninguna! Pero si el usuario quisiese ver a nuestro amigo de otra vez en acción debería pasar nuevamente por todo el proceso de presentación (etapa 1), lo que según mi punto de vista resulta engorroso y seguramente generador de fastidio.
Pero la búsqueda de un cierre más elegante y amable con aquella gente que quiera disfrutar de nuestro trabajo no estará carente de complicaciones y de sobresaltos: en la solución aquí propuesta nos enfrentaremos nuevamente a la necesidad de posibilitar el control entre scripts de nuestro proyecto. Sólo que esta vez no buscaremos que desde un script se genere el arranque de otro (lo que se soluciona con envío de mensajes)… necesitaremos ahora conseguir que desde un script pueda detenerse el accionar de otro: específicamente se pretenderá lograr que sólo un script en particular detenga su funcionamiento.

NOTA

Aunque es una aclaración ya más que recurrente, te recalco otra vez que lo analizado de aquí en más será una de las tantas soluciones que un programador podría encontrar para cumplir con las metas enunciadas… el norte que orienta nuestra elección tiene (más que nada) el fin de presentar un nuevo tema de discusión: el uso de flags (o banderas) dentro de los programas.

El cronómetro de Pier

Es conveniente recordar un poco más acerca del comportamiento esperado en esta etapa, algo mencionado al momento de planificar la estrategia de resolución del proyecto final apelando a una división del mismo en 4 etapas (lo que de hecho no te vendría de más releer).

"En la etapa 4 también hay una decisión involucrada, pero en este caso se agregará un factor temporal: la condición a ser analizada aquí es si el usuario opta por pedir un nuevo dibujo a Pier dentro de los 10 segundos pautados para indicar su decisión. Si se cumple, el flujo será redirigido al inicio de la etapa 2 , de lo contrario se finalizará el programa."

Podríamos imaginarnos entonces a Pier cronómetro en mano, listo para detener el programa si el usuario no dá señales [dentro de los 10 segundos pautados] de querer seguir usándolo. Pero deberíamos encontrar a su vez la forma de que nuestro amigo pueda detener esta "cuenta regresiva" en caso de que le sea solicitado un nuevo dibujo [algo que el usuario debe conseguir haciendo click sobre el mismísimo Pier] …llegó el momento de poner la neurona en funcionamiento.

Un primer acercamiento al problema (the plan)

Lo primero a tener en claro: en esta etapa se presentarán 2 posibles desenlaces, dependiendo de la interacción o no del usuario dentro de la ventana de tiempo estipulada (10 segundos).
Teniendo en cuenta la anterior idea del cronometrado y de su posible interrupción, una aproximación a la solución del problema podría ser plantearnos en primera instancia cómo resolverlo para el caso de que el usuario no interrumpa el conteo.
Una vez conseguido esto deberíamos pensar como hacer para que ante un cliqueado sobre Pier se vuelva a activar por un lado el funcionamiento de la etapa 2 (recordá aquel mítico diagrama de flujo), y que por otro lado se produzca la detención tanto de la cuenta regresiva como de las instrucciones que desatan la posterior finalización del programa.
Ya tenemos un plan… veamos hacia donde nos lleva.

El usuario no quiere otro dibujo

Siguiendo este último plan enunciado, vamos a pensar en un algoritmo que se adecue a las metas propuestas para esta etapa, siempre presuponiendo y por ahora que el usuario no quiere pedirle a Pier que realice otro dibujo (él se lo pierde…).
La etapa 4 va a empezar a tomar a su cargo el comando del proyecto cuando el mismo le sea delegado por la etapa previa, algo que se conseguirá mediante el envío/recepción del mensaje  repreguntar. Tomando en cuenta que no se debe alterar el dibujo realizado, la primera acción deberá ser subir lápiz.
aproximación a la solución
A partir de allí, y siempre siguiendo las metas propuestas, Pier se deberá colocar más o menos centrado respecto al escenario y recobrar su tamaño del 100%, listo para comunicarse de nuevo con el usuario.
Pier va a sorprender de nuevo al mostrar un mensaje en donde se incluye el nombre del usuario [dato que fue recolectado durante la ejecución de la etapa 1].
El mensaje dirá:   nombre, si querés que dibuje otro polígono hacé clic sobre mí … y a continuación otro aviso más indicando el tiempo asignado para la acción:   Tendrás 10 segundos para hacerlo. Hasta aquí nada que no hayamos usado alguna vez.
Para programar una acción de conteo y "de paso" mostrar a Pier "pensando" cada número del 1 al 10 vamos a necesitar del auxilio de una nueva variable, que como te imaginarás recibirá de nombre conteo.
La construcción necesaria será sencilla: el primer bloque fijará el valor inicial de la variable conteo en 0, y luego un bloque de repetición x 10 conteniendo 2 comandos: una instrucción que incremente el valor de la nueva variable en 1, seguida por un bloque pensar conteo por (1) segundo [que inherentemente incluye una espera de 1 seg.].
Siempre suponiendo por ahora que esta acción de conteo se va a llevar a cabo sin interferencia alguna, sólo resta que nuestro script se cierre con un último mensaje, esta vez de despedida:   ¡Chau nombre, nos vemos!

MUY IMPORTANTE

El script aquí ensayado sufrirá varios cambios de aquí al finalizar el análisis de esta etapa 4, ya que aún no contempla la interrupción del conteo para el caso de que el usuario peticione otro dibujo de Pier.
Aún así los bloques incorporados serán parte del script final al que arribaremos.
Llegó el momento de abrir el proyecto final sobre el cual estamos trabajando, y agregar el script que mostramos aquí arriba. Recordá que deberás crear una nueva variable de nombre conteo y que necesitarás fijarle un valor 0… si no lo hiciésemos lo que terminaría ocurriendo es que cada vez que se ejecute este grupo de instrucciones Pier empezaría a contar desde el último valor almacenado (but this is wrong! ).
Una vez hecho esto corré el programa y verificá el funcionamiento de esta etapa… si después de pensar hasta 10 Pier se despide amablemente desde el centro del escenario, con su tamaño original, muy posiblemente esté todo OK. Podés guardar los cambios.

Cliqueando a Pier  (primera discusión)

Todo objeto en Scratch tiene asociado a él un bloque tipo "sombrero" muy particular: nos brindará la posibilidad de programar scripts que se activen ante un eventual cliqueado por parte de un usuario sobre la imagen del susodicho objeto.
En nuestro caso el cliqueado sobre Pier es el medio que le damos al usuario para pedir el dibujo de otro polígono regular. Como vimos, necesitamos que esto desate dos acciones: la puesta de nuevo en funcionamiento de la etapa 2 —pregunta al usuario y posterior validación de su entrada—, y también que se detenga el script de conteo de esta etapa 4 (todavía no sabemos cómo).
Si sólo necesitásemos de la activación de la etapa 2, nos bastaría con agregar el simple script que estás viendo.
cliqueado 1
Es un ejercicio muy interesante que añadas transitoriamente este script al proyecto para ver cómo afecta al mismo… pónlo en funcionamiento muchas veces intentando ver lo que resulta de hacer clic sobre Pier en diferentes momentos.  It's a mess ! [es un lío].
En más de una ocasión te vas a encontrar a Pier contando al mismo momento que está dibujando, o preguntando por la cantidad de lados del polígono a dibujar cuando aún no concluyó el dibujo de ese momento, o incluso despidiéndose sin haberse concluído realmente la ejecución total del proyecto… lo que se dice un verdadero lío.
Desde ya que nos faltó detener el script de conteo, que es la segunda acción que se debe desencadenar al hacer clic sobre Pier.

Propondremos a continuación una solución a este problema, para después pasar a analizar si esto resulta suficiente como para dar por culminado el proyecto. Será necesario presentar un nuevo concepto (usualmente relacionado a la microprogramación), que es conocido ¿popularmente? como flag

Yo quiero a mi bandera… Luca not dead

Realmente no sé si serás de aquellos que tratan de buscar su solución al problema y sólo después leen lo aquí expuesto (mis respetos en tal caso), o de los que se conforman con seguir mi razonamiento (sesgado, discutible, personal, etcéteras).
Si sos del primer grupo quizás se te haya ocurrido alterar el script de conteo añadiendo algo como lo que ves a continuación: desde el punto de vista del razonamiento puro parece una solución impecable… pero lamentablemente no funciona.
si ratón presionado
Mejor dicho: sólo funcionaría en aquellas raras situaciones donde al ser evaluada la acción del usuario —mediante el bloque sensor ¿ratón presionado?— se verifique que el mismo está haciendo clic en ese preciso momento, ¡y todo esto aun cuando no sea un clic sobre Pier!.
Esto último podría llegar a ser corregible, pero el problema de fondo no: el problema de fondo es que el sensado no es constante en el tiempo, ya que hay lapsos de 1 segundo en donde el script se está dedicando a hacer pensar a Pier, y durante los cuales si se produce un clic del usuario el mismo no podrá ser detectado (RELEER).

Todos de vuelta a mirar al script  paralelo 

Con script paralelo me estoy refiriendo a aquel que comenzaba su ejecución al ser cliqueado Pier (los 3 bloques que bosquejamos más arriba para mandar el mensaje validar). En el mismo no hizo falta ningún sensor especial para detectar la acción externa [click del usuario] …el mismo entorno lo hace por nosotros.
Si pudiésemos TAMBIÉN controlar desde este script el proceso de conteo que está llevando a cabo el otro script tendríamos buena parte del problema allanado.
¿Cómo generar algún tipo de comunicación entre ambos? ¿usar mensajes, decís…? El obstáculo es que los bloques receptores de mensajes son todos del tipo "sombrero": sólo sirven como bloques de inicio, y el script de conteo ya tiene su propio bloque de inicio (el que está a la espera del mensaje repreguntar). DATE UN TIEMPO PARA PENSARLO.
¿Existirá alguna otra manera para que dos o más scripts compartan algún tipo de información?

La manera existe y ya la conocés: utilizar variables.

Las variables usadas como banderas —flags— 

Realmente desconozco la fuente de inspiración para que reciban funcionalmente este nombre, pero lo imagino surgido de pensar en banderas como forma de señalización: banderas en una playa indicando las condiciones del mar, banderas en los taxis indicando si están ocupados o no… algo así como un semáforo con sólo dos luces.
Más allá de esta digresión, las banderas no son más que variables que tienen como particularidad ser de tipo booleano: están pensadas para almacenar sólo 2 valores posibles —SI o NO, true o false, 1 ó 0, como quieras verlo—. Su lectura está disponible en todo momento para el programa, subrutina o script que lo necesite, a quienes también les será posible cambiar su valor (de escritura es que estamos hablando).

En un lenguaje que habilita trabajar con varios hilos de ejecución —como lo es Scratch— usar flags nos permitirá generar otra forma de comunicación [aunque más no sea pasiva], ya que un evento o cambio de situación determinado puede ser dejado asentado por un script en una variable booleana asignada a tal fin, y ese valor podrá ser consultado por cualquier otro script del programa que necesite informarse de dicha situación, y así poder tomar una decisión. Fuente: yo

Si, ya sé: toneladas de palabras para que no se entienda nada. Pero a continuación trataremos de aplicar este concepto para dar una vía de comunicación alternativa entre nuestros 2 scripts… quizás el verlo en la práctica te aclare las cosas.

Cliqueando a Pier (segunda discusión)

Primero que nada: vamos a crear una nueva variable con el fin de tener una bandera en donde dejar asentado un evento en particular: el cliqueado sobre Pier. El nombre que le daremos será flag, y estará pensada para almacenar sólo 2 valores: 0 ó 1.
En otros lenguajes esta variable debería ser definida con un tipo específico (booleano), pero la débil tipificación de Scratch y su filosofía de conversión automática de tipos —tema ya hablado— hace que el entorno tome esta decisión por nosotros.
aproximación a la solución

Problema detectado entre versiones

Los scripts mostrados fueron realizados para la versión 1.4 de Scratch, y funcionan correctamente al correr el programa completo en esa vieja versión. Pero al intentar ver el proyecto en acción desde la web de Scratch, o intentar correr el programa con la versión 2.0, no se produce la detención del conteo al ser cliqueado el objeto Pier.
Lo que detecté al corregir este malfuncionamiento es una incorrecta "traducción" de los bloques de detención detener programa y detener todo, conversión que es realizada de forma automática (tengamos en cuenta que en 2.0 aparecen nuevos bloques, y en otros aparecen cambios que no son tan ligeros).
Si agregamos una construcción condicional dentro del bucle de conteo —donde el elemento de control sea la variable flag— verificaremos que ante el caso de que el usuario no tome decisión el valor de la bandera quedará en 0, y la respuesta del script será en todo idéntica a la que ya analizamos anteriormente en esta misma página.
¿Y si el usuario hace uso de su opción? El valor de flag pasará a ser 1 (¡distinto de cero!), y cuando en el bucle de conteo se llegue al momento de chequear esta información —chequear el valor de la bandera es la clave de la comunicación— la respuesta a la pregunta flag = 0 será FALSO… y por lo tanto la instrucción que será ejecutada será detener programa.

 vs.  

Más allá de lo posiblemente trivial de esta aclaración, estos bloques ocasionan diferentes efectos sobre el programa: el 1ro sólo detiene el script que lo contiene, sin quedar afectado ningún otro script que esté activo en ese momento. El 2do detiene todos los scripts de todos los objetos de un proyecto.
Una última aclaración vinculada al script de cliqueado: como podés ver hay un bloque esperar (1) segundo previo al envío del mensaje validary tiene su razón de ser. Dado que el otro script chequea el valor de flag a intervalos de 1 segundo para ver si debe interrumpir su funcionamiento estaremos dándole tiempo para que lea el valor 1 y se detenga antes de ser enviado validar.

A probar todo esto

Volvé a abrir nuestro proyecto final, y empezá por crear la variable flag. Una vez hecho esto deberás modificar los scripts  existentes para que te queden exactamente como mostramos en nuestra última imagen.
¿Listo? Ahora a probar su funcionamiento tantas veces como puedas, dedicando especial atención en cambiar los momentos en que harás clic sobre Pier con el fin de testear nuestra interfaz bajo las más variadas condiciones… yo te espero.

¿Qué verificamos?

Si hacemos clic sobre Pieren un momento que no se corresponde a los 10 segundos asignados, el comportamiento del programa se vuelve errático (y hasta absurdo).

¿Desilusionado? ¡Arriba ese ánimo! Fijate que si nuestro potencial usuario fuese prolijo y optase por pedir un nuevo dibujo dentro de la ventana temporal de 10 segundos que decidimos ofrecerle  ¡todo funcionaría de maravillas!probalo, vas a ver…— .

Pero la única verdad es la realidad (aristotélica proposición). Esperar que un usuario se adapte a lo que nosotros necesitamos no es una opción realista para un buen programador… deberemos ser capaces de crear una interfaz a prueba de ansiosos, desprolijos y caóticos destinatarios (como solemos ser nosotros "cuando estamos del otro lado del mostrador").
El desafío del momento es, primero, diagnosticar la fuente del problema (error de análisis, mala o incompleta interpretación del mismo, error de programación, lo que sea). Una vez creyendo haber resuelto esto nos queda poder generar los cambios necesarios en el programa para sortear esta situación, y por último exponerse al veredicto de lo fáctico… prueba y error, que le dicen.

Una ventana en el tiempo

Espero que concordemos en el diagnóstico. Cuando propusimos la estrategia de dividir al proyecto en 4 etapas bien definidas estábamos pensando en una segmentación temporal, donde se decide asignar a cada una de las etapas la responsabilidad exclusiva de tener a cargo el control del programa durante el lapso de tiempo que les cupiera actuar… es por ello es que pudimos generar un diagrama de flujo que recrease esta situación. En blanco sobre negro: cuando se está ejecutando una etapa no se debería estar ejecutándose ninguna otra.
Volvamos nuestra atención al proyecto tal como lo dejamos, específicamente sobre el script de cliqueado (…le quedó ese apodo, pobre). En teoría este script corresponde a una funcionalidad de la etapa 4, y su activación debería esperarse sólo dentro del lapso de tiempo asignado a la misma.
Tal como están las cosas ¿en la práctica esto es realmente así? Decididamente no. El usuario podría hacer clic sobre Pier en cualquier momento del transcurso del programa (incluso, por ende, mientras se está ejecutando otra etapa distinta a la 4) y la serie de acciones que esto desencadena se ejecutaría paralelamente a otras, generando una respuesta imprevisible y seguramente incorrecta del mismo.

Primera conclusión: los efectos derivados del cliqueado sobre Pier deberían circunscribirse al lapso de tiempo asignado a la etapa 4, y deberían tener un efecto inocuo en cualquier otro momento.
Pero deberemos ser más estrictos aún: tal como fue pensado el comportamiento de esta etapa el cliqueado sobre Pier debe interrumpir la acción de conteo, y por lo tanto la ventana de tiempo durante la cual debería ser atendida esta acción es menor al tiempo total de trabajo de la etapa 4: sólo debe ocupar los 10 segundos —como máximo— correspondientes al conteo.

diagrama de flujo

Resumiendo:

Ya comprobamos empíricamente que si nuestro usuario es paciente y prolijo —léase… aquel que se avenga a cliquear sobre Pier sólo mientras se está dando el conteo— nuestro programa funciona OK. Por lo tanto, y a modo de antídoto contra usuarios ansiosos y/o caóticos, deberemos encontrar una forma de crear una ventana temporal sólo durante la cual el hacer clic sobre Pier tenga el efecto esperado (si, suena casi a obra de ciencia ficción cuando lo pensamos, y a mala obra de ciencia ficción cuando lo escribo). Esa será nuestra tarea en lo inmediato.

We'll make that window

Buen título para libro de Asimov (I robot y muchos etcéteras). Repasando todo: deberemos dedicar nuestros esfuerzos en construir una ventana temporal en donde sea aceptable la acción del usuario. Ya sabemos el por qué, basta pensar el cómo.
De nuevo todo apunta a un problema de comunicación (¿un reflejo de lo postmoderno?). El quid de la cuestión es poder comunicarle al script de cliqueo donde empiezan y donde terminan las ventanas de tiempo donde su acción es admisible —me viene a la mente la idea de un semáforo de tránsito, no sé si te sirve—.

¿Y quién debe comunicarle los momentos de ocurrencia de este marco temporal al script de cliqueo? Aquel quien lo sabe: el script de conteo. Y un script que debe comunicarse con otro… ¿a qué te suena?

Inevitable no pensar en banderas. Habilitar las acciones programadas para el cliqueo —justo antes de empezar el conteo— puede ser posibilitado seteando una nueva bandera que creemos a tal fin. Deshabilitarlas podría conseguirse con la maniobra opuesta: resetear esta misma bandera… (en este caso debería ocurrir al terminarse el conteo ya sea por causas naturales o por la acción del usuario).
etapa 4
Quizás te haya llamado la atención también el agregado de bloques fijar (flag) a (0): su finalidad tiene que ver con asegurarnos de que la próxima vez que se ejecute la etapa 4 encuentre a la bandera flag reseteada (este aparente abuso en la presencia de bloques fijar (flag) a (0) en realidad sirve para cubrir posibles interrupciones forzadas del proyecto que causen un inadecuado funcionamiento posterior del mismo).
De hecho sería más adecuado para nuestro proyecto agregar el reseteado de las banderas en el script correspondiente a la etapa 1 en vez de hacerlo aquí, pero para no complicar el desarrollo de nuestra implementación lo dejaremos tal como lo ves.

A destacar

Resaltemos aquí que es tan importante abrir la ventana temporal como cerrarla, ya que el funcionamiento correcto de nuestro proyecto depende en gran medida de que la misma esté abierta sólo durante lo que dura el conteo de Pier.

Aunque supongo que ya habrás puesto manos a la obra, siempre queda algún lector que lee todo atentamente antes de enroscarse con la programación. Si cerraste el proyecto final procedé a su apertura; deberás crear una nueva variable ventana destinada a ser usada como bandera, e insertar los bloques que alterarán —para bien— el funcionamiento de nuestro proyecto (recordá agregar los del reseteo de la variable flag too).
Llega la etapa del testeo: el punto crítico a verificar está vinculado al momento de cliqueo de Pier… deberás someter el proyecto a las más absurdas e inesperadas posibilidades de uso del más freaky de los usuarios imaginables. También probá a detener el programa en cualquier momento y volvé a arrancarlo, verificando que todo funciona OK —esta es otra batería de pruebas que debería superar una interfaz bien diseñada—.

Increíblemente… ¡a mí me funciona! [en Scratch 1.4]. Lo probé y recontra probé y anduvo todo perfecto.

Igualmente bajo Scratch pueden aparecer inconsistencias cuando se usan banderas como medio de comunicación entre scripts, quizás atribuibles a que tanto los valores de los estados internos de los objetos como los de las variables son "memorizados" cuando guardamos los proyectos, aún cuando estos sean interrumpidos antes de cumplirse el ciclo "natural" pensado inicialmente para los mismos (es la explicación que encontré…).
De aquí la importancia de fijar condiciones iniciales dentro de nuestros programas.
Cuando termines con tu testeo no olvides de guardar ¡por última vez! nuestro proyecto final: ya está culminado.

El principio del fin

Llegamos —por fin— al término de la implementación de la 4ta etapa de nuestro ambicioso proyecto. Tal como anticipamos su desarrollo no iba a ser sencillo, porque las metas propuestas de por sí eran bastante más complejas de materializar en lo que hace a su programación…
Por otro lado, su desarrollo nos permitió la presentación de una potente herramienta para un lenguaje que permite múltiples hilos de ejecución (como lo es Scratch) : el uso de flags.
En su momento dijimos que nos íbamos a embarcar lupa en mano en el análisis y resolución de cada una de las 4 etapas de este proyecto final, y el tránsito hacia su consecución fue lo que nos ocupó todo lo expuesto en estas últimas 4 páginas del tutorial. Será conveniente dedicar una última página para devolverle una perspectiva de unicidad a nuestro trabajo, exponiendo cada uno de los scripts definitivos que fuimos consiguiendo… only one step left to finish!

¡Y fue un desenlace épico, nomás! Ya con sólo mirar a los ojos de Pier podemos adivinar sentimientos de orgullo por la tarea cumplida, por toda la voluntad e ingenio puestos en juego, por no darse por vencido ni aún vencido —diría Almafuerte—. En esta imagen queda también simbolizado nuestro propio esfuerzo… dejemos, pues, que este sentimiento de satisfacción se irradie sin ponerle límites: el momento de la reflexión lo dejaremos para las páginas siguientes.
Última actualización: Septiembre 25, 2016

No hay comentarios.:

Publicar un comentario

© Scratch CodeLab | D153ñ0 b454d0 3n l4 pl4n71ll4 SimpleRWD d3 Oloblogger