You are on page 1of 34

Aprendiendo GtkBuilder y Python

Nota: Presionando los links en negro se puede ir directamente a las entradas del blog, lo mismo con las imgenes para verlas en tamao completo.
Arrancamos el primer tutorial donde voy a explicar de forma fcil GtkBuilder y Python. Para los ms novatos GtkBuilder es un sistema relativamente nuevo que ha sido el reemplazo de la tecnologa en desaparicin libglade. En Internet hoy en da no hay muchos tutoriales para poder aprender de manera rpida y eficiente GtkBuilder, entonces estoy dejando mi contribucin con este pequeo y fcil tutorial. Lo primero sera instalar las dos herramientas que vamos a usar para poder programar nuestro programa, que ya les cuento de que se trata. Con el siguiente comando instalaremos Glade y Geany. sudo apt-get install glade geany El programa que vamos a realizar se ve como este:

Bsicamente cuando lo cree, era un programa para crear una lista de contenido multimedia y enviarla por medio de D-Bus a Rygel y que este enviara un stream a un televisor LED Samsung. En nuestro caso no queremos hacer nada tan complejo porque tocara escribir un tutorial de D-Bus. Le haremos unas cuantas modificaciones para que simplemente sea un programa que reproduzca contenido multimedia usando Gstreamer.

Este programa permitir entender los siguientes conceptos. Gtk.Liststore Gtk.Treeview Gtk.Filechooser Gtk.Window Gtk.Combobox Adicionalmente el cdigo sera vigente, lo que quiere decir que nuestro programa podr correr en Gtk2 o Gtk3. NO usaremos nada de Pygtk porque como ya sabemos pygtk esta muriendo, usaremos solo dos elementos de la nueva tecnologa que reemplazara a pygtk.

GLADE
Manos a la obra, lo primero es abrir Glade y crear una ventana en blanco.

Despus de creada debemos hacerla visible.

Adicionalmente en la pestaa general pueden ponerle un tamao predeterminado a la ventana, a mi personalmente me gusta ponerle a este programa 500 x 500

Lo segundo sera crear cuatro cajas verticales como se muestra en la figura.

Lo tercero es poner una barra de herramientas en la primera caja de la parte superior.

En cuarto lugar pondremos una barra de herramientas en la caja 2

En el quinto paso realizaremos dos cosas, la primera sera poner una ventana con barras de desplazamiento y dentro de esa ventana recin creada, pondremos una vista de rbol.

Para el paso final dividimos el ultimo elemento en dos cajas verticales y ponemos un boton label y un boton combobox.

Ya tenemos lo bsico listo, ahora vamos a configurar un poco la interfaz para dejarla lista para comenzar a programar.

Lo primero sera editar la barra de herramientas o toolbar, para ello seleccionamos este elemento en la lista superior derecha y le damos en el icono parecido a un lapiz.

Agregamos los botones que requerimos que son: Aadir Abrir Guardar Eliminar Limpiar Separador (Le damos click en la columna tipo y seleccionamos separador) Subir Bajar Separador Reproducir Detener Salir Se debe ver as:

Ahora vamos a configurar la parte inferior para ello lo primero sera seleccionar el hbox y le damos en la pestaa empaquetado y en el comando expandir le damos que no. Se debe ver de la siguiente forma, adicional a la imagen pueden poner en donde dice separacin un valor de 4.

En la etiqueta Label ponemos el termino "Orden" y en donde dice separacin ponemos un valor de 4. Ya hemos terminado lo bsico del programa, en esta parte no realizaremos ms edicin sobre lo grfico en el segundo tutorial organizaremos muchas mas cosas visuales. Finalmente guardamos le ponen algn nombre y lo terminan con la extensin .ui en mi caso lo guarde como Mutiplay.ui

PYTHON
En el cdigo en python por ahora solo vamos a ejecutar el programa.
#!/usr/bin/python

from gi.repository import Gtk class main: def __init__(self): # Crea la ventana de trabajo Principal y obtiene los objetos en Glade builder = Gtk.Builder() builder.add_from_file("Multiplay.ui")

#Ejecucion del programa if __name__ == "__main__": main() Gtk.main()

Voy a explicar el cdigo rpidamente, la primera linea, from gi.repository import Gtk lo que hace es busca el modulo gi e importa Gtk esto antes se hacia con pygtk pero recuerden que esto ya esta muriendo, si quieren utilizar Gtk 3 tienen que instalar en synaptic el modulo gi de Gtk3 y el programa automticamente buscara Gtk3. Si quieren probar el programa con Gtk 3 deben: sudo apt-get install gir1.2-gtk-3.0 Nota: Si usan Fedora 15, Ubuntu 11.10 o cualquier distribucin con Gnome 3 o superior este paso anterior no es necesario. Bueno ahora que ya hemos importado Gtk debemos crear una clase de nombre main y dentro vamos a cargar la interfaz grfica que hemos realizado en Glade, para ello lo primero sera definir el builder que es una funcin para Gtk y lo segundo sera usar el builder definido para buscar desde un archivo el codigo XML eso lo realiza la linea builder.add_from_file("Multiplay.ui") Donde Multiplay.ui es el nombre que le pusimos al archivo que guardamos antes. Finalmente las ultimas lineas de ejecucin del programa lo que hacen es que si se llama la funcin __name__ entonces corra el programa. Vamos a terminar de organizar todos los elementos grficos para poder concentrarnos nicamente en el cdigo en Python. Lo primero es abrir glade y abrir nuestra interfaz grfica recin creada, la ma se llama Multiplay.ui. Lo primero es ponerle nombre a la Ventana en mi caso le puse Multiplay

Despus de agregado el titulo y anchura y altura predeterminado vamos a la pestaa de seales y en donde dice delete-event le ponemos la primera opcin que aparece en el men desplegable.

