Navegador simple con wxPython + Webkit/GTK

Hace algunos posts (¡casi un año ya!) escribí sobre una manera fácil y rápida de tener un componente «navegador web» en Python sobre Linux/BSD, gracias a PyGTK y WebkitGTK, llamado lógicamente, pyWebkitGTK. En pocas líneas de código uno puede disponer de un navegador potente y completo en un panel de su aplicación basada en PyGTK, ideal para integrar aún más la cada omnipresente Web.

Las vueltas de la vida y las ganas de experimentar y aprender te llevan a probar otros frameworks/librerías, como lo es wxPython; tanto es así que de vez en cuando tengo el placer de dar alguna charla al respecto [1], y una de las debilidades que le usualmente le encontraba es la falta de un componente «browser web» nativo y soportado en todas las plataformas (wxPython sólo incluye IE embebible como ActiveX en Windows).

En búsqueda de alternativas existe wxWebKit, pero al proyecto le faltan terminar algunas cosas para tener lista su versión «1.0», y si bien wxWebConnect funciona, no da soporte para wxPython, sólo wxWidgets desde C++. Eso nos deja con que en Linux/BSD no tenemos componente que nos dé esta posibildad, pero… si wxPython en estas plataformas utiliza GTK por debajo, ¿no podríamos usar pyWebkitGTK como componente para embeberlo en nuestra aplicación Python?

La respuesta por suerte es afirmativa, y en una rápida búsqueda en la Wiki de wxPython encontré un ejemplo de cómo hacerlo. Si bien podría copiar y pegar la receta, me gustaría «aggiornarla» un poquito, al menos traduciendo y explicando un poco más los comentarios.

Primero vamos a mostrar cómo se vería el módulo que incluye un widget HtmlPanel, wxwebkitgtk.py:

#!/usr/bin/env python
# coding:utf-8

"""
    wxWebkitGTK - Componente wxPython que embebe un navegador
                   utiliza la biblioteca Webkit GTK desde Python (PyWebkitGTK).

    Marcelo Fidel Fernández - http://www.marcelofernandez.info
    Basado en: http://wiki.wxpython.org/wxGTKWebKit
"""
import os
import wx
import gobject
gobject.threads_init()
import gtk, gtk.gdk
import webkit

class HtmlPanel(wx.Panel):

    def __init__(self, *args, **kwargs):
        wx.Panel.__init__(self, *args, **kwargs)
        # Aquí es donde se hace la "magia" de embeber webkit en wxGTK.
        whdl = self.GetHandle()
        window = gtk.gdk.window_lookup(whdl)
        # Debemos mantener la referencia a "pizza", sino obtenemos un segfault.
        self.pizza = window.get_user_data()
        # Obtengo el padre de la clase GtkPizza, un gtk.ScrolledWindow
        self.scrolled_window = self.pizza.parent
        # Saco el objeto GtkPizza para poner un WebView en su lugar
        self.scrolled_window.remove(self.pizza)
        self.webview = webkit.WebView()
        self.scrolled_window.add(self.webview)
        self.scrolled_window.show_all()

La «magia» consiste en que, sabiendo que pyWebkitGTK necesita un componente ScrolledWindow GTK como padre para funcionar correctamente, se utiliza la biblioteca PyGTK para buscar el ScrolledWindow GTK donde está embebido el wx.Panel de la clase (su «abuelo»), y reemplazar el hijo GTKPizza (un componente inventado por wxWidgets para funcionar) por el WebView de Webkit.

Aquí hay un ejemplo de cómo se puede utilizar este panel como widget de wxPython completo e independiente, copiando la funcionalidad básica del post de PyWebkitGTK, el archivo wxwebkitgtk_demo.py:

#!/usr/bin/env python
# coding: utf-8

"""
    wxSimpleBrowser - Navegador muy muy simple de internet, sólo de ejemplo,
                      que utiliza la biblioteca Webkit GTK desde wxPython.

    Marcelo Fidel Fernández - http://www.marcelofernandez.info
    Licencia: BSD. Disponible en: http://www.freebsd.org/copyright/license.html
"""

import sys
import wx
from wxwebkitgtk import HtmlPanel

DEFAULT_URL = 'http://www.python.org'

