Renderizando PDFs en Python con Poppler II

Hace unos días me llegó un mail de alguien preguntándome cómo, a partir de la parte I de este artículo, hacer un sencillo visor de PDFs con wxPython. Me encontré con algunas dificultades, principalmente que el ScrolledWindow de wxPython no permite actualizarse dinámicamente, o automáticamente según el contenido (esto sí es bastante sencillo en GTK); con lo cual se complicaba hacer zoom, modificar el tamaño de la ventana y adaptar los scrollbars, etc. Sin embargo, con alguna vuelta de más pude armar un ejemplo, que paso a dejar acá: ...

April 15, 2010 · 3 min · mfernandez

Reemplazando texto con expresiones regulares en Python

Hay veces en que uno necesita automatizar tareas, como reemplazar cierto texto por otro bajo ciertas condiciones, y el viejo “%s/cosa/otra/g” del vim nos queda corto. En mi caso en particular, estaba metiendo algunas pequeñas características en PyFpdf, y vi que había algunos archivos .py llenos de llamadas a la función chr(). Claro, PyFpdf es un port más o menos “haragán” ( lazy) de Fpdf para PHP, y el autor original evidentemente encontró más sencillo definir algunas fuentes (en binario) haciendo sucesivas llamadas a la función chr(), como esta: ...

April 2, 2010 · 3 min · mfernandez

Navegador simple con Python + Webkit/GTK

Hoy me encontré con otro un hilo en la lista de PyAr que me deja un link más que interesante: ¡Existe un binding para usar Webkit sobre GTK desde Python, y lo mejor de todo es que ya está incluido en los repositorios de Ubuntu 9.10! Webkit es un motor de renderizado (“dibujado”) de páginas web, que es utilizado en el corazón en cada vez más navegadores, como Chrome, Safari, Konqueror, etc. Es super completo y veloz; y permite ejecutarse en muchísimas plataformas y sistemas diferentes. Si bien existen otros métodos para embeber un navegador en una aplicación PyGTK, como por ejemplo gtkmozembed (que embebe el motor de Firefox), éste no es muy poderoso, o por lo menos no deja meterle mucha “mano” para personalizarlo, y uno termina teniendo relativamente muy poco “poder”. En cambio con Webkit/GTK se pueden hacer muchas más cosas, tan sólo hace falta ver la documentación y un ejemplo (links al final, claro). :-) ...

November 19, 2009 · 3 min · mfernandez

Visor de Imágenes Simple con PyGTK