Ahora nos movemos sobre los dems elementos que estn dentro de multiplay y nos vamos a la toolbar1 y vamos a configurarlo como se ve en la imagen, muchos siempre recomiendan usar siempre una separacin de 4 en nuestras aplicaciones y yo usualmente sigo esta recomendacin de las personas que ms saben de Gtk.

Vamos a configurar cada uno de los botones que estn dentro del toolbar1 de la siguiente forma, vamos a hacerlo de izquierda a derecha, recordemos que el primero es el de aadir o Add.

Al botn de aadir le ponemos on_Add_clicked A los dems le ponemos: on_AddFolder_clicked on_Saved_clicked on_Clearone_clicked on_Clearlist_clicked on_Up_clicked on_Down_clicked on_Play_clicked on_Stop_clicked Y finalmente el botn de cerrar le ponemos el mismo elemento que le pusimos a la ventana Multiplay on_Multiplay_delete_event Despus vamos al siguiente que es el GtkTreeView y lo vamos a configurar como se ve en la imagen. Las cabeceras no queremos que sean visibles, este acumulara bsicamente las columnas, si quieren pueden dejarlo activo para poder ver como se ve todo mas adelante. Yo lo he configurado de la siguiente forma.

Ahora con el botn del lpiz vamos a editar el TextView, nos mostrara esta ventana donde vamos a ir a jerarqua y vamos a agregar una columna. Yo e puse de nombre Archivos y de etiqueta use el mismo nombre Archivos.

Despus damos click derecho sobre la columna y nos aparece esto:

Despus de agregar el hijo Texto nos apareci un nuevo elemento en nuestra lista, donde lo nico que cambiaremos sera el valor del texto el cual pondremos en 0.

Una ves terminado este paso vamos a proceder con el siguiente elemento construido en la interfaz grfica que seria el combobox, vamos a dar click en el lpiz para editar y vamos a ir a jerarqua y agregamos un elemento como se ve en la figura, configuramos el valor texto en 0.

Ya casi estamos terminando, vamos a agregar ahora todos los dems Widgets que vamos a utilizar, que son la ventana de abrir, la ventana de guardar, la de abrir carpeta, la ventana acerca de. Para ello seguimos las siguientes imgenes.

Lo primero es crear el Selector de Archivos, despus vamos a volver a usar el mismo botn y creamos el selector de carpetas, pero le cambiamos la accin para que esta sea abrir carpetas como se muestra en la figura.

Creamos finalmente el dialogo de guardar y le ponemos accin guardar, configuramos los elementos con los nombres Add, AddFolder, Save y le ponemos un titulo a cada ventana recin creada como se ve en la siguiente imagen. En este caso como es la de guardar el titulo que le he puesto es Guardar Lista a las dems les puse Abrir Archivos y Abrir Carpeta respectivamente.

Como pudieron ver estas 3 ventanas recin creadas les falta unos cuantos elementos y con eso me refiero a los dos botones de la parte inferior el de Abrir y Cancelar, entonces lo que hacemos es agregarlos y los seleccionamos de Stock, para la ventana de guardar en vez de Abrir buscamos el botn que diga Guardar.

Finalmente a todos los botones cancelar le vamos a poner un ID determinado como se ve en la imagen con un valor de -6 que bsicamente le dice por defecto a la ventana que debe cerrarse. En el caso de Abrir y de Guardar le vamos a poner al ID de Respuesta un valor de -5 que bsicamente enva una seal afirmativa, esto sera importante cuando programemos en Python. A todos los botones de abrir y guardar les tenemos que configurar algo ms antes de terminar. Como se ve en la imagen. Le agregamos Si a puede por omisin.

Finalmente el ultimo paso que vamos a seguir es el acerca de, como se ve en la siguiente figura. Lo pueden configurar a su gusto.

Para continuar vamos a crear unos Gtk.ListStore, lo podemos hacer como se ve en la imagen.

Vamos a crear dos de estos y los vamos a nombrar como aparece en la siguiente imagen y adicional mente les vamos a poner elementos gchararray como se ve en las dos siguientes figuras.

Finalmente al OrderList le vamos a agregar dos filas una con el nombre "Orden Aleatorio" y otra con "Orden Alfabetico" como se ve en la siguiente figura.

Una vez terminado esto lo nico que nos falta para terminar la parte grfica es asociar estos dos modelos con otros elementos de la interfaz grfica, me imagino que ya suponen con cual, Medialist se va a asociar con el TreeView y OrderList se va a asociar con el Combobox que pusimos en la parte de abajo de la aplicacin. Esto se hace de la siguiente forma:

Con esto hemos terminado todo lo que tenia que ver con la interfaz grfica, puede ir a correrla para verificar que todo esta en orden y vamos con el cdigo en Python ahora. Lo primero que vamos a hacer es asociar los botones que estn en la parte de arriba de izquierda a derecha. El primero sera Aadir Lo primero es que como ya definimos en el tutorial pasado algunas acciones para los botones estas deberamos meterlas en un diccionario, en liblgade que ya esta extinto, esto se tenia que hacer de forma individual, con GtkBuilder no es necesario, por ahora vamos a ir agregando definiciones a nuestro diccionario de una en una, la primera sera asignar una accin a la seal on_Add_clicked. Los diccionarios en python se construyen con corchetes seguidos de una accin, en este caso es una accin propia que se llamara self.showAddFile, que bsicamente lo que har mas adelante es llamar el dialogo de abrir archivos que creamos en Glade en el tutorial pasado. Despus simplemente conectamos la seal con el diccionario que hemos creado usando la instruccin builder.connect_signals(dict) eso es muy parecido a lo que hacamos con libglade.

#!/usr/bin/python

from gi.repository import Gtk class main: def __init__(self): # Crea la ventana de trabajo Principal y obtiene los objetos en Glade builder = Gtk.Builder() builder.add_from_file("Multiplay.ui") # Diccionario de eventos y Conexion de los mismos. dict = {"on_Add_clicked": self.showAddFile, } builder.connect_signals(dict)

