dimarts, 7 de desembre del 2010

Com fer una GUI basada en Python i Glade amb Anjuta

Una molt bona opció per a fer aplicacions amb interfase gràfica d'usuari en entorns d'escriptori Gnome, o allà on es tingui disponible el GTK és fer servir la combinació de Glade i Python.

En realitat Glade es pot combinar amb d'altres llenguatges com C/C++, o Java. Tanmateix, la combinació amb Python ofereix alguns avantatges que cal tenir en compte.

la combinació de Python amb Glade ofereix un avantatge fonamental: Python és un llenguatge interpretat d'script. És adequat, per tant, per a l'automatització flexible de processos, diguem-ne macros, i per a tasques d'administració.

Python es pot trobar com llenguatge de macros en diverses aplicacions, com l'OpenOffice, o el Gimp. Amb tot, Python és molt potent, i prou senzill, com per a que se'l trobi en d'altres ambients.

El que faré en l'exercici que presento és crear una GUI amb Glade i connectar-la a un script Python.

L'entorn en el que desenvolupo és un Ubuntu 10.10, Lucid Lynx. Amb Python 2.6 i amb Glade 3.6.7. Faig servir com IDE l'Anjuta 2.30.1.0.

Primer de tot amb l'Anjuta creo un projecte de tipus Python:


Li indico la carpeta de projecte.

Som-hi: afegeixo una pantalla d'interfase gràfica: Fitxer - Nou - Fitxer del Glade.

S'obre la finestra de preferències per a la interfície gràfica. En aquesta primera pantalla el que cal és assegurar-se que és fer servir el GtkBuilder,  L'altre opció, LibGlade, està "deprecada".  En principi, com no sigui pel fet d'haver de desenvolupar o  mantenir interfases en versions antigues de GTK, la LibGlade no l'hauria de fer servir.


Un cop assegurat que el format és del GtkBuilder s'obre el dissenyador d'interfícies. Faig una interfície senzilla com la de la imatge. La he creat a partir d'una finestra de tipus diàleg que ja em proporciona un parell de botons. El nom de la finestra és dialog1. El posa automàticament el Glade. Podré comprovar-ho en revisar el fitxer generat.

He afegit un layout del tipus Vertical Box amb tres files. A la de dalt he posat un label per al títol (label1), a la del mig un camp d'entrada de text (entry1) i a la de sota, un altre camp d'entrada de text (entry2).


El que faré ara serà assignar als l'event clicked de cada botó una funcions que serà el seu "event-handler". És tan senzill com indicar-ho a les propietats.
Al boto1 li assigno l'event-handler on_button1_clicked i al botó 2 el on_button2_clicked



Deso el fitxer generat. És interessant revisar-ne el contingut amb un editor de text.


<?xml version="1.0"?>
<interface>
  <requires lib="gtk+" version="2.16"/>
  <!-- interface-naming-policy project-wide -->
  <object class="GtkDialog" id="dialog1">
    <property name="border_width">5</property>
    <property name="type_hint">normal</property>
    <property name="has_separator">False</property>
    <child internal-child="vbox">
      <object class="GtkVBox" id="dialog-vbox1">
        <property name="visible">True</property>
        <property name="spacing">2</property>
        <child>
          <object class="GtkVBox" id="vbox2">
            <property name="visible">True</property>
            <child>
              <object class="GtkLabel" id="label1">
                <property name="visible">True</property>
                <property name="xalign">0.47999998927116394</property>
                <property name="label" translatable="yes">Una prova amb Glade + Python</property>
              </object>
              <packing>
                <property name="position">0</property>
              </packing>
            </child>
            <child>
              <object class="GtkEntry" id="entry1">
                <property name="visible">True</property>
                <property name="can_focus">True</property>
                <property name="invisible_char">&#x25CF;</property>
              </object>
              <packing>
                <property name="position">1</property>
              </packing>
            </child>
            <child>
              <object class="GtkEntry" id="entry2">
                <property name="visible">True</property>
                <property name="can_focus">True</property>
                <property name="invisible_char">&#x25CF;</property>
                <property name="text" translatable="yes">AAAA</property>
              </object>
              <packing>
                <property name="position">2</property>
              </packing>
            </child>
          </object>
          <packing>
            <property name="position">1</property>
          </packing>
        </child>
        <child internal-child="action_area">
          <object class="GtkHButtonBox" id="dialog-action_area1">
            <property name="visible">True</property>
            <property name="layout_style">end</property>
            <child>
              <object class="GtkButton" id="button1">
                <property name="label" translatable="yes">Acci&#xF3;</property>
                <property name="visible">True</property>
                <property name="can_focus">True</property>
                <property name="receives_default">True</property>
                <signal name="clicked" handler="on_button1_clicked"/>
              </object>
              <packing>
                <property name="expand">False</property>
                <property name="fill">False</property>
                <property name="position">0</property>
              </packing>
            </child>
            <child>
              <object class="GtkButton" id="button2">
                <property name="label" translatable="yes">Sortir</property>
                <property name="visible">True</property>
                <property name="can_focus">True</property>
                <property name="receives_default">True</property>
                <signal name="clicked" handler="on_button2_clicked"/>
              </object>
              <packing>
                <property name="expand">False</property>
                <property name="fill">False</property>
                <property name="position">1</property>
              </packing>
            </child>
          </object>
          <packing>
            <property name="expand">False</property>
            <property name="pack_type">end</property>
            <property name="position">0</property>
          </packing>
        </child>
      </object>
    </child>
    <action-widgets>
      <action-widget response="0">button1</action-widget>
      <action-widget response="0">button2</action-widget>
    </action-widgets>
  </object>
</interface>


A partit d'aquí ja tenim la GUI preparada, ara cal connectar-la a l'script Python que la mourà. Vet aquí l'script:

Primer de tot, el preparo per a poder executar des de línia de comandes. També li indico que l'encoding per defecte serà el latin-1. Això és important per a poder escriure literals en català. A partir d'aquí, les explicacions es troben als comentaris del codi:


#!/usr/bin/python
# coding: latin-1


#Importo el paquets de Gtk, PyGtk i Glade
import sys
import pygtk  
import gtk  
import gtk.glade  


#Creo una classe que gestionarà la GUI. Serà el controlador bàsic.
class CGui:
    # propietats
    # Un objecte d'utilitat per utilitzar Glade 
    Glade = None


    # constructor
    def __init__( self ):
        # carrega la definició de la GUI
        # i el posa en l'objecte Glade
        self.Glade = gtk.Builder()
        self.Glade.add_from_file( "/media/DISC57GB/wk-python/ProvaGUI/src/prova01.glade" )
        
        # assigna events de la GUI a mètodes
        dic = { 
            "on_button2_clicked" : self.quit,
            "on_button1_clicked" : self.Mostra,
            "on_windowMain_destroy" : self.quit,
        }
        
        # els connecta
        self.Glade.connect_signals(dic)
        
        # executa la GUI
        # carrega la finestra
        self.window = self.Glade.get_object('dialog1')
        # la mostra
        self.window.show_all()
        # i inicia el dispatcher,
        gtk.main()




    # mètode Mostra
    # agafa el text de l'Entry2 i el posa a l'entry1 afegint-li un text de sufix
    def Mostra(self, widget):
        # és interessant veure com obté el widget i les seves propietats
        # self.wTree.get_widget("entry11").get_text()
       
        self.Glade.get_object("entry1").set_text(self.Glade.get_object("entry2").get_text() + " Passa a 1")
        
        
    # mètode sortir
    def quit(self, widget):
        sys.exit(0)
        


# El main
# senzillament, executa el constructor       
gui = CGui()


Des d'Anjuta es pot executar l'aplicació i obtenim el resultat esperat.



Efectivament, la parella Glade i Python, combinats a l'IDE Anjuta mereix amb tots els honors el qualificatiu de RAD.

Un IDE alternatiu a l'Anjuta és Eclipse amb el plugin PyDev.

Cap comentari:

Publica un comentari a l'entrada