ALGORITMOS DE ORDENACIÓN EN PYTHON: ORDENACIÓN POR MEZCLA.

Destacado

Continuamos, esta semana, con el repaso a los principales algoritmos existentes, para ordenar los elementos de una lista y sus respectivas implementaciones en Python. Hoy es el turno del llamado ‘ordenamiento por mezcla‘ (‘merge sort‘ en inglés). Consistente en una técnica eficiente basada en la estrategia «divide y vencerás». Esta metodología implica dividir repetidamente el arreglo en partes más pequeñas, ordenar cada parte por separado y luego combinarlas en un arreglo finalmente ordenado.

COMO FUNCIONA.

La singularidad del ‘ordenamiento por mezcla‘ radica en su enfoque recursivo, consistente en dividir el arreglo hasta alcanzar sub-listas de un solo elemento (lo que se conoce como el caso base). Luego, se fusionan estas sub-listas de forma ordenada, creando gradualmente una lista ordenada completa. Este algoritmo es conocido por su eficiencia en la ordenación de grandes conjuntos de datos y su estabilidad al preservar el orden de elementos con claves iguales.

IMPLEMENTACIÓN EN PYTHON.

La implementación de este algoritmo en Python se basa en la división recursiva del arreglo y la combinación de las sub-listas ordenadas. Veamos un sencillo ejemplo en el que usaremos una función (a la que hemos llamado ‘merge_sort()‘) que tomará como argumento una lista de valores desordenados (‘lista_elementos‘) y nos devolverá dicha lista ordenada (que se almacenará en la variable ‘lista_ordenada‘):

Aquí, la función ‘merge_sort()‘ va dividiendo recursivamente un arreglo en mitades más pequeñas hasta que quedan solo elementos individuales. Luego, combina y ordena esas mitades en un solo arreglo ordenado. Todo ello verificando si el arreglo tiene más de un elemento, (dividiéndolo en dos mitades en tal caso). Para después, ordenar recursivamente cada mitad y fusionar las mitades ordenadas en un solo arreglo. El proceso de fusión compara los elementos de las mitades ordenadas y los coloca en orden en el arreglo original. Después, copia cualquier elemento restante de las mitades en el arreglo. Terminado este proceso, devuelve la lista ordenada que será almacenada en la variable «lista_ordenada» que es la finalmente será impresa.

OUTPUT:

CONCLUSION:

El ‘ordenamiento por mezcla‘ destaca por su eficiencia en grandes conjuntos de datos y su estabilidad al mantener el orden entre elementos iguales. Sin embargo, puede requerir más memoria que otros algoritmos debido a su enfoque de dividir y fusionar. Además, la implementación recursiva podría impactar el rendimiento con arreglos enormes. No obstante, constituye una eficaz herramienta de ordenación utilizada en diversas aplicaciones donde se necesita una ordenación estable y eficiente de datos.

Saludos.

ALGORITMO ‘GLC’ PARA GENERACIÓN DE VALORES PSEUDO ALEATORIOS, E IMPLEMENTACIÓN EN PYTHON.

Destacado

El generador lineal congruente (‘GLC‘ o ‘LCG‘ por sus siglas en inglés) es uno de los algoritmos más simples para generar secuencias de números pseudo aleatorios. El cual, funciona mediante la aplicación de una serie de operaciones matemáticas (usando un conjunto de variables) a un valor inicial (al que solemos llamar «semilla«) para generar una secuencia de números que parecen ser aleatorios. No siéndolo en realidad, ya que, introduciendo un mismo valor para la «semilla», se obtendrá siempre la misma secuencia de valores.

COMO FUNCIONA:

La idea detrás de un GLC es bastante simple: se comienza con un valor inicial llamado «semilla» y se utiliza una serie de operaciones matemáticas para generar una secuencia de números. Efectuándose los cálculos de manera recursiva, mediante la siguiente fórmula:

Xn + 1​=(a x Xn ​+ c) x mod m

En donde ‘Xn+1‘​ es el siguiente número en la secuencia, ‘Xn‘​ es el número actual en la secuencia, ‘a‘ es un multiplicador, ‘c‘ es un incremento constante y ‘m‘ es el módulo, que determina el rango de los números generados.

Aquí, el valor de la semilla inicial ​ ‘X0‘ es crucial, ya que determina completamente la secuencia de números que se generará. De modo que si conocemos esta y los parámetros ‘a‘, ‘c‘, y ‘m‘, podemos predecir el resto de valores de la secuencia. Esto es útil en aplicaciones donde se necesita reproducibilidad, como pruebas o simulaciones.

Sin embargo, elegir valores apropiados para ‘a‘, ‘c‘, y ‘m‘ es esencial para obtener una secuencia de números pseudo aleatorios que parezcan aleatorios y que no exhiban patrones predecibles. Los valores de estos parámetros deben cumplir ciertas propiedades matemáticas para garantizar que la secuencia sea lo más uniforme y «aleatoria» posible. Elegir valores inadecuados puede resultar en secuencias con patrones evidentes o ciclos cortos.

IMPLEMENTACIÓN EN PYTHON:

Una vez visto en que consiste y como funciona el algoritmo ‘GLC‘ es hora de poner un sencillo ejemplo de implementación usando Python. Para ello crearemos un archivo al que llamaremos ‘lcg.py‘ con el siguiente código:

Como puede verse hemos creado una clase ‘LCG‘ que definimos con el constructor ‘__init__‘ que toma los cuatro parámetros: ‘seed‘ (semilla inicial), ‘a‘ (multiplicador), ‘c‘ (incremento constante), y ‘m‘ (módulo). Tras ello, el método ‘random‘, el generador actualiza su estado (almacenado en ‘self.state‘) usando la fórmula del GLC de la que hablamos más arriba pero aplicada a nuestro código Python: ‘self.state = (self.a * self.state + self.c) % self.m’. Para luego, devolver un número pseudo aleatorio normalizado en un rango de 0 a 1 mediante la división de ‘self.state‘ por ‘self.m‘. Ya fuera de la clase, se establecen los valores para los parámetros de un ‘LCG‘ específico: ‘seed‘, ‘a‘, ‘c‘, y ‘m‘. Siendo estos valores los que determinarán la secuencia resultante. Finalmente tras crear la instancia de la clase ‘LCG‘, (a la que hemos llamado ‘generador()‘) ejecutamos un bucle que llama al método ‘random‘ del generador 5 veces e imprime los números pseudo aleatorios obteniendo 5 valores pseudo aleatorios:

OUTPUT:

Obtenemos una secuencia de valores con apariencia de aletoriedad que en realidad vienen determinados por la semilla inicial (42 en este caso) introducida y el resto de variables. A su vez, podemos enmarcar ese último bucle (que se repetirá ahora 5 veces) dentro de otro que se repita 3, para ilustrar como la secuencia generada es siempre la misma:

OUTPUT:

CONCLUSIÓN:

En conclusión, hemos explorado el algoritmo Generador Lineal Congruente (GLC) y su implementación en Python. De modo que hemos visto como estos constituyen una forma simple pero poderosa de generar números pseudo aleatorios a partir de una semilla inicial mediante el empleo de operaciones matemáticas. A pesar de su simplicidad, son ampliamente utilizados en una variedad de aplicaciones, desde simulaciones y juegos hasta experimentos y modelado de procesos. Sin embargo, es crucial tener en cuenta que los parámetros de un GLC deben ser seleccionados cuidadosamente para evitar patrones predecibles en la secuencia de números generados. Además, los GLCs no son apropiados para aplicaciones críticas de seguridad, donde se requieren generadores de números aleatorios criptográficamente seguros. En resumen, comprender el GLC y su implementación en Python proporciona una base sólida para la generación de números pseudo aleatorios en una variedad de contextos, permitiendo la creación de aplicaciones y simulaciones más realistas y efectivas.

Saludos.

ALGORITMOS EN PYTHON: BUSQUEDA BINARIA.

En programación no son pocas la ocasiones en las que podemos necesitar que en un momento dado, nuestro programa realice la búsqueda de un elemento dentro de una lista ordenada (piénsese en una aplicación web en la que el nombre de usuario introducido he de buscarse en una base de datos). Este es un proceso que, para el caso de listas de pocos elementos, puede efectuarse con una simple búsqueda lineal (recorriendo la lista elemento a elemento). el problema surge cuando debemos hacer dicha búsqueda en listas extensas (de centenares, miles..de elementos) en el que dicho método de búsqueda se revela ineficiente, al hacer más lento (y con un mayor consumo de recursos) la ejecución de nuestro programa. Siendo en estos casos, más adecuados otros procedimientos como la «búsqueda binaria» del que hoy vamos a hablar e implementar en Python.

QUE ÉS Y COMO FUNCIONA:

También llamada «búsqueda de intervalo medio» o «búsqueda logarítmica«, la «búsqueda binaria« es un algoritmo que se emplea para localizar un elemento dentro de una lista o array, ordenada en el que se empieza tomando el valor del punto medio de la misma, comparándolo con el valor buscado, de modo que si ambos no coinciden se determina en cual de las dos mitades puede encontrarse el valor, desechándose aquella en la que no puede estar y repitiendo el proceso con la otra, hasta dar con dicho valor o concluir que este no se encuentra en la lista.

Veamos un sencillo ejemplo, para ilustrarlo mejor, en el que nos proponemos buscar un valor (en este caso, el 6) dentro de una lista de 7 valores numéricos ordenados de menor a mayor, cuyas posiciones se cuentan de 0 a 6:

Para la búsqueda de dicho valor daremos, en este caso, tres pasos:

1- Realizamos la comparación entre el valor buscado (2) y el elemento que ocupa la posición central dentro de la lista (8). Viéndose que este es mayor que dicho elemento buscado, lo que significa (en una lista ordenada de menor a mayor) que dicho valor (de encontrarse en la lista) estará entre los valores posicionados a la izquierda del valor central.

2- Se repite el proceso anterior con los valores de la izquierda, tomándose su elemento central (ahora el 4) y repitiéndose la misma comparación viéndose que 2<4 con lo que este tendrá que estar, otra vez, a la izquierda del valor central.

3- Finalmente llegamos al punto en al que solo queda un valor, que es (viendo el resultado de la comparación) precisamente, el buscado (2=2) con lo que queda así verificada su existencia en la lista, e identificada su posición (0).

Es posible que más de uno piense que este caso concreto podría haberse resuelto de modo más rápido mediante una búsqueda lineal al darse la doble circunstancia de tratarse de una lista de pocos elementos y hallarse el valor buscado, en la primera posición de la lista (de hecho habría bastado con un solo paso. Sin embargo no hay que olvidar que se trata de un sencillo ejemplo para ilustrar el método que emplearemos cuando tengamos que trabajar con listas extensas, en donde el elemento a buscar podrá en cada ocasión estar en cualquier posición de la misma.

Nota: En este artículo nos centramos en la búsqueda de valores numéricos, no obstante hay que recordar que este procedimiento podría aplicarse a la búsqueda de palabras en una lista, pues lo valores de comparación valen igualmente para caracteres alfabéticos. Lo importante es que nuestra lista esté ordenada.

IMPLEMENTACIÓN EN PYTHON:

A continuación vamos a ver un sencillo ejemplo de implementación de este algoritmo, usando la sintaxis de Python. En esta ocasión vamos a trabajar con una lista de 20 valores (con posiciones de 0 a 19) en la que (a diferencia del caso anterior) vamos a contemplar el peor escenario posible, en el que el valor a buscar será precisamente el ubicado al final de la lista. De todos modos, empezaremos empleando la búsqueda lineal para luego compararla con la binaria:

Output:

Tenemos aquí una sencilla función que ha recorrido la lista proporcionada como argumento, comparando cada valor con el valor buscado (20). Imprimiéndose en la salida la posición en el que este ha sido encontrado (19) y el número de comparaciones (o pasos) que se han tenido que efectuar (20 pasos).

Pasemos ahora a realizar la misma búsqueda empleado la «búsqueda binaria» objeto de esta entrada:

Output:

En este caso estamos traduciendo el procedimiento de «búsqueda binaria» a código Python, aplicándolo al mismo caso anterior, obteniendo la misma posición, pero en este caso en tan solo 5 pasos (frente a los 20 del caso anterior) lo que supone un nada despreciable ahorro de pasos en la búsqueda, ahorro que será más significativo en aquellos casos en los que necesitemos trabajar con grandes cantidades de información, que requieran una mayor optimización de recursos.

Saludos.

ESTIMACIÓN DEL VALOR DE «PI» MEDIANTE EL MÉTODO «MONTECARLO» EN PYTHON.

El método «Montecarlo» consiste en un método estadístico numérico, usado para estimar el valor de expresiones matemáticas complejas y costosas de evaluar con exactitud. El método se llamó así en referencia al Casino de Montecarlo por ser “la capital del juego de azar”, al ser la ruleta un generador simple de números aleatorios.

En general, se trata de un modelo de simulación basado en la generación de posibles resultados mediante la sustitución de un rango de valores (una distribución de probabilidad) para cualquier factor con incertidumbre inherente.

Después, calcula los resultados una y otra vez, cada vez usando un grupo diferente de valores aleatorios de las funciones de probabilidad. De esta forma, dependiendo del número de riesgos o incertidumbres y de los rangos especificados, pueden ser necesarios miles de recálculos para completar la simulación

El método de «Montecarlo» proporciona soluciones aproximadas a una gran variedad de problemas matemáticos posibilitando la realización de experimentos con muestreos de números pseudoaleatorios en una computadora. El método es aplicable a cualquier tipo de problema, ya sea estocástico o determinista. A diferencia de los métodos numéricos que se basan en evaluaciones en N puntos en un espacio M-dimensional para producir una solución aproximada.

ESTIMACIÓN DEL VALOR DE PI.

Dadas estas primeras explicaciones, procederemos a continuación a aplicar el método Montecarlo para estimar el valor de «PI«. La idea aquí consiste en generar de modo aleatorio una serie de puntos (x, y) en un plano 2-D cuadrado de lado 1. Donde a su vez insertaremos un círculo con el mismo diámetro e inscrito en dicho cuadrado. Luego calculamos la proporción de puntos numéricos que se encuentran dentro del círculo y el número total de puntos generados, para, a partir de esos datos, calcular el posible valor de «pi».

En la animación se muestra la manera en la que cambia la estimación de pi a medida que se varía el número de puntos generados.

Pero ¿cómo obtenemos el valor de pi a partir de las estimaciones del área del circulo y del cuadrado? Pues bien, partiendo de las superficies de nuestro círculo y de nuestro cuadrado:

Tenemos que:

Puesto lo que queremos averiguar aquí es el valor de «pi» despejamos este último, con lo que obtendremos:

Podemos así, calcular el valor de «pi» por el procedimiento expuesto, sabiendo aquí que la estimación del área del circulo vendrá dada por los puntos que se generen en su interior y el área del cuadrado por la totalidad de puntos generados. Con todo ello, procederemos a la estimación de «pi» mediante un sencillo script (al que daremos el nombre de «estimacion_pi.py«) cuyo código sería el siguiente:

Como se ve, lo primero que hacemos es importar el módulo «random» a través del cual vamos a generar de forma aleatoria los puntos (1000 en este caso). En la distribución, clasificaremos estos puntos en dos categorías: Aquellos puntos que están solo dentro del círculo («circle_points«) y los que se encuentran dentro del cuadrado, que son la totalidad de los mismos ya que nuestro círculo se encuentra en el mismo («square_points«). Como es natural ambos tendrán un valor inicial igual a 0. Tras esto iniciamos la generación aleatoria de los mismos cuyas coordenadas vendrán dadas por las variables «rand_x» y «rand_y» en un rango distribuido uniformemente (ya que para que el método sea válido dichos puntos han de estar distribuidos de modo uniforme) entre -1 y 1. Una vez generado el punto, calcularemos su distancia del centro («origin_dist«) .De modo que si dicha distancia es menor o igual a 1, interpretaremos que el punto se encuentra dentro del círculo incrementando en 1 el valor de «circle_points» (el valor de «square_points» será incrementado en 1 siempre, ya que la totalidad de los puntos se encuentran inscritos en el). Finalmente, una vez que tengamos el número de puntos de ambas categorías, pasaremos al cálculo de «pi» con la fórmula que ya conocemos.

Así al ejecutar nuestro código obtendremos el valor estimado de pi, partiendo de un rango de 1000 para generar 1000000 de puntos:

Este se trata de un valor meramente aproximativo que podremos ir mejorando a medida que empleemos un rango mayor para su cálculo. Así, si aplicamos un rango de 2000:

En el siguiente enlace tenéis el código del ejemplo:

https://github.com/antonioam82/ejercicios-python/blob/master/estimacion_pi.py

Saludos.

INTRODUCIENDO INFORMACIÓN OCULTA EN ARCHIVOS DE IMAGEN CON PYTHON Y «opencv».

La esteganografía es la práctica de ocultar un archivo, mensaje, imagen o video en formato binario dentro de otro archivo, mensaje, imagen o video. El origen de este término viene de las palabras griegas «steganos» (que significa oculto o cubierto) y «grafo» (que significa escritura).

Se trata de una técnica usada a menudo por los piratas informáticos para ocultar mensajes o datos secretos dentro de archivos multimedia, como imágenes, videos o archivos de audio. Aunque existen muchos usos legítimos para la esteganografía, (como la marca de agua), también se ha descubierto que los programadores de malware la utilizan para ocultar la transmisión de código malicioso. Así, en el artículo de hoy, veremos como podemos ocultar un mensaje en una imagen empleando la librería «opencv» y usando la técnica del «bit menos significativo» en el que el último bit de cada píxel (es decir: El último bit de la representación binaria del valor RGB de cada pixel) se modifica y se reemplaza con el bit de la información a camuflar. Este método solo funciona con imágenes de compresión sin pérdida (formatos PNG, TIFF y BMP), lo que significa que los archivos se almacenan en un formato comprimido, sin que produzca una pérdida o alteración de los datos del original.

En la siguiente foto se muestra de forma muy gráfica en consiste la esteganografía en imágenes usando la técnica del «bit menos significativo» («LSB»):

Para empezar, crearemos un nuevo archivo «.py» de nombre «steganography.py» en el que comenzaremos realizando las importaciones necesarias «opencv» (para la lectura y escritura del archivo de imagen) y «numpy«. Las cuales deberemos instalar previamente con «pip» :

Comencemos creando una función para convertir cualquier tipo de datos en formato binario, de modo que podamos convertir los datos secretos y los valores de píxeles a binarios tanto en la fase de codificación como de decodificación:

Tras ello, pasaremos a crear la función que se encargará de codificar la información secreta en nuestra imagen (en nuestro caso «image.PNG«):

Imagen original («image.PNG»)

Como se ve, la función «encode()«, lee primero la imagen usando la función «cv2.imread()» tras lo cual, contará los bytes máximos disponibles para efectuar la codificación de los datos. Comprobando a su vez si es posible codificar todos los datos en la imagen (y es que la longitud o tamaño de la información que se pueda ocultar dependerá del número de pixeles que tenga la imagen). A su vez, añadimos un criterio de parada, de modo que el decodificador deje de decodificar al cumplirse el criterio indicado. Finalmente se modificará el último bit de cada píxel reemplazándolo por el bit de datos. Así, al final del proceso, obtendremos una nueva imagen igual a la original pero con la información oculta incluida.

«encoded_image.PNG»: Esta imagen contiene el mensaje «This is a top secret message.» en la representación binaria de los valores RGB de parte de sus pixeles.

Una vez, creada una función para codificar la información en una imagen, vamos a proceder a implementar otra función «decode()» que lo que hará será tomar la nueva imagen obtenida con la anterior y extraer (decodificar) el mensaje o información que hemos ocultado:

Esta nueva función empezará abriendo la imagen que oculta la información, e iniciará la lectura de la misma convirtiendo los valores RGB de cada pixel, a binario, para luego agrupar esa información en bytes (1 byte=8 bits) para luego convertir cada uno de dichos bytes en cada uno de los caracteres del mensaje oculto (usando la función «chr()«).

Ya tenemos listas nuestras funciones de codificación y decodificación. Ahora solo falta hacer las llamadas oportunas a las mismas para su ejecución:

Empezamos especificando los nombres del archivo de imagen original (variable «input_image«) y el nombre del nuevo archivo de imagen (variable «output_image«) con el mensaje oculto (el cual también definiremos en la variable «secret_data«). Tras ello, pasaremos el nombre del archivo original y el mensaje, a la función «encode()» la cual generará la nueva imagen con el mensaje oculto incluido. Para asegurarnos que todo ha salido bien, ejecutaremos la función «decode()» para obtener el mensaje oculto en la nueva imagen generada en el paso anterior:

Para saber más acerca de la técnica de la esteganografía y del algoritmo del «bit menos significativo» aquí dejo el siguiente enlace que puede seros de interés:

https://es.wikipedia.org/wiki/Esteganograf%C3%ADa#:~:text=La%20esteganograf%C3%ADa%20(del%20griego%20%CF%83%CF%84%CE%B5%CE%B3%CE%B1%CE%BD%CE%BF%CF%82,no%20se%20perciba%20su%20existencia.

A su vez, en el siguiente link tenéis el código completo junto al archivo de imagen original para que hagáis la prueba por vosotros mismos:

https://github.com/antonioam82/Steganography

Saludos.

En el siguiente enlace también tenéis el código de una versión con interfaz gráfica basada en el ejercicio visto y que podéis probar:

https://github.com/antonioam82/Steganography/blob/main/esteganopy_GUI.py

Más códigos aquí:

https://www.lawebdelprogramador.com/codigo/usuario.php?id=352736

GENERANDO NUBE DE PALABRAS EN PYTHON, CON «wordcloud».

Saludos y bienvenidos una semana más a vuestro blog sobre programación en lenguaje Python, en una ocasión en la que, haciendo uso de la librería «wordcloud«, vamos a ver como podemos generar una «nube de palabras«, la cual consiste en una representación visual (frecuentemente usada en los sitios web) en la que se muestra el peso de cada una de las principales palabras dentro de un texto, de modo que estas aparecerán con un tamaño proporcional a su frecuencia de aparición en el referido texto. Como hemos señalado, vamos a usar una librería creada al efecto («wordcloud«) la cual deberemos instalar previamente en nuestro equipo usando el comando «pip install wordcloud«:

Una vez que tengamos instalada nuestra nueva librería, pasaremos a crear un nuevo archivo con el nombre «ejemplo_wordcloud.py» En el que procederemos a crear nuestra nube a partir de un texto extraído de wikipedia (para lo cual usaremos también la librería «wikipedia» que instalaremos con «pip install wikipedia«). Finalmente haremos uso también de «matplotlib» la cual emplearemos para mostrar el resultado (dicha librería la instalaremos usando el mismo procedimiento que las anteriores. Cuando lo tengamos todo, haremos las importaciones necesarias en nuestro archivo:

Como queda dicho, para nuestro ejemplo, empezaremos importando un texto sacado de wikipedia. Esto ya lo tratamos en un artículo anterior en el que decíamos que podemos extraer el resumen de u n tema concreto mediante el método «.summary()«:

Imprimiendo el contenido de la variable «info» accederemos al texto a partir del cual generaremos nuestra nube de palabras:

Una vez que tenemos nuestro texto, pasaremos a hacer uso de «wordcloud» (y más concretamente de su módulo «WordCloud«) para generar la nube, la cual, luego mostraremos con «matplotlib» y el método «imshow()«:

Llegados a este punto, ejecutamos y obtenemos el siguiente resultado en el que se puede ver el peso de cada una de las palabras más repetidas del texto:

También podemos mostrar y guardar nuestra nube de palabras haciendo uso del método «to_image()» que como indica su nombre, pasará la información de la variable «wordCloud» a una imagen que luego guardaremos en un archivo «png» con la función «.save()«:

Con el que en este caso obtendremos un archivo de imagen (al que hemos dado el nombre «wordcloud.png«) y que mostraremos con la función «.show()«:

Podéis ver el código fuente de este ejemplo en el siguiente enlace:

https://github.com/antonioam82/ejercicios-python/blob/master/ejemplo_wordcloud.py

Saludos.

ENCRIPTAR Y DESENCRIPTAR DATOS EN PYHON, CON «cryptography».

Saludos y bienvenidos una semana más a vuestro blog sobre programación en Python. Hoy vamos a hablar de una tema que no tocamos desde hace un tiempo. Y es el relativo al cifrado y encriptado de información. Concretamente, hablaremos de «cryptography«, una librería que nos permitirá realizar el encriptado de información (por ej: un mensaje de texto o un archivo) .

En el proceso de encriptado, utilizamos un algoritmo (en este caso del tipo AES) que lo que hará será generar una clave (en una archivo de extensión «.key«) a través de la cual podremos encriptar nuestra información. A su vez, al tratarse de un método de cifrado simétrico, podremos usar esa misma clave, tanto para encriptar como para desencriptar nuestra información. A su vez, sobre el mencionado algoritmo (AES), en el siguiente enlaces tenéis información acerca del método de cifrado que emplea:

https://es.wikipedia.org/wiki/Advanced_Encryption_Standard

Hecha esta introducción, empezaremos a realizar nuestro primer ejercicio, encriptando un sencillo mensaje de texto con «cryptography«, librería que deberemos instalar previamente en el equipo con «pip install cryptography«.

Para ello, empezaremos, creando una función (a la que hemos llamado «genera_clave()«) que como se deduce por su nombre, se encargará de crear el archivo «.key» (al que llamaremos «clave.key«), usando «Fernet.generate_key()» (para lo cual hemos importado, previamente el módulo «Fernet«), que luego utilizaremos tanto para encriptar la información, como para desencriptarla:

Ya tenemos la función que creará nuestra clave. No obstante, para poder realizar el cifrado de nuestro mensaje, necesitamos contar con dicha clave, por lo que necesitaremos cargarla previamente. Para ello definiremos una segunda función (de nombre «cargar_clave()» ) que, simplemente, abrirá (función «open()«) y leerá («.read()«) el archivo «clave.key«, creado anteriormente:

Pasemos ahora a aplicar ambas funciones para encriptar un mensaje de texto. Así lo primero será ejecutar la función «genera_clave()«, mediante la correspondiente llamada a la misma:

La ejecución de esta función, generará en nuestro directorio actual, el archivo «.key» al que dimos el nombre de «clave.key«. Es importante guardar este archivo por que es el que vamos a usar tanto para cifrar nuestro mensaje, como para luego descifrarlo de nuevo:

A continuación, aplicaremos esta clave para cifrar nuestro mensaje, para lo cual, cargaremos dicha clave, usando la función «cargar_clave()» previamente definida. Tras lo cual, estableceremos el mensaje a cifrar, aplicandole a su vez, el método de cifrado «encode()«:

Tras ello, pasaremos a crear la versión cifrada de «mensaje«. Para lo cual, empezaremos iniciando el método «Fernet()» (el cual, iniciado previamente, se encargará de asegurarse que nuestro mensaje no pueda leerse sin la clave creada) pasandole como argumento la clave creada y cargada «clave.key«. Una vez hecho ello, pasaremos a encriptar nuestro mensaje haciendo uso del método «encrypt()» introduciendo como argumento la variable «mensaje«:

Con esto ya tendríamos nuestro mensaje cifrado en la variable a la que hemos dado el nombre de «encriptado«. Para verlo podemos escribir «print(encriptado)«:

Tal y como hemos apuntado más arriba, podemos usar, también, nuestra clave, para descifrar mensajes cifrados previamente con ella. Para ello, usaremos el método «decrypt()«:

El resultado de tal operación lo almacenamos en la variable a la que hemos llamado «desencriptado», que, a continuación (como en el anterior caso) hemos mostrado con «print«.

ENCRIPTAR/DESENCRIPTAR ARCHIVO.

Hemos visto como encriptar un sencillo mensaje de texto. Sin embargo también podemos encriptar (y desencriptar) la información contenida en un archivo. Así, en este segundo ejemplo vamos a encriptar el contenido de un documento de texto, de nombre «texto.txt» y ubicado en nuestro directorio de ejecución:

Para encriptar su contenido, usaremos la misma clave que creamos en el caso anterior, con lo que no será necesario llamar a la función encargada de generarla. Lo que si haremos será definir las funciones «carga_clave()» y «encript_archivo()«, para cargar la clave y encriptar el contenido de nuestro documento de texto:

Como se ve, hemos definido la función «carga_clave()» del mismo modo en que lo hicimos en el primer ejemplo. No obstante la cosa cambia con respecto a la función de encriptado. Lo cual, es debido a que, previo proceso de encriptado, debemos empezar abriendo el documento (para lo cual escribimos «with open(nom_archivo,»rb») as file:«) y leerlo («file.read()«). La información leída de ese modo (variable «archivo_info«) es la que en el paso siguiente encriptaremos mediante el método «encrypt()» que vimos en el ejemplo anterior. El contenido encriptado es el que almacenaremos en la variable «encrypted_data«. Una vez obtenido el contenido encriptado, pasaremos a escribirlo en nuestro archivo original, «nom_archivo«, sustituyendo al texto original («file.write(encrypted_data)«).

Para el caso de la función de desencriptado, usaremos exactamente el mismo procedimiento, solo que usando el método «decrypt()» (para desencriptar) en lugar de «encrypt()«:

A continuación aplicaremos nuestras funciones sobre el referido documento «texto.txt«:

Así, lo primero que hacemos es definir la variable «clave«, cuyo valor será el resultado de la ejecución de la función «carga_clave()» vista con anterioridad. Tras ello, introducimos el nombre del documento de texto cuya información queremos encriptar, (variable «nom_archivo«). Finalmente, para el proceso de encriptado del contenido, llamaremos a la función «encript()«, definida más arriba, introduciéndole el nombre del documento de texto («nom_archivo«) y la clave a emplear («clave«). Una vez hecho esto, si miramos al contenido de nuestro documento, podremos ver como ha sido cifrado el texto original:

Para recuperar el contenido original, procederemos con a ejecutar la función para desencriptar el contenido (aquella a la que llamamos «desencript()«), pasandole, nuevamente, el nombre de nuestro documento de texto y la clave (ya que, como recordamos, nos vale tanto para encriptar como para desencriptar la información):

Hecha esta última operación, comprobaremos en nuestro documento, aparece, ahora, el texto original:

Como se ve, «cryptography» constituye una interesante y efectiva herramienta, que podemos usar en nuestro proyectos y a través de la cual, podremos proteger nuestros archivos e información más valiosa de eventuales intromisiones de terceros.

Podéis ver el código de la primera parte de este tutorial en el siguiente enlace:

https://github.com/antonioam82/ejercicios-python/blob/master/encryption.py

Saludos.

CREANDO GRAFICADOR DE FUNCIONES CON «matplotlib», «tkinter» Y «numpy».

Bienvenidos una vez más, a «El Programador Chapuzas». Aquellos que sigan este blog, recordarán que hace un tiempo, estuvimos viendo el modo de integrar una gráfica creada con «matplotlib«, en una ventana creada con «tkinter«. Por su parte, en una ocasión posterior, vimos también el modo de actualizar una gráfica «matplotlib» en tiempo real, a medida que íbamos cambiando los datos de entrada. Bien, en la presente ocasión, vamos a combinar los que aprendimos en aquellas 2 ocasiones, para crear un programa consistente en una ventana que representará la gráfica correspondiente a la función que introduzcamos en una entrada que se mostrará debajo de la gráfica:

Representación de»sin(x)» para un rango de -10 a 10.

Como es natural, lo primero que haremos será crear la ventana que integre nuestra gráfica (que crearemos con «matplotlib«) con los elementos que emplearemos para introducir la función a representan (y también el rango de «x») así como el botón «SET» que mostrará la representación (elementos, estos, creados con «tkinter«). No obstante, antes de iniciar esta labor, importaremos los recursos necesarios de las librerías «tkinter» (para los widgets), «matplotlib» (para mostrar la gráfica) y «numpy» (para efectuar los cálculos sobre las series de datos):

Ya que vamos a crear una ventana «tkinter» que contenga la gráfica, el siguiente paso será crear dicha ventana (a la que con «wm_title«) le daremos el nombre de nuestra aplicación «Graficador«. A su vez, también especificaremos las dimensiones de la misma (que serán adecuadas para una buena visualización gráfica):

Aunque podemos usar el que viene por defecto, también podemos especificar el estilo visual que va atener nuestra gráfica (para ello empleamos el la función «style.use()«) a la que pasaremos el nombre del estilo deseado («fivethirtyeight» en nuestro caso). Para ver el listado de estilos disponibles usaremos el método «style.available» como se muestra a continuación:

Estilos de gráfica, disponibles.

Una vez escogido el estilo de la gráfica, procederemos a crear el objeto que la representará, el cual insertaremos en el área de dibujo que crearemos a continuación:

Para ver como esta quedando la aplicación, vamos a introducir la función para visualización de gráficas, «plt.show()«:

Si ahora ejecutamos lo hecho, obtendremos el siguiente resultado:

Como se ve, hasta ahora ya tenemos creada la ventana, con el área en el que se va a dibujar nuestra gráfica. Como dijimos antes, nuestro programa va a representar la gráfica correspondiente a la función que nosotros le proporcionemos (y si queremos, también dentro del rango que pidamos). Por ello, tendremos que dotar a nuestra ventana de dos espacios en los que podamos introducir la función y el rango (que se especificará mediante dos valores separados por una coma). Dichos espacios los ubicaremos en la parte inferior de la ventana:

Como se ve, hemos añadido una entrada («Entry«) para la función a representar y otra para el rango (que se ubicará en la parte inferior derecha), acompañada de la etiqueta («Label«) «RANGO DE ‘X'». A su vez, también hemos creado un botón («Button«) «SET» con el que ordenaremos a nuestro programa que cree la gráfica a partir de los datos introducidos en la entrada. Una vez creados los elementos, pasaremos a ubicarlos en la ventana con el método «.pack()«:

Con ello, tendríamos creados (y ubicados) los elementos para la introducción de datos en el programa. Sin embargo, tal y como está ahora mismo, si introdujésemos una expresión en la entrada, y le diéramos al botón «SET» veríamos que no ocurre nada, y es que nos falta crear las funciones encargadas de tomar los datos introducidos y plasmarlos en la correspondiente gráfica.

Como lo que queremos es crear una gráfica que se actualice con los datos introducidos en las entradas (si abrir ni generar ventanas adicionales) usaremos una función (a la que llamaremos «animate()«), la cual, es la que vamos a pasar como argumento del método «FuncAnimation()«, de modo que los cambios y operaciones que realice dicha función («animate()«) son los que se plasmarán en el transcurso de la actualización (a intervalos regulares) que llevará a cabo FuncAnimation(). Para ver como funciona esto, como ejemplo, vamos a hacer que nuestra función de actualización «animate()» imprima un mensaje:

Y ejecutamos.

Así, vendría ser como un ciclo «while» con la diferencia de que aquí podemos alterar la actividad de la función durante su ejecución cíclica.

Otra función que vamos a necesitar (a la que llamaremos «represent()«), es aquella que tome los datos introducidos en las entradas (tanto de la destinada a la función a representar, así como la destinada al rango de «x«) proporcionando las variables necesarias para que la gráfica sea posteriormente dibujada por la función «anim()» (que completaremos más adelante):

Función «represent()».

Antes de continuar, hemos de tener en cuenta que a la hora de introducir una entrada, el usuario introduzca, para , por ejemplo, representar el coseno de «x«, «cos(x)«. El problema con esto se encuentra en que para hacer dicho calculo con «numpy«, habría que introducir «np.cos(x)«. De modo que para evitar dicho problema, tendremos que hacer que el programa, recibiendo como entrada «cos(x)» lo traduzca internamente como «np.cos(x)«. Para ello, haremos uso de una nueva función (de nombre «reemplazo()» mediante la que añadiremos «np.» a las funciones que lo requieran para ser calculadas por «numpy«. Para usar tal función, nos valdremos, igualmente, de un diccionario («funciones{}«) en el que incluiremos las funciones y su correspondiente cadena de reemplazo. La cadena resultante de esta función «reemplazo()» es la que finalmente guardaremos en la variable «graph_data«, que emplearemos para dibujar la gráfica:

Función «reemplazo()» y diccionario «funciones{}».

La otra entrada que va a tomar «represent()» es la correspondiente al rango de «X». Dado que más adelante vamos a necesitar tomar los dos valores del rango por separado, separaremos (usando «split()» empleando la coma «,» como separador) dichos valores («ran=rann.split(«,»)«). Puesto que vamos a dar la posibilidad de que el usuario no especifique ningún rango (ets.get()=»») estableceremos que dicha acción consistente en la separación se produzca solo si no se da tal condición («if ets.get()!=»»«).

Representación de «cos(x)» para un rango no especificado (que por defecto será de 1 a 10).

Finalmente, haremos que esta función se ejecute al pinchar en el botón «SET«, por ello añadiremos a dicho botón «command=repesent«:

Ya tenemos creada la función «represent()«, encargada de tomar y preparar los datos de las entradas. Ahora le toca el turno a la función «animate()«, que dibujará la gráfica y que se irá actualizando a intervalos regulares durante la ejecución. Dentro de la cual, empezaremos por la parte dedicada al rango:

Para empezar, la función distinguirá el hecho de que el usuario haya especificado un rango para «x» («act_rango=True«) o no («act_rango=False«). En el primer caso, nuestro programa creará dos variables («lmin» y «lmax«) que serán los limites mínimo y máximo del rango de «x«, los cuales, se corresponden con las posiciones 0 y 1 de la lista «ran» generada en la función «represent()» ya vista. A su vez, para evitar resultados erróneos que pudieran derivarse del hecho de que en la entrada se introdujese un primer valor más alto que el segundo, el establecimiento del rango, con el método «np.arange()» solo se produzca en el caso de que, efectivamente el valor de «lmin» sea menor que el de «lmax» («if lmin<lmax:«). Una vez que se haya completado esta operación de establecimiento del rango crearemos una nueva variable («ul_ran«) que almacenará el citado rango, por un motivo que veremos más adelante.

Otro posible error que podría darse es que el usuario introdujese un dato arbitrario que no tuviera nada que ver con el establecimiento de un rango (por ejemplo, una palabra cualquiera). Es por ello, que hemos hecho uso de la sentencia «try«, de modo, que si el programa no pudiera funcionar con los datos proporcionados, se produjese una excepción («except«) consistente en la muestra de una ventana de error, que crearemos introduciendo «messagebox.showwarning(«Error»,»Entrada no válida»)«. En este caso se borrará la entrada («ets.delete(0,len(ets.get()))«), una vez cerrada la ventana de error, estableciéndose, automáticamente el último rango, válido, introducido (almacenado en «ul_ran«).

Ante una entrada para el rango no válida, el programa mostrará un mensaje de error.

Lo visto, sería para el caso en el que se haya especificado rango, en caso contrario, este será de 1 a 1o, si se trata de una primera ejecución (en donde «ul_ran==»»«) o será el correspondiente al último rango introducido (si «ul_ran!=»»«):

A continuación, nuestra función «animate()» pasará a trazar la gráfica correspondiente a la expresión que queremos representar, la cual, quedó definida en «represent()» y almacenada en la variable «graph_data«:

Puesto, que, al introducir la función a representar, cabe el mismo peligro que veíamos en el caso del rango, de introducir una cadena, imposible de ser tratada (con «eval«), usaremos una sentencia «try«. De modo que si a expresión es apta, se almacenará en un variable «calculo_funcion«, para acto seguido, ser representada gráficamente con el método «.plot()» para el valor actual de cada uno de los que vaya tomando la variable «x» («ax1.plot(x,calculo_funcion)«). Si la expresión no es apta («except«), el programa no empleará «graph_data«. Simplemente representará los valores por defecto (si no se he hecho una representación correcta previamente) o dejará la última representación realizada con éxito.

Si se introduce una cadena no válida, el programa mantendrá la última gráfica representada con éxito.

Finalmente, para una mejor visibilidad, hemos marcado los ejes «x» e «y» con un tono de gris («gray«) mediante los procedimientos «axhline()» y «axwline()» respectivamente:

Y con esto tendríamos creado nuestro graficador de funciones, que nos permitirá visualizar gráficas correspondientes a funciones sencillas combinando los recursos proporcionados por las librerías «tkinter«, «matplotlib» y «numpy«:

Tenéis el código fuente en el siguiente enlace:

https://github.com/antonioam82/graficador/blob/master/graficador.py

INCLUYENDO BARRA DE HERRAMIENTAS:

No obstante aún podemos hacer más, como por ejemplo, incluir la barra de herramientas de «matplotlib» (que nos permitirá hacer cosas tales como hacer zoom, mover la imagen o guardar la gráfica creada) en la parte inferior de nuestro graficador. Para ello, lo primero que haremos será importar «NavigationToolbar2Tk» para después crear el objeto con la variable «toolbar«, tal y como se ve en la siguiente captura:

A su vez, para que la ejecución continuada no interfiera en el posible manejo de la barra de herramientas, necesitaremos que tras cada nueva actualización de la gráfica, se detenga la animación. Para los cual incluiremos el método «event_source.stop()» al final de la función «animate()«:

Sin embargo, una vez detenida la animación, necesitamos que esta se reanude posteriormente, si queremos dibujar una nueva gráfica. Es por ello que incluiremos el método «event_source.start()» en la función «represent()» (que se activará con el botón «SET«):

Esta versión mejorada del programa, que incluye la barra de herramientas de «matplotlib«, puede verse en el siguiente enlace:

https://github.com/antonioam82/graficador/blob/master/graficador_con_toolbar.py

Saludos.

CREANDO CALCULADORA DE MATRICES CON «numpy» (EJERCICIO EN PYTHON).

Muy buenas, aquí Antonio Alfonso Martínez, les da la bienvenida a «El Programador Chapuzas», en una ocasión en la que nos disponemos a crear una sencilla calculadora de matrices haciendo uso de la librería «numpy«.

Como es costumbre, lo primero que haremos será importar las librerías que vamos a necesitar para hacer funcionar nuestro programa. Así importaremos la librería «numpy» (la cual tendremos que tener previamente instalada, de modo, que de no ser así, procederemos a su instalación mediante el comando «pip install numpy«). Si trabajamos en windows, podremos también importar «subprocess» para la limpieza de pantalla tras cada ejecución:

Diseñaremos nuestro programa, de modo que, tras mostrar en pantalla las distintas operaciones con matrices que va a poder realizar, pedirá al usuario que introduzca las dimensiones (definidas por el número de columnas) de la primera matriz con la que va a operar. Ambos datos se almacenarán en las variables «fil» (para el número de filas) y «col» (para el número de columnas):

Puesto que queremos que nuestro usuario solo pueda introducir un valor entero para «fil» y «col» utilizaremos una función creada por nosotros (de nombre «OKI«) que no permita la introducción de ningún otro tipo de dato:

Con los datos proporcionados referentes al número de filas y de columnas, nuestro programa usará la función «crea_matriz()» (de la que hablaremos más adelante) para construir la matriz con la que va a operar. Dicha matriz será almacenada en la variable «acum«.

A su vez, y puesto que nuestro programa va a poder trabajar tanto con matrices como con valores numéricos, definiremos una función («dato()«), que, a partir del resultado generado por otra función («val()«), activará la función «crea_matriz()«, si el valor generado por «val()» es «M» (matriz) o pedirá un valor numérico, si el resultado devuelto por «val()» es «N» (número).

Nuestro programa también podrá efectuar operaciones entre matrices y valores naturales.

Así, nuestra función «dato()«, generará la matriz o número con el que va a trabajar a partir del valor devuelto por «val()» con la que acotaremos las posibles respuestas del usuario («M» para matriz y «N» para dato numérico) almacenado la respuesta en la variable «tipo_de_dato«. De modo que si el input introducido es «M» («if tipo_de_dato==»M»: «) se ejecutara una nueva función (a la que hemos llamado «crea_matriz()«) que tomará como argumentos el número de filas («fil«) y de columnas («col«), previamente establecidos por el usuario). Por contra, de no ser así («else:«) por haber introducido «N«, el programa nos pedirá que ingresemos un valor numérico.

Función «val()»: Para establecimiento del tipo de dato.

Tal y como hemos dicho, si el input introducido para «dato()» es «M«, se ejecutará la función «crea_matriz()» a través de la cual el programa nos pedirá los valores que conforman la matriz, cuyas dimensiones vienen dadas por las variables «fil» y «col«:

Tras la introducción de los elementos de la matriz, el programa mostrará la matriz recién creada.

Esta función lo que hará será recorrer cada columna («for c in range (col): «) para cada fila («for f in range (fil): «) e ir almacenando (mediante el correspondiente input) cada uno de los valores de la matriz, los cuales, primero se almacenaran en una lista «e_col» («e_col.append(valor)«), la cual, a su vez, luego se añadirá a la lista «e_fil» («e_fil.append(e_col)«), para, finalmente, crear la matriz, la cual, a continuación, se almacenará en la variable «matri«, con «np.array()» («matri=np.array(e_fil,float)«) Esta variable «matri» es la finalmente devolverá la funcióm («return matri«).

Una vez que nuestro programa tenga la información referente a la matriz (o al valor numérico) con la que va a trabajar, lo siguiente que hará será pedir a nuestro usuario que introduzca el operador correspondiente a la operación que desea realizar. Dicha información se almacenará en una variable a la que hemos llamado «oper«:

Una vez que el programa tenga el operador con el que trabajar, para el caso de la suma y de la resta, procederá nuevamente a pedir el tipo de dato («matr=dato()«). Tras lo cual, se efectuará la suma o la resta a la variable «acum» de la matriz, generada con la función «crea_matriz()«.

Como sabemos, para sumar y restar matrices es necesario que ambas tengan las mismas dimensiones (mismo número de dilas y de columnas). Siendo por ello por lo que el programa ha trabajado con las mismas dimensiones, inicialmente establecidas al comienzo (variables «fil» y «col«). No obstante, para el caso de la multiplicación (que efectuaremos con el método «.dot()«) debemos tener en cuenta que el número de filas de la segunda matriz ha de ser el mismo que el número de columnas de la primera:

Error al intentar multiplicar dos matrices de iguales dimensiones.

Es por ello que, para esta operación, vamos a empezar haciendo que el numero de columnas, establecido con anterioridad («col«) sea igual al de filas («fil«), tras lo cual, solo, pediremos el número de columnas que va a tener la nueva matriz. Con tal información, volveremos a aplicar la función «crea_matriz()«, cuyo resultado (la nueva matriz) se almacenará en la variable «matr«:

Tras esto, ya solo quedará multiplicar el valor matricial acumulado de operaciones anteriores (variable «acum«) por la nueva matriz creada. Tal multiplicación entre matrices lo llevaremos a cabo mediante el método «.dot()» (por ello escribiremos «acum=np.dot(acum,matr)«).

Finalmente, si el operador introducido fuera «=«, nuestro programa simplemente mostrará el valor acumulado, durante la ejecución del mismo, en la variable «acum» tal y como se ve en la imagen.

Una vez que se haya mostrado la matriz resultado, el programa nos pedirá que le indiquemos si queremos continuar con la ejecución del mismo, mediante la variable «conti«, la cual, ya hemos utilizado en ocasiones anteriores:

Y con esto tendríamos creada nuestra calculadora de matrices, con el módulo «numpy» de python.

Como viene siendo habitual, podéis ver el código en mi página de github:

https://github.com/antonioam82/calculadora-de-matrices/blob/master/calculadora_matrices.py

Repositorio:

https://github.com/antonioam82/calculadora-de-matrices

Saludos.

CREANDO UNA SENCILLA APLICACIÓN DE DIBUJO, CON «turtle» Y «tkinter».

Saludos a los lectores de «El Programador Chapuzas». Mi nombre es Antonio Alfonso Martínez, y en la presente ocasión, vamos a crear una sencilla aplicación de dibujo haciendo uso de la librería gráfica «tkinter» y la de dibujo «turtle«.

El planteamiento de nuestro programa es bien sencillo: Consistirá en un puntero que trazará líneas rectas sobre un espacio de dibujo (elementos, estos, que crearemos con «turtle«) y que podremos controlar mediante una serie de botones y opciones (creados con «tkinter«) que ubicaremos debajo del mencionado espacio de dibujo.

Como hacemos siempre, lo primero será importar los recursos necesarios para que nuestro futuro programa funcione. En este caso, solo tendremos que importar las mencionadas librerías:

Una vez que tenemos instalados los mencionados recursos, pasaremos a crear la ventana en la que incluiremos el área de dibujo de nuestra aplicación, para ello escribiremos «root = tk.Tk()«. A su vez, daremos nombre a nuestra ventana usando el método «.title()«. Para crear el área de dibujo crearemos el «canvas» especificando la ventana en la que queremos que se encuentre insertada («master = root«), el ancho, que será de 700 («width = 700«) y el alto, con el mismo valor («height = 700«). Es decir: «canvas = tk.Canvas(master = root, width = 70, height = 7oo)«:

En este punto introducimos «root.mainloop()» para mantener abierta la ventana en tanto no sea cerrada por el usuario. Vamos a ejecutar lo hecho hasta ahora:

Ya tenemos creada nuestra área de dibujo, el siguiente paso será incluir el puntero que vamos a usar para dibujar las líneas. El objeto que vamos a usar para dibujar, lo representaremos con la variable «t» («t = turtle.RawTurtle(canvas)«). El color de la línea que trazará nuestro puntero, lo podemos establecer mediante el método «.pencolor()«, dicho color será, en principio, el negro («t.pencolor(«black»)«)

Si hecho esto ejecutamos, veremos como aparece el puntero en el centro del área de dibujo:

Sin embargo, aún no podemos manejar nuestro puntero. Así que tendremos que crear los botones a través de los cuales lo rotaremos hacia la derecha (botón «Derecha«), a la izquierda (botón «Izquierda«), lo desplazaremos hacia delante por el área de dibujo, describiendo una línea (botón «Mover«) y hacia atrás (botón «Atras«):

Para la creación de cada botón, emplearemos el método «tk.Button()«, especificando la ventana en la que queremos que se ubique (ventana «root«), el texto que queremos que tenga el botón (lo que haremos con el campo «text«) y el sitio entro de la ventana en el que queremos que se sitúe (lo haremos con «side = tk.LEFT» con lo que haremos que los botones se vayan posicionando desde el extremo izquierdo).

Así ya tendríamos creados nuestros botones. Sin embargo estos aún no pueden controlar el puntero. Para ello, tendremos que asociar cada botón a una función que mueva el puntero. Por ello, el siguiente paso será crear las funciones de movimiento del puntero:

Como se aprecia en la imagen, hemos creado 4 funciones: Las funciones «izq()» y «der()» que rotarán el puntero a la izquierda (con «.left()«) y derecha («.right()«), respectivamente, un número de grados que especificaremos como argumentos de ambos métodos para movimiento, que será, en principio, de 90º (teniendo en cuenta que cuanto menor sea el angulo especificado, mayor número de inclinaciones podrá adoptar las líneas que creemos en nuestro dibujo). También definiremos una función para desplazamiento en línea recta (con una distancia de 100), hacia delante (función «mover()«) usando la función «.forward()» y otra para retroceder (función «atras()«) con el método «.back()» . Una vez definidas estas funciones asociaremos su ejecución con sus respectivos botones, con «command» tal y como se ve en la imagen.

Hecho esto, veremos que, ahora si, el puntero se moverá al pulsar en cada uno de los botones:

Un angulo de giro de 90º solo nos permitirá trazar líneas verticales y horizontales.

Con lo visto, tendremos un cursor que podemos controlar, describiendo líneas rectas, usando los botones creados. Sin embargo, podemos introducir más elementos, por ejemplo, ¿que tal si incluimos una opción para cambiar el color tanto de la línea descrita por el puntero, así como del fondo?.

Para ello, lo primero que haremos será crear una función (a la que llamaremos «color_linea()«) que partiendo de una variable «c» (que le proporcionará el respectivo botón de color que crearemos después) cambiará el color del puntero (y de las líneas descritas por este) mediante el método «.pencolor()«. Acto seguido, ubicaremos los correspondientes botones de color, junto a la etiqueta «COLOR» en el extremo derecha de la ventana:

Cada uno de los botones, usarán una función «lambda» para activar la función «color_linea()» definida previamente, agregándole como argumento, el string correspondiente al color deseado.

Hecho esto, podremos cambiar el color de la línea descrita por el puntero mediante tales botones:

Lo hecho para el color de las líneas que describa el puntero, nos sirve para establecer el color del fondo. Para lo cual, volveremos a crear los correspondientes botones de color, con la etiqueta «COLOR FONDO«, los cuales, activaran la función «color_fondo()» que cambiará el color del fondo usando el método «.screen.bgcolor()» y tomando el string de color, especificado para cada botón:

Hecho esto, ahora también podremos cambiar el color de nuestra área de dibujo:

Finalmente, vamos a introducir un último atributo para la línea: El grosor. Para ello vamos a proceder de modo similar a como lo hacíamos respecto a los colores del fondo y la línea: Creando, por una parte, una función para el cambio de grosor de la línea (función «grosor()«) que realizará tal operación, mediante el método «.pensize()» que esta vez, tomará como argumento una valor numérico que expresará dicho grosor, y que vendrá proporcionado por cada uno de los botones que llevará como texto («text=«) dicho valor (usando funciones «lambda«, como en el caso de los colores):

Y con ello, podremos elegir el grosor de la línea, que en nuestro ejemplo podrá ser de 1, 3, 5, 7 y 10:

DEFINICIÓN DE COLOR CON «colorchooser.askcolor».

Como se ve en el ejemplo, para la elección del color, hemos hecho uso de una serie de botones, los cuales, representan, cada uno de los colores que podemos aplicar a nuestra imagen. No obstante, «tkinter» nos ofrece la posibilidad de definir dichos colores mediante el uso de una ventana de selección de color. Para poder hacer uso de este recurso tendremos que, previamente, haber importado el módulo «colorchooser«:

Importado el módulo, sustituiremos los botones de color, por los botones «COLOR PINCEL» y «COLOR FONDO«, los cuales activarán, respectivamente, las funciones «color()» y «color_fondo()«:

Tal y como se aprecia en la imagen, hemos modificado ambas funciones, de modo que al ser ejecutadas, hagan uso del método «.colorchooser.askcolor()» el cual, mostrará una ventana a través de la cual, podremos establecer el color deseado:

El color, escogido al dar en «Aceptar» (que se expresará en una tupla de dos valores) se almacenará en una variable «cl» que es la que luego (tras usar una segunda variable «co«) tomarán los métodos «.pencolor()» (usada por la función «color()«) y «.screen.bgcolor()» (para el caso de la función «color_fondo()«) para establecer el color de la línea y el fondo, respectivamente. A su vez, para evitar posibles errores, haremos que el establecimiento del color, solo se lleve a cabo para el caso de que el valor de «cl» sea distinto de «(None, None)» («if cl != (None, None)«), que es valor que tomaría «cl» en el caso de que cerrásemos la ventana de elección de color, sin darle a «Aceptar«).

Y hasta aquí, este ejercicio en el que hemos visto como crear una aplicación por la que podemos controlar el trazo de líneas rectas. Obviamente «turtle» nos permite ofrece muchas más posibilidades, para dibujado, que iremos viendo en futuros artículos.

Se puede ver el código de este ejercicio (en sus dos versiones) en el siguiente enlace a github:

https://github.com/antonioam82/ejercicios-python/blob/master/tutorial_painty.py

https://github.com/antonioam82/ejercicios-python/blob/master/tutorial_painty2.py

Saludos.