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 · Marcelo

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 · Marcelo

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 · Marcelo

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 · Marcelo

PyCon Argentina 2009, Cappuccino y LLVM

Me gustaría hacer un post bien largo acerca de todo lo que me dejó personalmente la última PyCon Argentina, pero lamentablemente estoy complicado con el tiempo, ya que no sólo quiero leer y escribir acerca de todo el “bombardeo” de información que te deja cada charla, sino que también quisiera investigar un poco cada cosa y dejar acá algo más que solamente los links. Pero bueno, vamos a hacer el intento de resaltar lo primero que me viene a la mente. ...

September 14, 2009 · 3 min · Marcelo

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 · Marcelo

PyCon Argentina 2009: Abierta la Inscripción

Sólo me voy a limitar a copiar el texto de la invitación… de más está decir que espero ansioso los días para ir, y creo que va a estar buenísima. Si sos programador o estás haciendo tus primeras armas en la programación, ¡no podés faltar! Está abierta la inscripción para participar de PyCon Argentina 2009, la primera conferencia en castellano sobre el lenguaje de programación Python, a realizarse los días 4 y 5 de septiembre en la sede de la Universidad de Belgrano, Zabala 1837, en la Ciudad de Buenos Aires. ...

August 15, 2009 · 1 min · Marcelo

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 · Marcelo

¡Hola Mundo!

Bueno, este es el primer post de mi blog sobre Wordpress en dominio propio, marcelofernandez.info. No hace falta cambiar el Feed RSS, ya que al hacerlo yo en FeedBurner, directamente va a levantar los nuevos posts del dominio nuevo. El cambio lo hice estrictamente porque me cansé de esperar que Blogger avance… es demasiado básico para un montón de cosas. Vamos a ver cómo nos va, por ahora me enamoré de Wordpress. :-) Saludos ...

May 25, 2009 · 1 min · Marcelo

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 · Marcelo