class wxSimpleBrowser(wx.Frame):

    def __init__(self):
        wx.Frame.__init__(self, None)
        self.TxtUrl = wx.TextCtrl(self, wx.ID_ANY, style=wx.TE_PROCESS_ENTER)
        self.TxtUrl.Bind(wx.EVT_TEXT_ENTER, self.OnTxtURL)
        self.Box = wx.BoxSizer(wx.VERTICAL)
        self.Box.Add(self.TxtUrl, proportion=0, flag=wx.EXPAND)
        self.SetSizer(self.Box)
        self.SetSize((800,600))
        self.Show()
        # Necesitamos tener mostrado el componente padre del Panel para que funcione,
        # por eso mostramos primero el Frame y después creamos el HtmlPanel
        self.HtmlPanel = HtmlPanel(self)
        self.Box.Add(self.HtmlPanel, proportion=1, flag=wx.EXPAND)
        self.SendSizeEvent() # Para acomodar el panel al tamaño del frame

    def OnTxtURL(self, event):
        self.Open(self.TxtUrl.GetValue())

    def Open(self, url):
        # Podemos acceder a todos los métods del objeto WebView
        # http://webkitgtk.org/reference/webkitgtk-webkitwebview.html
        self.HtmlPanel.webview.load_uri(url)
        self.TxtUrl.SetValue(url)
        self.SetTitle('wxSimpleBrowser - %s' % url)

if __name__ == '__main__':
    if len(sys.argv) > 1:
        url = sys.argv[1]
    else:
        url = DEFAULT_URL
    app = wx.App()
    browser = wxSimpleBrowser()
    browser.Open(url)
    app.MainLoop()

Creo que para la enorme funcionalidad que nos brinda el proceso de ponerlo en práctica es bastante simple, y aunque depende de PyGTK, ésta biblioteca hoy está disponible «de fábrica» en cualquier distribución moderna de GNU/Linux.

De aquí en más es ser muy sencillo dejar al lector el armado de un widget para wxPython que en Windows muestre el componente navegador de IE y en Linux un navegador Webkit.

[1] ¡La semana que viene voy a estar en la PyCon Argentina 2010 dando una charla de Introducción a wxPython! 😀

¡Saludos!


Comentarios

7 respuestas a «Navegador simple con wxPython + Webkit/GTK»

  1. estimados
    he generado un pequeño programa que aunque funciona en linux no logro hacer andar en windows ya que tengo problemas con el módulo webkit.
    como se puede importar este módulo en windows para que python lo pueda reconocer?
    un saludo y disculpen por las molestias
    juan josé

  2. Hola Juan José, según lo que busqué en su momento aún no funciona del todo webkitgtk en Windows… podés averiguar en su IRC a ver qué te dicen:

    http://www.webkitgtk.org/?page=contact

    Saludos

  3. Hola Marcelo, te encontrado buscando algo de información para la aplicación que estoy intentando realizar. Me he topado con un problema que no consigo solucionar y te lo expongo a ver si me puedes echar un cable. Es el siguiente

    Quiero habilitar un boton que me abra un nueva ventana(frame) y haga una serie de pasos y desplegables y al darle a un boton de la misma, vuelva al Frame principal con ese valor,,, pero no consigo realizarlo pq digamos que pierdo

    GUI llamo a FrameIni

    def OnInit(self):
    wx.InitAllImageHandlers()
    MainFrame = FrameIni(None)
    ## Main frame of the GUI.
    self.MainFrame = MainFrame
    self.SetTopWindow(MainFrame)
    MainFrame.Show()

    en el evento del boton de FrameIni hago

    def EventGotoKeyConfig( self, event ):
    SecondFrame = FrameKeyConfig(None)

    self.SecondFrame = SecondFrame
    self.SecondFrame.Show()

    Esto permite mostrar el segundo frame, cuando le doy al boton de FrameKey que me cierra el frame creado y me devuelve el valor seteado, hago lo siguiente

    def Event( self, event ):

    self.Destroy()
    FrameIni.function(True)

    TypeError: unbound method function() must be called with FrameIni instance as first argument (got bool instance instead)

    Digamos que no tengo visibilidad desde un Frame al otro, uan opcion que me he planteado es hacerlo todo global o algo asi, pero no lo veo claro, tú como lo ves??

    gracias
    saludos

  4. Hola Dani, te pido un pequeño favor. Fijate de postear un ejemplo básico mínimo y funcional que demuestre el error/problema que tenés. Podés usar pastebin.com para que vea el código (no hace falta comentarlo acá).

    De esa manera lo puedo probar mejor y ver/comprender más rápido qué es lo que te está pasando.

    Saludos!

  5. Avatar de Jorge Luis
    Jorge Luis

    Muchas gracias, esto es justo lo que andaba buscando.

    Felicidades por tu blog

  6. Avatar de joaquin

    Hola,
    Algunas de las librerías no están disponibles (y puede que no lo vuelvan a estar nunca) en python 3.
    Hay algun otro camino para tener un navegador en python 3 linux con wxpython (phoenix) ?

  7. Ufff, debería actualizar este post. Prometo hacerlo.

    Saludos

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *