Archivo

Archivo para la categoría ‘programación’

Free Pascal y Lazarus en Ubuntu

Domingo, 20 de junio de 2010 Marcelo Sin comentarios

Creo que la gran mayoría de los programadores debemos recordar aquellos primeros momentos en que uno pasaba tardes y noches enteras escribiendo y escribiendo líneas de código en aquellas XT, AT, Commodores 64/128, etc., sólo por amor al arte y puro fanatismo. Supongo que diríamos lo mismo de los primeros años de facultad, cuando en materias de título “Programación I” uno repasaba el paradigma de la Programación Estructurada, casi siempre en el lenguaje de aprendizaje facultativo por excelencia, al menos acá en Argentina: Pascal, usando Turbo Pascal, su versión más popular.

Viniendo más acá en el tiempo, y buscando alguna herramienta donde aprovechar mis conocimientos de este lenguaje pero para desarrollar sobre Linux, supe de la existencia de Free Pascal, un compilador Pascal libre, multiplataforma y con soporte a diferentes arquitecturas, que genera ejecutables binarios con dependencias mínimas. También estaba disponible Lazarus, su inseparable IDE, no casualmente muy parecido a Delphi.

El problema era que hasta Ubuntu 9.04 y por una cuestión de madurez, las bibliotecas gráficas de Lazarus aún dependían de GTK 1.2 ( completamente en desuso desde hace “siglos” en las distribuciones Linux), y se veía así:

Se veía “duro”, anticuado y lejos de toda amigabilidad con el usuario programador, carente de características básicas hoy en día tales como Unicode, antialiasing y mucho más… por lo tanto, concluí en ese momento que uno no podía encarar ningún desarrollo nuevo para Linux/Unix usando GTK 1.2 (y por ende Free Pascal+Lazarus), sucedido por la versión 2.0 allá por el año 2002.

En estos días leí un mail en la lista de Ubuntu-Ar sobre Lazarus y Free Pascal, y decidí a instalarlo para echarle un vistazo otra vez. Por suerte la situación cambió bastante, me encontré con un bonito e importante cambio:

Ahora, con Lazarus sobre GTK 2, ¡sí que está mucho más decente! Junto a Gambas es lo más parecido a un entorno RAD “estilo Visual Basic” que hay dando vueltas para Linux. Y es aún mejor que éstos dos:

  • Libre: Licencia LGPL (salvo el compilador que es GPL). Permite desarrollar software con cualquier licencia, incluso comercial.
  • Gratuito: Viene con los fuentes de sí mismo ¡y se compila a sí mismo!
  • Multiplataforma: DOS, Linux, Windows, Mac OSX, FreeBSD, Solaris, y más.
  • Soporte de diferentes arquitecturas: Intel, AMD64, ARM, PowerPC, Sparc…
  • Diferentes Toolkits GUI: Win32/Win64, GTK1/2, QT3/4, Carbon, Cocoa, (aunque en diferentes estados de madurez).
  • Compatibilidad en un alto porcentaje con Object Pascal y Delphi.

Por último, me hice un “Hola Mundo” muy muy sencillo y lo compilé. Luego, ejecuté el comando ldd, para saber de qué bibliotecas dependía. El binario, que ocupaba 14 MB (ojo, era todo info de debug, luego de pasarle un strip ocupó 5.1 MB) dependía de esto:

