domingo, 29 de enero de 2023

Python. Estructuras.

Importar funciones

Cuando trabajamos con built-in functions el mismo lenguaje no provee de  ellas, por lo que no tenemos más que llamarlas y (en su caso) pasarle los atributos que corresponda. Pero ¿ qué podemos hacer si queremos trabajar con funciones creadas por nosotros o con funciones de terceros?. En este caso disponemos de la opción de importarlas.

Para simplificar la cuestión, vamos a suponer que en un proyecto antiguo hemos creado una serie de funciones, las cuales guardamos en un archivo .py y deseamos  reutilizar dichas funciones en un nuevo proyecto. Al fin y al cabo trabajar con funciones tiene precisamente la ventaja ahorrarnos crear de nuevo código ya creado antes, reutilizando las funciones propias o de terceros sin que sea necesario escribirlas de nuevo.

Cierto que podríamos resolver el problema copiándolas y pegándolas al inicio de nuestro nuevo script, pero por suerte Python dispone de una opción mucho más funcional: importar el archivo python, aunque para ser más correcto habría que hablar de archivos, módulos y librerías (lo de librería por traducción literal de library, pero en realidad habría que traducir por biblioteca).

La ejecución de la orden y su sintaxis es muy simple: al inicio de nuestro script escribimos: import NombreArchivo y ya tenemos a nuestra disposición todas las funciones que estén escritas en ese archivo.

Retomando la idea del ejemplo, suponemos que este archivo se denomina funcionesAreas.py y contiene cuatro funciones mediante las cuales se calcula el área de cuatro figuras planas básicas: cuadrado, rectángulo, triángulo y círculo.

def area_cuadrado(l):
    area = l**2
    return area
 
def area_rectangulo(b,h):
    area = b *h
    return area
  
def area_triangulo(b,h):
    area = (b*h)/2
    return area
 
def area_circulo(r):
    area= 3.1416*(r**2)
    return area

Nuestro proyecto actual (nada complicado) requiere utilizar estas funciones para crear una sencilla aplicación que nos permitirá calcular el área de la figura que elija el usuario, el cual, además deberá introducir los datos necesarios según los parámetros de la función. 

NOTA 1No voy escribir aquí el código de este script ya que es meramente accesorio, pero sí te lo dejo, al final de la entrada, como adjunto, al igual que el anterior script de las funciones y alguna referencia documental.

Aunque no tiene sentido escribir el script de nuestro proyecto actual, sí me interesa que observes el modo en que se emplea y puede emplear la expresión import y sus repercusiones:

  • Si lo expresamos como import funcionesAreas la llamada a las función que nos interese deberemos expresarla como sigue: 

variable = nombreArchivo.NombreFunción(argumento/s)

... lo que, en nuestro caso, se puede concretar par el caso del cuadrado como sigue:

resultado = funcionesAreas.area_cuadrado(23)

  • Dado que esta expresión puede resultar excesivamente larga, tenemos la posibilidad de acortarla identificando el archivo importado con un alias, usando después este alias como parte de la llamada a la función. Este es el formato que utilizamos en nuestro script...

import funcionesAreas as fa

... y este el modo de llamar a la función:

 resultado = fa.area_cuadrado(argum)

Aunque el modo que emplees depende de tus preferencias, puede ser recomendable utilizar el modo abreviado cuando la llamada resultante sea excesivamente larga, pero, en mi opinión, de no ser éste el caso, yo prefiero mantener el nombre completo, ya que facilita comprender del código resultante, especialmente cuando importamos diferentes archivos de funciones.

No es esta la única forma de acceder a archivos externos, pero por ahora es suficiente para empezar a trabajar de forma más organizada.

Fuentes:

  • Alberto Cuevas (2016:133-136). Python 3. Curso práctico. Ed. Ra-Ma.
  • Errodringer. Curso Tutorial Python. Vídeo 9   (You Tube). A continuación.


viernes, 27 de enero de 2023

Python. Estructuras.

Funciones Python. Funciones lambda

Además de la definición formal de una función que ya conocemos, existe otro modo de definir unas funciones especiales que, por su sencillez, se conocen como funciones anónimas o funciones Lambda. estas funciones pertenecen al paradigma funcional y su presencia en Python evidencia que este lenguaje también soporta este paradigma de programación del cual hablamos brevemente en una entrada anterior.


El uso de funciones Lambda se produce a partir de cierto nivel de dominio del lenguaje Python y de complejidad en la formulación de los algoritmos, pero es conveniente conocer en qué consiste y cómo se formulan estas funciones para comprender el código que nos podemos encontrar, aunque no sepamos cómo formular este tipo de funciones ni las vayamos a emplear en este momento de nuestro nivel de aprendizaje.