#Ejecucion del programa if __name__ == "__main__": main() Gtk.main() El cdigo anterior no produce absolutamente nada mas que un error de definicin porque no tenemos la definicin de self.showAddFile El siguiente paso es crear una definicin propia para self.showAddFile Las definiciones en python se crean con el comando def. En este caso vamos a crear una definicin propia que aparte de eso recibe un widget, un widget es bsicamente un elemento grfico, este elemento grfico le pondremos como nombre addfile y despus lo vamos a llamar en otra parte del codigo.
#Definicion del comando Agregar

def showAddFile(self, widget): self.addfile.run() self.addfile.hide()

Despus de definido showAddfile hemos agregado un nuevo elemento que python no conoce y es self.addfile y al final hemos usado el comando run() para que este elemento se muestre y el comando hide() para que al presionar cancelar dentro de la ventana creada esta se esconda. Pero vamos a definir self.addfile en la parte superior donde hemos llamado al builder, debe ser algo asi: self.addfile = builder.get_object("Add") esto basicamente lo que hace es buscar en el archivo .ui que es el de la interfaz grfica un objeto que tiene el nombre Add, nuestro cdigo completo quedara de la siguiente forma.

#!/usr/bin/python

from gi.repository import Gtk class main: def __init__(self): # Crea la ventana de trabajo Principal y obtiene los objetos en Glade builder = Gtk.Builder() builder.add_from_file("Multiplay.ui") self.addfile = builder.get_object("Add")

# Diccionario de eventos y Conexion de los mismos. dict = {"on_Add_clicked": self.showAddFile} builder.connect_signals(dict) #Definicion del comando Agregar def showAddFile(self, widget): self.addfile.run() self.addfile.hide() #Ejecucion del programa if __name__ == "__main__": main() Gtk.main() Podemos proceder a correr el codigo que tenemos en este punto y ver los resultados. Al presionar el botn agregar aparece el dialogo de agregar archivos. Vamos a repetir el mismo procedimiento para cada uno de los botones que tienen que desplegar ventanas nuevas, en este caso estamos hablando de acerca de, abrir carpeta y guardar. Entonces lo primero es en el diccionario agregar los nuevos elementos "on_AddFolder_clicked": self.showAddFolder, "on_Saved_clicked": self.showSave, "on_About_clicked": self.showAbout,

Despus de agregados estos elementos buscamos los elementos del builder y los agregamos. self.addfolder = builder.get_object("AddFolder") self.save = builder.get_object("Save") self.about = builder.get_object("About")

Y los definimos igual que lo hicimos con agregar. Vamos a hacer una modificacin en la definicin de acerca de, esto se debe a que este widget no debe recibir ningn tipo de datos. Los dems si queremos que reciban datos, por eso despus de widget agregamos data = None. Nuestro archivo final va a quedar de la siguiente forma. #!/usr/bin/python from gi.repository import Gtk class main: def __init__(self): # Crea la ventana de trabajo Principal y obtiene los objetos en Glade builder = Gtk.Builder() builder.add_from_file("Multiplay.ui") self.addfile = builder.get_object("Add") self.addfolder = builder.get_object("AddFolder") self.save = builder.get_object("Save") self.about = builder.get_object("About") # Diccionario de eventos y Conexion de los mismos. dict = {"on_Add_clicked": self.showAddFile, "on_AddFolder_clicked": self.showAddFolder, "on_Saved_clicked": self.showSave, "on_About_clicked": self.showAbout, } builder.connect_signals(dict) #Definicion del comando Agregar def showAddFile(self, widget): self.addfile.run() self.addfile.hide() #Definicion del comando Abrir Carpeta def showAddFolder(self, widget): self.addfolder.run() self.addfolder.hide() #Definicion del comando Guardar Permite guardar una lista def showSave(self, widget): self.save.run() self.save.hide() #Definicion del Comando Acerca de: def showAbout(self, widget, data=None): self.about.run() self.about.hide()

#Ejecucion del programa if __name__ == "__main__": main() Gtk.main() Lo primero que les voy a ensear es a manejar archivos en python, hasta el momento solo podemos invocar la ventana que muestra el selector de archivos pero como pueden notar esto no hace absolutamente nada cuando presionamos doble click sobre un archivo. Lo primero que queremos hacer es permitirle al botn abrir hacer alguna cosa, en este caso simplemente le vamos a dar la habilidad de obtener el nombre de los archivos que se seleccionen, esto se hace con el comando get_filenames() que lo que hace es obtener los nombres de los archivos seleccionados por la interfaz self.addfile. Se acuerdan que al botn cerrar y aceptar le pusimos un id de -6 y -5 respectivamente. Bueno el -6 por defecto siempre es cerrar y el -5 implica una respuesta positiva, por eso usamos un if que simplemente hace algo si la respuesta es -5. #Definicion del comando Agregar def showAddFile(self, widget): respt = self.addfile.run() self.addfile.hide() if respt == -5: fileselected = self.addfile.get_filenames() print fileselected En este caso si la respuesta del botn es afirmativa (-5) entonces en la variable fileselected va a guardar los nombres de los mismos y los va a imprimir en el terminal con el comando print, mas adelante vamos a mejorar esto importando algunos mdulos de python pero por ahora vamos a dejar esta parte as. Como muchos de ustedes han notado la mayora de dilogos de abrir tienen un filtro en la parte de abajo, como se ve en la imagen justo arriba del boton de abrir donde dice Media Files.

