15 ago. 2011

GtkBuilder y Python Parte 6

Quizás deberías pasar por el tutorial 5 antes de comenzar con este.


En este tutorial 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 enseñaran algunos comandos nuevos y nos permitirán entender de alguna forma que es un Gtk.TreeIter que es una de las cosas más complejas que hay. La documentación de Gtk3 y 2 es más 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 señales.
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 definición nueva que vamos a agregar es clearone, que básicamente 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 definición 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 código donde llamamos al objeto de nombre TextView que habíamos 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 básicamente 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 interés 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 básicamente muestre el Treepath pero dentro del treepath están los números de las filas es decir la fila 1 la 2 y así en adelante por eso si usamos un for podemos iterar estos números 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 más complejo que eso, pero básicamente 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 patrón 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 más 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)


También sera necesario entonces crear la definición para borrar todo. Esta es bastante fácil porque en Gtk habían 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 código, eso es cosa de cada quien, a mi particularmente me gusta hacerlo en una sola definición. También es curioso porque este código que les voy a mostrar esta hecho para ser compatible con Gtk3 y con Gtk2 y quizás si alguno de ustedes ha programado antes me va a decir que existe una forma más fácil 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 tecnologías.



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 botón. La linea 3 lo que hace es seleccionar el Treeview ya eso lo explique antes, la 4 también la explique, obtenemos el modelo y las filas.



La 5 básicamente es un if que nos indica que si se presiona el botón 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 números normales, entonces hay que hacerles una conversión, lo primero es pasar el numero que teníamos a string es decir a texto y después 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 daría 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 botón 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 función, 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 intención es que el programa solo funcione con la tecnología vieja, es decir Gtk2 podemos reducir esto en 4 lineas, pero insisto que no sera compatible con Gtk3, si alguien quiere esa solución me puede escribir en los comentarios.



En el próximo tutorial explicare como trabajar con Combobox y con nuevas funciones y módulos que tendremos que importar para hacer funcionar aleatorio y alfabético.



El código del programa completo hasta donde vamos es el siguiente:

#!/usr/bin/python import os from gi.repository import Gtk filepattern = ( ("MP3","*.mp3"), ("AVI","*.avi"), ("MPEG-4","*.mp4"), ("FLV ","*.flv"), ("OGG","*.ogg"), ) pattern = (".mp3",".mp4",".avi",".flv",".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.medialist = builder.get_object("MediaList") self.treeview = builder.get_object("TextView") self.up = builder.get_object("Up") self.down = builder.get_object("Down") 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, "on_Clearone_clicked": self.clearone, "on_Clearlist_clicked": self.clearlist, "on_Up_clicked": self.updown_move, "on_Down_clicked": self.updown_move, } 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() for files in fileselected: (dirs,files)= os.path.split(files) self.medialist.append([files,dirs]) #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]) #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) print iteration self.medialist.remove(iteration) #Definicion del comando Borrar Lista def clearlist(self, *args): self.medialist.clear() #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) #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()

No hay comentarios:

Publicar un comentario