marcelo@marcelo-laptop:/tmp$ ldd project1
	linux-vdso.so.1 =>  (0x00007fff441ff000)
	libX11.so.6 => /usr/lib/libX11.so.6 (0x00007f6603e2f000)
	libgdk_pixbuf-2.0.so.0 => /usr/lib/libgdk_pixbuf-2.0.so.0 (0x00007f6603c13000)
	libgtk-x11-2.0.so.0 => /usr/lib/libgtk-x11-2.0.so.0 (0x00007f66035f0000)
	libgdk-x11-2.0.so.0 => /usr/lib/libgdk-x11-2.0.so.0 (0x00007f6603343000)
	libgobject-2.0.so.0 => /usr/lib/libgobject-2.0.so.0 (0x00007f66030fb000)
	libglib-2.0.so.0 => /lib/libglib-2.0.so.0 (0x00007f6602e1c000)
	libgthread-2.0.so.0 => /usr/lib/libgthread-2.0.so.0 (0x00007f6602c17000)
	libgmodule-2.0.so.0 => /usr/lib/libgmodule-2.0.so.0 (0x00007f6602a13000)
	libpango-1.0.so.0 => /usr/lib/libpango-1.0.so.0 (0x00007f66027c8000)
	libpthread.so.0 => /lib/libpthread.so.0 (0x00007f66025ab000)
	libatk-1.0.so.0 => /usr/lib/libatk-1.0.so.0 (0x00007f660238a000)
	libcairo.so.2 => /usr/lib/libcairo.so.2 (0x00007f6602106000)
	libdl.so.2 => /lib/libdl.so.2 (0x00007f6601f02000)
	libc.so.6 => /lib/libc.so.6 (0x00007f6601b80000)
	libxcb.so.1 => /usr/lib/libxcb.so.1 (0x00007f6601963000)
	libgio-2.0.so.0 => /usr/lib/libgio-2.0.so.0 (0x00007f66016b0000)
	librt.so.1 => /lib/librt.so.1 (0x00007f66014a8000)
	libm.so.6 => /lib/libm.so.6 (0x00007f6601224000)
	libXext.so.6 => /usr/lib/libXext.so.6 (0x00007f6601012000)
	libXrender.so.1 => /usr/lib/libXrender.so.1 (0x00007f6600e08000)
	libXinerama.so.1 => /usr/lib/libXinerama.so.1 (0x00007f6600c04000)
	libXi.so.6 => /usr/lib/libXi.so.6 (0x00007f66009f4000)
	libXrandr.so.2 => /usr/lib/libXrandr.so.2 (0x00007f66007eb000)
	libXcursor.so.1 => /usr/lib/libXcursor.so.1 (0x00007f66005e0000)
	libpangocairo-1.0.so.0 => /usr/lib/libpangocairo-1.0.so.0 (0x00007f66003d3000)
	libXcomposite.so.1 => /usr/lib/libXcomposite.so.1 (0x00007f66001d0000)
	libXdamage.so.1 => /usr/lib/libXdamage.so.1 (0x00007f65fffcc000)
	libXfixes.so.3 => /usr/lib/libXfixes.so.3 (0x00007f65ffdc6000)
	libpangoft2-1.0.so.0 => /usr/lib/libpangoft2-1.0.so.0 (0x00007f65ffb9c000)
	libfreetype.so.6 => /usr/lib/libfreetype.so.6 (0x00007f65ff915000)
	libz.so.1 => /lib/libz.so.1 (0x00007f65ff6fe000)
	libfontconfig.so.1 => /usr/lib/libfontconfig.so.1 (0x00007f65ff4c9000)
	libpcre.so.3 => /lib/libpcre.so.3 (0x00007f65ff29a000)
	/lib64/ld-linux-x86-64.so.2 (0x00007f6604184000)
	libpixman-1.so.0 => /usr/lib/libpixman-1.so.0 (0x00007f65ff041000)
	libdirectfb-1.2.so.0 => /usr/lib/libdirectfb-1.2.so.0 (0x00007f65fedbd000)
	libfusion-1.2.so.0 => /usr/lib/libfusion-1.2.so.0 (0x00007f65febb3000)
	libdirect-1.2.so.0 => /usr/lib/libdirect-1.2.so.0 (0x00007f65fe99a000)
	libpng12.so.0 => /lib/libpng12.so.0 (0x00007f65fe772000)
	libxcb-render-util.so.0 => /usr/lib/libxcb-render-util.so.0 (0x00007f65fe56e000)
	libxcb-render.so.0 => /usr/lib/libxcb-render.so.0 (0x00007f65fe365000)
	libXau.so.6 => /usr/lib/libXau.so.6 (0x00007f65fe160000)
	libXdmcp.so.6 => /usr/lib/libXdmcp.so.6 (0x00007f65fdf5a000)
	libresolv.so.2 => /lib/libresolv.so.2 (0x00007f65fdd41000)
	libselinux.so.1 => /lib/libselinux.so.1 (0x00007f65fdb22000)
	libexpat.so.1 => /lib/libexpat.so.1 (0x00007f65fd8f9000)

Si bien 5 MB sigue siendo un poco mucho (y no incluye GTK ni nada, aunque no jugué con las opciones del compilador) para un simple binario, a partir de ahora FreePascal + Lazarus en mi ranking personal entran en la categoría de “interesantes” :-)

Es bastante tarde, es cierto, más cuando gracias a Python disfruto y me divierto al programar; tengo todo lo que necesito y más que lo que podría tener con FreePascal + Lazarus (siempre lo digo, Python no sería Python si no fuera por PyAr), pero… bueno, rescato y festejo que haya otra herramienta de desarrollo libre, potente y al alcance de cualquiera. :-)

