jueves, 28 de enero de 2010

wxPython, el principio

Vamos a crear la versión mas sencilla de un programa wxPython, que muestre únicamente una ventana.

# -*- coding: cp1252 -*-

'''Mínimo código para crear una ventana'''

import wx

class App(wx.App):
def OnInit(self):
ventana = wx.Frame(parent=None, title='Titulo')
ventana.Show()
return True

app = App()
app.MainLoop()

NOTA: Es importante, si vas a utilizar tildes, por ejemplo, que incluyas al principio del fichero: # -*- coding: cp1252 -*-

Con import wx importamos módulo de wx. Es importante aquí destacar que siempre hay que importar este módulo en primer lugar si después vamos a importar algo más de wxPython. En Python el orden en la carga de módulos no importa, pero en wxPython si influye. Esto se debe a que wx originalmente viene de C++ (y utiliza SWIG, pero no me voy a meter aquí).

Lo que aparece al ejecutar el código es una ventana (ó window). Bien, en wxPython, esto no es una window, sino un frame, ya que window (ó widget) en wxPython es el término genérico que se utiliza para cualquier objeto que se muestra por pantalla. Por tanto, window ó widget se refiere a cualquier objeto que se pinte, como botones ó cajas de texto. Esto viene de C++. Para no liarnos llamaremos a cualquier objeto que se muestre por pantalla con el término de widget.

En este programa se muestran los 5 pasos para escribir una aplicación wxPython, a saber:
1) Importar el módulo necesario de wxPython.
import wx

2) Crear subclase a partir de la clase aplicación de wxPython.
class App(wx.App):

3) Definir un método de inicialización de la aplicación.
def OnInit(self):
ventana = wx.Frame(parent=None, title='Titulo')
ventana.Show()
return True

4) Crear una instancia de la clase aplicación.
app = App()

5) Ejecutar el evento MainLoop de la aplicación.
app.MainLoop()

Una vez que se importa el módulo wx podemos crear la aplicación y el objeto frame. Todos los programas wxPython tienen que tener un objeto aplicación y por lo menos un objeto frame. Además, el objeto aplicación debe ser una instancia de wx.App o una subclase que definas cuando declares un método OnInit(), el cual se llamará por la clase padre wx.App cuando comience la aplicación.

En el método OnInit() es donde a menudo se crearán los objetos frame, aunque lo normal no es crear directamente instancias de wx.Frame, como hemos hecho en el ejemplo. En su lugar definiremos una propia subclase de wx.Frame, de la misma manera que hemos definido nuestra subclase aplicación a partir de wx.App.

Cuando se invoca el método Show() el frame se hace visible. Si no se invoca, el frame existe, pero no se hace visible. Así tendremos que ventana.Show() es equivalente a ventana.Show(True), y hace visible el frame. Por otra parte ventana.Hide() es equivalente a ventana.Show(False), lo que oculta el frame.

NOTA: Darse cuenta que no hemos definido un método __init__() para nuestra clase aplicación. En Python, wx.App.__init__() se invoca automáticamente al crear el objeto. Si se quiere definir un propio método __init__() no se puede olvidar invocar al de la clase base, ya que de lo contrario el método OnInit() no será llamado, y no funcionará nada. Lo que en realidad estamos haciendo es un override del método constructor. Pero claro, nos interesa que el constructor de la clase base se ejecute, o no funcionará. Es decir:

class App(wx.App):
def __init__(self):
# Llamamos al constructor de la clase base.
wx.App.__init__(self)

# Nuestro código
.....
.....

Como hemos visto, el paso final es crear una instancia de la subclase wx.App e invocar el método MainLoop(). De esta manera el programa wxPython lo que hace es mostrar el frame (app = App()) y esperar a que ocurran eventos (app.MainLoop()), del tipo de hacer click con el ratón, teclear por el teclado, mover el frame, etc. Cuando todos los frames se cierran el método MainLoop termina y por tanto el programa finaliza (ya que no hay más código después de app.MainLoop().

A continuación vamos a ver una versión mejorada del programa anterior, en donde creamos una subclase frame a partir de la clase wx.Frame. ¿Por qué? Pues porque nos interesará en el futuro incluir dentro de una frame botones, cajas de texto y demás widgets (recordar, objetos que pueden pintarse en pantalla). Así tendremos que:

# -*- coding: cp1252 -*-

'''Mejora de programa con único frame. Ahora el frame es subclase de clase wx.Frame'''

import wx

class frame(wx.Frame):
pass

class App(wx.App):
def OnInit(self):
self.frame = frame(parent=None, title='Titulo')
self.frame.Show()
self.SetTopWindow(self.frame)
return True

app = App()
app.MainLoop()

Aquí podemos ver varias cosas, a saber:
1) Hemos creado una subclase frame, a partir de la clase wx.Frame. Dicha clase contiene la instrucción Python pass (no hace nada).
class frame(wx.Frame):
pass

2) Dentro de OnInit(), vemos que creamos una instancia de la clase frame que hemos declarado antes, esto es, self.frame, de la siguiente manera:
self.frame = frame(parent=None, title='Titulo')

3) Hemos incluido el método SetTopWindow(), el cual es heredado de la clase padre wx.App. Un programa wxPython puede tener varios frames, pero solo uno es designado como el widget principal de la aplicación. Por ejemplo, un frame MDI sería el widget principal. Para ello utilizamos este método, pasándole como parámetro el frame que queremos sea el principal.

4 comentarios:

  1. Hola, yo de nuevo, estoy comenzando desde cero a seguir tus post.

    Tengo Windows XP, Instalé Python 2.6 y wxPyhon.

    Al querer correr tu ejemplo me da este error:

    http://img580.imageshack.us/img580/3839/errorfl.jpg


    Qué es? Problema de identación?

    ResponderEliminar
  2. Por si no lo resolviste, tienes que separar def de OnInit,

    ResponderEliminar
  3. Hola, 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

    ResponderEliminar
    Respuestas
    1. pudiste resolverlo,, estoy algo parecido a ti, tengo varias ventanas y quiero sacar los valores en cada ventana y pasarlo a la otra ventana, como lo resolviste???

      Eliminar