En principio, una función Lambda (expresión Lambda es llamada en otros lenguajes) no es otra cosa que una forma simplificada de formular una función simple, lo que  nos evitará el procedimiento ordinario de formulación de una función y nos ahorrará escribir cierto número de líneas de código.

La sintaxis básica de una función Lambda es la siguiente:

lambda argumentos:expresión  

  • lambda es la palabra reservada que identifica este tipo de función.
  • Los argumentos pueden ser 0 o tantos como se deseen (se separan con comas).
  • La expresión debe ser necesariamente única.

Observemos un ejemplo comparando la formulación estándar de una función...

def cuadrado(num):
return num**2

... y su versión como función lambda: lambda num:num**2

Si queremos dar funcionalidad de estas funciones deberemos completar el script, pero lo escrito nos permite apreciar las diferencias y similitudes entre ambas formulaciones:

  • La principal similitud es la simplicidad: no podremos expresar como función Lambda una función compleja, pero sí funciones simples como cuadrado() 
  • Y la principal diferencia también resulta evidente: mediante lambda no necesitamos una definición diferenciada y previa de la función, podemos embeberla en el código desde el que se llama a la función y utilizarla directamente.
Dado que el uso de las funciones lambda está reservado para la elaboración de código de cierta complejidad y aun no estamos en disposición de crearlo, no me voy a detener ahora en estas funciones por más tiempo. No obstante, si quieres saber algo más de ellas, te recomiendo la lectura de este breve artículo de  la web freecodecamp.org y el visionado de este vídeo del Curso Python de Píldoras Informáticas.


jueves, 26 de enero de 2023

Introducción. Modelos de programación

 Programación funcional. Conceptos básicos

Hemos estado hablando de forma reiterada de funciones y es momento para detenernos brevemente en lo que implica su uso y el basarnos en ellas para desarrollar un algoritmo.



Trabajar con funciones consiste, en esencia, en subdividir un problema general en partes y afrontar cada una de esas partes de modo independiente. Reconstruir el todo consiste en desarrollar procedimientos para interrelacionar las partes dando respuesta al interrogante que inicialmente constituyó el todo.

Aunque esto pueda parecer muy abstracto, en realidad es una forma muy común de proceder cuando nos encontramos con problemas complejos. El famoso "vamos por partes..." no es más que la vulgarización de esta filosofía de trabajo.

Desde el punto de vista de la programación (que es lo que nos ocupa), el siguiente esquema representa el procedimiento de trabajo que supone aplicar el modelo o paradigma funcional.


A partir de un problema planteado genéricamente, vamos identificando sucesivamente las diferentes partes en que se puede ir descomponiendo, ideándolas como funciones, hasta llegar a la función más simple que ya no es necesario o no es posible descompones. Este procedimiento se denomina diseño de arriba a abajo y consiste en aplicar un proceso de refinamiento gradual o por etapas.

En esto consistiría la primera fase del desarrollo del proyecto, siendo la segunda el desarrollo de cada una de las funciones y su implementación en la estructura de relaciones que hemos representado esquemáticamente en la imagen anterior. Este segundo proceso se puede desarrollar, a su vez, mediante dos estrategias básicas:
  • De arriba a abajo
  • de abajo a arriba
Las ventajas de este modo de trabajar se pueden sintetizar como sigue:
  • Simplifica la resolución de problemas complejos.
  • Facilita la comprensión del algoritmo
  • Simplifica la realización de pruebas y la depuración del código
  • Permite reutilizar código
  • Reduce el tiempo de escritura del programa
  • Genera programas más fáciles de mantener y de modificar
  • Facilita trabajar en grupo y la propia organización de este modo de trabajo.
La programación funcional hace uso intensivo de dos conceptos que podemos considerar interrelacionados: abstracción y encapsulamiento.

Aplicándola a la programación funcional, entendemos por abstracción la cualidad que posee una función de poder diferenciar su definición de su uso. Definir una función implica saber cómo es su funcionamiento interno. La abstracción nos permite desconocer dicho funcionamiento sin que ello afecte al uso de la función, siempre que respetemos su forma de funcionar. 

Desde esta perspectiva, la función se comporta como una caja negra (encapsulamiento), de modo que desconocer su código no nos impide beneficiarnos de su funcionalidad. Si la función es mejorada, seguiremos ajenos a su conocimiento, pero nos beneficiaremos de esta mejora igualmente. Esto tiene muchas ventajas (ver el listado anterior), siendo posible, por ejemplo, diferenciar el trabajo de los diferentes creadores del código, y el de estos del que precisa un usuario. También hace posible realizar mejoras parciales sin afectar al funcionamiento general del programa.

Para finalizar esta breve incursión por la programación funcional, decir que existen funciones recursivas que son especialmente interesantes por simplificar procesos de programación que, de realizar de modo lineal, supondría aplicar complejos procesos iterativas. Una función recursiva es sencillamente aquella que se llama a si misma. Veamos un sencillo ejemplo realizado en Python:

def factorial(n):
    if n==0:
        return 1
    else:
        return n * factorial(n-1) # En este punto se produce la reutilización de la función
   dentro de la misma función (recursividad)

def main():
    n=eval(input("Introduce un entero positivo para calcular su factorial: "))
    print("El factorial de ", n , " es ", factorial(n))

main()  

miércoles, 25 de enero de 2023

Python. Estructuras.

Funciones Python. Colecciones como parámetros

Para finalizar con el tema de los parámetros/argumentos de una función, trataremos en esta entrada sobre parámetros basados en colecciones de datos.


De hecho, hasta ahora hemos estado tratando los parámetros como variables o, a lo sumo, como elementos de un diccionario (por eso de palabra clave = valor), pero no hemos contemplado la posibilidad pasar un número variable de elementos, esto es: un número de elementos no predefinidos a priori. Para hacer esto podemos definir parámetros mediante fórmulas específicas: *args y **kwargs.

Los parámetros basados en colecciones permiten, por la propia condición de las colecciones de datos, hacer variable el número efectivo de datos que se pasa a la función como argumento en el momento en que es llamada.

Para identificar este tipo de parámetros, en realidad lo distintivo son los asteriscos que preceden al nombre, siendo arg (arguments) y kwargs (keyword arguments) identificadores convencionales. Lo que diferencia *args de **kwargs, es que un asterisco (*) indica que estamos empleando una lista como parámetro/argumento y dos asteriscos (**) que estamos utilizando un diccionario (keyword=value). Pero ambos tienen una utilidad básica común: hacer variable y flexible el número se elementos que se pasa como argumentos a una función.

NOTA 1Recuerda que una lista es una colección de datos de diferente tipo, ordenada, modificable que permite datos repetidos y se identifica por ir entre []. Un diccionario, por el contrario, es una colección de datos no ordenada, indexada (keyword = valor), modificable, que no admite datos duplicados y que se identifica por ir entre {}.

El uso de ambas colecciones como argumentos no supone mayor diferencia respecto al uso de variables: simplemente se separa cada argumento por una coma, aunque cuando empleamos **kwargs debemos utilizar la fórmula clave=valor.

Aunque es posible utilizar los tres tipos de componentes (variables, listas y diccionarios) a la hora de establecer los parámetros de una función, pudiendo además estar situados de cualquier posición, cuando pasamos los argumentos que se asocian a estos parámetros es necesario emplear la fórmula clave=valor (argumentos nombrados) a partir del argumento que se asocia con una colección de datos (esto es, con *args o con **kwargs), ya que, en caso contrario, pueden darse interpretaciones erróneas en la asociación parámetro-argumento. Tal sería el siguiente caso en el que definimos la función func_prueba1() con los siguientes parámetros:

def func_prueba1(a,b,*args,c)

Al pasar argumentos desde la llamada a la función...

func_prueba1(4,5,7,9,8)

... el último elemento (8) es interpretado como perteneciente/asociado al parámetro c, dando error por no estar identificado mediante keyword (palabra clave). Así que o lo identificamos convenientemente...

 func_prueba1(4,5,7,9,c=8)

... o asignamos 8 a *args y reorganizamos el posicionamiento de c en el listado de parámetros o asignamos un valor 0 a c, mientras mantenemos 8 como argumento perteneciente a *args.

                     func_prueba1(4,5,7,9,8,c='')

Además de los parámetros anteriores, también podemos utilizar colecciones de datos asignándolas a variables que, a su vez, utilizamos como argumentos. En este caso, lo que hacemos es pasar como argumento el conjunto de componentes de la colección (por ejemplo una lista, como en este script). Si observamos la salida de la función comprobaremos que efectivamente hemos pasado una colección de datos:

                    [2, 4, 6, 8] -> que implica que se trata de una lista
  4                -> que implica que funciona la built-in function len(), apropiada para el
                       tratamiento de listas 
 
Es más, si hubiéramos definido el argumento como *args, lo que obtendríamos como salida de la función...

([2, 4, 6, 8],)
1
... nos indica que Python está identificando el conjunto [2, 4, 6, 8] como una lista, pero también que entiende el conjunto de potenciales parámetros como una colección, por lo que este elemento es a la vez un elemento de esa colección de orden superior; un elemento identificado como lista, pero un único elemento, por eso cuando aplicamos la función len() nos devuelve 1 como resultado, no 4 como en el caso anterior.

Debemos tener en cuenta esta diferencia en el tratamiento de los parámetros empleados como colecciones de datos para saber cómo procesarlos mediante una función. Un segundo ejemplo de la importancia de lo anterior: si deseáramos que la función nos devolviera el elemento 0 de la lista pasada como argumento (print(a[0])), en caso de emplear la fórmula *args, ese primer elemento sería la propia lista, por lo que me devolvería [2, 4, 6, 8], mientras que si el argumento es tratado como variable, el resultado sería 2. Las implicaciones de todo esto son obvias.

martes, 24 de enero de 2023

Python. Estructuras.

 Funciones en Python. retun()

En entradas anteriores hemos estado trabajando con los datos que puede recibir una función mediante argumentos que se asocian a los parámetros que se han definido en ella. En esta entrada trabajaremos con la devolución de valores, algo que la función realiza mediante la instrucción return().

Del mismo modo que una función no tiene por qué tener parámetros, tampoco tiene por qué devolver nada: en ese caso carece de la instrucción return(), lo que altera (en Python) su consideración de función. De hecho ya hemos trabajado con este tipo de funciones propias en anteriores ocasiones.

NOTA 1. En otros lenguajes como OOo Basic, ya no estaríamos hablando de funciones, sino de subrutinas.

Ahora vamos a empezar a utilizar return() como recurso para devolver datos de la función secundaria (o de las funciones secundarias) a la(s) funciones principal(es), aunque es posible desarrollar sistemas complejos de interdependencia entre funciones, siguiendo los principios de la programación funcional. Pero no es este el camino que vamos a seguir en estos momentos: no lo haremos porque aun no corresponde, dado el momento en que se encuentra nuestro proceso de aprendizaje.

Lo que sí corresponde es explicar cómo implementamos return() en la función y que implicaciones tiene para la llamada a esa función desde el flujo principal del algoritmo. Para ello desarrollaremos y analizaremos en primer lugar un script sencillo. Posteriormente lo haremos algo más complejo para ver el modo en que podemos utilizar return() para devolver varios valores y no sólo uno.

NOTA 2En otros lenguajes (OOo Basic por ejemplo, de nuevo) una función no sólo se define como tal si consta de parámetros y devuelve valores, es que además únicamente puede devolver un valor.

Este primer ejemplo es en realidad una modificación y simplificación de un script anterior, pero nos sirve perfectamente para ejemplificar el uso simple de la instrucción return() y lo que implica en la función y en su llamada:

def funcion_suma (a, b):

    op = a + b

    return (op)

def main():

    print("Dame dos números:")

    a = eval(input("Primer número: "))

    b = eval(input("Segundo número: "))

    resultado = funcion_suma(a,b)

    print("El resultado de la operación es ", resultado)

main()

Cuando llamamos a funcion_suma() desde main(), empezamos por emplear una variable como recurso para recoger el resultado (return()) de dicha función. Posteriormente imprimimos ese resultado, aunque podemos imaginarnos cualquier otro proceso. En la función, a return() asignamos una variable (op), pero podríamos asignar directamente una expresión ( a + b), ya que ambas opciones son posibles.

Realmente este modo de funcionar no presenta mayor dificultad, lo que dota a las funciones Python de gran sencillez de uso. Pero además son sensiblemente más potentes y flexibles, como veremos a continuación en este segundo script:

def func_opera (a, b):
    su = a + b
    re = a - b
    mu = a * b
    return (su,re,mu, a/b,a**b)

def func_imprime(suma, resta, multiplica, divide, potencia):
    print("El resultado de la suma es ", suma)
    print("El resultado de la resta es ", resta)
    print("El resultado de la multiplicación es ", multiplica)
    print("El resultado de la división es ", divide)
    print("El resultado de la potenciación es ", potencia)

def main():
    print("Dame dos números:")
    a = eval(input("Primer número: "))
    b = eval(input("Segundo número: "))
    suma, resta, multiplica, divide, potencia = funcion_suma(a,b)
    imprime_res(suma, resta, multiplica, divide, potencia)

main()

Lo primero que debemos observar es que en este caso: 

  1. Utilizamos tres funciones: una principal (main()) y dos subordinadas: func_opera() y  func_imprime()
  2. Mientras que func_opera() cuenta con la instrucción return()func_imprime() no.
  3. En func_opera()return() devuelve hasta cinco datos: los tres primeros se expresan mediante variables (previamente has sido definidas como resultados de tres operaciones) y las dos últimas mediante expresiones.
  4. En main(), la llamada a func_opera() va precedida de cinco variables: cada una de ellas recoge cada uno de los valores que devuelve (la instrucción return() de) la función func_opera(), siendo el orden de presentación de dichas variables determinante del valor que recibe.
  5. Desde main() llamamos a func_imprime() pasándole como argumentos las variables que antes definimos como receptoras de los resultados que devuelve la func_opera() según expresamos antes en (4). 
  6. Dada la naturaleza de func_imprime(), las misma llamada a la función (en este caso desde main()) genera el resultado esperado: se imprimen en pantalla las cinco frases que genera esa función.