Saludos

Categories: codear, linux, programación, ubuntu-ar Tags:

PyDay Rafaela 2010 – Introducción a wxPython

Lunes, 10 de mayo de 2010 Marcelo 1 comentario

Para no ser menos que Juanjo y Fisa, ¡qué bueno estuvo el PyDay Rafaela 2010!

Muchos (muchos) asistentes, muy buena onda, y una chorizeada + fernet a la noche inolvidables… En la sección de Charlas del blog están los slides de mi “Introducción a wxPython”, más los ejemplos de rigor. También hice propaganda de Ubuntu en las Lightning Talks. :-P

Acá hay un par de fotos… con los días van a ir apareciendo más cosas en el sito del evento… incluso los videos de las charlas. :-)

Actualización: Acá están los videos de las charlas.

Gracias a los chicos de PyRafaela por la organización, dedicación y aguante…. ¡espero que se repita!

Saludos

Categories: codear, personal, programación, python, ubuntu-ar Tags:

Renderizando PDFs en Python con Poppler II

Jueves, 15 de abril de 2010 Marcelo Sin comentarios

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á:

#!/usr/bin/env python
# coding: utf-8
 
""" 
    wxPDFViewer - Simple PDF Viewer using Python-Poppler and wxPython 
    Marcelo Fidel Fernandez - MIT License
    http://www.marcelofernandez.info - marcelo.fidel.fernandez@gmail.com
"""
 
import wx
import wx.lib.wxcairo as wxcairo
import sys
import poppler
 
 
class PDFWindow(wx.ScrolledWindow):
    """ This example class implements a PDF Viewer Window, handling Zoom and Scrolling """
 
    MAX_SCALE = 2
    MIN_SCALE = 1
    SCROLLBAR_UNITS = 20  # pixels per scrollbar unit
 
    def __init__(self, parent):
        wx.ScrolledWindow.__init__(self, parent, wx.ID_ANY)
        # Wrap a panel inside
        self.panel = wx.Panel(self)
        # Initialize variables
        self.n_page = 0
        self.scale = 1
        self.document = None
        self.n_pages = None
        self.current_page = None
        self.width = None
        self.height = None
        # Connect panel events
        self.panel.Bind(wx.EVT_PAINT, self.OnPaint)
        self.panel.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown)
        self.panel.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown)
        self.panel.Bind(wx.EVT_RIGHT_DOWN, self.OnRightDown)
 
    def LoadDocument(self, file):
        self.document = poppler.document_new_from_file("file://" + file, None)
        self.n_pages = self.document.get_n_pages()
        self.current_page = self.document.get_page(self.n_page)
        self.width, self.height = self.current_page.get_size() 
        self._UpdateSize()
 
    def OnPaint(self, event):
        dc = wx.PaintDC(self.panel)
        cr = wxcairo.ContextFromDC(dc)
        cr.set_source_rgb(1, 1, 1)  # White background
        if self.scale != 1:
            cr.scale(self.scale, self.scale)
        cr.rectangle(0, 0, self.width, self.height)
        cr.fill()
        self.current_page.render(cr)
 
    def OnLeftDown(self, event):
        self._UpdateScale(self.scale + 0.2)
 
    def OnRightDown(self, event):
        self._UpdateScale(self.scale - 0.2)
 
    def _UpdateScale(self, new_scale):
        if new_scale >= PDFWindow.MIN_SCALE and new_scale <= PDFWindow.MAX_SCALE:
            self.scale = new_scale
            # Obtain the current scroll position
            prev_position = self.GetViewStart() 
            # Scroll to the beginning because I'm going to redraw all the panel
            self.Scroll(0, 0) 
            # Redraw (calls OnPaint and such)
            self.Refresh() 
            # Update panel Size and scrollbar config
            self._UpdateSize()
            # Get to the previous scroll position
            self.Scroll(prev_position[0], prev_position[1]) 
 
    def _UpdateSize(self):
        u = PDFWindow.SCROLLBAR_UNITS
        self.panel.SetSize((self.width*self.scale, self.height*self.scale))
        self.SetScrollbars(u, u, (self.width*self.scale)/u, (self.height*self.scale)/u)
 
    def OnKeyDown(self, event):
        update = True
        # More keycodes in http://docs.wxwidgets.org/stable/wx_keycodes.html#keycodes
        keycode = event.GetKeyCode() 
        if keycode in (wx.WXK_PAGEDOWN, wx.WXK_SPACE):
            next_page = self.n_page + 1
        elif keycode == wx.WXK_PAGEUP:
            next_page = self.n_page - 1
        else:
            update = False
        if update and (next_page >= 0) and (next_page < self.n_pages):
                self.n_page = next_page
                self.current_page = self.document.get_page(next_page)
                self.Refresh()
 
 