Vamos a crear este filtro para nuestra aplicacin, este tipo de filtros se llama Gtk.FileFilter() y lo ideal seria que lo invocramos debajo de los builders para poder usarlo varias veces en la ventana de abrir carpeta y en la de guardar. Lo invocamos con self.filterbox = Gtk.FileFilter() como se ve en la linea 8 def __init__(self): # Crea la ventana de trabajo Principal y obtiene los objetos en Glade builder = Gtk.Builder() builder.add_from_file("Multiplay.ui") self.addfile = builder.get_object("Add") self.addfolder = builder.get_object("AddFolder") self.save = builder.get_object("Save") self.about = builder.get_object("About") self.filterbox = Gtk.FileFilter() Con esto listo volvemos a trabajar, para ello vamos a crear una tupla, que es bsicamente una lista que contiene dos elementos en forma de columna, a esta tupla le vamos a poner el nombre filepattern o en espaol seria algo como patronesdearchivo y lo vamos a ubicar justo antes de la clase main. La primera columna contiene el nombre (name) de los archivos MP3, AVI y la segunda contiene el patron de los archivos (pattern) *.mp3, *.avi #!/usr/bin/python from gi.repository import Gtk filepattern = ( ("MP3","*.mp3"), ("AVI","*.avi"), ("MPEG-4","*.mp4"), ("FLV ","*.flv"), ("OGG","*.ogg"), ) class main: Una vez creada esta tupla vamos a continuar trabajando en el dialogo de abrir archivos, para cada elemento en la tupla vamos a crear dos nuevas variables una de nombre Name y la otra de nombre Pattern y todo esto es posible porque usamos un for name, pattern in filepattern: Esto permite iterar sobre la tupla y el comando self.filterbox.add_pattern(pattern) lo que hace es crear el patrn en el filtro. El comando self.addfile.add_filter(self.filterbox) lo que hace es agregar el filtro a la base de nuestro dialogo de abrir archivos, cada vez que creamos un filtro al cerrar la aplicacin debemos eliminarlo por eso en la linea 10 agregamos el comando self.addfile.remove_filter(self.filterbox) que lo que hace es eliminar el filtro al cerrar el dialogo. #Definicion del comando Agregar def showAddFile(self, widget): self.filterbox.set_name("Media Files") for name, pattern in filepattern: self.filterbox.add_pattern(pattern) self.addfile.add_filter(self.filterbox) respt = self.addfile.run() self.addfile.remove_filter(self.filterbox) self.addfile.hide() if respt == -5: fileselected = self.addfile.get_filenames() print fileselected

Hacemos el mismo procedimiento para abrir carpetas y al final nuestro programa queda de la siguiente forma. #!/usr/bin/python from gi.repository import Gtk filepattern = ( ("MP3","*.mp3"), ("AVI","*.avi"), ("MPEG-4","*.mp4"), ("FLV ","*.flv"), ("OGG","*.ogg"), ) class main: def __init__(self): # Crea la ventana de trabajo Principal y obtiene los objetos en Glade builder = Gtk.Builder() builder.add_from_file("Multiplay.ui") self.addfile = builder.get_object("Add") self.addfolder = builder.get_object("AddFolder") self.save = builder.get_object("Save") self.about = builder.get_object("About") self.filterbox = Gtk.FileFilter() # Diccionario de eventos y Conexion de los mismos. dict = {"on_Add_clicked": self.showAddFile, "on_AddFolder_clicked": self.showAddFolder, "on_Saved_clicked": self.showSave, "on_About_clicked": self.showAbout, } builder.connect_signals(dict) #Definicion del comando Agregar def showAddFile(self, widget): self.filterbox.set_name("Media Files") for name, pattern in filepattern: self.filterbox.add_pattern(pattern) self.addfile.add_filter(self.filterbox) respt = self.addfile.run() self.addfile.remove_filter(self.filterbox) self.addfile.hide() if respt == -5: fileselected = self.addfile.get_filenames() print fileselected #Definicion del comando Abrir Carpeta def showAddFolder(self, widget): self.filterbox.set_name("All Media Files") for names, patterns in filepattern: self.filterbox.add_pattern(patterns) self.addfolder.add_filter(self.filterbox)

respt = self.addfolder.run() self.addfolder.remove_filter(self.filterbox) self.addfolder.hide() if respt == -5: addmultiple = self.addfolder.get_filename() #Definicion del comando Guardar Permite guardar una lista def showSave(self, widget): self.save.run() self.save.hide() #Definicion del Comando Acerca de: def showAbout(self, widget, data=None): self.about.run() self.about.hide() #Ejecucion del programa if __name__ == "__main__": main() Gtk.main() NOTA: PARA COPIAR EL CODIGO PUEDEN VENIR A ESTE LINK Hasta este punto hemos configurado los dilogos de abrir y seleccionar carpeta. Por suerte para nosotros Python ha creado mdulos que hacen el trabajo bastante simple, lo que vamos a configurar es quizas una de las caractersticas ms difciles de trabajar cuando se hace en Pygtk, me estoy refiriendo al Gtk.Treeview que es sin duda alguna una pesadilla, pero les voy a mostrar de forma fcil como trabajar esto. Gracias a Glade ya nosotros creamos un modelo que se llama MediaList y otro que se llama OrderList los TreeView bsicamente permiten construir modelos dentro de ellos, esto suele ser un tema altamente complejo porque en Pygtk es un monstruo esto. Nosotros contamos con la suerte de haber creado un Gtk.Liststore y asociarlo a un Gtk.Treeview y a su vez dentro del Liststore hemos creado dos gchararray, si yo se que suena todo de una forma miedoso pero todo eso lo hicimos en glade en el tutorial 3 sin necesidad de crear todo una cadena de cdigos en Pygtk. Hemos reducido todo este texto a una sola linea y nos hemos ahorrado usar Pygtk que es una tecnologa que ya no se esta usando y permanecemos con tecnologa moderna como Gtk 3 y Gtk 2 dentro de GtkBuilder self.medialist = builder.get_object("MediaList") Esta linea que esta en roja va justo donde hemos llamado los GtkBuilders quedando ubicada como la linea numero 9 de nuestro codigo.

