Crear ventanas y botones en Rust con GTK

Cuando salió el Macintosh allá por 1984, fue novedosa su interfaz gráfica. En efecto, fue la primera interfaz gráfica popular y ha servido de inspiración para muchos otros sistemas de ventanas.

Hoy vamos a introducir una manera de crear interfaces gráficas de usuario (GUI) con Rust. Para ello usaremos GTK. GTK funciona sobre GNU/Linux, macOS y Windows (aunque es un poco más complicado de lo que debiera).

Para ello usaremos la fantástica crate gtk del proyecto Gtk-rs.

Instalando gtk

Añade al fichero Cargo.toml las siguiente líneas:

Y ejecuta cargo build.

Creando una ventana

Lo primero que vamos a hacer es crear una ventana.

Importamos gtk e iniciamos GTK.

Ahora podemos crear una ventana. GtkWindow es un struct con métodos asociados, por lo que podemos ajustar ciertos parámetros. En este código parece que estamos usando orientación a objetos, pero recuerdo que Rust técnicamente no tiene clases ni herencia.

Ahora vamos a añadir un evento para que cuando se pulse la X en nuestra ventana, se cierre el programa. Para ello tenemos que entender como funciona GTK. Cuando programamos en GTK lo que hacemos es configurar la aplicación y posteriormente ceder la ejecución a una función gtk_main que procesa todos los eventos según haya sido configurado. Para configurar los eventos usaremos callbacks, que en Rust se pueden implementar con closures. Las funciones que en GTK nos permiten conectar eventos siempre tienen el prefijo connect.

Ahora vamos a mostrar la ventana. Por defecto en GTK todos los widgets (todo lo que se muestra en pantalla) está oculto. Para mostrar todos los widgets que se han añadido a la ventana se suele usar show_all, que va a haciendo show de forma recursiva. Por último, le damos el control de la aplicación a gtk::main.

Una vez hecho esto, si compilamos con cargo run ya deberíamos ver una preciosa ventana GTK.

Y por supuesto, si pulsamos la X, la aplicación se cierra.

Layouts y botones en GTK

Vamos ahora a añadir dos cosas: un botón y un label que nos de un número del dado. Para poner varios elementos en una aplicación GTK es recomendable usar un layout. Voy a usar el layout Box, que permite agrupar los widgets de forma vertical u horizontal. Para los números aleatorios voy a usar la crate rand. El código final sería así:

Aquí pasan varias cosas interesantes. La primera es que usamos add para ir añadiendo de forma jerárquica los widgets. Debajo de Window está Box y debajo de Box tenemos Button y Label.

Por otra parte, vemos que la función asociada al evento del click de los botones es connect_clicked. Bien, en este ejemplo he introducido algo importante. Estoy modificando un widget desde un evento relacionado a otro widget. ¿Esto como se lleva con las reglas de propiedad/ownership de Rust? Bastante mal. Rust no puede saber si cuando se ejecuta el evento tenemos acceso al widget en cuestión que vamos a modificar. Afortunadamente, la API de gtk-rs ha sido diseñada con esto en cuenta y simplemente podemos hacer una llamada a clone para obtener otra referencia al objeto, que podemos pasar al closure (con move). Este clonado no lo es tal, sino que hace uso de Rc. Simplemente se nos presenta de forma transparente.

El ejemplo final: dibujando con Cairo

En este último ejemplo voy a añadir un widget donde se podrá ver la cara del dado que ha salido. Para ello uso un GtkDrawingArea, que permite usar la API de Cairo.

Con esto tenemos lo básico para empezar a diseñar GUIs con GTK y Rust.

 

Y sigo sin entender nada pero da igual

Hoy hace ya unos años, un susodicho fue parido. Los médicos habían predicho que nacería una semana antes, pero en un inesperado giro de los acontecimientos (plot twist para los modernos), el que iba a ser Virgo nació Libra. Y así el destino de este personaje cambió para siempre.

Desde entonces ha ido tratando de obtener respuestas, para llegar a la conclusión de que lo más interesante son las preguntas.

¿Y qué toca hacer hoy? Otros años, llegada esta fecha publicaba algo en lo que había estado trabajando, sin embargo este año no tengo nada que enseñar. Así que habrá que inventar una nueva tradición. ¡Escuchemos canciones de Monty Python todos juntos!

Quizá este año haya sido uno en los que más haya crecido personalmente. Para ser sinceros, al cumplir años el año anterior creía que este sería mi año definitivo con las mujeres. No ha sido así, aunque por el camino he ido recopilando anécdotas que solo mis más íntimos amigos conocen y que el resto de la humanidad no conocerá hasta que me aproxime a mi lecho de muerte. Sin embargo, ahora mismo no me parece algo tan importante. Quizá la clave sea dejarle de dar la importancia que le estaba dando antes. Creo que he leído a Feynman en el mejor momento que podía de mi vida, y ha supuesto una enorme influencia en mí.

Entrar en la asociación BEST, sin apenas saber mucho de ella, ha sido una de las cosas que más me ha cambiado: en mi manera de ver el mundo, en mi manera de relacionarme con la gente. Todavía tengo mucho por disfrutar de esta aventura, he conocido a multitud de gente. Curiosamente quedé por primera vez con ellos un 24 de septiembre, aunque las copas se alargaron hasta ya el día 25, mi cumpleaños. Qué curioso todo ¿verdad?

 

Leer de teclado en Rust

En muchas aplicaciones es necesario leer datos de teclado. Si bien esto podría considerarse sencillo en lenguaje como Python o Ruby, lo cierto es que sí se quiere hacer bien es complicado. Funciones como scanf de C son consideradas inseguras y en Java, hasta la llegada de la clase java.util.Scanner era un dolor de cabeza. En Rust no es distinto, es por ello que muchos tutoriales de Rust obvian esta parte. No obstante, leer de teclado no es tan difícil, como veremos a continuación.

read_line

El método principal para leer de teclado es read_line, que nos lee una línea como String. Para acceder a read_line primero necesitamos tener on objeto stdin. La manera más fácil de hacerlo es usar el módulo std::io.

El procedimiento es el siguiente, en primer lugar creamos una variable de tipo String vacía y mutable donde se va a alojar el resultado, posteriormente leemos y tratamos el resultado.

Como vemos, al leer la línea también se nos guarda el salto de línea. Si queremos quitarlo podemos usar trim.

Este código sin embargo generará una advertencia por el compilador y es que read_line genera devuelve un valor, concretamente un Result, que como vimos, sirven para el manejo de errores en Rust. Si no queremos tratar este Result con especial interés, podemos usar ok y opcionalmente especificar un mensaje de error con expect.

Si quieres tratar el error mejor puedes, pero read_line no suele fallar.

Leyendo enteros

Hasta aquí todo sencillo, porque leíamos String, en lo que entra todo lo que el usuario puede meter. Pero, ¿y si queremos leer un número de teclado? La cosa se complica. Normalmente se lee de teclado como String y luego se intenta pasar a número. Veamos como.

Como vemos, hay que importar std::str::FromStr para tener disponible las operaciones from_str en los tipos elementales. También se observa que hemos hecho un unwrap, porque from_str devuelve un Result. Este error sin embargo conviene que lo tratemos con más cuidado, pues es bastante probable que salte.

Un ejemplo ideal

En este código vamos a ver como pedir un entero, asegurándonos de que el usuario realmente introduce un entero e insistiendo hasta que finalmente introduce un entero válido.

He decidido separar la parte de pedir el número a otra función que devuelve Result para así poder usar el operador ?. También he usado print! y io::stdout().flush() en vez de println! para que tanto el mensaje como la entrada se realice en la misma línea y quede más bonito.

 

¿Por qué ha estado Adrianistán 10 días offline?

Si eres de ese tipo de personas que visitan frecuentemente páginas nada recomendables como esta, es posible que te hayas percatado de un ligero detalle. Adrianistán no iba.

Sí, ya sé que ahora mismo habréis perdido toda la confianza en mí, pero voy a detallaros qué es lo que pasó exactamente.

tl;dr: La nota del servicio técnico de Vodafone que le pongo tiende asintóticamente a cero.

Agosto

Agosto es el mes favorito del año para toquetear los servidores.

La primera quincena estuve de excavación arqueológica en Numancia, gracias a un campo de trabajo organizado por la Diputación de Soria. Así que durante esa quincena no hice nada. Si realmente os interesa, puedo explicar como fue la experiencia.

Sin embargo al llegar ya podía empezar a tocar el servidor. Quizá algunos ya lo sabéis, pero esta web está alojada en mi casa, en una Raspberry Pi 2. Al menos eso era antes, gracias a páginas chinas y a suculentas promociones de bancos que han acabado vendidos a 1 € conseguí hacerme con una Orange Pi PC, una Raspberry Pi 3, así como switch y cables Ethernet. Me dispuse a montar un clúster para este blog y algún proyecto extra más que tengo entre manos. El resultado es Mastín de Jade.

En un par de días tuve todo funcionando. Reinstalé todo de cero (¡los backups funcionan!) y de paso modifiqué la arquitectura general. Ahora WordPress está en la Pi 3, no en la 2 pero la base de datos MariaDB se ha quedado en la Pi 2.

¿Por qué te cuento esto? En realidad el hecho de modificar la arquitecura ligeramente ha provocada que no estuviese 100% operativa desde que se solucionase el verdadero problema.

 

Septiembre

Septiembre es un mes complicado para mí. El 1 de septiembre salí de casa para ser organizador de un curso BEST en Valladolid. Sí, es mi ciudad, pero eso no quiere decir que pasase/durmiese en casa. El 2 de septiembre llegó la mala noticia. El internet había dejado de funcionar en mi casa. Dejé de registrar visitas en Google Analytics. Algo pasaba. En principio se pensó que el router estaba mal. Pero no. El fallo parecía estar en el módem. ¿Cómo es que tengo módem a día de hoy? Para encontrar respuesta tendremos que retroceder en el tiempo…

 

El pasado

Hace muchos años, en Castilla y León existía una compañía de telecomunicaciones llamada Retecal. Esta compañía ofrecía servicios de televisión por cable entre otros. En su momento se contrató televisión por cable en mi casa y se montó una instalación. Esta instalación se basa en cables coaxiales. En un determinado momento (próximo a la llegada de la TDT), quitamos la televisión por cable pero la instalación no se desmontó.

Este anuncio ya da bastante pena

Años más tarde, decidimos abandonar Orange como ISP y nos pasamos a Vodafone-ONO. Contratamos fibra óptica, pero como la velocidad contratada podía pasar por el cable coaxial sin problemas nos dijeron que podían reutilizar la instalación de Retecal. Es decir, la fibra óptica llega hasta mi portal, lugar donde engancha al antiguo cable de Retecal para llegar a casa. Ese es el motivo por el que hace falta un módem aparte.

Septiembre otra vez

El módem no tiene encendidas las luces de siempre. Definitivamente el problema está por ese lado. Sin yo poder asistir a la ceremonia, el técnico visita la instalación y ¡sorpresa!, el módem tampoco está estropeado. El técnico verifica que el problema es que llega una señal muy débil al módem. Sin embargo en ese momento, la empresa subcontratada por Vodafone ya no se encarga de eso, hay que llamar a otra empresa subcontratada para verificar el cableado entre mi casa y la centralita.

We Bare Bears

A partir de ese momento, todo es infructuoso, pasan los días y no viene nadie. Llamadas a Vodafone todos los días que muchas veces se resumen en: ¿ha probado a reiniciar el router?.La situación era tan desesperante que ya buscábamos otra compañía. Si Vodafone no nos lo iba a arreglar, al menos contratábamos a otra empresa y que montase otra instalación.

Finalmente llego a casa el 11 de septiembre y sigue sin ir. Mencionar que no solo no tenía Internet, sino que el teléfono fijo también estaba inoperativo por compartir instalación.

Finalmente el día 13 aparece un técnico de otra empresa. No hay nadie en casa. Gracias a un vecino consigue entrar en los cuadros, ojo, no de la planta baja, sino del primer piso y encuentra el problema allí. Llego del Carrefour justo cuando el técnico ya casi ha terminado. Resulta que justamente de camino me había quedado unos minutos de más viendo como demolían una casa con una excavadora (superpoder de jubilado desbloqueado) y llegué por los pelos.

¡El caso es que ya iba todo! Le dimos las gracias y me puse a comprobar que todo siguiese en orden.

Evidentemente todo lo relacionado con Mastín de Jade necesitaba reajustes, el router había perdido la IP asignada y me tocó modificar los dominios. También me tocó ajustar toda la LAN, ya que el router perdió la memoria y me asignó IPs distintas a cada pieza del clúster.

En realidad hasta hoy mismo no ha funcionado de verdad, pues se me olvidó modificar una configuración de MariaDB y debido al cambio de IPs, no aceptaba conexiones entrantes desde el WordPress.

Y esta ha sido la historia de por qué Adrianistán ha estado offline ni más ni menos que 11 días.

Adivináis qué días estuvo Vodafone dándome largas

 

Autómatas celulares unidimensionales en Python

Estaba yo leyendo este verano un libro titulado Think Complexity cuando en un capítulo empezó a hablar de los autómatas celulares unidimensionales. El tema me interesó y por eso esta entrada. Veamos primero a qué nos referimos cuando hablamos de esto.

Cuando hablamos a autómatas celulares, nos referimos a pequeñas entidades independientes pero que interaccionan entre sí. Celulares porque son la unidad elemental del universo donde van a existir y autómatas porque deciden por ellas mismas, basadas en un conjunto de reglas predefinido, cuando el tiempo avanza de forma discreta (es decir, a pasos).

Este concepto abstracto puede visualizarse con facilidad si nos imaginamos una rejilla. Cada celda es una célula capaz de cambiar su estado según su entorno.

Los autómatas celulares fueron objeto de estudio de Stephen Wolfram, matemático conocido por haber diseñado el programa Mathemathica y Wolfram Alpha.

Los autómatas celulares unidimensionales son aquellos que forman parte de un universo unidimensional. Es decir, cada célula tiene una vecina a su izquierda y a su derecha. En los bordes se pueden definir varios comportamientos pero el concepto no varía. Pensemos en ello como una tira de celdas.

El estudio de estos autómatas es interesante, ya que pueden generarse patrones/situaciones muy complejas en base a unas reglas sencillas.

¿Cómo se definen las reglas?

Wolfram usó un sistema para definir las reglas de estos autómatas que hoy conocemos como Wolfram Code. Se basa en definir una tabla con los estados presentes de la célula y sus vecinas así como el valor que deberá adoptar en esa situación la célula. Como Wolfram usó células con solo dos estados, todo está en binario, y la parte baja de la tabla es un número de 8 bits. Este número se suele pasar a decimal y así se identifica.

Estados presentes 111 110 101 100 011 010 001 000
Estado futuro 0 0 1 1 0 0 1 0

Esta tabla representa la Regla 50, porque 00110010 en binario es 50.

¿Cómo se representan?

Una manera muy interesante de representar estos autómatas es poner cada paso en una fila distinta dentro de una imagen.

Una vez que sabemos esto vamos a hacer un programa en Python que nos permita observar la evolución de estos autómatas.

Usaremos el procedimiento original, que es empezar con todos los estados de los autómatas en 0 salvo el del autómata central, que será 1.

La clase Automata

La clase autómata va a contener las reglas, así como un ID y el estado que posee. Además, por cuestiones técnicas conviene guardar el estado anterior que tuvo.

Como podéis ver, rules no lleva self, es decir, va a ser una variable compartida entre todas las instancias de Automata. Esto es porque las reglas son idénticas a todos los autómatas.

La clase World

Ahora vamos a definir el universo donde residen estos autómatas. Este universo almacena una lista con los autómatas, se encarga de actualizarlos según las normas y de dibujarlos usando PIL. También he insertado el código que codifica las normas según el número en decimal.

Con esto ya lo tenemos casi todo. Ahora faltaría poner en marcha todo. La idea es simplemente crear una instancia de World, hacer unas cuantas llamadas a add, y después ir haciendo el ciclo update/draw_row. Una vez hayamos acabado, hacemos save y obtendremos un PNG con la imagen.

Código completo

Algunas reglas importantes

Regla 30

Una de las más importantes a nivel matemático. Ha sido objeto de mucho estudio, sin embargo no vamos a entrar en detalles más allá de su aspecto visual.

Vista ampliada

Regla 110

Esta regla es también muy interesante. ¡Se demostró que era Turing completa!

Vista en detalle

Regla 126

Esta regla no es tan importante, pero personalmente me parece muy bonita.

Vista ampliada

Reglas 57 y 99

Son dos reglas isomorfas. Es decir, son en realidad la misma regla pero aplicada a lados distintos. Elijo estas dos porque se aprecia muy bien el isomorfismo.

Regla 57
Regla 99

Regla 169

Vista en detalle

Regla 129

Regla 90

Es el famoso triángulo de Sierpinski.

Regla 150

Regla 105

Esta regla no tiene isomorfo.

En este artículo no he querido entrar en las complejidades matemáticas de todo esto. Es algo que todavía no entiendo así que no sería sincero por mi parte exponerlo.

Bonus: Richard Feynman y Steve Jobs

Quien me conoce sabe de sobra que uno de los personajes de la historia que más ha influido en mi vida es Richard Feynman. Debo reconocer que entré en un estado de éxtasis al descubrir que Feynman y Wolfram no solo trabajaron juntos, sino que lo hicieron alrededor de la regla 30 antes mostrada. También me sorprendió que Steve Jobs y Wolfram resultasen ser amigos de toda la vida. No dejo de sorprenderme de los contactos de ciertos personajes históricos entre sí.

Feynman a la izquierda, Wolfram a la derecha