class MyFrame(wx.Frame):
 
    def __init__(self):
        wx.Frame.__init__(self, None, -1, "wxPdf Viewer", size=(800,600))
        self.pdfwindow = PDFWindow(self)
        self.pdfwindow.LoadDocument(sys.argv[1])
        self.pdfwindow.SetFocus() # To capture keyboard events
 
 
if __name__=="__main__":
    app = wx.App()
    f = MyFrame()
    f.Show()
    app.MainLoop()

Si bien tengo entendido que el scroll no es “óptimo”, ya que en cada OnPaint() se redibuja todo el PDF, anda bastante bien en mi instalación y no parece ser un problema. Lógicamente sí se puede redibujar un área en particular con Cairo (sería la que queda “invalidada” por el scroll), pero wxPython me explotó al intentar algunas cosas por un lado, y me parece que en realidad no sé hacerlo bien por el otro (si alguien puede tirar una soga en esto en particular, bienvenido sea).

Me entretuve mucho haciendo esto, y espero que a alguien le sea de utilidad; y aunque todavía Python-Poppler no está disponible para Windows en forma binaria (pero puede ser compilado, bug acá), sí se puede usar en cualquier Linux, OSX, etc. Y nuevamente, hay una demo para hacer esto mismo con PyGTK, y la demo de Poppler en C es mucho más completa, además de fácil de leer y traducir a Python.

Saludos

Categories: codear, linux, programación, python, ubuntu-ar Tags:

Reemplazando texto con expresiones regulares en Python

Viernes, 2 de abril de 2010 Marcelo Sin comentarios

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:

fpdf_charwidths['times']={chr(0):250, chr(1):250, chr(2):250, chr(3):250,} # Sigue...

Enseguida pensé en mejorarlo, reemplazando esas sucesivas llamadas a chr() por la misma función pero ya evaluada y en forma de byte string; por ejemplo, la idea era convertir lo anterior en:

fpdf_charwidths['times']={'\x00':250,'\x01':250,'\x02':250,'\x03':250,} # Sigue...

De esa manera, los archivos .py se simplificarían (se harían más legibles), no sufrirían modificaciones en su comportamiento y hasta serían más veloces en su interpretación y ejecución.

Primero se me ocurrió primero hacer algo más “a mano”, pero en cuanto se me complicó un poquito enseguida pensé que la mejor herramienta era usar expresiones regulares para el matching del patrón “chr(x)” y, por consiguiente, el módulo re de la librería estándar de Python.

Leyendo esta excelente página y la documentación del módulo en cuestión, armé un script, y me quedó así:

#!/usr/bin/python
# coding:utf-8
 
# This script tries to identify all chr(XX) constant calls in python scripts
# and replace them with '\xXX' strings.
# Author: Marcelo Fernández - License: MIT
 
import sys
import re
 
def chrrepl(match):
    # See http://www.amk.ca/python/howto/regex/regex.html
    # Use the captured group to get the hex string value.
    char_number = match.group(1)
    return repr(chr(int(char_number)))
 
if __name__ == '__main__':
    if len(sys.argv) != 3:
        print 'Usage: python chr_cleaner.py infile.py outfile.py'
        sys.exit(1)
 
    infile = sys.argv[1]
    outfile = sys.argv[2]
    # Open file for reading
    try:
        fin = open(infile, 'r')
        fout = open(outfile, 'w')
    except IOError:
        print 'Error when reading %s or trying to write %s' % (infile, outfile)
        sys.exit(2)
 
    intext = fin.read()
    pattern = 'chr\\((\\d+)\\)' # Group the chr() function parameter to capture it
    p = re.compile(pattern)
    outtext = p.sub(chrrepl, intext)
    fout.write(outtext)
    fout.flush()
 
    fin.close()
    fout.close()