def __init__(self): # Crea la ventana de trabajo Principal y obtiene los objetos en Glade builder = Gtk.Builder() builder.add_from_file("Multiplay.ui") self.addfile = builder.get_object("Add") self.addfolder = builder.get_object("AddFolder") self.save = builder.get_object("Save") self.about = builder.get_object("About") self.medialist = builder.get_object("MediaList") self.filterbox = Gtk.FileFilter() Ya se que aun no entienden que hemos logrado, pero les voy a dar el ejemplo ms claro, vamos a terminar de configurar definitivamente los dialogos de Abrir y Seleccionar Carpeta y vamos a mostrar cada uno de los archivos que seleccionemos dentro del programa en el Gtk.Treeview. Para ello tenemos que importar un modulo que trabaja con archivos y permitir ahorrar mucho trabajo, este modulo es usado para todo lo relacionado con archivos, cosas como abrir, ver rutas, guardar y dems, la belleza de python reside en que existe muchos mdulos que hacen la vida tan simple. import os Esta linea la ponemos antes de from gi.repository import Gtk Ahora debemos recordar la definicin del comando Agregar, como pueden notar ahora las lineas 14, 15 y 16 han sido agregadas, la linea 14 lo que hace es iterar con todos los nombres de archivos seleccionados, recuerden que en la linea 13 usamos get_filenames() que obtiene todos los nombres de archivos seleccionados en oposicin aget_filename() que solo obtiene el nombre de un archivo seleccionado. La linea 15 va a crear una tupla con dos columnas, la primera contendr las direcciones y la segunda contendr el nombre de los archivo, la funcin os.path.split(files) es una funcin que divide en dos columnas el nombre de un archivo de la siguiente forma. Ejemplo: Seleccionamos el archivo siguiente: /home/geojorg/Msica/Aerosmith/Aerosmith - Just Push Play.mp3 Al aplicar os.path.split a la direccin anterior nos mostrara lo siguiente. ("/home/geojorg/Msica/Aerosmith/" , "Aerosmith - Just Push Play.mp3") Como pueden ver creo una tupla con dos columnas, en la primera esta la ruta del archivo y en la segunda el nombre del archivo. #Definicion del comando Agregar def showAddFile(self, widget): self.filterbox.set_name("Media Files") for name, pattern in filepattern: self.filterbox.add_pattern(pattern) self.addfile.add_filter(self.filterbox) respt = self.addfile.run() self.addfile.remove_filter(self.filterbox) self.addfile.hide() if respt == -5: fileselected = self.addfile.get_filenames() for files in fileselected: (dirs,files)= os.path.split(files) self.medialist.append([files,dirs])

Ahora que hemos creado la tupla con la linea 15 lo ideal es es que la linea 16 agregue estos dos elementos a nuestro Gtk.Treeview, lo agregara de la siguiente forma, la primera columna es la 0 y esa columna contendr los nombres del archivo, en nuestro ejemplo seria Aerosmith - Just Push Play.mp3 y la columna 1 no es visible pero en ella guardara la Ruta /home/geojorg/Msica/Aerosmith/ se preguntaran porque esto. Les debo decir que la respuesta esta en el tutorial 2, se acuerdan que pusimos un valor de 0 al crear el TextView, bueno eso bsicamente hace que solo se pueda ver la columna 0 y esa es la idea. Ahora en la definicin de Seleccionar Carpeta vamos a hacer unos cambios tambin. Hemos agregado la linea 16, 17 y 18. Vamos a explicar entonces. La linea 16 va iterar en la carpeta seleccionada con la linea 14, para carpetas siempre usamos get_filename() porque solo vamos a seleccionar una y de ah seleccionamos todas la hijas que estn dentro de esta, se puede lograr esto usando el comando os.walk que es un comando que funciona de forma iterativa y recursiva hacia abajo. Para que quede un poco mas claro vamos a hacer un ejemplo.

Ejemplo: Supongamos que vamos a seleccionar la carpeta Msica y esta carpeta contiene 3 ms. Las carpetas hijas son: Aerosmith, Pink, Rolling Stones y cada una de estas tiene varios archivos dentro. os.walk es capaz de buscar dentro de Msica las 3 carpetas y ver que archivos hay dentro de estas, al final arroja 3 parmetros root, dirs, filelist. Root indica cual es la fuente, en nuestro caso sera: /home/geojorg/Msica Dirs indica todas las carpetas dentro de Root ["Aerosmith","Pink","Rolling Stones"] Filelist indica todos los archivos dentro de Dirs ["Aerosmith - Just Push Play.ogg ","Pink - Just Like a Pill.mp3","Rolling Stones Angie.mp3"] Como pueden ver os.path no crea tuplas, crea listas y esto no es ideal, por eso hay que agregar la linea 16. La linea 16 funciona de la siguiente forma. Para todos los archivos que estn dentro de un valor que esta almacenado en filelist va a buscar algo. Ese algo es este comando endswith. Voy a explicar que hace entonces con un ejemplo. Ejemplo: Tenemos los 3 archivos y ya los tenemos en una lista en columna que fue lo que logramos con el comando f for f in filelist Quedaron representados entonces tal como se ve ms abajo, el comando endswith se traduce al espaol de forma similar a "termina en" y lo que hace es buscar un patrn final dentro de un listado, en este caso lo que va a buscar es que la lista tenga archivos que terminen en "mp3, mpeg, avi, ogg" y los que terminen en alguna de esas extensiones los pondr como validos (True) , los que no terminen en esas extensiones no sern validos (False) entonces los eliminara de la lista. El comando endswith siempre se aplica a una variable, en este caso se lo aplicamos a f por eso queda f.endswith y dentro

