gtk

Acabo de hacer la presentación de TBO en la fase final del concurso universitario de software libre, y aquí está la presentación

Estoy pensando en cómo implementar el sistema de deshacer/rehacer de TBO, y no se cuál sería la mejor forma de hacerlo.
Las dos opciones que estoy barajando son:
1. Guardar el estado. La idea es que antes de cada operación que se pueda deshacer se guarde el estado actual del documento en la lista de deshacer.
pros: creo que es fácil de implementar/mantener
contras: El coste en memoria puede ser considerable, y clonar el estado en cada operación puede ser computacionalmente costoso.
2. Guardar la operación y su inversa. La idea es que antes de cada operación que se pueda deshacer se guarde la operación a realizar junto con su inversa en la lista de deshacer.
pros: Menor coste tanto en memoria como de calculo.
contras: Pueden existir operaciones cuya inversa sea muy compleja.
Me decanto por la segunda opción, pero lo mismo debería mirar un poco el código de gimp o inkscape un poco para ver qué es lo que hacen.


Lo he estado pensando bien, y me he decidido por implementar TBO con C, con GTK+Cairo, en lugar de usar python.
La razón por la cual prefiero usar C es porque con python ya he realizado algunas aplicaciones de escritorio, y aunque esta añade elementos con los que no he tratado, como el uso de cairo, la mayor parte de la aplicación será en pygtk, y creo que esa tecnología ya la conozco suficiente.
Por otra parte está el lenguaje C, siempre me ha gustado. Es simple a la vez que potente y elegante, y desde que programo en python, C lo tengo más que abandonado, por eso estoy perdiendo soltura.
Así pues prefiero usar C que será más divertido e instructivo, y además como sigo usando gtk y cairo no implicará mucha más complicación que si lo hiciera con python.


Con el proyecto TBO pretendo hacer un editor fácil de usar para poder crear cómics, presentaciones guías de programas, etc.
La idea del proyecto es presentar una interfaz (hecha con gtk) más o menos sencilla que ofrezca un editor para que quién no sepa dibujar pueda hacer un cómic en cinco minutos para hacer una presentación de algo, un tutorial de cómo funciona algo o para dibujar un cómic gracioso.
Para ello el editor dispondrá de diferentes modelos prediseñados para ir añadiendo a las viñetas y el usuario tan sólo tendrá que colocar los personajes y escribir los bocadillos correspondientes para tener un cómic.
Quiero desarrollar el proyecto con python, gtk y cairo. Y tengo intención de que se pueda exportar a png y pdf y además quiero que añadir nuevos sets de personajes a añadir.

Hace ya algún tiempo que hice la interfaz gráfica de GECO y esto lo hice usando glade para generar un fichero xml que posteriormente se carga desde python y se trabaja con los controles. Así se consigue separar la capa de presentación de la lógica del programa y es muy simple cambiar casi cualquier aspecto gráfico sin tener que tocar código.
Para hacer aplicaciones de escritorio con gtk lo más simple es utilizar glade-3, en casi todas las distribuciones hay varios paquetes, glade o glade-2 y glade-3. En las versiones anteriores el editor glade presentaba varias ventanas dispersas, al estilo gimp, pero glade-3 presenta una interfaz unificada y mucho más intuitiva.
Es muy sencillo crear una interfaz con este programa, sólo tienes que ir pinchando en los controles que quieras añadir y se van dibujando sobre la marcha, luego cambias sus propiedades y le das el aspecto visual que prefieras.
La mayoría de los manuales de pygtk y glade indican que hay que hacer lo siguiente para cargar una interfaz creada con glade desde código python:
import gtk.glade class Ventana: def __init__(self): self.gladefile = "window.glade" self.glade = gtk.glade.XML(self.gladefile) #accediendo a los controles self.button = self.glade.get_widget('button1') self.button.connect('clicked', action)
¿Qué simple verdad? pues en principio no funciona, cuando lo intentas hacer no funciona, a no ser que en el glade le des a guardar como libglade. Esto es porque desde hace algún tiempo, y yo no me había enterado, se utiliza otro formato que es gtkbuilder. Para utilizar interfaces generadas con glade y guardadas como gtkbuilder (que es como debería ser) hay que hacerlo de la siguiente manera:
import gtk class Ventana: def __init__(self): self.gladefile = "window.glade" self.glade = gtk.Builder() self.glade.add_from_file(self.gladefile) #accediendo a los controles self.button = self.glade.get_object('button1') self.button.connect('clicked', action)
Como podrás observar no ha cambiado mucho la cosa, solo cambiar get_widget por get_object y poco más.
Una vez cargado un objeto ya se puede trabajar con él, modificándolo, obteniendo información de él, etc. Para conocer todos los métodos y señales disponibles lo mejor es mirar la documentación.
Más facilidades, connect_signals, antiguo autoconnect
Desde glade, además de poner los controles en su sitio y definir los nombres se pueden definir funciones asociadas a los diferentes eventos que el objeto en cuestión pueda recibir. Así podemos evitarnos el tener que enlazar todos los botones con sus correspondientes funciones con el método connect, como se puede ver en los ejemplos anteriores y se pueden enlazar todas las funciones con una serie de funciones o métodos definidos.
Supongamos que tenemos una interfaz con un par de botones y hemos enlazado la señal clicked de cada botón con dos funciones llamadas boton1 y boton2 respectivamente.
Para que estos botones hagan algo, tendríamos un código así:
import gtk class Ventana: def __init__(self): self.builder = gtk.Builder() self.builder.add_from_file('test.glade') self.window = self.builder.get_object('window1') self.window.show_all() # Magia :P self.builder.connect_signals(self) def boton1(self, widget): print "boton1" def boton2(self, widget): print "boton2" if __name__ == '__main__': v = Ventana() gtk.main()
Si al método connect_signals se le pasa una instancia, buscará el nombre de las funciones en sus métodos, sin embargo también puede recibir un diccionario, y en tal caso, buscará las funciones a enlazar en ese diccionario.
También es posible pasarle argumentos a las funciones, pasándole otro argumento al método connect_signals:
... self.builder.connect_signals(self, 'mas info') ... def boton1(self, widget, data): ... def boton2(self, widget, data): ...
Y con esto y un bizcocho, hacer interfaces gráficas con pygtk y glade es mucho más simple de lo que nunca pudieras imáginar :P