Este segundo script nos ha permitido ver el uso simultáneo de los dos de los tres  tipos de función con que cuenta Python: sin parámetros ni return(), con parámetros pero sin return() y con parámetros y return(). Vimos también cómo pasar varios datos desde return() y el efecto que tiene en la llamada a la función.

NOTA 3.  Gracias a func_imprime() pudimos observar uno de los efectos más interesantes del uso de las funciones: la simplificación y estructuración del código.

Pyhon. Estructuras.

Funciones en Python. Parámetros

Siguiendo con las funciones en Python, retomo el modo en que se definen los parámetros de una función, en este caso para analizar el uso del procedimiento de definición clave-valor.


Habíamos visto en su momento que podemos asignar/asociar valores o datos desde la función principal (main()) utilizando la vinculación parámetro-argumento mediante dos procedimientos: por posición y por nombrado (clave-valor). En ese segundo caso, la denominación del parámetro sirve de clave para us identificación mediante el argumento:

resulta = nombre_funcion(clave1 = 3, clave3 = 5)

... siendo que la función se ha definido como sigue: def nombre_funcion(clave1,clave2):

En esta entrada veremos que también es posible asignar valores explícitos asociados a los nombres de los parámetros (que asumen explícitamente funciones de clave). En ese caso, la función utiliza dichos valores como datos por defecto cuando en la llamada a la función no se aportan valores específicos como argumentos. Veamos un ejemplo que puedes descargar desde este enlace:

def funcion_param (num_a = 10, num_b = 5):

    op = num_a + num_b

    return (op)

def main():

    print("Dame dos números:")

    a = eval(input("Primer número:"))

    b = eval(input("Segundo número:"))

    resulta = funcion_param() #Llamada a funcion sin argumentos

    print("El resultado de la operación es ", resulta)

    resulta2 = funcion_param(a,b) #Llamada a función con argumentos

    print("El resultado de la operación es ", resulta2)

main()

Cuando llamamos a la función sin aportar argumentos, el resultado que devuelve la función es 15, ya que utiliza como valores los indicados por defecto en la expresión de sus parámetros, pero cuando aportamos valores como argumentos (en resulta2), la función devolverá el resultado de sumar a + b. 