del parntesis estarn todos las extensiones (mp3,mpeg y demas) pero para evitar meter todo una lista de extensiones es ms fcil crear una variable que contenga todas las extensiones que nos interese. pattern = (".mp3",".mp4",".avi",".flv",".ogg") Aerosmith - Just Push Play.ogg Pink - Just Like a Pill.mp3 Rolling Stones - Angie.mp3 #Definicion del comando Abrir Carpeta def showAddFolder(self, widget): self.filterbox.set_name("All Media Files") for names, patterns in filepattern: self.filterbox.add_pattern(patterns) self.addfolder.add_filter(self.filterbox) respt = self.addfolder.run() self.addfolder.remove_filter(self.filterbox) self.addfolder.hide() if respt == -5: addmultiple = self.addfolder.get_filename() for root, dirs, filelist in os.walk(addmultiple): for allfiles in [f for f in filelist if f.endswith(pattern)]: self.medialist.append([allfiles,root])

Finalmente la linea 18 pondr dentro del Gtk.Treeview los archivos y la ruta respectivamente en la columna 0 y 1. A continuacin vamos a aprender que son los Gtk Tree Iters y como se relacionan con el Gtk.Treeview, terminaremos entonces de organizar algunos botones que hacen falta como Limpiar, Arriba, Abajo y eliminar.

Vamos a terminar de configurar algunas cosas, como ya pudieron notar en el anterior logramos agregar archivos a una lista dentro de un Gtk.Treeview.

En esta entrada vamos a realizar trabajos con los botones eliminar archivo , limpiar, arriba y abajo, estos botones nos ensearan algunos comandos nuevos y nos permitirn entender de alguna forma que es un Gtk.TreeIter que es una de las cosas ms complejas que hay. La documentacin de Gtk3 y 2 es ms bien muy regular en este apartado. Entonces los botones que vamos a configurar son:

Lo primero que vamos a hacer es agregar al diccionario las siguientes seales. Eliminar uno, borrar lista, arriba, abajo. "on_Clearone_clicked": self.clearone, "on_Clearlist_clicked": self.clearlist, "on_Up_clicked": self.updown_move, "on_Down_clicked": self.updown_move,

Ahora que hemos agregados estos 4 elementos al diccionario es fundamental crear las definiciones para cada uno ya que de lo contrario el programa nos mostrara un error. La primera definicin nueva que vamos a agregar es clearone, que bsicamente se encargara de eliminar un solo elemento de la lista. Voy a explicar con detalle, la primera linea define un elemento propio self que admite argumentos, esta definicin no tiene Widget porque no va a crear una ventana ni un dialogo. La segunda linea le da el nombre clearone al treeview pero este tiene que estar definido propiamente, por eso hay que buscar el objeto treeview dentro del archivo .ui y agregarlo al programa, esto se hace en los builder con esta linea de cdigo donde llamamos al objeto de nombre TextView que habamos creado en Glade self.treeview = builder.get_object("TextView") El comando get_selection() de la linea 2 es un comando que es valido para los Gtk.Treeviews y bsicamente lo que hace es seleccionar todo el Gtk.Treeview. La linea 3 crea una tupla que contiene dos elementos, el primero es el modelo que no es de inters en el 95% de los casos y la segunda es la fila, eso es posible al usar el comando get_selected_rows() este es un comando que selecciona las filas dentro del Treeview y que bsicamente muestre el Treepath pero dentro del treepath estn los nmeros de las filas es decir la fila 1 la 2 y as en adelante por eso si usamos un for podemos iterar estos nmeros de filas. Voy a explicar entonces que es un iter. Un iter es una especia de valor iterativo dentro de Gtk, claro que es algo mucho ms complejo que eso, pero bsicamente todos los modelos de Treeviews son iterativos lo que significa que tienen una secuencia ordenada. La linea 6 crea una nueva variable con nombre iteration y lo que hace es dentro del modelo encontrar el patrn iterativo para la fila i. Recuerden que i no es un simple numero es un valor que solo es compatible con los Gtk.TreeIters, por eso si ponemos 2 en vez de i el programa no funcionara. Yo se que lo anterior suena complejo y ojala pudiera usar palabras ms simples pero no es posible. Dentro de iteration quedara entonces guardado un valor similar visualmente a esto "gtktreeiter 0x17004c0" con esto la ultima linea lo que hace es remover ese valor y organizar de manera iterativa la nueva lista. #Definicion del comando Borrar un Archivo def clearone(self, *args): clearone = self.treeview.get_selection() (model, rows) = clearone.get_selected_rows() for i in rows: iteration=model.get_iter(i) self.medialist.remove(iteration) Tambin sera necesario entonces crear la definicin para borrar todo. Esta es bastante fcil porque en Gtk haban pensado en esto, entonces no tendremos que rompernos la cabeza pensando en Iters. El comando clear() que es un comando de Gtk.Liststore permite borrar todo el contenido de MediaList #Definicion del comando Borrar Lista def clearlist(self, *args): self.medialist.clear()

Ahora vamos con el comando Arriba y Abajo, esto es particularmente interesante, se pueden configurar por separados o en un solo cdigo, eso es cosa de cada quien, a mi particularmente me gusta hacerlo en una sola definicin. Tambin es curioso porque este cdigo que les voy a mostrar esta hecho para ser compatible con Gtk3 y con Gtk2 y quizs si alguno de ustedes ha programado antes me va a decir que existe una forma ms fcil de hacerlo usando ItersPath, pues bueno para los que no saben eso ya no funciona en Gtk3 y lo ideal es programar usando las nuevas tecnologas. Primero agregamos estos dos elementos a nuestro Gtkbuilder en la parte de arriba donde estamos definiendo los builders. Recuerden que Up y Down son los nombres que estan en Glade para el boton de Arriba y Abajo, recuerden usar siempre el mismo nombre. self.up = builder.get_object("Up") self.down = builder.get_object("Down") Entonces explico la definicion. Aqu ya no vamos a esperar argumentos, esta vez esperamos respuesta de un botn. La linea 3 lo que hace es seleccionar el Treeview ya eso lo explique antes, la 4 tambin la explique, obtenemos el modelo y las filas. La 5 bsicamente es un if que nos indica que si se presiona el botn Self.Up realice algo. Voy a explicar entonces que es ese algo. Como les explique ItersPath no existe en Gtk3 entonces hay que crearlos. El path1 es similar al i que explique antes y arroja un numero 1, 2, 3 dependiendo de la fila seleccionada. El path 2 obtiene el valor anterior al numero que seleccionamos, pero como les explique los i o path no son nmeros normales, entonces hay que hacerles una conversin, lo primero es pasar el numero que tenamos a string es decir a texto y despus a int, y de esa forma podemos restarle un numero normal como 1. En la linea 8 vamos a definir que si estamos en la fila 0 es decir la primera, path2 no puede ser menor que 0 porque eso dara un error al programa, en ese caso debe retornar. De lo contrario si estamos en una linea diferente obtenga el valor del path1 y del path2 y los intercambie eso se logra con el comando swap() que es capaz de intercambiar dos iters. Si por el contrario presionamos el botn Self.Down en vez de buscar el iter anterior debemos buscar el siguiente, por suerte Gtk2 y Gtk3 incluyen una funcion que lo hace sin tanto complique iter_next() busca el elemento siguiente en la lista, ya se que algunos se preguntaran porque no existe iter_before() y la verdad no se la respuesta, pero no existe este comando, por eso toca hacerlo usando path. Si estamos parado en la ultima fila entonces el iter siguiente sera None si esto pasa debe devolver la funcin, en el caso contrario debe intercambiar el iter 1 y el iter 2

#Definicion del comando Arriba y Abajo def updown_move(self, button): line = self.treeview.get_selection() (model, rows) = line.get_selected_rows() if button == self.up: for path1 in rows: path2 = int(str(path1))-1 if path2 < 0: return else: iter1 = model.get_iter(path1) iter2 = model.get_iter(path2) model.swap(iter1,iter2) if button == self.down: for path1 in rows: iter1 = model.get_iter(path1) iter2 = model.iter_next(iter1) if iter2 == None: return else: model.swap(iter1,iter2) Si nuestra intencin es que el programa solo funcione con la tecnologa vieja, es decir Gtk2 podemos reducir esto en 4 lineas, pero insisto que no sera compatible con Gtk3, si alguien quiere esa solucin me puede escribir en los comentarios. En el prximo tutorial explicare como trabajar con Combobox y con nuevas funciones y mdulos que tendremos que importar para hacer funcionar aleatorio y alfabtico. El cdigo del programa completo hasta donde vamos lo pueden descargar de aqu. Vamos a configurar el Combobox

Y vamos a entender como se puede hacer Orden Aleatorio y Orden Alfabtico en Python. La verdad esto es bastante simple pero tendremos que volver un momentito a glade a realizar algo que olvidamos antes y es crear una seal cuando el combobox cambie.

Despus de realizado esto en Glade podemos volver de nuevo sobre el codigo en Python.

Lo primero sera agregar al builder el OrderList que habamos creado en glade. self.combolist = builder.get_object("combobox1") Lo segundo sera agregar al diccionario la seal que creamos en glade. "on_ComboOrder_changed": self.newList, Despus vamos a crear la definicin newList, que sera la encargada de hacer los cambios cuando se seleccione alguna de las opciones del combobox. Explico entonces el cdigo. La linea 3 define una variable de nombre var que bsicamente sera un valor numrico que se obtiene de la lista dentro del combobox si se selecciona el primer elemento del combobox este tendr un numero 0 si se selecciona el segundo tendr un valor de 1 y as para dems opciones. Esto se hace con que get_active(). Si el valor de var es igual a 0 quiere decir que se selecciono la opcin Aleatorio. Lo primero que se hace es crear una variable contadora n que arranca en 0 y entonces se itera sobre cada uno de los elementos dentro del Medialist. Si hay 20 canciones entonces itera de una en una sobre las 20, al final el contador n mostrara 19 elementos. Con estos elementos se crea un indice o una lista, como le quieran decir, esta arranca en 0 y termina en 19 para este ejemplo, eso es posible gracias al comando range(). La linea 19 dice shuffle(index) bueno shuffle es un modulo de python que simplemente podemos importarlo, este modulo crea valores aleatorios basados en una lista. Agregamos entonces en la parte superior el modulo ramdon y de este importamos shuffle. from random import shuffle Con eso hemos terminado de crear un nuevo listado aleatorio, si el largo o len() de la lista index es mayor que 1 entonces si aplica el comando reorder() que es un comando de Gtk que permite reordenar una Gtk.Liststore basado en un nuevo orden. #Definicion para ordernar la lista def newList(self, widge): var = self.combolist.get_active() #Si se activa Aleatorio if var == 0: n=0 for musicfiles in self.medialist: n += 1 index = range(0, n) shuffle(index) #Reorganiza la lista de musica llamad medialist if len(index) > 1: self.medialist.reorder(index) #Si se activa Alfabetico if var == 1:

iter=self.medialist.get_iter_first() newlistfiles = [] while iter: files = self.medialist.get_value(iter,0) source = self.medialist.get_value(iter,1) newlistfiles.append([files,source]) iter = self.medialist.iter_next(iter) newlistfiles = sorted(newlistfiles) self.medialist.clear() for files,source in newlistfiles: self.medialist.append([files,source]) Ahora si var es igual a 1 estamos hablando de alfabtico, para ello tenemos que iterar sobre todos los elementos de la lista y tenemos que exportar los nombres de las canciones y guardarlos en una variable. La linea 18 se para en el Gtk.Treeview y obtiene el primero elemento con el comando get_iter_first(), este comando es parecido a get_iter_root() que obtiene el primer elemento del treeview y obtiene la ruta hasta este, recuerden que get_iter_root() ya no existen en Gtk3 pero si esta presente en Gtk2 por si lo requieren. La linea 19 crea una variable vaca. Mientras se itera sobre cada uno de los elementos de la lista va a obtener el nombre de los archivos que estn en la columna 0 y los guardara en la variable files, esto mismo para la ruta de los archivos pero la guardara en la variable source y finalmente con el comando append() vamos a unir el nombre y la ruta y quedaran guardados en newlistfiles. La linea 24 simplemente pasa al siguiente iter y como estn dentro de un while pues el proceso se hace hasta la ultima linea. Despus de realizado este procedimiento se utiliza el comando sorted() que bsicamente es una funcin de python que organiza los elementos de una lista de forma alfabtica, para terminar borramos toda la lista que tenemos en nuestro Treeview y metemos la nueva con el comando append([files,source]) y hemos terminado de organizar nuestra lista de forma alfabtica.

Ahora vamos a trabajar sobre el dialogo de guardar, vamos a refinarlo y vamos dejarlo listo para configurar los ltimos elementos de la interfaz, lo dems sera refinar el cdigo en algunas partes. Voy entonces a explicar cada uno de los elementos del codigo. Las lineas 3, 4, 5, 6 Bsicamente estn definiendo un formato de archivo a guardar, este caso el formato tiene como nombre Lista Multimedia y la extensin es pls que es bsicamente una extensin que programas como Banshee y Totem usan para contenido multimedia diverso. Como en el caso de los dilogos de abrir y seleccionar carpeta si la respuesta es afirmativa (-5) lo primero que tenemos que hacer es obtener el nombre que el usuario le ponga a la lista, esto se hace con la linea 12 usando el comando .get_filename(). La linea 13 es particularmente importante porque se utiliza para crear cualquier tipo de archivos, ya sea de texto, lista de msica, base de datos y todo lo que se puedan imaginar, el comando open(filename + ".pls", w) lo que hace es crear un archivo con el nombre que se obtenga de la variable filename y lo terminara con la extensin .pls, la letra W quiere decir Write lo que significa que le damos permiso a este archivo de escribir. La linea 14 le va a poner un header o titulo al archivo que hemos creado, el formato pls siempre tiene un titulo en corchetes con el nombre playlist y el valor que le sigue es bsicamente es igual a dar un enter dentro del archivo. "\n" #Definicion del comando Guardar Permite guardar una lista def showSave(self, widget): self.filterbox.set_name("Lista Multimedia") self.filterbox.add_pattern("*.pls") self.save.add_filter(self.filterbox) respt = self.save.run() self.save.remove_filter(self.filterbox) self.save.hide() if respt == -5: n=0 filename = self.save.get_filename() playlistsaved = open(filename + '.pls', 'w') playlistsaved.write("[playlist]" + "\n") iter=self.medialist.get_iter_first() while iter: n+=1 allmusicfiles1 = self.medialist.get_value(iter,1) allmusicfiles2 = self.medialist.get_value(iter,0) allmusicfiles = allmusicfiles1+"/"+allmusicfiles2 pathname = urllib.pathname2url(os.path.abspath(allmusicfiles)) playlistsaved.write("File"+str(n)+"=file://"+pathname.encode('utf-8') + "\n") iter = self.medialist.iter_next(iter) En la linea 16 vamos a crear un iter de la medialist y vamos iterar por cada uno de los elementos de la lista y vamos a obtener de esta la columna 0 y 1, para crear un listado completo de los archivos con nombre y ruta. En la linea 23 vamos a transformar todos las rutas a Uris. Yo se alguien me puede preguntar porque vamos a hacer esta transformacin y porque en vez de hacer esto no usamos desde el

principio get_uris() en vez de get_filenames(), voy a explicar con un ejemplo y dare la respuesta con el ejemplo. Ejemplo: El resultado del comando get_filenames() es este: /home/geojorg/Msica/Pink/Pink - Just Like a Pill.mp3 El resultado del comando get_uris() es: file:///home/geojorg/M%C3%BAsica/Pink/Pink%20%26%20-Just %20Like%20a%20Pill.mp3 Ambos resultados son archivos y ambos son validos en todo el sentido de la palabra, el problema de trabajar con Uris es que toca hacerles una pequea transformacin antes de poder usarlos, lo primero seria eliminar file:// y pasar el resto del nombre a utf8 y para exportarlos nuevamente toca transformarlos. Por recomendacin la mayora de las personas que saben del tema recomiendan trabajar con Uris, pero yo tengo una aproximacin distinta y voy a dar la explicacin. Yo siempre prefiero agregar cdigo a quitar cdigo, cuando uno ve un cdigo por primera vez es muy difcil quitar cosas. Si quitas algo puedes daar lo dems y se vuelve un li. Con agregar la linea 23 convertimos el nombre de los archivos a Uris, esto se hace con dos comandos. os.path.abspath() Este convierte cualquier ruta a un archivo en un elemento absoluto que este ligado desde la raiz / o c: si se trabaja en windows. urllib.pathname2url() es un comando que convierte cualquier /home/geojorg en file:///home/geojorg es decir de pathname a url, para ello tenemos que importar el modulo urllib de python con el comando import urllib Finalmente la linea 24 guarda un vinculo a un archivo de la siguiente forma, n es un valor iterativo numrico que se usa en este formato de archivo .pls File1=file:///home/geojorg/M%C3%BAsica/Pink/Pink%20%26%20-Just%20Like%20a%20Pill.mp3 La linea 25 lo que hace es iterar al siguiente elemento y termina cuando ya no hay mas elementos porque todos estos estn dentro de un while, pueden guardar alguna lista y usar totem para reproducir lo que acaban de guardar y vern que funciona perfectamente. En la prxima entrada vamos a configurar los botones de play, stop y cerrar. El cdigo final es el que se encuentra en este link.

You might also like