REPRODUCTOR DE AUDIO CON «tkinter» Y «pyaudio».

Bienvenidos una semana más a «El Programador Chapuzas» vuestra página sobre programación en Python en la que en esta ocasión nos proponemos programar un sencillo reproductor de audio (cuyo código completo podéis ver en el enlace que dejamos al final) con «tkinter«, el cual, tendrá la particularidad de permitirnos añadir nuestros temas (los cuales habremos buscado previamente a través de la navegación por nuestro sistema de carpetas) favoritos a un «listbox» de modo que podamos oírlo de modo directo seleccionándolo de la lista. Dicho esto, el reproductor que vamos a crear tendría este aspecto:

Como se ve, se trata de un reproductor que cuenta con algunos de los elementos típicos de este tipo de aplicaciones (contador de tiempo, espacio para mostrar el título, botón de búsqueda, de ‘play’, ‘stop’. A lo que e añade un botón para incorporar el archivo seleccionado a un ‘listbox‘ el cual mostrará su contenido cada vez que iniciamos la aplicación y que nos permitirá un acceso más directo a los archivos contenidos en el mismo.

Como solemos hacer, empezaremos creando el apartado gráfico de nuestra aplicación, no sin antes importar las librerías y recursos que vamos a emplear:

En este caso, importamos «tkinter» para la interfaz, «wave» para abrir los archivos de audio, «pyaudio» para reproducirlos, «threading» para los procesos en paralelo, «json» (del que hablamos la semana pasada) para serializar la información de acceso a los elementos del «listbox» y «os» para mostrar el directorio de ejecución.

Antes de continuar, nos aseguraremos de tener instalados todos estos recursos, de modo que aquel que no tengamos, procederemos a instalarlo mediante el comando «pip«:

Pasemos ahora a diseñas el aspecto gráfico de nuestra aplicación, empezando, como no, por definir las dimensiones de la ventana (mediante el método «geometry«) en la que se ubicarán los distintos elementos, el color de fondo (mediante el atributo «bg» del método «configure()«) y el título de la ventana (con el método «title()«):

El siguiente paso será el de ir introduciendo los diferentes elementos interactivos de nuestra interfaz, empezando por el temporizador, el espacio para el nombre del audio y los botones principales:

Como se ve, utilizamos las funciones «Button()«, «Label()» y «Entry()» para crear los botones, la etiqueta para el contador de tiempo y la entradas para mostrar el nombre del archivo abierto y el directorio de ejecución, respectivamente. Para la muestra del directorio de ejecución usamos la variable «self.currentDir» cuyo valor será el referido directorio que obtenemos con «os.getcwd()«. En este punto también crearemos la variable «self.filename» cuyo valor será el nombre del archivo abierto.

Pasemos a los elementos de la parte inferior, consistentes en dos botones para eliminar el contenido íntegro (o algún elemento) del «listbox» un «Label» indicador del número de elementos y el propio «listbox» al que asociaremos la pertinente barra lateral de «scroll». Aquí, a las funciones vistas antes, añadiremos la función «Listbox()» que mostrará los elementos guardados en nuestra lista de favoritos «self.fav_list«. Este elemento (al igual que las barras de scroll que crearemos mediante la función «Scrollbar()«) lo insertaremos dentro de un componente «Canvas» que habremos definido con anterioridad («self.canvas=Canvas(self.root)«):

Pasemos a continuación a crear las funciones que se encargarán de hacer funcionar nuestro reproductor. Aunque antes debemos tener en cuenta que para almacenar el título y ruta de los audios añadidos a la lista de favoritos (el «listbox«) almacenaremos tal información en un archivo «JSON» con estructura de diccionario y al que llamaremos «data.json» y que deberá estar ubicado en el mismo directorio de ejecución. No obstante podemos hacer que el programa, antes de mostrar la ventana compruebe si dicho archivo se encuentra presente en el directorio, creándolo en el caso de que no sea así:

Una vez asegurada la presencia del archivo «data.json«, haremos que se muestren los archivos guardados en el «listbox«. Esta función (a la que hemos llamado «show_list()» será la primera que se ejecute al iniciar el programa:

Ahora pasemos a definir la función de selección de archivo (que se ejecutará al clickar en el botón «SEARCH») a la que hemos llamado «open_file()«:

La función «open_file()» tiene como cometido, navegar por el sistema de archivos y carpetas para seleccionar el archivo (en formato «wav») que luego podremos reproducir y/o almacenar en nuestra lista de favoritos. No sin antes ejecutar las funciones «stop_music()» (para parar el audio que pueda estar reproduciéndose) y «if_any_selected()» que devolverá «True» o «False» en función de si hay o no, algún elemento seleccionado en las lisbox de modo que en caso afirmativo, proceder a deseleccionarlo con la función «selection_clear()«.

La función «is_any_selected()» recorrerá los elementos del listbox comprobando si se encuentra o no seleccionado. En cuyo caso se rompe el ciclo y devuelve el valor «True».

Una vez seleccionado el archivo, lo normal es querer reproducir el audio, para ello usamos las funciones «init_task()» (para iniciar con «threading» el proceso de reproducción en paralelo) y «music()» (para reproducir el archivo):

Tenemos aquí, en primer lugar la función «init_task()» que se encarga de iniciar el proceso en paralelo consistente en ejecutar la función «music()» (encargada de reproducir el audio haciendo uso de las librerías «wave» y «pyaudio«). Dicha función «init_task()» se inicia ejecutando «is_any_selected()» para comprobar si hay algún elemento seleccionado en el listbox. Esto es importante en la medida que de estar seleccionado algún elemento, el archivo a leer será ese.

A su vez, también crearemos la función (a la que hemos llamado «add()«) encargada de añadir un archivo abierto al lisbox de modo que el acceso a este sea más rápido la próxima vez que ejecutemos el programa:

Del mismo modo que hemos creado una función para añadir elementos a la lista de favoritos, también crearemos otras dos funciones encargadas de eliminar la totalidad de los elementos o solo uno previamente seleccionado. Para ello usaremos las funciones «remove_playlist()» y «remove_from_list()«:

Nótese que en ambos casos, antes de ejecutarse la acción que modifica el contenido de «data.json«, usamos la función «messagebox.askquestion()» para que el usuario confirme la realización de la misma:

Finalmente, definiremos las funciones encargadas de hacer funcionar el contador de tiempo durante la reproducción de los audios. Dichas funciones son «clear_counter()» (para establecer el valor inicial de los minutos, segundos y horas), «counter_format()» (para mantener durante toda la ejecución el formato de dos dígitos para los minutos y segundos) y «timer_count()» (para representar el transcurso del tiempo en el marcador durante la reproducción):

Estas serían las funciones que nos permitirán hacer funcionar nuestro reproductor. Eso sin olvidar que algunas de ellas se han de ejecutar al pinchar en un determinado botón. Por ello para terminar, usaremos el atributo «command» para vincular cada botón con la ejecución de la correspondiente función:

Dejando a nuestra aplicación lista para ser usada.

Tal y como señalamos al principio de este artículo, en el siguiente enlace encontrareis el código completo del programa junto con el archivo «data.json«:

https://github.com/antonioam82/Music-Player

Saludos.

TECLADO MUSICAL (PASO A PASO) CON PYTHON Y «pydub».

Hace un par de semanas, estuvimos viendo el modo de generar tonos de audio haciendo uso de la librería «pydub» (la cual instalamos mediante el comando «pip install pydub«). Lo que hoy nos proponemos hacer es poner en practica los conocimientos adquiridos entonces para crear un sencillo teclado musical (usando «tkinter«) de dos octavas como el que mostramos a continuación:

Como siempre, lo primero que haremos es importar los recursos que vamos a necesitar, recordando que debemos instalar previamente tanto la librería «pydub» como el software «ffmpeg«:

Importados los recursos que vamos a emplear, pasaremos a crear nuestro teclado, empezando por definir sus dimensiones, título y color:

A continuación, procedemos a introducir los distintos elementos de nuestro teclado, empezando por las teclas blancas (usando la función «Button»):

Pasando a continuación a hacer los mismo con las teclas negras:

A partir de ahora, nuestra labor será la de hacer que nuestro teclado emita un sonido para cada tecla, pudiendo a su vez variar una serie de características tales como la duración de cada nota, el tipo de onda, la amplitud de esta, el volumen y el desvanecido tanto inicial como final. Para poder estableces tales parámetros, necesitaremos incluir una serie de elementos en nuestra interfaz:

En este bloque, nos hemos encargado de introducir los elementos gráficos. Concretamente un «Entry» para establecer la duración en milisegundos de cada nota (para lo cual también hemos creado la variable «self.duration«) que establecemos en 1000 como valor predeterminado), un «comboBox» en el que mostraremos las posibles formas de onda que podrá adoptar nuestro sonido (las cuales se almacenan en la variable «self.WaveForms«) y cuatro «sliders» con los que podremos regular el volumen, la amplitud (ganancia) y el desvanecido inicial y final (para estos parámetros crearemos también las respectivas variables). Si ejecutamos ahora obtenemos el siguiente resultado:

A su vez, nos hemos asegurado que en le campo «DURATION:» únicamente puedan introducirse valores enteros, para lo que hemos creado la variable «validatecommand» y la función «self.valid_duration()«:

Ya tenemos los elementos mediante los cuales podemos configurar la sonoridad de nuestro teclado, ahora hace falta hacerlo sonar. Para ello crearemos una función a la que hemos llamado «self.make_tone()» que generará nuestra nota en función del tipo de onda, duración y frecuencia (la cual será única para cada tecla y que definiremos más adelante):

A su vez, para evitar que al tocar una nota la ventana se nos pueda quedar congelada, usaremos una nueva función «self.init_task()» que ejecutará (con un «Thread«) la función anterior como un subproceso en paralelo:

Esta es la función que conectaremos directamente con cada uno de los «Button» correspondientes a cada tecla y que tomará como argumento, ahora sí, el valor correspondiente a la frecuencia de cada nota. Valor que especificaremos a través de la correspondiente función «lambda()» tanto para las teclas blancas como para las negras:

Y con ello tendríamos creado nuestro sencillo teclado musical cuyo código completo podéis consultar en el siguiente enlace:

https://github.com/antonioam82/MusicKeyBoard-/blob/main/KeyBoard.py

Saludos.

LISTAS DESPLEGABLES («ComboBox») EN PYTHON, CON TKINTER.

Las listas desplegables son un elemento usado habitualmente en aplicaciones gráficas mediante la cual, se da al usuario, la capacidad de seleccionar una opción de entre las presentadas en dicha lista. En esta ocasión vamos, pues, a ver como crear dicho elemento, empezado por la creación de la ventana en la que vamos a incluirla y a importación tanto de «Tkinter» como del módulo «ScrolledText» (con el que crearemos nuestra lista desplegable):

Ejecutando el código sobre estas líneas, obtenemos la ventana en la que insertaremos nuestra lista desplegable:

Ya tenemos la ventana, ahora, pasemos a crear nuestra lista desplegable de opciones. Para lo cual vamos a usar el módulo «ttk» previamente importado y de este vamos a emplear el método «ComboBox()«:

De este modo si volvemos a ejecutar, obtendremos nuestra ventana de antes con la lista desplegable incluida:

Tenemos ya creada nuestra lista desplegable, la cual, sin embargo, se encuentra vacía (por o que de momento nos serviría de poco). Es por ello, que lo que haremos a continuación será añadirle las opciones. Para ello, comenzaremos creando una lista (de nombre «opciones«) de tres opciones a incluir, que en un alarde de creatividad, llevarán por nombre, «opcion 1«, «opcion 2» y «opcion 3«. Tras ello, incluiremos los elementos de dicha lista del modo en que se aprecia en la siguiente imagen:

Hecho ello, veremos como nuestro listado aparece ahora en nuestra lista desplegable:

A su vez, para obtener el elemento de la lista, seleccionado, haremos uso del método (.get()«):

Ello trasladado a nuestro ejemplo, se materializa en la creación de un botón, que ejecutará la función «obtener_info()«, la cual muestra el elemento seleccionado en cada momento, mediante el referido método:

Por otro lado, del mismo modo que podemos obtener el elemento seleccionado, podemos, también obtener el indice, que ocupa este dentro de la lista de opciones, para lo cual usaremos otro método («.current()«):

Introducido este cambio en nuestro código, al pinchar en el botón, ahora obtenemos…:

Por otra parte, tal y como tenemos escrito el programa, nada impide que el usuario pueda, introducir manualmente, una nueva opción no contemplada en el listado original:

Sin embargo habrá casos en lo que no querremos que pueda darse esa posibilidad. Para ello, agregaremos en la creación de nuestra lista desplegable, una nueva variable de nombre «state» a la que daremos el valor «readonly«:

Imposibilitando, de ese modo, la introducción de opciones adicionales a las previamente establecidas.

Por último, haremos referencia a la posibilidad de introducir alguna opción predeterminada, para que se muestre al abrir el programa. Esto lo haremos con el método «.set()» tal y como se muestra a continuación:

En este caso hemos establecido «opcion 1» como predeterminada, lo cual, se demuestra al ejecutar el programa:

Y con esto tenemos visto, lo fundamental acerca de la creación y gestión de este elemento gráfico con el que poder introducir información de entrada a nuestras aplicaciones.

Saludos.

CONVERSOR DE ARCHIVOS DE AUDIO CON TKINTER Y «pydub».

Hace un tiempo hicimos una primera aproximación a «librosa«, una librería orientada al manejo y análisis de archivos de audio (de la que volveremos a hablar próximamente). En aquella ocasión hicimos referencia a la necesidad de que el archivo de audio a analizar estuviese en un formato descomprimido (por ejemplo «wav» o «ogg«). Para ello teníamos que contar con un archivo de audio en dicho formato. En este punto, una cosa que podemos hacer es convertir a dicho formato algún archivo que ya tengamos. Para ello podemos hacer uso de cualquiera de los conversores de audio que hay online…Aunque también podemos programar nuestro propio conversor que nos permita exportar un archivo de audio a diferentes tipos de formatos de un modo rápido, mediante la selección del archivo que queremos convertir y el formato (también de audio) al que queremos exportarlo (conservando el original). Esto lo haremos con una sencilla interfaz elaborada con «tkinter» que tendrá este aspecto:

Como se ve, la interfaz consta de una etiqueta de fondo negro (que en cada momento reflejará el nombre del archivo de audio que vamos a exportar), un botón («BUSCAR ARCHIVO«) con el que, haremos la búsqueda y selección del archivo que queremos exportar, un conjunto de 8 botones con los que seleccionaremos el formato («wav«,»ogg«,»mp3«,»flv«…) al que queremos exportar el audio previamente seleccionado y finalmente un botón («CARPETA DESTINO«) con el que podremos seleccionar la carpeta en la que se generará el archivo exportado (que por defecto será aquella en la que se encuentre alojado el programa).

Pasemos a la elaboración de nuestra aplicación: Como ya sabrán, lo primero es, siempre, realizar la importación de los módulos, librerías y demás recursos que vayamos a necesitar:

Entre dichos recursos cabe destacar «tkinter» (con la que elaboraremos la interfaz de la aplicación) y la librería «pydub» con la que realizaremos la acción de conversión-exportado de nuestro audio a otro formato. Cabe recordar que esta última funciona con el software de «ffmpeg» por lo que antes de instalarla (con el comando «pip«) deberemos proceder, también, a la instalación de dicho software.

Una vez que contemos con todos los recursos necesarios instalados. Pasaremos a la elaboración de nuestro programa, empezando, claro está, por la interfaz del mismo, definiendo también ciertas variables iniciales:

Así, creamos la ventana («root = tkinter.Tk()«), en la cual, iremos incluyendo los distintos elementos de interacción (fundamentalmente «Button«, «Label» y «Entry«), terminando con la correspondiente función «mainloop()» («root.mainloop()«). Antes de ello, ejecutaremos ya una primera función («dire()«) cuya misión será mostrar en la parte superior, el directorio de ejecución:

Como el propósito de nuestra aplicación es el de exportar un archivo de audio en un formato determinado a otro formato (también de audio). Lo primero que tenderemos que proporcionarle, es una función a través de la cual se pueda (navegando por el sistema de carpetas) seleccionar dicho archivo. Esa tarea la llevará a cabo la función «buscar_archivo()«:

Lo primero que hará esta función es cambiar el texto de la etiqueta «etiName» de «NINGÚN ARCHIVO SELECCIONADO» a «IMPORTANDO ARCHIVO…«. Para la búsqueda, usaremos la el método «askopenfilename()» al que pasaremos el conjunto de extensiones (en este caso de audio) entre las que podremos hacer la selección. Una vez completada la búsqueda (es decir que se haya seleccionado algún archivo y por lo tanto «ruta != «») extraeremos del string «ruta» el último elemento dentro de la lista resultante de aplicarle (al string) el método «split()» usando el símbolo «/» como separador. El referido elemento extraído no es otro que el nombre del archivo de audio que queremos exportar (de donde diferenciaremos el nombre «nom» y la extensión «ex» con «os.path.splitext()» (la razón de esto último la veremos en el estudio de la siguiente función) . Por su parte si resulta que no se cumple la condición «ruta != «»» (es decir que «ruta == «»«) el texto de «etiName» volverá a su estado original, y no se procederá a la ejecución de la siguiente función («abrir_archivo()«):

La primera condición que establece esta función, es que la extensión del archivo seleccionado en el paso anterior («ex«) se encuentre en la lista «formatos» (dicha lista es una de las variables iniciales que definimos al crear la ventana) que alberga los formatos de audio que puede anejar nuestro programa. Así, si no se da tal condición, usaremos el método «showwarning()» con el texto «Formato no soportado». Por contra, si resulta que sí se cumple la condición («ex in formatos») el programa procederá a cargar el archivo mediante el método «AudioSegment» correspondiente a la extensión «ex» del archivo que se quiera cargar, que vimos cundo hablamos de la librería «pydub«. A su vez, enmarcamos esta acción en un «try«. La razón de ello, es que existe la posibilidad de que se interrumpiese el proceso, por ejemplo, al cerrar la consola de «ffmpeg» que se muestra durante el mismo. En tal caso, «except:» se mostraría una nueva ventana de error, y las variables «nom» y «audio» (variable está que contendrá el archivo de audio ya cargado) volverían a su estado original.

El programa mostrará, en cada momento el nombre del archivo que vamos a convertir.

En este punto, si la carga se ha completado sin interrupciones, nuestro audio estará ya listo para ser exportado a otro formato (dejando siempre el archivo original, intacto) para ello, el usuario solo tendrá que pinchar en uno de los 8 botones de color rojo, en función del formato en el que quiere se se genere el nuevo archivo. Para ello, incluiremos en la definición de cada botón un «command» que active una función a la que hemos llamado «inicia()» que consistirá en la activación de un proceso paralelo («threading.Thread()«) consistente en la ejecución de la función destinada a realizar la conversión de formato:

Como se ve, la función que a partir de ese momento se va ejecutar, es la llamada «convert()«:

Como se ve, lo primero que hace la función es comprobar si la variable «audio» contiene alguna información (lo que pasará solo si el proceso de carga realizado por «abrir_archivo()» se ha completado satisfactoriamente). De ser así, la etiqueta ubicada debajo de la principal, mostrará el texto «PROCESO EN CURSO…«, para a continuación, llevar a cabo la operación, con el método «.export()» al que pasaremos el nombre del archivo original (el que vamos a exportar) y la extensión («format=ty«) correspondiente al nuevo formato deseado y definido como argumento de la función «lambda» definida en el «command» de cada uno de los botones de formato. En este punto cabe destacar el uso de una variable «executing» que, al iniciarse la ejecución de esta función, torna su valor a «True«. La razón de ello, es que la mayoría de funciones asociadas a algún botón, solo se ejecutarán si el valor de esta variable es «False«. Con esto se evita la ejecución de otras funciones mientras esta este funcionando, evitando posibles conflictos entre procesos (por ello, una vez que termine de ejecutarse «convert()» «executing» retornará al valor «False«, desbloqueando el resto de funciones.

Al terminar el proceso, la etiqueta inferior mostrará un mensaje con el nombre del nuevo archivo creado.

Una vez completado este proceso, se generará un nuevo archivo de audio, con el mismo nombre que el original pero, obviamente, con la extensión correspondiente al formato deseado. El archivo se generará en la ubicación del programa, a no ser, que hayamos especificado una ubicación diferente, lo cual, podrá hacerse con el botón «CARPETA DESTINO«, que activará la función «cambia_dir()«:

El proceso aquí es muy sencillo: Siempre que «executing==False» la variable «directorio» almacenará la ubicación de la carpeta deseada para alojar el nuevo archivo, a la que podremos acceder y seleccionar con «filedialog.askdirectory()«, para que si «directorio» tiene algún valor, (lo que no sucederá, por ejemplo si cerramos el navegador de carpetas sin haber seleccionado ninguna ubicación) usar «os.chdir()» para cambiar el directorio de ejecución (y por tanto cambiar el directorio en el que se generará el nuevo archivo). Finalmente, reflejaremos este cambio, en la parte superior de la ventana con «currentDir.set(os.getcwd())«, donde «currentDir» es el nombre que hemos dado al «entry» de la parte superior y «os.getcwd()» la función con la que obtenemos el directorio actual.

Finalmente, y como viene siendo habitual en estos casos, podéis consultar el código completo del programa en el siguiente enlace:

https://github.com/antonioam82/AudioFile_Converter/blob/master/audioFile_converter.py

Saludos.

INTRODUCCIÓN A «PyQt5» (CREAR VENTANA Y BARRA DE PROGRESO).

Saludos, lectores, y bienvenidos una semana más a «El Programador Chapuzas». Los que sigáis con regularidad este blog, sabréis que en esta página, a menudo, creamos aplicaciones con interfaces gráficas, creadas con la librería «Tkinter«. No obstante, hemos de saber que existen otras muchas librerías para Python orientadas a la creación de elementos gráficos de interacción con el usuario. Una de esas librerías es «PyQt5» (derivada de su anterior versión «PyQt4«) con la que empezaremos a trabajar en esta ocasión.

Por otra parte, a diferencia de lo que sucede con «Tkinter«, antes de utilizar «PyQt5» tendremos que realizar la instalación de la misma (lo que podremos hacer introduciendo «pip install PyQt5» en el cmd). A su vez, debemos tener en cuenta también que mientras «PyQt4» podía usarse en Python 2 y Python 3, «PyQt5» (el que vamos a usar) solo es compatible con «Python 3«.

Empezaremos practicando con esta librería, por lo más sencillo: Creando nuestra primera ventana:

Después de importados los recursos y módulos necesarios, pasamos a construir nuestra aplicación. Para ello, debemos empezar definiendo el objeto de la misma (al que llamaremos «app«), con «app = QApplication(sys.argv)«. Por su parte «QWidget» es la clase base de todos los objetos de interfaz de usuario en «PyQt5«, la cual, definiremos con «w = QWidget()«. En las tres siguientes líneas definiremos algunas características de nuestra ventana: Usaremos «.resize()» para definir el alto y ancho de la ventana, con «.move()» determinaremos la posición de nuestra ventana en la pantalla, finalmente, con «setWindowTitle()» daremos un título a la misma. Una vez definidas dichas características, mostraremos la ventana en pantalla con «.show()«. Por último, usaremos el método «.exit()» para definir el evento de finalización de la aplicación principal («app«).

Ejecutando estas sencillas líneas, obtendremos el siguiente resultado:

Tal y como podremos ir viendo en futuros artículos, «PyQt5» nos ofrece la posibilidad de crear elementos gráficos muy interesantes. Así por ejemplo, el siguiente código nos generará una barra de progreso, perfecta para marcar procesos de carga y descarga en tiempo real:

En este caso, para hacer uso de ese widget, hemos tenido que importar «QProgressBar» de nuestro módulo «QTWidgets«, para poder crearla en nuestra ventana («self.progress=QprogresBar(self)«). A su vez, hemos introducido un botón, con la función (que tendremos que haber importado previamente) «QPushButton» en la que introduciremos el texto del mismo, como argumento. Puesto que queremos que este botón inicie la demostración de la barra de progreso (demostración que se activará mediante la función «onButtonClick()«) le indicaremos al programa que queremos que esta función se ejecute al pinchar en dicho botón. Para ello, en «PyQt5» usaremos el método «clicked.connect()» introduciendo entre sus paréntesis el nombre de la función que queremos que se ejecute (esto es el equivalente al «command» que usamos en «Tkinter«). De ese modo, cuando pinchemos en el botón «Start«, se iniciará dicha función que hará avanzar (en este caso, cada segundo) la barra de progreso, mediante la función «setValue()» .

Hemos visto así, el modo de usar «PyQt5» para crear nuestra primera ventana. Para a continuación, ver el modo de introducir una barra de progreso en nuestras aplicaciones. No obstante, esta librería nos ofrece aún muchas más posibilidades que iremos viendo en sucesivas ocasiones.

Saludos.

CREANDO APLICACIÓN PARA LEER CÓDIGOS «QR» CON LA CÁMARA WEB.

Bienvenidos una semana más a vuestro blog sobre programación en Python. Como recordarán, en semanas anteriores vimos el modo en que podíamos efectuar la lectura de códigos «QR«, en python, (aunque en esta ocasión haremos uso de la librería «pyzbar«). A su vez, hace dos semanas, vimos como podíamos hacer uso de la cámara web, usando «opencv«. Hoy vamos a combinar los conocimientos que adquirimos en esas ocasiones para crear un sencillo programa (a cuyo código integro podéis acceder a través del enlace que dejo al final), mediante el cual podremos efectuar la lectura de dichos códigos de un modo sencillo:

La aplicación, como se ve, consistirá en una ventana, con un espacio en el que se mostrará la entrada recogida por la cámara (cuando esta sea activada mediante el botón «INICIAR CAPTURA POR CAMARA«), de modo que una vez iniciada la cámara, simplemente tendremos que poner el código a leer, delante de la misma, hasta que su información aparezca en el espacio de texto inferior. A su vez, (sin necesidad de usar la cámara) nuestra aplicación nos dará la opción de leer códigos, a partir de archivos «jpg» o «png» almacenados en nuestro ordenador, con el botón «CARGAR ARCHIVO» (esta opción también nos permitirá detectar códigos de barras). Finalmente, podremos leer, directamente, códigos que pueda haber en pantalla, usando el botón «DETECTAR EN PANTALLA» (para lo que nos ayudaremos de la librería «pyautogui«).

Visto someramente en que va a consistir nuestra aplicación, comenzaremos a escribir nuestro código. Para lo cual, naturalmente, empezaremos importando las librerías y recursos que va a emplear nuestro programa para realizar su función:

Como se ve, son varios los recursos y librerías que vamos a emplear: «tkinter» para la elaboración de la ventana, con sus elementos (canvas, botones y caja de texto), «pyzbar» para la lectura de los códigos «QR» (y eventualmente, también, de barras), «pyautogui» para car capturas de pantalla, «numpy«, para trabajar con los arrays de los frames generados por la cámara, «opencv» («cv2») fundamentalmente, pare el uso de la cámara web y «PIL» («pillow») para mostrar los frames generados por la cámara, en el visor. Hemos de señalar también que algunas de estas librerías («opencv«, «pyautogui«, «numpy«, «PIL» y «pyzbar«) deberán haber sido instaladas previamente en nuestro equipo (para lo que podremos usar, en «windows», el comando «pip install «) para poder usarlas. Una vez importados los recursos, pasaremos a iniciar la clase (a la que llamaremos «App«) que contendrá los elementos y funciones de nuestro programa:

La clase App (que iniciaremos con la función «def __init__()«) empezará creando los elementos visuales de nuestra aplicación: Empezando por la ventana misma (que crearemos con «self.ventana=Tk()«) en la que insertaremos los tres botones (definidos en los objetos «self.btnLoad«, «self.btnCamera» y «self.btnScreen«) el espacio de texto en el que se mostrará la información leída (con «self.display») y el objeto canvas, en el que se mostrará la imagen, captada por la cámara web («self.canvas«). A su vez, usaremos «mainloop()» para mostrar la ventana abierta.

Ejecutando lo hecho hasta ahora, obtendremos nuestra interfaz tal y como se muestra en la imagen sobre estas líneas. No obstante, nuestro programa aún no puede hacer nada (aparte de mostrar la referida ventana) siendo necesario incluir las distintas funciones encargadas de leer los códigos «QR» y mostrar el resultado de la lectura en el «display» inferior.

Empezaremos por la función encargada de leer información, a partir de un archivo «jpg» o «png», almacenada en nuestro equipo (esta se ejecuta con el botón «self.btnLoad«, «CARGAR ARCHIVO«). Dicha función llevará por nombre «abrir()«:

Esta función usará el método «askopenfilename()» para que, navegando por nuestro sistema de carpetas, podamos escoger el código «QR» en formato «png» o «jpg» que queramos leer. De modo que si por dicho procedimiento seleccionamos un archivo («if ruta != «»«) este (que será un archivo de imagen) será leído por nuestro programa (con «cv2.imread(ruta)«) siendo almacenada dicha lectura en la variable «archivo«. Sobre este archivo es el que vamos a aplicar la función de lectura «decode()«, que solo devolverá (en la variable «info«) un resultado, si en el archivo de imagen, se detecta un código «QR» (en cuyo caso después de borrar el contenido que pueda haber en la caja de texto («self.display.delete(‘1.0’.END)«) insertaremos el texto correspondiente a la información leída («self.display.insert(END,self.info[0][0])«). Si con tal operación, no se detecta ningún «QR» mostraremos la correspondiente ventana de aviso («messagebox.showwarning()«):

La segunda opción de lectura es la que aplicaremos seleccionando el botón «DETECTAR EN PANTALLA» (elemento «btnScreen» en nuestro script) mediante una nueva función de nombre «screen_shot()«:

Esta función usa el método «.screenshot()» de la librería «pyautogui» para realizar una captura de pantalla, en la que se supone que se encuentra el código «QR» a leer. Esta captura crea un archivo de imagen «.jpg» que, de modo similar al caso anterior, será tomado como argumento de la función «decode()» de modo que se si esta consigue detectar el «QR» mostrará su información en el cuadro de texto inferior. En caso contrario, mostrará la correspondiente ventana de código no encontrado.

La tercera (y última) vía de lectura, que ofrecerá nuestro programa es, también, la principal, ya que es la que va a hacer uso de la cámara web de nuestro dispositivo para la lectura del «QR«. Dicha vía de lectura comenzará con la ejecución de la función «active_cam()«:

El cometido de esta función no es otro que el activar o desactivar la cámara web, en función del estado actual de la variable «active_camera» (que definimos al crear la ventana, con un valor inicial de «False» («self.active_camera=False«)). De modo que si el estado actual de dicha variable es «False» («if self.active_camera==False:«), en primer lugar cambiará su valor a «True» y pasará a ejecutar las funciones «VideoCaptura()» (para iniciar la cámara) seguida de la función «visor()» ,para mostrar en el componente canvas, el contenido de la entrada de la cámara (ambas funciones las veremos a continuación). Por contra, si el valor de «active_camera» no es «False» (por ser «True«), en primer término cambiará su estado a «False«, finalizará la cámara («self.vid.release()«) y borrará el contenido que haya en el componente canvas («self.canvas.delete(‘all’)«) para dejar la pantalla del visor en negro. A su vez, cambiaremos el texto del botón «btnCamera» a «INICIAR CAPTURA POR CAMARA».

Como hemos dicho, al activar la cámara la primera función que se ejecutará será «VideoCaptura()«:

Una de las circunstancias que debemos tener en cuenta, para que nuestro programa acceda a la cámara, es si esta, se encuentra o no activada. Así para evitar errores derivados del intentar acceder a una cámara no activada, después de crear el objeto encargado de acceder a la misma («self.vid=cv2.VideoCapture()«) asegurarnos de que esta está encendida («self.vid.isOpened()«) de modo que si se da dicha circunstancia («if self.vid.isOpened():«) pasar a adaptar las dimensiones del componente canvas al alto y ancho de la imagen de entrada de la cámara. Por contra, si la cámara no se encuentra activa, para evitar errores en el programa, abriremos una ventana de aviso con el correspondiente mensaje de error, tras lo cual, como se ve en el código , el valor de la variable «active_camera» volverá a su estado original («False«).

Una vez que, se ha comprobado que la cámara está activada, y (en caso afirmativo) una vez que las dimensiones del canvas se ha adaptado a las dimensiones de los futuros frames de entrada, pasaremos a mostrar la entrada en el canvas, con la función «visor():

El cometido de esta función es bien sencillo: El de mostrar, en la pantalla de la cámara, con una periodicidad de 0,015 segundos, los frames, obtenidos por la misma, mostrando sucesivamente cada frame en el canvas mediante el método «create_image()«. Dichos frames, a su vez, serán creados por la función «get_frame()» que vemos a continuación:

Esta función será la encargada de ir proporcionado, a la función «visor()», cada uno de los «frames», que, por medio del método «.read()» («self.vid.read()«), irá generando a partir de la información de entrada de la cámara web (el producto de esa lectura se almacenará en la variable «frame«) otra variable que nos proporcionará aquí «self.vid.read()» es «verif«, la cual la cual adoptará un valor «True» o «False» en función de si la lectura puede o no efectuarse (la lectura no podrá realizarse si la cámara, estando activada, está sin embargo, siendo utilizada por otra aplicación). Así, si el valor de «verif» es «True» (o lo que es lo mismo: «if verif:«) la función devolverá el frame a «visor()» para que esta lo muestre en el canvas. De no ser así, («else:«) se mostrará una nueva ventana de aviso, tras lo que el estado de «active_camera» retornará a «False«.

Otra consecuencia que, dentro de la función «get_frame()» tendrá «verif=True», es que, al «frame» generado le aplicaremos la función «capta()«:

Lo que hará esta función es tomar cada uno de los frames generados por la función anterior, e ir aplicando la función «decode()» para decodificar el contenido de códigos «QR«. El resultado de esta operación será una lista (asignada a la variable «info«) con la información de dicho «QR«. No obstante, si en el frame analizado no se detecta ningún «QR» (por no haberse puesto este delante de la cámara), el resultado de dicha operación será una lista vacía («info = []«). Por contra, si en el frame se detecta un código «QR» cuya información puede leerse («if self.info!=[]:«), pasaremos a limpiar el contenido que pueda haber en el display de texto, («display.delete()«) e insertar el texto correspondiente al contenido decodificado del «QR» («self.display.insert(END,self.info[0][0])«). También en este caso procederemos a dibujar sobre el «frame» un rectángulo que enmarque el código detectado/leído, con la función «draw_rectangle()«:

La función «draw_rectangle()» dibujará un recuadro azul que enmarcará el «QR» detectado.

El cometido de «draw_rectangle()«, como puede deducirse por su nombre, es el de dibujar sobre los frames que se muestran en el canvas, un el rectángulo en el que se enmarca el código «QR» detectado. Para la creación de dicho rectángulo (cuyas dimensiones, se actualizarán frame a frame, con el movimiento del «QR» delante de la cámara y que vendrá definido por las variables «x«, «y» (coordenadas de posición) y «w» y «h» (anchura y altura del código detectado) usaremos el método «rectangle()» en el que también definiremos el color de su contorno («(255,0,0)» en «BGR«, correspondiente al azul) y un grosor de 6. Igualmente incorporaremos un texto («QR Code«) en rojo, con el método «putText()«.

Finalmente, incluiremos la función («def __del__(self):«) para finalizar el programa, la cual, al cerrar la ventana del programa, finalice la cámara («self.vid.release()«) en caso de que esta esté siendo usada por el programa al tiempo de cerrarlo («if self.active_camera==True:«).

Con lo visto, tendríamos creada nuestra sencilla aplicación para lectura de códigos «QR» en Python y cuyo script completo podéis ver en el siguiente enlace:

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

Una segunda versión, de esta aplicación, con visor de cámara desplegable, puede verse en el siguiente enlace:

https://github.com/antonioam82/QR_Camera/blob/master/QRCamera_new.py

Saludos.

AÑADIENDO PESTAÑAS A VENTANA «Tkinter», CON «ttk.Notebook».

Saludos programadores, mi nombre es Antonio Alfonso Martínez y os doy la bienvenida a El Programador Chapuzas, blog sobre programación en Python, en el que, en esta semana, vamos a ver el modo en que podemos añadir pestañas a las ventanas de nuestras aplicaciones gráficas creadas con «tkinter«.

Para ello existe en «tkinter» un componente («ttk.Notebook«) por el cual, podremos crear un panel en nuestra ventana, en el que a su vez, podremos incluir nuestras pestañas. Para entenderlo mejor, vamos a mostrar un sencillo ejemplo en el que empezaremos importando la librería «tkinter» con todos los recursos necesarios:

Una vez que hemos importado los recursos que vamos a necesitar (por ahora), pasaremos a crear nuestra ventana principal mediante el procedimiento al que estamos habituados:

Tendríamos así creada nuestra ventana principal («root«), en la que acto seguido, mediante la referida «ttk.Notebook» vamos a incluir el panel en el que incorporaremos nuestras pestañas al que llamaremos «nb«:

En este punto, hemos usado el método «.pack()» para especificar que queremos que nuestras futuras pestañas llenen completamente la superficie de la ventana («fill=’booth,expand=’yes’«) también hemos aprovechado para definir las dimensiones de la ventana, con «.geometry()«. Llegados a este punto, si ejecutamos, obtenemos el siguiente resultado:

Vemos así, nuestra ventana con su título y el las dimensiones especificadas, y en cuyo interior se desplegarán las pestañas (para las que hemos definido el componente «ttk.Notebook«). El siguiente paso será, ahora, añadirlas empezando por la primera de ellas, a la que llamaremos «p1«:

Como se ve, el proceso para agregar pestañas, consta de 2 fases: Una primera en la que creamos la pestaña, usando el método «ttk.Frame() especificando el nombre de la ventana en la que queremos incluirla (que en este caso será el panel «nb«) y una segunda fase en la que añadiremos la pestaña creada al «nb» (nombre que hemos dado a nuestro componente «ttk.Notebook«) mediante el método «.add()» en el que nuevamente definiremos la raíz y en donde también definiremos el título que tendrá esta («text=’Pestaña1′«). Esta misma operación la efectuaremos tantas veces como pestañas queramos añadir (4 en nuestro caso).

Una vez que tenemos creadas y agregadas las pestañas deseadas, pasaremos a ejecutar lo hecho hasta ahora:

Como se ve, tenemos creada nuestra ventana con sus correspondientes pestañas que podremos ir seleccionando. No obstante, se puede comprobar que dichas ventanas se encuentran completamente vacías. Por ello, a continuación pasaremos a incluir elementos para interactuar con el usuario, en cada una de ellas, empezando por la primera de ellas («Pestaña1«).

Como puede verse, hemos incluido dos elementos en «Pestaña1» (para lo cual hemos introducido como primer argumento, el nombre que le dimos «p1«): Un botón con el texto «Saludar» y una entrada para introducir nuestro nombre (definido con variable de tipo «string«, «nombre«), declarada previamente («nombre=StringVar()«). Dicho botón, ejecutará la función «saludo()» que habremos definido antes, y que simplemente mostrará el mensaje «Hola» seguido del nombre que se haya introducido en el Entry (que obtenemos con «nombre.get()«).

Para el resto de pestañas, vamos a hacer que cada una de ellas sume, reste, multiplique y divida, respectivamente, por 5, el número que se ingrese en el Entry: Para ello, deberemos declarar una nueva variable de tipo «int«, a la que llamaremos «numero» («numero=IntVar()«).

Para que resulte más clara la función de las pestañas, vamos a cambiarle el nombre, por uno que haga referencia al tipo de operación que realizan. Incluiremos también una quinta pestaña («p5«) para dividir por 5, que llevará por título «Divid5«:

Por supuesto, también definiremos las funciones encargadas de realizar tales operaciones con el valor 5, a partir del número que ingresemos:

Finalmente, agregaremos los elementos (botones y entradas) en cada una de las pestañas, indicando la pestaña raíz, y en el caso de los botones (mediante «commnad«) la función a ejecutar por cada uno:

Así, recapitulando, nuestro script completo quedaría del siguiente modo:

Ejecutando el código, obtendremos nuestra ventana con sus pestañas, las cuales, cada una realizará una operación distinta (la función de saludo de la primera y las operaciones aritméticas de las demás , que en este caso hemos empleado ingresando 6 en el Entry).

Como viene siendo habitual cuando hacemos este tipo de ejercicios, en el siguiente enlace dejo el código completo para que puedan probarlo:

https://github.com/antonioam82/ejercicios-python/blob/master/ventana_pesta%C3%B1as.py

Saludos.

GRABADORA DE AUDIO mp3 CON INTERFAZ GRÁFICA, EN PYTHON, CON «Tkinter» Y «Pyaudio».

Bienvenidos una semana más a vuestro blog sobre programación en Python. Hace un tiempo, estuvimos viendo el modo en que, haciendo uso de la librería «pyaudio«, podíamos efectuar la grabación de un audio registrada por el micrófono de nuestro dispositivo. En la presente ocasión, pondremos en practica (haciendo uso de «Tkinter«) lo que vimos en esa ocasión, para crear una sencilla aplicación gráfica que nos permitirá crear nuestras grabaciones en formato mp3, así como reproducir audios previamente grabados:

Nota: Para una mejor comprensión de este trabajo, es recomendable hacer una previa lectura del referido artículo sobre grabación de sonido con «pyaudio«, que puede leerse aquí:

https://programacionpython80889555.wordpress.com/2018/10/16/grabacion-de-sonido-con-pyaudio-ejercicio-basico-en-python/

Empezando a elaborar nuestra aplicación, o primero es realizar la importación de las librerías y recursos que vamos a necesitar. Entre los que, en un primer momento, destacaremos «pyaudio» (pata el proceso de grabación), «wave» (para leer archivos ya grabados) y «tkinter» (para crear la interfaz gráfica):

INTERFAZ.

Una vez que tenemos importados los recursos necesarios, empezaremos creando con «tkinter«, la interfaz de nuestra aplicación, cuyos elementos incluiremos en un espacio al que daremos el nombre de «ventana«. En este punto, también definiremos algunas de las variables que vamos a utilizar para el proceso de grabación, además de una variable «directorio_actual» que será de tipo string, que albergará la ruta al directorio actual de ejecución del programa:

Como se ve, una vez creada la ventana, hemos procedido a incluir los distintos elementos gráficos de la misma, que nos permitirán interactuar con el programa. Así hemos empezado creando un contador de tiempo, cuyo estado inicial, estableceremos mediante una etiqueta «time«, que realizaremos con la función «Label()» a la que pasaremos, el nombre de la ventana raíz, el color del texto, (fg=’green’), el ancho (width=20), el texto, que inicialmente será «00:00:00», (text=»00:00:00″) el fondo (bg=’black’) y la fuente del mismo (font=(«»,»30″)). Finalmente lo ubicaremos en la ventana, mediante el método «.place()«. También definiremos un «Entry» («etDir«) en el que se mostrará en cada momento la ruta a la carpeta donde se guardarán nuestros audios grabados.

El siguiente elemento a definir son los 4 botones que tendrá nuestra aplicación: Uno para iniciar la grabación, «btnIniciar«, otro para detener la grabación (o reproducción), «btnParar«, otro para abrir un archivo de audio ya creado, «btnAbrir» y un último botón para cambiar el directorio de ejecución «btnDir«. Para cada uno de estos, definiremos opciones tales como el texto («text«), fondo («bg«), color de texto («fg«), anchura («width«), ubicación (mediante el método «.place()«) y la función que se ejecutará al pinchar en ellos («command«) las cuales iremos viendo en el siguiente bloque.

FUNCIONES DE LA GRABADORA.

Creados los elementos gráficos, pasamos a examinar las funciones que harán funcionar a nuestra aplicación. Dado que queremos que en todo momento, se muestre en el «Entry» superior, el directorio sobre el que se está ejecutando nuestro programa, la primera función que se ejecutará nada más iniciar la aplicación, es la que muestra dicha información. A dicha función le daremos el nombre de «dire()«:

Como se ve, se trata de un a función bien sencilla que únicamente actualiza el valor de la variable «directorio_actual» (que es la que se muestra en el «Entry» antes mencionado) para que muestre el directorio en el que se está ejecutando inicialmente el programa (el cual se obtiene a través del método «.getcwd()«). Con esto se consigue que dicha información se muestre nada más ejecutar el programa.

No obstante, aunque se muestre la ruta a la carpeta inicial, nuestro programa, deberá permitirnos cambiar dicho directorio por otro que elijamos nosotros. Es por ello que el botón denominado «btnDire» ejecutaba una función de nombre «direc()» («command=direc«) que permite realizar dicho cambio:

Lo que hará esta función será abrir una ventana por la que podremos seleccionar la carpeta en la que deseemos que se guarden las grabaciones que vayamos a hacer, «filedialog.askdirectory()» la nueva dirección establecida con dicho procedimiento se almacenará en la variable «directorio» que, a continuación usaremos (siempre que el valor de «directorio» no sea nulo) para, haciendo uso de la función «chdir()«, cambiar el dicho directorio, introduciendo como argumento, el valor de «directorio» («os.chdir(directorio)«). Finalmente, tras ello, volveremos a invocar a la función «dire()«, vista antes, para actualizar la dirección que se muestra en el «Entry» del cuadro superior.

Queda vista así, la función «direc» asociada al botón «btnDirec«. Pasemos a continuación al botón «btnIniciar» el cual ejecutará la función «iniciar()» («command=iniciar()«), cuya función será hacer que se ejecuten de forma simultanea el proceso de grabación del sonido y el correspondiente al funcionamiento del contador de tiempo:

Lo primero que hará esta función es definir el estado inicial de determinadas variables involucradas en el proceso de grabación («audio«,»FORMAT«,»CHANNELS» y «RATE«) de las que, en su momento hablamos en el artículo al que nos remitimos al comienzo. A su vez actualizaremos el valor de las variables globales «grabando«, «proceso» y «act_proceso«, de las que hablaremos más adelante. Una vez definidas y actualizadas las variables necesarias, iniciaremos dos procesos que correrán de forma simultanea (lo cual haremos con «threading.Thread()«: El proceso «t1«, que consistirá en la ejecución de la función «grabacion()» («target=grabacion«) que usará como argumentos («args=()«) las variables, de grabación, antes mencionadas. El otro proceso que se iniciará en este punto es el llamado «t» que consistirá en la ejecución de la función «cuenta()» («target=cuenta«) que hará funcionar el contador de tiempo de nuestra grabación. Pasemos a ver, a continuación las funciones «cuenta()» y «grabacion()«:

La función «cuenta()«, que como hemos dicho es la que se encargará de representar, en tiempo real, la duración de nuestra grabación, en un formato de horas:minutos:segundos, donde los valores que representarán tales unidades de tiempo, se corresponderán con las variables «contador1«, «contador2» y «contador«, respectivamente, los cuales se introducirán como texto de la etiqueta «time» que definimos en su momento («time[‘text’]:…«). Por otro lado, para lograr que cada una de las unidades de tiempo tenga siempre 2 dígitos (00,01,02…10..) iremos aplicando sobre el valor actual de «contador1«, «contador2» y «contador«, la función «formato()«, la cual, simplemente añadirá un «0» a la izquierda del valor actual, cuando este sea inferior a 10. A su vez, para que se contabilicen correctamente los minutos y segundos estableceremos que cuando el valor actual de «contador«, que se incrementará en 1 en cada ejecución del proceso, la cual se actualizará cada 1000 milisegundos («proceso=time.after(1000,cuenta)«), sea igual a 60 (recordemos que contador es el valor actual correspondiente a los segundos) dicha variable vuelva a ser 0 y se incremente en 1, el valor de los minutos («contador2+=1«). La misma lógica seguiremos en el caso de los minutos («contador2«) con respecto a las horas («contador1«).

A su vez, para restablecer los valores iniciales (00:00:00) del contador de tiempo (lo cual habrá de suceder cada vez que se inicie una nueva grabación o reproducción), usaremos la función a la que hemos llamado «clear_contador()«, que simplemente pondrá el valor de «contador1«, «contador2» y «contador» a 0:

La otra función que se iniciará a partir de la función «iniciar()» (y por tanto correrá simultaneamente con la del contador de tiempo) es la destinada a la grabación del sonido introducido a través del micrófono del dispositivo. A dicha función le hemos dado el nombre se «grabacion()«:

La función «grabación()» consta de tres bloques: Un primero, encargado de llevar a cabo el registro del audio. Para ello, comenzamos creando el objeto «stream» , a partir de los datos proporcionados desde la función «inicia()» antes vista, para iniciar el registro del audio, cuya información de irá almacenando de modo temporal en la lista «frames» previa,mente creada («frames=[]«). a través de la lectura del «stream» («stream,read()«) y su posterior inclusión en «frames«. Como se ve, esta actividad de registro se llevará a cabo mientras que el valor actual de a variable «grabando» sea «True» («while grabando==True:«). De modo que cuando dicho valor sea «False» (lo que sucederá cuando pinchemos en el botón «parar»), la ejecución del proceso pasará al siguiente bloque destinado a la finalización del «stream» («stream.stop_stream()«), su cierre («stream.close()«) y la terminación del objeto «audio» («audio.terminate()«).

Una vez almacenada la información de audio, el tercer bloque de la función, se encargará de guardarla en un archivo «.mp3» en una carpeta. Dicho archivo llevará, por defecto, el nombre de «grabacion.mp3«. No obstante, es posible que en algún momento, queramos guardar más de un audio en la misma carpeta. Así, para evitar que se sobrescriban, usaremos «glob.glob» para generar una lista «grabs» que contenga todos los audios de extensión «.mp3» del directorio actual, de modo que al recorres dicha lista, cada vez que se encuentre «grabacion» en el título, una variable de nombre «count» se incrementará en 1 («count+=1«). De modo que, si al final del proceso «count>0» el nombre con el que se creará el archivo «.mp3» incorporará dicho valor entre paréntesis («archivo=»grabacion»+»(«+str(count)+»)»+».mp3″«). Determinado el nombre del nuevo archivo, se procederá a su escritura mediante la variable «waveFile«.

La otra acción principal que podrá ejecutar nuestra aplicación, es la reproducción de un archivo «.mp3» previamente grabado con la misma (dicha reproducción correrá igualmente, de forma simultanea con el contador de tiempo). Para ello usaremos una función a la que hemos llamado «abrir()» y que se ejecutará al pinchar en el botón «Abrir» (con «command=abrir«) :

Tenemos una función que lo primero que hará será abrir una ventana que nos permitirá buscar el archivo «.mp3» que queremos reproducir, de modo que el nombre de dicho archivo se almacene en una variable de nombre «open_archive«, la información de dicho archivo será extraída mediante el método «open» de la librería «wave» («f=wave.open(open_archive,»rb»)«), para a continuación, ser leídos los frames mediante el método «readframes()«. Tras ello, nuevamente se iniciarán dos procesos simultáneos («t«, para ejecutar el contador de tiempo y «t2» para ejecutar la función «reproduce()» encargada de reproducir el audio del archivo «.mp3«:

La función «reproduce()» leerá la información contenida en la variable «data» mientras que haya información por leer y el valor de la variable «reproduciendo» sea «True» (dicho valor pasará a ser «False» cuando pinchemos en el botón «Parar» de nuestra aplicación).Además, cuando finalice el proceso de reproducción (al igual que en el caso de la grabación), finalizará igualmente, el proceso de actualización del contador de tiempo («time.after_cancel(proceso)«). A continuación, cabe hacer mención de la llamada a una función a la que hemos llamado «bloqueo()«:

La función de esta función no será otra que, en función del argumento «s» (que solo podrá ser «normal» o «disabled«), bloquear los botones «Grabar«, «Carpeta» y «Abrir» cuando se esté grabando o reproduciendo una grabación y habilitandolos cuando no sea así . Dicha acción la realizaremos con «config()» y concretamente, con la opción «state«, que adoptará uno de los referidos valores.

Finalmente, el botón «Parar«, detendrá la grabación o reproducción del audio. Para ello ejecutará la función «parar()» que lleva asociado el botón «btnParar()» («btnParar=(….command=parar)«):

Lo que hace, fundamentalmente, esta función es cambiar el valor («True» o «False«) de las variables «grabando» (que mientras sea «True» la aplicación estará grabando sonido) y «reproduciendo» (que mientras sea «True» y haya audio que reproducir, reproducirá el audio grabado). A su vez, vemos aquí, una nueva llamada a la función «bloqueo()» introduciendo como argumento el estado «normal«. Lo que habilitará la totalidad de los botones de nuestra aplicación.

Con esto quedaría visto, a grandes rasgos, el funcionamiento lógico, interno, de nuestra grabadora mp3, cuyo código integro podéis ver en el siguiente enlace a github:

https://github.com/antonioam82/grabadora/blob/master/Grabadora_GUI4.py

Versiones:

https://github.com/antonioam82/grabadora/releases

Saludos.

DIBUJADO DE LÍNEAS EN UN CANVAS, MEDIANTE EVENTOS DE RATÓN.

Saludos (y Felices Fiestas). Como muchos ya sabrán «Canvas» es un componente de «tkinter» que nos permite introducir funciones de dibujo en nuestras aplicaciones gráficas Así, en el día de hoy, vamos a realizar un sencillo ejercicio en el que vamos a dibujar líneas (para lo que usaremos el método «create_line()«) en un componente «canvas» haciendo uso del ratón de nuestro ordenador.

Para empezar, vamos a crear un nuevo archivo «.py» al que llamaremos «crea_lineas.py» y en el en primer lugar importaremos los recursos que vamos a usar en esta ocasión:

Por su parte, puesto que nuestro componente «canvas» vamos a crearlo dentro de una ventana «tkinter«, empezaremos creando esta, dándole el nombre de «ventana» («ventana=Tk()«) y poniéndole el correspondiente título («ventana.title(«CREA LÍNEAS«)») y en el interior de dicha ventana, incorporaremos nuestro «canvas«, usando el método «Canvas()» especificando la ventana en la que se va a insertar, su anchura («width«), su altura («height«) y color de fondo («background«):

Como se ve en estas líneas, una vez insertado el canvas en nuestra ventana, utilizaremos el método «.bind()» para establecer los eventos del ratón. Tal método «.bind()» usará como argumentos, 1º la referencia al botón del ratón y 2º la función que ejecutará dicho botón. Así en nuestro caso, vamos a emplear dos botones, ‘<Button-1>’ (correspondiente el botón derecho del ratón, y ‘<Button-3>‘ (correspondiente al botón izquierdo), para ejecutar las funciones (definidas como segundo argumento) «crea_linea()» y «borra_linea()«, respectivamente, las cuales, deberemos haber definido previamente:

La primera de estas funciones («crea_linea()«) usará como argumento el evento («event«) generado con el método «.bind()» para dibujar una línea (haciendo uso del método «create_line()«) cuyas coordenadas de posición «x1«, «y1«, «x2» e «y2» adoptará un valor aleatorio, generado mediante la función «randint()» (para la que, en su momento importamos el módulo «random«). Por su parte el color de la línea (que definiremos con «fill«), también se generará de forma aleatoria y la anchura («width«) de cada una de estas líneas será de 20. Estos datos son los que pasaremos como argumentos del método «create_line()» para que se dibuje una línea en nuestro canvas cada vez que pulsemos el botón derecho de nuestro ratón.

Por su parte, la función «borrar_linea()«, la cual, usará el evento del botón izquierdo (‘<Botton-3>‘) del ratón, simplemente se encargará de borrar («.delete()«) todas las líneas (‘all‘) que se hayan dibujado.

Con esto, tendríamos creado nuestro programa, cuyo código completo mostramos a continuación:

Saludos.

CREANDO GRABADORA DE PANTALLA CON PYTHON Y «pyautogui».

Saludos nuevamente, y bienvenidos a vuestro sitio sobre programación en lenguaje Python. En la presente ocasión, nos proponemos ver el modo de realizar la grabación de nuestra pantalla usando «pyautogui» (que instalaremos con «pip install pyautogui«) y «opencv» (que igualmente instalaremos con «pip install opencv-python«) Empezaremos con un ejemplo básico de como se realiza dicha grabación de pantalla con «pyautogui» empezando por importar los módulos y librerías necesarios:

Tras ello, procederemos a establecer el formato mediante el que se escribirá nuestra grabación. En este punto, proporcionaremos como argumento del método «VideoWritter()» el nombre del futuro archivo de vídeo («output.avi«):

Otra información que deberemos proporcionar es el tamaño de nuestra pantalla («screen_size«). Para obtenerlo, podemos hacer uso del módulo «ctypes» mediante la introducción del siguiente código:

Con lo que obtenemos que la pantalla (en nuestro caso) tiene un ancho de 1600 y un alto de 900.

Una vez que proporcionemos esta información inicial, pasaremos a generar (mediante el método «.screenshot()«) las capturas de pantalla de nuestro vídeo:

Así, iniciaremos un bucle que irá generando capturas de pantalla (imágenes fijas) las cuales, más adelante, (mediante el método «.write()«) se emplearán para crear el vídeo. A su vez, durante el proceso (que podremos interrumpir pulsando «esc«) podemos mostrar (con «imshow()«) las capturas de pantalla que constituirán los «frames» del vídeo final:

El bucle mostrará, en cascada, las capturas de pantalla que más adelante conformarán el archivo de vídeo.

Una vez que hayamos finalizado el proceso, pulsando «esc«, nuestro vídeo se creará en la carpeta en la que se aloje nuestro script, con el nombre que le dimos:

USANDO INTERFAZ GRÁFICA.

Partiendo de este ejemplo básico, vamos a crear nuestra propia (y sencilla) aplicación para realizar grabaciones de pantalla mediante una ventana creada con «tkinter«.

Así, como es habitual, comenzaremos importando los recursos que vamos a necesitar. Entre ellos «pyautogui» (para realizar las vídeo capturas) y «tkinter» (para la interfaz gráfica):

La forma con la que vamos a interactuar con el programa, es a través de una sencilla (y minimalísta) ventana, creada con «tkinter«, la cual, constará únicamente de una etiqueta (que pondrá «Screen Recorder«) y dos botones: Uno inferior con el que podremos cambiar la carpeta en la que se guardarán nuestras grabaciones (que por defecto será el directorio en el que se encuentre alojado nuestro programa) y un botón central, que usaremos tanto para iniciar la grabación como para finalizarla:

Para ello, introduciremos el código correspondiente a la creación de la ventana en la que insertaremos los respectivos botones, que asociaremos a las funciones (que habremos definido previamente) «record_state()» y «direct()» y que veremos más adelante, así como la etiqueta:

Uno de los datos principales, que va a necesitar nuestro programa para ejecutarse correctamente, es el de las dimensiones de la pantalla cuya grabación queremos realizar (variable «screen_size«). Para que nuestro programa pueda ejecutarse en otros ordenadores, necesitaremos crear una función que le proporcione (al programa) tal información. En nuestro caso, dicha función tendrá por nombre «screen_s» y el valor de la variable «screen_size» será el que nos devuelva (con «return«) dicha función («screen_size=screen_s()«):

Esta función, hará uso del método «GetSystemMetrics()» importado del módulo «ctypes» (tal y como vimos anteriormente) para proporcionar al programa, la información relativa al ancho y alto de la pantalla de nuestro equipo (que dentro de la función vendrá representada por la variable «dimensions«.

Como hemos señalado anteriormente, antes de crear al archivo de vídeo de nuestra vídeo captura, vamos a proporcionar la posibilidad de escoger la carpeta en la que se almacenará el archivo «.avi«:

Para ello creamos una función (de nombre «direct()«) que haciendo uso del método «askdirectory()» (mediante el que se desplegará una ventana a través de la cual podremos navegar por nuestro sistema de carpetas) nos permitirá camiar la ubicación del futuro vídeo. Lo cual, a su vez, haremos con el método «chdir()» del módulo «os«.

Como ya hemos dicho, junto a este botón, incluiremos otro, a través del cual, iniciaremos la grabación, cuando no se esté grabando, y la interrumpiremos, cuando sí lo este haciendo. Aquí, el hecho de que el programa este o no grabando, lo determinaremos a través de una variable (de nombre «recording«) que adoptará los valores «True» o «False» en función de si esté o no grabando, respectivamente. Dicha variable, la utilizaremos dentro de la función «record_state()» mediante la que controlaremos el estado de grabación:

Así esta función (que se ejecutará al pinchar en el botón «record«), en el momento en que se ejecute, si el valor actual de «recording» es «True» («if recording==True:«) por encontrarse en proceso de grabación, pasará a «False» («recording=False«). De no ser así («else:«), en primer lugar, su valor pasará a ser «True» y pasaremos a crear el objeto «out«, el cual, necesitará datos tales como el tamaño de la pantalla (variable «screen_size«) obtenida previamente, la variable «fourcc«, también definida en un momento anterior, y el nombre del futuro archivo de vídeo , el cual, generaremos con la función «file_name()«:

El objeto de esta función es el dar un nombre distinto (en este caso «screenvideo«, «screenvideo(1)«, «screenvideo(2)«…) a los sucesivos vídeos que vayamos generando, dentro de una misma carpeta, evitando que se sobrescriban los unos a los otros:

Sucesivas grabaciones de pantalla.

Para ello, haremos uso de «glob.glob()» mediante el cual obtendremos la lista de archivos presentes en la carpeta actual, pero solo de aquellos que tengan la extensión indicada como argumento («.avi» en esta ocasión). De este modo, usaremos un «for» para iterar sobre dicho listado, en busca de aquellos archivos que lleven el string «screenvideo» en su nombre. De modo que cuando eso suceda, incrementaremos el valor de «count» una unidad. De modo que al final del proceso si «count>0:» el nombre con el que se almacenará el futuro vídeo será el resultado de sumar el string «screenvideo«, el string del valor final de «count» y la extensión «.avi«. Por contra, si no se da que «count>0«, el archivo se guardará simplemente con el nombre predeterminado (por nosotros) de «screenvideo.avi«.

Volviendo a la función «record_state()» tras crearse el objeto «out» con los datos relativos al tamaño de pantalla y nombre del futuro archivo, iniciaremos un proceso paralelo, con «threading.Thread()» que ejecutará (variable «target«) la función «record» que es la que llevará a cabo el proceso en sí de grabación de la videocaptura:

Esta función será la encargada de crear nuestra videocaptura, mediante el mismo procedimiento que vimos en el ejemplo primero (con la salvedad de que en este caso no mostraremos la cascada de capturas y finalizaremos el proceso mediante el botón «Stop» de nuestra ventana) y cuya ejecución se producirá mientras el valor de «recording» sea «True» (recordemos que cambiábamos el valor de esa variable con «record_state()«). De modo que una vez finalicemos el proceso, el texto del botón volverá a ser «Record» y se creará el vídeo en la carpeta que hayamos indicado (y si no lo hemos hecho, en donde se encuentre alojado el programa).

Podéis ver el código integro de los dos ejemplo vistos aquí en los siguientes enlaces a Github:

https://github.com/antonioam82/Screen_Recorder/blob/master/ejemplo_screenr.py

https://github.com/antonioam82/Screen_Recorder/blob/master/screen_recorder.py

Saludos.