Esto nos permite apreciar que funciones definidas por el usuario mantiene coherencia en su funcionamiento con el que observamos en las build-in function: cuando un parámetro es opcional, si no aportamos valor (argumento), la función emplea el disponible en su definición (el que está establecido por defecto como valor asociado a la clave en la definición de sus parámetros.

domingo, 15 de enero de 2023

Python. Interface.

 TKinter. Segunda ventana

Mientras que en la entrada anterior vimos cómo trabajar con dos frames diferentes dentro de una misma ventana, en esta veremos cómo crear ventanas secundarias. Para simplificar el procedimiento prescindiremos de las frames y trabajaremos directamente sobre la ventana.

Crear una ventana secundaria puede ser una forma de dividir la presentación de la información en una aplicación de escritorio: mientras que la ventana principal contiene la GUI principal con los controles de introducción de datos, la ventana secundaria sirve para exponer el resultado de procesamiento realizado con esos datos, esto es, cumple funciones de output.

Veremos a continuación un ejemplo muy simple (mero ejercicio en realidad) de uso de dos ventanas, una principal y otra secundaria. Este es el código del script, desarrollado a partir de esta fuente:

from tkinter import *

'''
Esta función crea una nueva ventana (secundaria), sitúa en ella una etiqueta
a la que manda el contenido de un campo de texto situado en la ventana principal.
Esta forma es válida para aplicaciones de escritorio.
'''
def envia_boton():
   ventana2 = Toplevel() # Crea la ventana secundaria
   ventana2.geometry("400x200")#dimensión
   ventana2.title("Ventana secundaria")#y título
   valor_entrada = entrada.get() #Entrada de texto desde ventana principal
   etiqueta = Label(ventana2, text="El valor introducido en la ventana principal es: " + valor_entrada).grid(row=0)
   cerrarSecundaria = Button(ventana2, text="Cerrar la ventana secundaria", command=ventana2.destroy).grid(row=3)#Cierre de ventana secundaria

root = Tk() #Creamos la ventana principal
root.title("Ventana principal")# título
root.geometry("300x100")#y dimensión

entrada = Entry(root, width=35) # campo de texto
entrada.grid(row=0)

envia = Button(root, text="Enviar", command=envia_boton).grid(row=1)#Comando de paso de contenido a ventana secundaria
cerrarPrincipal= Button(root, text="Cerrar ventana principal", command=root.destroy).grid(row=3)#Comando de cierre de la ventana principal

mainloop()

En este caso he optado por crear la ventana secundaria mediante una función que se ejecuta utilizando el comando envia. La ventana secundaria es generada por medio de la función Toplevel(), perteneciente a la librería TKinter. 

NOTA 1También se puede identificar como tk.Toplevel(), ya que se trata de una clase de TKinter.

El resto del código es muy simple y su comprensión no presenta ninguna dificultad. Además los comentarios incluidos en el código (excesivos de no ser por el carácter didáctico del propio script) permiten puntualizar cada uno de los componentes de este script, el cual puedes descargar desde este enlace.

miércoles, 4 de enero de 2023

Python. Aplicación

 Aplicación simple de escritorio

Tomando como referencia la información que aportan las fuentes consultadas y algunas más, voy a iniciar el estudio del uso de TKinter (TK) partiendo de la exposición y explicación del funcionamiento de una sencilla aplicación de escritorio basada en TK. Esta aplicación no está pensada específicamente para un SEO, pero tampoco cabe esperarlo; precisamente uno de los objetivos (finales) de este blog es desarrollar este tipo de aplicaciones. 😉


Como dije en la entrada anterior, el modelo que tenemos presente es el de creación de GUI en OOo Basic, así que bueno será explicitar el esquema básico de su desarrollo para tener presente el punto de partida.

En OOo Basic partimos de tras opciones GUI que se pueden presentar en estado puro o en diferentes combinaciones:

  • Las ventanas emergentes (MsgBox e Input/InputBox), que se construyen por entero mediante código (de hecho son las únicas que se construyen de este modo).
  • Los formularios, que se construyen manualmente implementado los controles (widgets) sobre el documento del servicio.
  • Y los cuadros de diálogo, que se construyen desde el IDE y se superponen al documento a modo de ventanas emergentes.

Formularios y cuadros de diálogo se construyen manualmente, no mediante código, aunque sí es posible manipular su funcionamiento u acceder a su contenido mediante script.

Es esta la primera diferencia que nos encontramos respecto a TK, ya que la interfaz TK que creemos mediante Python deberemos crearla directamente desde el código. Más adelante veremos cómo.

Aunque se pueden dar diferentes modos de trabajo y funcionamiento, el algoritmo que he empleado con más frecuencia en los docap que he creado se basan en el siguiente procedimiento:

La fase de entrada (input) se representa por un formulario (o cuadro de diálogo) que consta de una serie de controles, uno de ellos un botón de comando, que sugiere que detrás de la interfaz también hay código asociado (en este caso al menos una función que se activa con ese comando).

En la fase procesamiento se diferencian 5 fases las cuales describo a continuación:

    1. Con la fase 1 se asocia el contenido de los controles del formulario a variables de tipo objeto. Esto es necesario por ser los controles objetos.
    2. La fase 2 supone la asignación del contenido de las variables objeto de paso 1 a variables tipo string, integer, booleano o del tipo que corresponda. Esta fase, aunque posiblemente innecesaria, me permite trabajar con el contenido capturado de los controles con mayor comodidad y de forma más comprensible y simple.
    3. Trabajo que se desarrolla en la fase 3, representada con un icono que ilustra el procesamiento de esas variables mediante las estructuras lógicas del lenguaje.
    4. De nuevo reconvertimos las variables de la fase 2 a variables objeto (4)..
    5. ... ya que el proceso de almacenamiento (fase 5) para exponer en el soporte correspondiente (fase salida) requiere de nuevo manejar objetos.    

Finalmente, la fase de salida (output) presenta peculiaridades en un docap peculiaridad que la diferencia radicalmente de lo que podemos esperar de un programa creado con Python. Aquí la represento mediante esos dos iconos, el primero y superior se puede identificar con el documento del servicio sobre el que se construye el docap, y el inferior el soporte que puede identificarse con el anterior en unos casos, o diferenciarse en otros, pero que en todo caso se identifica con la posibilidad (opcional, pero frecuente) de trasladar a papel el resultado obtenido, previo paso por el formato .pdf, o directamente como documento .odt.

Partiendo, como prometí, del ejemplo de sencilla aplicación de escritorio construida en Python con interfaz gráfico basado en TK, voy a analizar las semejanzas y diferencias entre docap y aplicación; pero antes es necesario explicar qué identifica y cómo se entiende una aplicación (gráfica) de escritorio:

  •  Es una aplicación y no un docap, ya que no se basa ni queda delimitada por un servicio de un paquete ofimático (vg. el procesador de texto o la hoja de cálculo).
  • Se identifica como de escritorio por oposición al soporte web. Esto implica que funciona de forma independiente a cualquier programa y que no requiere acceso a la red.
  • Es gráfica por utilizar una interfaz gráfica (GUI) tanto para la entrada de datos como para la salida.
  • Y es encilla porque omitimos (en estos momentos) cualquier complejidad añadida como el almacenamiento de la información resultante. 
NOTA 1. Un ejemplo representativo de este tipo de aplicación es la calculadora, herramienta presente en todos los sistemas operativos y que frecuentemente se realiza como proyecto en esta fase del proceso de aprendizaje, aunque ahora no será el caso

Iniciar este proyecto requiere despejar algunas dudas cuya solución es previa al desarrollo de nuestra propuesta: las funciones, las librerías (bibliotecas) de funciones y su uso en un proyecto.

Efectivamente, ya sabemos que TKinter está incluido en Python, lo que quiere decir que sus librerías (bibliotecas) de funciones están disponibles sin necesidad de tener que instalarlas (cosa que no sucede con otras librerías, con lo que esto implica), pero no están disponibles directamente: hace falta llamarlas o lo que es lo mismo, importarlas.

He manejado aquí cuatro conceptos que es necesario aclarar para poder avanzar; pero no vamos hacerlo en esta entrada, ya que resultaría un texto muy largo y complejo. En su lugar, utilizaré el principio de modularidad y una de las ventajas del hipertexto para dirigirte como potencial interesad@ a documentación específica que entronca con el enfoque sistemático de aprendizaje de Python que también estamos desarrollando en este blog.

NOTA 2. De paso evitamos al usuario o usuaria que ya conozca estos temas que pierda el tiempo repasando esos conceptos con lo que nos adaptamos de este modo a los diferentes niveles de conocimientos previos del lector o lectora, practicando así uno de los principios básicos del modelo inclusivo.

  • Concepto de función y su posición en el algoritmo. Una primera aproximación en esta entrada.
  • Aproximación al concepto de paquetes y librerías (bibliotecas).
  • Instalar paquetes que no forman parte del núcleo principal del Python.
  • Importar componentes. Diferentes formas.
Para no demorar más la presentación de una primera aplicación de escritorio, y más por interés didáctico que funcional, paso a comentar mi primer script python, creación a medio camino entre un mero ejercicio y una primera (y muy básica) aplicación de escritorio basada en interfaz gráfica. Los dos archivos de que está compuesta son accesibles al final de esta entrada. Este análisis o comentario del código se realiza en comparación con la construcción paralela del mismo script en soporte OOo Basic.

NOTA 3. La utilidad práctica de esta aplicación es nula, por lo que, desde esta perspectiva, no se puede considerar un proyecto en sentido estricto, estando realmente más cerca a un ejercicio. No obstante, funcionalmente tiene todos los ingredientes para ser modelo de aplicación de escritorio, eso sí, simple en estructura y funcionamiento, pero aplicación al fin y al cabo. Sólo es cuestión de utilidad, lo que exige, además de imaginación,  posiblemente también un incremento (cuantitativo y cualitativo) de su complejidad para que sea una auténtica aplicación de escritorio. Estamos en los comienzos y es aun largo el camino por recorrer, por lo que hará falta crear muchas otras aplicaciones de este tipo para lograr niveles adecuados de habilidad y de funcionalidad.

Creé este proyecto sobre dos bases: 

  • En cuanto al contenido, basándome en la creación  de informes a partir de los datos recabados mediante formulario. Este modo de trabajo (que aquí se presenta simplificado en exceso) es muy habitual como docap OOo Basic debido a la utilidad que tiene para nuestro trabajo, de modo que son muchas los docap que he creado dentro de esta perspectiva. Partir de ella para crea una aplicación en Python facilita la comparación de procedimientos entre ambos lenguajes.
  • y a nivel de lenguaje de programación, me he basado en las fuentes documentales reseñadas en la entrada anterior, y especialmente en los vídeos de Píldoras informáticas, pero también en algunos de los videos de esta quinta fuente sin la cual, he de reconocer, me habría sido difícil resolver algunas cuestiones fundamentales para el correcto funcionamiento de la aplicación.
NOTA 4. La creación del icono de la ventana TKinter fue posible gracias a una aplicación on-line gratuita. En este enlace tienes acceso a una página interesante por los iconos que aporta. La que yo utilicé es esta otra, que los realiza a partir de una imagen que aporta el usuario o usuaria. Uno de los dos archivos que te dejo al final de la entrada es precisamente el icono, que es necesario para el correcto funcionamiento del formulario TKinter: lo debes situarlo en la misma carpeta donde copies el script de Python.

La primera diferencia que se aprecia entre OOo Basic y Python cierto es que se debe en gran parte al recurso empleado para crear el formulario en Python, ya que TKinter no dispone de soporte para crear visualmente el formulario, lo que obliga a crearlo directamente mediante código. Un breve análisis cuantitativo del código Python revela que hasta cerca del 69% del total de las palabras empleadas en su escritura están dedicadas a la creación de la interfaz, lo que deja un 32% para el funcionamiento del script, esto es: más del doble del código teclado se dedica a construir el formulario que a determinar su funcionamiento. 

NOTA 5. Cosa que sí se da en otras opciones, como  PYQT (ver Cuevas Álvarez (2016) Cap 8) sí dispone de un framework o ventaja de trabajo con esa utilidad.

La segunda diferencia tiene que ver con el acceso a contenidos externos, algo que también se puede dar en OOo Basic, pero de forma muy limitada, pero que es muy habitual en Python, lo que indica la fuerte presencia de la modularidad y el peso del paradigma funcional en éste frente al peso del modelo jerárquico y lineal de OOo Basic. Como ejemplo valgan dos cuestiones:

  • En Python iniciamos el script llamando a la librería TKinter

from tkinter import *

  •   ... y seguimos creando dos funciones básicas para el funcionamiento del script

def Guardar():

textoResult = vNombre.get() + " " +vApellidos.get()+ " cursa " + vCurso.get() + " en el " + vCentro.get()
vResultado.set(textoResult)

def Borrar():

vNombre.set("")
vApellidos.set("")
vCurso.set("")
vCentro.set("")
vResultado.set("")

... a lo que debemos añadir la función incorporada en el código que define el tercer comando (boton CERRAR), que por cierto plantea una peculiaridad respecto al modo de trabajar con funciones en Python, inexistente en OOo Basic. 

cmdCerrar = Button(vs,text="CERRAR", fg="blue",font=("Arial",12), command=raiz.destroy

NOTA 6. Esta línea nos permite observar el peso relativo de la codificación destinada a la creación del formulario (en rojo) frente a su funcionamiento (en azul). Además nos permite apreciar la posibilidad de trabajar con funciones (y funcionalidades) directamente, frente al modelo indirecto (invocar funciones) que representa el código de los otros dos comandos ( a modo de ejemplo:)

cmdVer = Button(vp,text="VISUALIZAR", fg="blue",font=("Arial",12),command=Guardar)

Me interesa destacar la simplicidad que presenta Python para asignar una función a un comando (command=Guardar), así como su versatilidad (las dos formas vistas antes, que no son las únicas).

También es de destacar la simplicidad con la que se consigue acceder al contenido de los controles (widged) en Python, frente a la complejidad que presenta en OOo Basic:

  • Primero se asigna/declara (a) una variable su calidad de variable de clase:

vNombre = StringVar()

  • Después se asocia con el contenido del control

txtNombre = Entry(vp, width=30, textvariable=vNombre, bg="#F0F8FF",...)

  • Y finalmente, en la función se trabaja con las funciones .get()/.set() para acceder a su contenido, procesarlo y trasladarlo donde sea necesario. Véase en las funciones Guardar() y Borrar() vistas antes.
Para finalizar, me interesa hacer hincapié en el bucle con el que finaliza el script, ya que sin él no llegaríamos a captar su existencia: raiz.mainloop()Como recordaremos por lo dicho en otro momento, tan importante como el propio bucle es la posición que ocupa en el script.

En resumen:
  • Con Python es mucho más sencillo crear aplicaciones similares a docap, aunque para alcanzar el mismo nivel de funcionalidad es necesario un nivel de conocimiento del lenguaje muy superior al que se necesita en OOo Basic. 
  • La construcción del soporte gráfico del formulario resulta muy complejo en Python, dado que TKinter no cuenta con soporte gráfico (framwork) para crear el formulario. Si bien es posible reducir este hándicap empleando otros recursos que sí disponen de framework, esto no significa necesariamente  una reducción significativa de la complejidad del proceso.
  • Acceder al contenido de los controles (widget) y el procesamiento de esta información es mucho más sencillo en Python que en OOo Basic. Lo es aun sin haber desarrollado todo el potencial de ese lenguaje, ya que únicamente nos hemos limitado a los enfoques jerárquico y funcional, estando pendiente trabajar desde el enfoque de la programación orientada a objetos (PPO), de la que el uso de las variables de clase es un adelanto.
  • No obstante, desde una perspectiva de funcionalidad, inversión de tiempo y esfuerzo, y aprovechamiento de los conocimientos adquiridos a partir de la práctica, desarrollar docap basados en script (OOo Basic o VBA) conlleva ventajas inmediatas sobre lo que implica el aprendizaje de lenguajes de programación, incluso del tipo de Python.
Seguiremos profundizando en estas cuestiones. De momento aquí te dejo los enlaces prometidos. Recuerda que tienes que descargar ambos y ubicarlos en las misma carpeta, sea esta cual sea.