Dado que alguien me pidió vía twitter un ejemplo de cómo hacer un zoom de una imagen en PyGTK, hice este ejemplito sencillo que sólo carga una imagen en un widget Gtk.Image. Maneja el movimiento de la imagen con el mouse, las teclas del cursor y hace zoom con F1 (“0 o adaptar a ventana”), F2 (+25%), F3 (+50%), F4(+75%) y F5("%+100 o 1:1"). #!/usr/bin/env python # -*- coding: utf-8 -*- """ SimpleImageViewer - Visor simple de imágenes, de ejemplo, que utiliza PyGTK. Marcelo Fidel Fernández - http://www.marcelofernandez.info Licencia: BSD. Disponible en: http://www.freebsd.org/copyright/license.html TODO: * Dar la opción de usar el scroll del mouse para hacer zoom. * Mejorar el código y peformance (quizás). """ import os import sys import pygtk pygtk.require('2.0') import gtk # Variables globales para el ejemplo; podrían ir en un archivo de configuración, # como por ejemplo 'config.py' e importarlo. # Mapeo de teclas - Ver constantes en el modulo gtk.keysyms import gtk.keysyms as kb # Estructura: teclas (en mayúscula, contempla minúsculas también) # (offset_X_pixeles, offset_Y_pixeles) OFFSET_GRAL = 50 MOVE_KEYS = { kb.Up : (0, -OFFSET_GRAL), # Arriba kb.Down : (0, OFFSET_GRAL), # Abajo kb.Right : (OFFSET_GRAL, 0), # Derecha kb.Left : (-OFFSET_GRAL, 0), # Izquierda } # Estructura: tecla: nivel de zoom (zoom_ratio) ZOOM_KEYS = { kb.F1: 0.0, kb.F2: 25.0, kb.F3: 50.0, kb.F4: 75.0, kb.F5: 100.0, } DEFAULT_IMAGE = '/usr/share/backgrounds/Cherries.jpg' class SimpleImageViewer: def __init__(self, image_file): self.window = gtk.Window(gtk.WINDOW_TOPLEVEL) self.window.connect("delete_event", self.close_application) self.window.set_position(gtk.WIN_POS_CENTER_ALWAYS) self.window.set_default_size(800, 600) self.pixbuf = gtk.gdk.pixbuf_new_from_file(image_file) self.ancho_pixbuf = float(self.pixbuf.get_width()) self.alto_pixbuf = float(self.pixbuf.get_height()) self.image = gtk.Image() self.image.set_from_pixbuf(self.pixbuf) self.viewport = gtk.Viewport() # No están por defecto, los agrego self.viewport.add_events(gtk.gdk.BUTTON_RELEASE_MASK | gtk.gdk.BUTTON1_MOTION_MASK) self.viewport.connect('button-press-event', self.on_button_pressed) self.viewport.connect('button-release-event', self.on_button_released) self.viewport.connect('motion-notify-event', self.on_mouse_moved) # Lo conecto a la ventana, ya que siempre tiene el foco self.window.connect('key-press-event', self.on_key_press) self.viewport.add(self.image) self.scrolled_wnd = gtk.ScrolledWindow() self.scrolled_wnd.add(self.viewport) self.window.add(self.scrolled_wnd) self.window.show_all() def _update_image(self, zoom_ratio): """ Updates the image in the widget according to the zoom_ratio Actualiza la imagen en el widget Image con el zoom_ratio de parámetro """ # TODO: Prioriza que encaje el ancho por sobre el alto de la imagen # al estar maximizado. Mejorar. # Obtengo las dimensiones actuales del viewport rect = self.viewport.get_allocation() # Resize de la imagen conservando las proporciones de la imagen if self.ancho_pixbuf > self.alto_pixbuf: base = self.ancho_pixbuf - rect.width ancho = int(rect.width + (base * (zoom_ratio/100))) relacion = (self.alto_pixbuf*100)/self.ancho_pixbuf alto = int(ancho * relacion/100) else: base = self.alto_pixbuf - rect.height alto = int(rect.height + (base * (zoom_ratio/100))) relacion = (self.ancho_pixbuf*100)/self.alto_pixbuf ancho = int(alto * (relacion/100)) scaled_buf = self.pixbuf.scale_simple(ancho, alto, gtk.gdk.INTERP_BILINEAR) self.image.set_from_pixbuf(scaled_buf) def _move_image(self, offset_x, offset_y): """ Moves the image inside the viewport to the specified offset (+ or - pixels) Mueve/Desplaza la imagen del viewport según el offset que se le especifique """ vport = self.viewport xadjust = vport.props.hadjustment newx = xadjust.value + offset_x yadjust = vport.props.vadjustment newy = yadjust.value + offset_y # Si las cosas están dentro de los bordes, seteo if (newx >= xadjust.lower) and \ (newx <= (xadjust.upper - xadjust.page_size)): xadjust.value = newx vport.set_hadjustment(xadjust) if (newy >= yadjust.lower) and \ (newy <= (yadjust.upper - yadjust.page_size)): yadjust.value = newy vport.set_vadjustment(yadjust) def on_key_press(self, widget, event): """ Callback to handle the keys pressed in the main window Callback que maneja las teclas que se presionan en la ventana """ keycode = gtk.gdk.keyval_to_upper(event.keyval) newx = newy = 0 if keycode in MOVE_KEYS.keys(): offset_x, offset_y = MOVE_KEYS[keycode] self._move_image(offset_x, offset_y) elif keycode in ZOOM_KEYS.keys(): self._update_image(ZOOM_KEYS[keycode]) else: return False return True # Con True cancelo el evento def on_mouse_moved(self, widget, event): """ Callback to the mouse movement inside the viewport Callback que es llamado cuando el mouse se mueve en el viewport """ # Ver: http://www.pygtk.org/pygtk2tutorial-es/sec-EventHandling.html if event.is_hint: x, y, state = event.window.get_pointer() else: state = event.state x, y = event.x_root, event.y_root if state & gtk.gdk.BUTTON1_MASK: offset_x = self.prevmousex - x offset_y = self.prevmousey - y self._move_image(offset_x, offset_y) self.prevmousex = x self.prevmousey = y def on_button_pressed(self, widget, event): """ When the user presses the left mouse button, save the x and y pixel positions, and change the cursor. Cuando el usuario presiona el botón izquierdo, guardo los puntos x, y de origen del evento y cambio el cursor a "moviéndose". """ if event.button == 1: self.change_vport_cursor(gtk.gdk.Cursor(gtk.gdk.FLEUR)) self.prevmousex = event.x_root self.prevmousey = event.y_root return True def on_button_released(self, widget, event): """ When the user releases the left mouse button, set the normal cursor. Cuando el usuario suelta el botón izquierdo, vuelvo el cursor al normal """ if event.button == 1: self.change_vport_cursor(None) return True def change_vport_cursor(self, type): self.viewport.window.set_cursor(type) def close_application(self, widget, event, data=None): gtk.main_quit() return False if __name__ == "__main__": if len(sys.argv) > 1 and os.path.exists(sys.argv[1]): image_file = sys.argv[1] else: image_file = DEFAULT_IMAGE SimpleImageViewer(image_file) gtk.main() Queda pendiente manejar el scroll del mouse para hacer zoom (ya que GTK mueve el gtk.Scrollwindow que contiene la imagen por defecto). Si bien funciona copiando y pegando esto en un archivo, también pueden descargar el ejemplo desde acá. ...

November 16, 2009 · 5 min · mfernandez

Desensamblando Python

Mucho no me gusta hacer posts con casi nada de aporte propio, pero en este caso se lo merece. Este post sólo va a referenciar al excelente sitio de Doug Hellman, PyMOTW (Python Module of the Week). Básicamente el autor presenta una vez por semana un módulo de la biblioteca estándar de Python, realizando un análisis del mismo y mostrando ejemplos de uso. En particular, esta semana hizo una introducción al módulo dis, que desensambla código Python mostrando el bytecode respectivo. Lo bueno es que muestra varios usos para el mismo (nuevos para mí), como en el caso de un debug o en el de optimizar el funcionamiento ahorrando bytecodes. ...

August 30, 2009 · 1 min · mfernandez

Control de Versiones: Manejando las diferencias entre distribuido y centralizado

Dando vueltas por ahí, me entero que es muy sencillo utilizar localmente versionado de código fuente (gracias a los sistemas de control de versiones distribuidos, como por ejemplo Mercurial, GIT y Bazaar) para luego subir/actualizar los cambios a un repositorio Subversion principal. Es decir, en vez de usar los comandos svn como clientes de un servidor Subversion, es posible utilizar alguno de estos sistemas distribuidos como clientes “consumidores” del repositorio principal, para luego aprovechar sus importantes ventajas en forma local. ...

July 25, 2009 · 3 min · mfernandez

Applets Java en Ubuntu de 64 bits recargados

Ayer actualicé mi Ubuntu 9.04 (64 bits) y vi que había una actualización de los paquetes " sun-java6", que corresponden a la Máquina Virtual Java. Para mi sorpresa, se trata de una actualización bastante importante de todos los paquetes de la implementación distribuida por Sun de Java (que aún no es lo mismo que el proyecto OpenJDK), y que incluye (después de ¡6 años de espera!) una implementación del Java Plugin de Sun para 64 bits. ...

July 18, 2009 · 1 min · mfernandez

PostgreSQL 8.5

No, no me equivoqué de número de versión. :-) Si bien PostgreSQL 8.4 ya salió y tiene unas cuantas novedades, quisiera hacerme eco de lo que está en la cola de parches a aprobar para integrar la nueva rama de desarrollo de PostgreSQL 8.5. Esta lista está compuesta por cosas como: GRANT ON ALL IN schema: Una manera de otorgar permisos a todos los objetos de un esquema. Machine-readable explain output v2: Salidas del comando EXPLAIN fácilemente legibles por otro software, es decir, fácilmente parseables para su procesamiento. El comando explain aceptaría como segundo parámetro el formato (además de los actuales) la keyword FORMAT, quedando así: EXPLAIN FORMAT [TEXT | JSON | XML]. Está bueno porque abre la puerta del desarrollo de herramientas de auto-tunning, basado en reglas estilo Sistema Experto. SE-PostgreSQL: Security-Enhanced PostgreSQL, una mejora sustancial en la seguridad del SGBD y en el nivel de detalles del sistema de privilegios. Replicación Sincrónica: Si algo le faltaba a PostgreSQL es una manera de replicación sincrónica integrada en el gestor. Esperemos que esto sí entre, aunque está bastante discutido el asunto porque es un cambio relativamente “gordo” en los internals del código, pero por lo que leí de estas discusiones parece haber consenso en llevarlo adelante e implementarlo. Por mi parte… ¡lo necesito ya! :-) \dL para listas los lenguajes utilizados en psql: Es una nueva característica en psql, la de poder listar los lenguajes instalados en la base de datos (plpythonu, plpgsql, etc.). Estadísticas de esperas por bloqueos: La idea es poder tener un historial de los bloqueos sufridos por los clientes en la BD, para poder detectar problemas y saber bien qué pasa en todo momento. Bueno, de más está decir que no hay ninguna garantía de que todo esto sea finalmente integrado en 8.5, sino que se está evaluando al mejor estilo Software Libre ™: peer-review, discusión de estrategias del proyecto, consenso en la comunidad de desarrolladores y posterior aceptación/pedido de mejoras/rechazo. Así que veremos cómo sigue. ...

July 4, 2009 · 2 min · mfernandez

Renderizando PDFs en Python con Poppler

Buenas… hoy voy a mostrar una forma de renderizar archivos PDF dentro de nuestras aplicaciones y desde Python, para casi cualquier plataforma con la estemos trabajando. Introducción Si bien cuando se arma una aplicación de escritorio se suele “llamar” a un software externo ( Acrobat Reader, Fox-It, Evince, etc.) para que muestre el contenido de un archivo PDF, generalmente queda “feo”; nuestro programa pierde la interacción y el control de lo que el usuario hace con el mismo, implica superponer un programa arriba del otro, etc. Esto suele producir una experiencia dificultosa para el usuario de nuestro querido programa, y como corresponde, los programadores nos ponemos tristes. :-( ...

May 2, 2009 · 5 min · mfernandez

PyCon Chicago 2009

Hace algunos días que están disponibles los videos (versión más “navegable” acá) de unas cuantas charlas de PyCon 2009 - Chicago (terminó hace unos pocos días), y como me apasionan este tipo de eventos, suelo dedicarle algún tiempo libre a ver las que más me interesan (que “filtro” por el título, no me queda otra). Les dejo una lista de las más importantes según mi criterio* (aclaro, sin haberlas visto): A Whirlwind Excursion through Writing a C Extension: Excelente charla, me viene en un momento justo, por decirlo de alguna manera, cuando quería meter algo de mano en Python desde C. Los slides y la charla en formato de texto están acá (aunque obviamente no es lo mismo que el video de Ned Batchelder). Se trata de una introducción bastante completa y sencilla a extender Python con C (por performance, por interacción con componentes nativos o por capricho). :-) Un detalle más que quiero agregar es que el proyecto Boost tiene una biblioteca Boost.Python esto que hace todo automágicamente (pero sólo sirve usando C++). PyPy status talk. Reinteract: a better way to interact with Python Introduction to Python Profiling Python in a sandbox Jython Progress: Bueno, me alegra saber que queda poco para que Jython se actualice a Python 2.5 de manera oficial. Básicamente pusieron el énfasis en compatibilidad, tienen unas cuantas mejoras bajo el capó y espero que lo saquen lo antes posible. Sun (Oracle ahora) está (estaba?) metiéndole fichas al desarrollo de Python en la JVM… veremos qué pasa. Panel: Object Relational Mappers: Philosophies and Design Decisions. Python 2.6 and 3.0 compatibility Dejavu: Language INtegrated Query for data Dabo: Rich Client Web Applications in 100% Python: Miren el último cuarto de la charla, todo lo anterior es una gran intro de ventajas y desventajas de las desktop apps vs. browser apps, para hablar de una aplicación que parece muy piola (y que se integra con Dabo) para “lanzar” aplicaciones y que se actualizen automágicamente. Recomendable. Concurrency and Distributed Computing with Python Today The Browser Interface, Local Server (BILS) Application Keynote: Guido van Rossum: Es un groso, no hay ninguna duda… pero siendo sincero me aburrió bastante, ya que no tocó ningún tema técnico, y fue un “gracias a la comunidad” de una hora. Easy AI with Python Introduction to Multiprocessing in Python ¡Puff! Son un montón, y revisando un poco los títulos, se ve que me interesaron los temas bien técnicos. Para cada uno tengo un motivo, pero en vez de gastarme escribiendo (el tiempo es tirano, je), prometo que a medida que las vaya viendo (cada una dura algo así como 40 minutos), voy a actualizar este post. ...

April 8, 2009 · 3 min · mfernandez