Si bien creo que el snippet es bastante legible, lo interesante es que con el módulo re se puede:

  • Identificar un patrón en un texto: En este caso el patrón sería “chr(\d)” (\d porque busco un número allí).
  • Marcar grupos en ese patrón utilizarlos luego; como sé que voy a utilizar el número en cuestión para convertirlo a su byte string, lo defino dentro de un grupo. El patrón queda entonces “chr((\d))”. También es posible usar named groups, identificables por nombre en vez de por posición, pero eso lo dejo de tarea al lector :-P
  • Reemplazar todo el texto que coincide con ese patrón, y en cada reemplazo, llamar a una función que especifique “lo que hay que poner” allí, devolviendo un string. Esta es la frutilla del postre… :-)
    Si bien puedo usar p.sub() para reemplazar lo que busco por un texto constante, también puedo hacer que en cada ocurrencia del patrón una función cualquiera sea llamada, y  se le pase la instancia de MatchObject para que decidamos qué hacer con lo encontrado y devolver un string. Esto hace la función chrrepl() del snippet. Allí tomo el primer grupo (el grupo 0 es el string completo, el 1 es el primer grupo definido posicionalmente, y así sucesivamente), lo convierto a entero, ejecuto la función chr() y devuelvo su representación en byte string.

En fin, después de experimentar un buen rato, esto funcionó y acá se puede ver el diff del commit correspondiente para que se aprecie un poco mejor…

Saludos

Categories: codear, programación, python, sysadmin, ubuntu-ar Tags:

Navegador simple con Python + Webkit/GTK

Jueves, 19 de noviembre de 2009 Marcelo 1 comentario

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). :-)

No podía dejar de probarlo. Entonces me puse manos a la obra, y salió esto, tratando de imitar lo que se posteó en la lista:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
 
""" 
    SimpleBrowser - Navegador muy muy simple de internet, sólo de ejemplo,
                    que utiliza la biblioteca Webkit GTK desde Python (PyWebkitGTK).
 
    Marcelo Fidel Fernández - http://www.marcelofernandez.info 
    Licencia: BSD. Disponible en: http://www.freebsd.org/copyright/license.html
"""
 
import sys
import gtk
import webkit
 
DEFAULT_URL = 'http://www.python.org'
 
class SimpleBrowser:
 
    def __init__(self):
        self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
        self.window.set_position(gtk.WIN_POS_CENTER_ALWAYS)
        self.window.connect('delete_event', self.close_application)
        self.window.set_default_size(800, 600)
 
        vbox = gtk.VBox(spacing=5)
        vbox.set_border_width(5)
 
        self.txt_url = gtk.Entry()
        self.txt_url.connect('activate', self._txt_url_activate)
 
        self.scrolled_window = gtk.ScrolledWindow()
        self.webview = webkit.WebView()
        self.scrolled_window.add(self.webview)
 
        vbox.pack_start(self.txt_url, fill=False, expand=False)
        vbox.pack_start(self.scrolled_window, fill=True, expand=True)
        self.window.add(vbox)
 
    def _txt_url_activate(self, entry):
        self._load(entry.get_text())
 
    def _load(self, url):
        self.webview.open(url)
 
    def open(self, url):
        self.txt_url.set_text(url)
        self.window.set_title('SimpleBrowser - %s' % url)
        self._load(url)
 
    def show(self):
        self.window.show_all()
 
    def close_application(self, widget, event, data=None):
        gtk.main_quit()
 
if __name__ == '__main__':
    if len(sys.argv) > 1:
        url = sys.argv[1]
    else:
        url = DEFAULT_URL
 
    # PyWebkitGTK necesita habilitar el soporte de los hilos en PyGTK
    gtk.gdk.threads_init()
    browser = SimpleBrowser()
    browser.open(url)
    browser.show()
    gtk.main()

¡Y Listo!

Pantallazo_PyWebkitGTK

Todo lo que se necesita en Ubuntu 9.10 para poder correr esto es instalar el paquete “python-webkit”; sin embargo, esta versión es la 1.1.5, mientras que PyWebkitGTK va por la 1.1.7 y Webkit/GTK va por la 1.1.15, así que todavía hay lugar para mejoras.

Aquí dejo algunos links:

  • PyWebkitGTK se llama el proyecto de llevar Webkit/GTK (escrito naturalmente en C) a Python.
  • Acá hay un ejemplo muchísimo más completo de un navegador con múltiples pestañas y todo.
  • Lamentablemente, la documentación de la biblioteca en Python no existe aún (es un ticket del proyecto), así que por ahora habrá que conformarse con la documentación de Webkit/GTK; sin embargo, yo lo encuentro bastante legible, ya que enseguida uno se adapta a “traducir” cómo se llamaría un método en C a uno en Python.

Espero que les sirva. Me divertí mucho haciéndolo. :-)

Saludos

Categories: codear, programación, python, ubuntu-ar Tags: