Libertonia
Portada · Todo · Software Libre · Desarrolladores · Comunidad · Internet · Tecnología · Meta · Diarios
wxpython, ventanas para nuestros scripts (incluso en windows)

trollete's Diary
Por trollete
departamento , Sección Diarios
Puesto a las Fri May 16th, 2003 at 07:00:08 PM CET

De todos es conocidos la potencia, flexibilidad y facilidad de uso de Python. Sin embargo, muchos de nuestros usuarios siempre se quejan de que usar la consola es difícil, y que ese script que les libera espacio en su $HOME sería perfecto si tuviera sus menús, botones, etc.

Como no todo va a ser LART, estuve buscando distintas opciones para dar una interfaz gráfica a mis scripts. Durante un tiempo estuve jugando con python + glade, pero me encontré con el problema de portar la aplicación (bueno, el script, el amable lector ya me entiende) a windows.

 


Entonces descubrí wxpython, y parafraseando a Eric S. Raymond, estoy convencido de que debería convertirse en la GUI estándar de python.

Como decía mi madre, obras son amores, y no buenas razones, así que en vez de vender wxpython, razonando sus ventajas, voy a describir como realizar un programa sencillito, consitente en una única ventana, con un menú. El código ocupa 159 líneas, incluidos comentarios, pero sirve perfectamente como toma de contacto con wxpython.

Un comentario antes de empezar, no soy programador, no me gusta leer documentación, y soy más bien vago. Lo que quiero decir es que no es necesario tener muchos mimbres para hacer esta cesta. A base de mirar ejemplos, y con un poco de prueba y fallo, el script me ha llevado una hora escasa. Así que si yo he sido capaz, cualquiera puede hacerlo.

Dicho esto, empecemos:

    import sys, os, time, re
    from wxPython.wx import *
    from poplib import POP3

    refrom = re.compile("From:")
    resubj = re.compile("Subject:")
    lista = []

    class main_window(wxFrame):

        def __init__(self, parent, id, title):
                wxFrame.__init__(self, parent, -1, title, size = (600, 410), \
                style=wxDEFAULT_FRAME_STYLE|wxNO_FULL<U>REPAINT</u>ON_RESIZE)


    class App(wxApp):

        def OnInit(self):
                frame = main_window(None, -1, "wxcorreo")
                self.SetTopWindow(frame)
                return true

    

app = App(0)
app.MainLoop()

Lo primero de todo es crear la estructura básica de nuestra ventana. Todas los programas en wxpython tienen en común la clase App, con el método OnInit, que en este caso crea una ventana, con el título "wxcorreo".

Vamos a añadirle algo más a la clase main_window, para que realmente parezca algo, empecemos con una barra de menús, y sus menús correspondientes:

                self.mainmenu = wxMenuBar()
                mainwindow = self

                menu = wxMenu()

                exitID = wxNewId()
                menu.Append(exitID, 'Conexión', 'Conecta con el servidor')
                EVT_MENU(self, exitID, self.OnConect)
                exitID = wxNewId()
                menu.AppendSeparator()
                menu.Append(exitID, 'Salir', 'Cierra la aplicación')
                EVT_MENU(self, exitID, self.OnExit)self.mainmenu.Append(menu, 'Principal')

                menu = wxMenu()

                exitID = wxNewId()
                menu.Append(exitID, 'Acerca de', '')
                EVT_MENU(self, exitID, self.OnAbout)self.mainmenu.Append(menu, 'Ayuda')

                self.SetMenuBar(self.mainmenu)

A la vez que hemos creado la barra de menús, con sus dos menús, "Principal" y "Ayuda", hemos indicado que hacer con cada una de las opciones, mediante las líneas EVT_MENU, que llaman a las funciones OnConect, OnExit y OnAbout. Más adelante nos pondremos con ellas.

Ahora vamos a añadir una barra de estado a la ventana. Como nuestros usuarios gustan de las pijaditas, vamos a añadir también un reloj. La cuestión es arropar bien el script.

                sb = self.CreateStatusBar(2)
                sb.SetStatusWidths([-1, 150])
                self.timer = wxPyTimer(self.Notify)
                self.timer.Start(1000)
                self.Notify()

En realidad, lo que hemos hecho es crear una cuenta atrás, que cada 1.000 milisegundos, llama a la función Notify, que es la que realmente pone la fecha y hora. Esta función la veremos después.

Finalmente, para terminar con nuestra ventana (de momento sólo tenemos un marco, con una barra de menús y una barra de estado), añadimos una ventanita de texto, en la que se mostrará el resultado de ejecutar el script (el script original, el que se lanzaba en consola, y que tan poco gustaba a nuestros usuarios).

                self.win = wxTextCtrl(self, 1, style = wxTE_MULTILINE|
                wxTE_READONLY|
                wxHSCROLL)

Vamos a echar una ojeada a las funciones.

        def OnExit(self, event):
                self.Close()

No tiene mucha complicación, ¿verdad? Queremos cerrar, y eso es lo que hace. Punto.

        def OnAbout(self, event):
                dlg = wxMessageDialog(self, "(c) 2003 Benjamín Albiñana Pérez\n"
                                            "foobar@escomposlinux.org\n",
                                            "wxcorreo.py", wxOK | wxICON_INFORMATION)
                dlg.ShowModal()
                dlg.Destroy()

La típica ventanita que aparece cuando se pulsa la opción "Acerca de...", un texto, una tecla "Ok", y un icono.

        def Notify(self):
                t = time.localtime(time.time())
                st = time.strftime(" %d-%b-%Y  %I:%M:%S", t)
                self.SetStatusText(st, 1)

La función que llama la cuenta atrás, coge la fecha y hora del sistema, y la muestra dentro de la barra de estado.

El script original no tenía ningún misterio, pedía al usuario que introdujera el servidor, su nombre de usuario y su pass, y devolvía una lista de sus correos, con el From: y el Subject:

wxpython tiene varias ventanas de diálogo "listas para su uso", como hemos visto en la función OnAbout, pero en este caso, necesitamos construirnos nuestro propio diálogo, al necesitar tres campos de texto, incluyendo uno que enmascare el texto con asteriscos. Para construir nuestro propio diálogo creamos otra clase, con sus cajas de texto y sus botones, en la función OnConect validamos los datos introducidos (en realidad, le decimos que sí el usuario ha pulsado Ok, tome el texto de las cajas), llamamos al script original, convenientemente transformado en función, y todo ello lo conectamos en el método __init__ de la clase main_window.

Empecemos con la clase wxDialog.

    class ConectServer(wxDialog):
        def __init__(self, parent, id, title):
                wxDialog.__init__(self, parent, -1, title, size = (300, 160))
                wxStaticText(self, -1, "Servidor:", (10, 10))
                self.servidor = wxTextCtrl(self, -1, "pop3.escomposlinux.org", (70, 10), (200, 20))
                wxStaticText(self, -1, "Usuario:", (10, 35))
                self.usuario = wxTextCtrl(self, -1, "foobar", (70, 35), (100, 20))
                wxStaticText(self, -1, "Password:", (10, 60))
                self.password = wxTextCtrl(self, -1, "", (70, 60), (100, 20), wxTE_PASSWORD)
                wxButton(self, wxID_OK, " OK ", (50, 110)).SetDefault()
                wxButton(self, wxID_CANCEL, " Cancelar ", (175, 110))

Los botones y las cajas se han puesto a ojímetro, y es conveniente probar tanto en linux como en windows como quedan. No tengo muy claro porqué, pero lo que en linux se ve correctamente, en windows puede quedar demasiado desplazado hacia abajo.

        def OnConect(self, event):
                val = self.connect.ShowModal()
                if val == wxID_OK:
                        self.servidor = self.connect.servidor.GetValue()
                        self.usuario = self.connect.usuario.GetValue()
                        self.password = self.connect.password.GetValue()
                self.Correo()

La función toma el valor del evento que resulta al pulsar uno de los dos botones del diálogo anterior, y si este es OK, asigna los valores pasados por el usuario a las variables servidor, usuario y password, y llama a la última función Correo. Para que todo esto conecte, habremos añadido la línea

    self.connect = ConectServer(self, -1, "Conexión al servidor")

al método __init__ de la clase main_window.

Finalmente, la función Correo, que mediante el módulo poplib, conecta con el servidor y devuelve una lista de los correos, mostrando las líneas "From:" y "Subject:". Estas líneas se envían a la ventana del programa.

        def Correo(self):
                m = POP3(self.servidor)
                m.user(self.usuario)
                m.pass_(self.password)
                n = len(m.list()[1])
                for j in range(n):
                        men = m.top(j+1, 0)[1]
                        for i in range(len(men)):
                                linea = refrom.search(men[i])
                                if linea != None:
                                        lista.append(men[i])
                                linea = resubj.match(men[i])
                                if linea != None:
                                        lista.append(men[i])

                m.quit()
                for k in range(len(lista)):
                        self.win.AppendText(lista[k] + '\n')
                        linea = resubj.match(lista[k])
                        if linea != None:
                                self.win.AppendText('\n')

Ya sólo queda lanzar el script y ver que pinta tiene. Aquí se ve la aplicación con el menú principal desplegado en linux, y aquí en windows, con la ventana "Acerca de..."

Obviamente este script es sólo un ejemplo de lo que se puede hacer con wxpython. Tengo muy claro los fallos que tiene (sobre todo el pasar olímpicamente de las posibles excepciones que se puedan producir), y, por supuesto, que no es para tirar cohetes. Simplemente he querido compartir con todo el mundo mi entusiasmo al descubrir wxpython.

Como ya he dicho al principio, no me gusta mirar documentación, y prefiero con diferencia el estilo de programación anárquico (programo como me sale de los Objetos). En el paquete wxwin2.2-examples, uno de los Suggests del paquete libwxgtk2.2-python en debian, se encuentran un montón de ejemplos y sobre todo, el programa demo.py. Imprescindible para arrancar con wxpython.

Nada más, espero que toda esta parrafada haya servido de algo. A mí me ha servido para tener otra entrada en mi diario. Algo es algo.

< Petición de Ponencias para el VI Congreso Hispalinux (9 comments) | BArceloneses DOmadores de PIngüinos (badopi) (6 comments) >
Enlaces Relacionados
· escomposlinux.org
· Python
· LART
· glade
· descubrí
· wxpython
· código
· Aquí
· aquí
· More on trollete's Diary
· Also by trollete

Menu
· crear cuenta
· FAQ
· búsqueda
· Fuentes de Noticias

Login
Nueva cuenta
Usuario:
Contraseña:

Ver: Modo: Orden:
wxpython, ventanas para nuestros scripts (incluso en windows) | 3 comentarios (3 temáticos, editoriales, 0 ocultos)
Un apunte (none / 0) (#1)
por jorginius ("jorginius" en Google Mail) a las Sun May 18th, 2003 at 11:19:24 AM CET
(Información Usuario) http://www.rodriguezmoreno.com

Colocar los controles a ojímetro ya no se lleva, hombre :-). Usa wxSizer y tus aplicaciones se verán igual en todas las plataformas. Vale, ahora funciona en Windows y en Linux/Gtk, pero ¿a qué no has probado en MacOS X?, pues eso...

Por ejemplo, podrías hacer:

###
class ConectServer(wxDialog):
   def __init__(self, parent, id, title):
      wxDialog.__init__(self, parent, -1, title)
      box = wxBoxSizer(wxVERTICAL)

      box.SetMinSize((300, 160))
      box.Fit(self)

      grid = wxFlexGridSizer(3, 2, 5, 5)
      box.Add(grid, 0, wxEXPAND | wxALIGN_CENTER | wxALL, 15)

      self.servidor = wxTextCtrl(self, -1, "pop3.escomposlinux.org", size=(190,30))
      self.usuario = wxTextCtrl(self, -1, "foobar")
      self.password = wxTextCtrl(self, -1, "", style=wxTE_PASSWORD)

      grid.Add(wxStaticText(self, -1, "Servidor:"))
      grid.Add(self.servidor)
      grid.Add(wxStaticText(self, -1, "Usuario:"))          
      grid.Add(self.usuario)
      grid.Add(wxStaticText(self, -1, "Password:"))
      grid.Add(self.password)

      bt = wxButton(self, wxID_OK, " OK ")
      bt.SetDefault()

      b1 = wxBoxSizer(wxHORIZONTAL)
      b1.Add(bt, 0, wxRIGHT, 15)
      b1.Add(wxButton(self, wxID_CANCEL, " Cancelar "))
      box.Add(b1, 0, wxALIGN_CENTER_HORIZONTAL)

      self.SetAutoLayout(true)
      self.SetSizer(box)
      self.Layout()
###

Y aún es mejorable, sigue investigando :-)



Ah, y se me olvidaba... (none / 0) (#2)
por jorginius ("jorginius" en Google Mail) a las Sun May 18th, 2003 at 11:39:58 AM CET
(Información Usuario) http://www.rodriguezmoreno.com

Si usas un editor de diálogos para wxWindows, como el XRCed, el editor del Boa Constructor, el wxrcedit, el wxGlade o lo que sea para generar los archivos de recursos de wxWindows (XRC), te quitarás de todos los problemas de distribución espacial de los controles, ya que todos trabajan con "sizers" más que con posiciones absolutas.

Te lo digo más que nada porque comentas que estás familiarizado con Glade... A lo mejor wxGlade te saca de un apuro.

[ Padre ]


Lo probaré. (none / 0) (#3)
por trollete a las Sun May 18th, 2003 at 12:26:25 PM CET
(Información Usuario)

Lo de los sizers ya lo tengo catado, y no es especialmente complicado. Pero le echaré un ojo al wxGlade.

[ Padre ]


 
wxpython, ventanas para nuestros scripts (incluso en windows) | 3 comentarios (3 temáticos, editoriales, 0 ocultos)
Ver: Modo: Orden:

ecol Logo Powered by Scoop
Todas las Marcas Registradas y copyrights de esta página son propiedad de sus respectivos dueños.
Los comentarios son propiedad del que los escribe.
Los iconos de las noticias y el logotipo son propiedad de Javier Malonda.
El Resto © 2002 Escomposlinux.org y aledaños.

Puedes sindicar los contenidos de libertonia en formato RSS 1.0 y RDF 0.9. También se puede sindicar la cola de envíos pendientes de moderación.

El proyecto escomposlinux.org está dedicado a la memoria de tas

crear cuenta | faq | búsqueda