divendres, 30 de setembre del 2011

10a Diada de les Telecomunicacions a Catalunya.

Avui -29 de setembre, Sant Gabriel, patró de les Telecomunicacions- s'ha celebrat la 10a Diada de les Telecomunicacions a Catalunya.

Organitzat pel COETTC, es tracta d'un esdeveniment important dins el món TIC al nostre país.

Com podem llegir  a la mateixa web del esdeveniment:

"La Diada està orientada als professionals del sector TIC: empresaris, directius, universitaris, comercials i polítics relacionats amb les TIC. També s’adreça als estudiants d'enginyeria i FP, i a tots aquells que tinguin inquietud per estar al dia en el sector TIC.

A la Diada es pren el pols al Sector; s'analitza el que ha estat l'últim any, i es fa una prospecció del que serà el futur immediat. Les diferents ponències no són panells d'alt contingut científic, sinó exposicions i plantejaments de tendències futures: tot això amb un llenguatge fàcil i assequible."

Dins els marc de la Diada, al CosmoCaixa de Barcelona s'han dut a terme diverses activitats,
  • Conferencia inaugural.
  • Taules rodones.
  • Networking, facilitant als assistents diversos moments per a relacionar-se, comunicar-se i fer negocis entre ells.
  • El lliurament dels Premis Excel·lència a les Telecomunicacions, després de l'acte de cloenda
Les diverses activitats de la Diada han pogut preparar-se i seguir-se online per les xarxes socials Twitter, FaceBook i LinkedIn

diumenge, 18 de setembre del 2011

Taules hash amb C a Linux

Les taules hash , hash maps són una estructura de dades que, mitjançant una funció hash (o funció resum) identifica uns valors, anomenats claus, amb uns valors associats.

L'avantatge de les taules hash sobre altres estructures de dades és la gran rapidesa d'accés a la informació i el cost constant d'aquest accés. Per això,  les taules hash tenen un munt d'aplicacions.

En tot cas, les taules hash són una solució molt senzilla i eficient per a l'emmagatzematge i l'accés a la informació que es pugui modelar directament amb taules de  parelles clau valor.

Amb C sobre Linux (en el meu cas Ubuntu 11.04) apareixen almenys tres opcions ràpides per a implementar taules hash:
Amb funcions de la llibreria estàndar GlibC
Amb funcions de la llibreria GTK, Glib

Amb "bases de dades" del tipus DBM, a Linux Gdbm; o amb l'evolució amb capacitat concurrent de Gdbm desenvolupada per a SAMBA: TDB

Anem a repassar les opcions:


Amb Glibc (La llibreria estàndard de C)

Un exemple de taules hash amb les funcions de la llibreria estàndard Glibc (hcreate, hsearch i hdestroy)

/* aquest exemple està adaptat de la documentació del man */
#include <stdio.h>
#include <search.h>
#include <string.h>

struct info {        /* aquesta estructura és la dels valors emmagatzemats a la taula */
    int age, room;  
};

#define NUM_EMPL    5000    /* # número d'elements. */

int main(void)
{
    char string_space[NUM_EMPL*20];       /* espai d'emmagatzematge de la taula de valors de les claus. */
    struct info info_space[NUM_EMPL];     /* espai per emmagatzemar la informació de claus. */
    char *str_ptr = string_space;         /* següent posició a l'espai de valors. */
    struct info *info_ptr = info_space;   /* següent posició a l'espai de claus. */
    ENTRY item;
    ENTRY *found_item;                    /* nom a buscar. */
    char name_to_find[30];
    int i = 0;

    /* Create table; no error checking is performed. */
    printf("Crea la taula\n");
    (void) hcreate(NUM_EMPL);

    printf("Introdueix clau age room a la taula. Per acabar d'entrar valors prem CTRL-D\n");
    while (scanf("%s %d %d", str_ptr, &info_ptr->age,
           &info_ptr->room) != EOF && i++ < NUM_EMPL) {


        /* Put information in structure, and structure in item. */
        printf("Afegeix %s %d %d a la taula\n", str_ptr, info_ptr->age, info_ptr->room);
        item.key = str_ptr;
        item.data = info_ptr;
        str_ptr += strlen(str_ptr) + 1;
        info_ptr++;


        /* Put item into table. */
        printf("Afegeix - hsearch(item, ENTER)\n");
        (void) hsearch(item, ENTER);
    }



    /* Access table. */
    printf("Busca valors a la taula. Per acabar CTRL-C\n");
    item.key = name_to_find;
    while (scanf("%s", item.key) != EOF) {
        printf("Buscant %s amb hsearch(item, FIND)\n", item.key);
        if ((found_item = hsearch(item, FIND)) != NULL) {
           
            /* If item is in the table. */
            (void)printf("trobat %s, age = %d, room = %d\n",
                found_item->key,
                ((struct info *)found_item->data)->age,
                ((struct info *)found_item->data)->room);
        } else
            (void)printf("no trobat %s\n", name_to_find);
    }
    return 0;
}


Una excució podria ser, per exemple:


albert@atenea:~/wk-c/prova-hash-glibc$ ./prova_hash_glibc
Crea la taula
Introdueix clau age room a la taula. Per acabar d'entrar valors prem CTRL-D
clau1 1 2
Afegeix clau1 1 2 a la taula
Afegeix - hsearch(item, ENTER)
clau2 3 4
Afegeix clau2 3 4 a la taula
Afegeix - hsearch(item, ENTER)
clau3 5 6
Afegeix clau3 5 6 a la taula
Afegeix - hsearch(item, ENTER)
Busca valors a la taula. Per acabar CTRL-C
prova
Buscant prova amb hsearch(item, FIND)
no trobat prova
clau1
Buscant clau1 amb hsearch(item, FIND)
trobat clau1, age = 1, room = 2
clau3
Buscant clau3 amb hsearch(item, FIND)
trobat clau3, age = 5, room = 6
^C
albert@atenea:~/wk-c/prova-hash-glibc$



Amb GLib (la llibreria de GTK)

La referència per a taules hash amb Glib es troba a http://developer.gnome.org/glib/2.28/glib-Hash-Tables.html

En resum:

Per a crear una taula hash de Glib (una GHashTable), fem servir: g_hash_table_new().

Per afegir una clau i el seu valor associat a la GHashTable, fem servir: g_hash_table_insert().

Per a cercar el valor associat a una clar fem servir  g_hash_table_lookup()  i g_hash_table_lookup_extended().

Per a eliminar una aprella clau-valor, g_hash_table_remove().

Per a  invocar una funció per a cada parella clau-valor,  g_hash_table_foreach().

Per a esborrar del tot una GHashTable,  g_hash_table_destroy().


Vet aquí l'exemple amb GLibC implementat amb les funcions de GLib. L'exemple ha estat desenvolupat fent servir Anjuta. He partit d'un projecte genèric mínim amb C. M'ha calgut afegir la llibreria glib2.0-0 i l'include a glib.h al fitxer Makefile.am (automake)

## Process this file with automake to produce Makefile.in

## Created by Anjuta

AM_CPPFLAGS = \
    -DPACKAGE_DATA_DIR=\""$(datadir)"\"

AM_CFLAGS =\
     -Wall\
     -g\
     -I/usr/include/glib-2.0\
     -I/usr/lib/glib-2.0/include
     
bin_PROGRAMS = hashtables

hashtables_SOURCES = \
    main.c

hashtables_LDFLAGS = -lglib-2.0

hashtables_LDADD =



El codi és el següent. Pràcticament només he tingut que canviar les funcions:


#include <stdio.h>
#include <string.h>
#include <glib.h>


struct value {        /* aquesta estructura és la dels valors emmagatzemats a la taula */
    gint age, room;  /* gint és el tipus glib que encapsula a int*/ 
};

#define NUM_KEYS    5000    /* número d'elements. */
#define KEY_SIZE    20      /* tamanys de la clau*/

int main(void)
{
    GHashTable *hashTable;

    gchar keys[NUM_KEYS * KEY_SIZE];    /* espai d'emmagatzematge de la taula de valors de les claus. */
    struct value values[NUM_KEYS];      /* espai per emmagatzemar la informació de claus. */
    gchar *keyPtr = keys;               /* següent posició a l'espai de valors. */
    struct value *valuePtr = values;    /* següent posició a l'espai de claus. */
    gchar keyToFind[KEY_SIZE];          /* clau a buscar, màxim de KEY_SIZE caràcters */
    int i=0;                   

    /* Crea taula hash.
      g_str_hash és una funció hash que es proporciona amb Glib
      per a claus del tipus (gchar *)
      g_str_equal és una funció que es proporciona amb Glib per verificar
      l'igualtat de dues claus del tipus (gchar *)
    */
    printf("Crea la taula\n");
    hashTable = g_hash_table_new(g_str_hash, g_str_equal);

    printf("Introdueix clau age room a la taula. Per acabar d'entrar valors prem CTRL-D\n");
    while (scanf("%s %d %d",
                 keyPtr,
                 &valuePtr->age,
                 &valuePtr->room) != EOF && i++ < NUM_KEYS) {
                 
        /* traça */
        printf("Afegeix %s %d %d a la taula\n", keyPtr, valuePtr->age, valuePtr->room);

        /* els posa a la taula */
        printf("Afegeix - g_hash_table_insert\n");
        /* afegeix clau-valor a taula hash.
          g_hash_table_insert rep tres paràmetres
          punter a la taula hash
          punter a la clau
          punter al valor
        */        
        g_hash_table_insert(hashTable, keyPtr, valuePtr);

        /* prepara els punters per a nova clau valor*/
        keyPtr += strlen(keyPtr) + 1;
        valuePtr++;
    }


    /* Ara busca per la taula. */
    printf("Busca valors a la taula. Per acabar CTRL-C\n");
    keyPtr = keyToFind;
    while (scanf("%s", keyPtr) != EOF) {
        printf("Buscant %s amb g_hash_table_lookup\n", keyPtr);
        if ((valuePtr = g_hash_table_lookup(hashTable, keyPtr)) != NULL) {
           
            /* si ha trobat diferent de nul, el mostra. */
            /* nota: això pot ser un problema, perquè no es pot distingir
               el "no trobat" del "trobat el valor nul".
               En aquests casos, cal fer servir g_hash_table_lookup_extended
            */
            printf("found %s, age = %d, room = %d\n",
                keyPtr,
                valuePtr->age,
                valuePtr->room);
        } else
            printf("not found %s\n", keyPtr);
    }
    return 0;
}


L'execució del programa anterior segueix les mateixes indicacions que al primer exemple:

albert@atenea:~/wk-c/prova-hash-glib$  hashtables
Crea la taula
Introdueix clau age room a la taula. Per acabar d'entrar valors prem CTRL-D
albert 42 192
Afegeix albert 42 192 a la taula
Afegeix - g_hash_table_insert
montse 40 201
Afegeix montse 40 201 a la taula
Afegeix - g_hash_table_insert

^D
Busca valors a la taula. Per acabar CTRL-C
kok
Buscant kok amb g_hash_table_lookup
not found kok
albert
Buscant albert amb g_hash_table_lookup
found albert, age = 42, room = 192
montse
Buscant montse amb g_hash_table_lookup
found montse, age = 40, room = 201
^C



GDBM i TDB

En el desenvolupament de SAMBA es va veure que calia un sistema de base de dades senzill del tipus hash: una taula de claus i valors.

DBM (la BD de BSD) oferia la interfase adequada, però no permetia l'accés concurrent. Aleshores, es va reescriure DBM permetent aquest accés concurrent i el resultat va ser TDB. TDB es pot utilitzar com un alternativa concurrent de DBM.

A Ubuntu es poden instal·lar des del Centre de Programari tant la versió GNU de DBM, la GDBM, com la TDB.

TDB i GDBM ocupen molt poc espai i poden ser una bona sol·lució per a implementar taules hash amb C allà on calguin, en comptes de fer servir les funcions que ofereixen la llibreria estàndard de C, GLibc, o les de la llibreria de GTK, GLib.

Una preacució a tenir en compte és que existeixen diferents formats de fitxer dbm.

La instal·ació de TDB inclou, a més  dels llibreries i dels include,  els executables següents:

tdbbackup: és una eina que es pot fer servir per fer el backup des fitxers .tdb de Samba i verificar-ne la integritat. Si troba un fitxer .tdb de Samba fet malbé i un fitxer de backup previ, aleshores restaura el fitxer anterior.
tdbdump: és una eina que fa el volcat, o dump, d'un fitxer .tdb en un format legible. A més també pot fer el volcat d'una clau específica.           
tdbtool: permet manipular fitxers .tdb. Les opcions que ofereix aquest programa són:

albert@atenea:~$ tdbtool
tdb> help
database not open

tdbtool:
  create    dbname     : create a database
  open      dbname     : open an existing database
  transaction_start    : start a transaction
  transaction_commit   : commit a transaction
  transaction_cancel   : cancel a transaction
  erase                : erase the database
  dump                 : dump the database as strings
  keys                 : dump the database keys as strings
  hexkeys              : dump the database keys as hex values
  info                 : print summary info about the database
  insert    key  data  : insert a record
  move      key  file  : move a record to a destination tdb
  store     key  data  : store a record (replace)
  show      key        : show a record by key
  delete    key        : delete a record by key
  list                 : print the database hash table and freelist
  free                 : print the database freelist
  check                : check the integrity of an opened database
  speed                : perform speed tests on the database
  ! command            : execute system command
  1 | first            : print the first record
  n | next             : print the next record
  q | quit             : terminate
  \n                   : repeat 'next' command

per exemple, puc fer

albert@atenea:~$ tdbtool
tdb> create prova.tdb
tdb> insert clau1 valor1
tdb> insert clau2 valor2
tdb> insert clau3 valor3
tdb> dump

key 5 bytes
clau1
data 6 bytes
[000] 76 61 6C 6F 72 31                                 valor1

key 5 bytes
clau2
data 6 bytes
[000] 76 61 6C 6F 72 32                                 valor2

key 5 bytes
clau3
data 6 bytes
[000] 76 61 6C 6F 72 33                                 valor3
 tdb> quit
albert@atenea:~$ tdbdump prova.tdb
{
key(5) = "clau1"
data(6) = "valor1"
}
{
key(5) = "clau2"
data(6) = "valor2"
}
{
key(5) = "clau3"
data(6) = "valor3"
}


Amb TDB

Anem a repetir el mateix cas de prova de GLibC i GLib, però implementant-lo amb l'API C de TDB. Amb GDBM seria molt semblant.

El package de TDB em proporciona eines per a examinar fitxers de bases de dades TDB. Aquest pot ser un avantatge decisiu a l'hora de fer decantar-se per TDB per a implementar les taules hash d'una aplicació. No cal dir que l'API C de TDB permet volcar, i recuperar, taules hash a, i de, fitxers.

Com en els casos anteriors, he desenvolupat el programa amb Anjuta, amb un projecte C genèric mínim. M'ha calgut tocar el fitxer makefile.am (per a l'automake) per a afegir-li la referència a la
llibreria libtdb:


## Process this file with automake to produce Makefile.in


## Created by Anjuta


AM_CPPFLAGS = \
-DPACKAGE_DATA_DIR=\""$(datadir)"\" 


AM_CFLAGS =\
-Wall\
-g


bin_PROGRAMS = hashtables_tdb


hashtables_tdb_SOURCES = \
main.c


hashtables_tdb_LDFLAGS = -ltdb 


hashtables_tdb_LDADD = 



Vet aquí el codi (consulteu API TDB)


#include <stdio.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>     /* mode_t */
#include <tdb.h>


struct value {         /* aquesta estructura és la dels valors */
    int age, room;          /* emmagatzemats a la taula */
};


#define NUM_KEYS    5000    /* número d'elements. */
#define KEY_SIZE    20      /* tamanys de la clau*/


int main() {   
    TDB_CONTEXT *tdbHashTable;
    TDB_DATA tdbKey, tdbValue;
    char keysData[NUM_KEYS * KEY_SIZE]; /* espai d'emmagatzematge de la taula de claus. */
    struct value valuesData[NUM_KEYS]; /* espai d'emmagatzematge de la taula de valors. */
    char *keyPtr = keysData;     /* següent posició a l'espai de valors. */
    struct value *valuePtr = valuesData; /* següent posició a l'espai de claus. */   
    char keyToFind[KEY_SIZE]; /* clau a buscar, màxim de KEY_SIZE caràcters */
    int i=0; /* comptador de claus afegides*/


    /* Crea taula hash.*/
    printf("Crea la taula\n");
    tdbHashTable = tdb_open("prova.tdb", 
    0,
                            TDB_CLEAR_IF_FIRST, 
    O_RDWR | O_CREAT | O_TRUNC, 
    0666);


    printf("Introdueix clau age room a la taula. Per acabar d'entrar valors prem CTRL-D\n");
    while (scanf("%s %d %d", 
 keyPtr, 
 &valuePtr->age, 
 &valuePtr->room) != EOF && i++ < NUM_KEYS) {  
        /* traça */
        printf("Afegeix %s %d %d a la taula\n", 
               keyPtr, 
               valuePtr->age, 
               valuePtr->room);


        /* els posa a la taula */
        printf("Afegeix - tdb_store\n");
        
tdbKey.dptr = keyPtr;
tdbKey.dsize = strlen(keyPtr) + 1; /* caràcter 0 de final*/

tdbValue.dptr = valuePtr;
tdbValue.dsize = sizeof(struct value);

        tdb_store(tdbHashTable, tdbKey, tdbValue, TDB_INSERT);


/* prepara els punters per a nova clau valor*/
        keyPtr += strlen(keyPtr) + 1;
        valuePtr++;
    }




    /* Ara busca per la taula. */
    printf("Busca valors a la taula. Per acabar CTRL-C\n");
    keyPtr = keyToFind;
    while (scanf("%s", keyPtr) != EOF) {
        printf("Buscant %s amb tdb_fetch\n", keyPtr);
/* prepara la clau per a la cerca*/
tdbKey.dptr = keyPtr;
tdbKey.dsize = strlen(keyPtr) + 1;   
   
        tdbValue = tdb_fetch(tdbHashTable, tdbKey);
        if (tdbValue.dptr != NULL) {
            /* si ha trobat diferent de nul, el mostra. */
    /* obté el punter al valor a partir del TDB_DATA retornat*/
    valuePtr = tdbValue.dptr;


            printf("found %s, age = %d, room = %d\n",
                   keyPtr,
                   valuePtr->age,
                   valuePtr->room);
        } else
            printf("not found %s\n", keyPtr);
    }


    tdb_close(tdbHashTable);
return (0);
}


No hi han gaires diferències amb els casos anteriors. Si de cas remarcar l'ús de l'estructura TDB_DATA, per a passar, i recuperar, les claus i els valors a, i de, les funcions de l'API.

Amb la funció tdb_open es crea físicament un fitxer prova.tdb que podrem examinar (i modificar) amb tdbtool. Tanmateix, hauria pogut fer servir l'opció TDB_INTERNAL per a crear  la taula hash només en memòria.

Una execució del programa anterior:


albert@atenea:~/wk-c/prova-hash-tdb$ hashtables_tdb 
Crea la taula
Introdueix clau age room a la taula. Per acabar d'entrar valors prem CTRL-D
albert 42 125
Afegeix albert 42 125 a la taula
Afegeix - tdb_store
montse 40 125
Afegeix montse 40 125 a la taula
Afegeix - tdb_store
artiom 6 125
Afegeix artiom 6 125 a la taula
Afegeix - tdb_store
^D
Busca valors a la taula. Per acabar CTRL-C
prova
Buscant prova amb tdb_fetch
not found prova
albert
Buscant albert amb tdb_fetch
found albert, age = 42, room = 125
montse
Buscant montse amb tdb_fetch
found montse, age = 40, room = 125
artiom
Buscant artiom amb tdb_fetch
found artiom, age = 6, room = 125
^C
albert@atenea:~/wk-c/prova-hash-tdb$ 

I, a més, tenim un fitxer prova.tdb amb la taula generada. Aquest fitxer el podem examinar amb tdbtool:

albert@atenea:~/wk-c/prova-hash-tdb$ tdbtool prova.tdb
tdb> dump

key 7 bytes
artiom
data 8 bytes
[000] 06 00 00 00 7D 00 00 00                           ....}.. 

key 7 bytes
montse
data 8 bytes
[000] 28 00 00 00 7D 00 00 00                           (...}.. 

key 7 bytes
albert
data 8 bytes
[000] 2A 00 00 00 7D 00 00 00                           *...}.. 
tdb> quit
albert@atenea:~/wk-c/prova-hash-tdb$ 

Dump fa un volcat de la taula en format de cadena. Què obtenim? pèr a cada registre de la taula obtenim: la clau i 8 bytes de dades en hexadecimal. No cal dir que 7D és 125; 06 hex. és 6 decimal; 28 hex. és 42 decimal i 2A hex, 40 decimal.


Conclusió

Evidentment, aquests petits escripts només són una aproximació a la implementació de taules hash amb GlibC, Glib i TDB.

Segurament, per a la majoria d'aplicacions n'hi ha prou i són més que suficients les funcions per a hashtables de la llibreria estàndard de C (GLibC); tanmateix, en aplicacions  GTK, la GLib hi és present i aporta funcions més flexibles -per exemple, permetent definir la funció hash- i, a més, fan servir els tipus de dades de GTK, sent consistents amb la resta de l'aplicació.
Finalment, TDB pot ser una bona tria en el cas que ens interessi poder disposar de la taula hash en un fitxer i, a més, ens proporciona eines addicionals, la tdbtool, per a manipular aquests fitxers. 

divendres, 2 de setembre del 2011

Python i Tkinter

En un post anterior vaig presentar la construcció d'interfases gràfiques amb Python i Glade. En ewl post d'avui presento la construcció d'interfaces gràfiques amb Python fent us d'una llibreria clàssica i que agermana el llenguatge amb el Tcl/Tk: em refereixo a Tkinter (o tkinter, amb el nom en minúscula, a partir de Python 3.0). Tkinter és una interface entre Python i Tk, d'aqui el nom. Tk és quelcom de diferrent de Python, però tots dos es troben a sistemes Unix. Tkinter permet a Python utilitzar Tk.

Tkinter permet desenvolupar  interfases gràfiques senzilles de forma ràpida. En aquest post faig una senzilla GUI d'exemple. Es tracta d'una pantalla que té dues caixa d'entrada, superior i inferior, i dos botons. En prémer el botó de l'esquerra la informació que hi ha a la caixa superior es mourà a la caixa inferior; i si no hi ha informació a la caixa superior es mostrarà un missatge d'avís. De forma similar, en prémer el botó de la dreta  la informació que hi ha la caixa inferior es mourà a la caixa superior; i si no hi ha  informació a la caixa inferior es mostrarà un missatge d'avís.

El codi és el següent:

#!/usr/bin/python
# coding: latin-1
#
# http://www.tutorialspoint.com/python/
# http://www.astro.washington.edu/users/rowen/TkinterSummary.html
# http://effbot.org/tkinterbook/

import tkMessageBox
from Tkinter import *
# alternativa
# import Tkinter
# Tkinter.Tk()

class App:
	def __init__(self, root):
		Label(root, text='Caixa superior').grid(row=1,column=1, ipadx=5, ipady=5, padx=5, pady=5)
		self.EntryDalt = Entry(root)
		self.EntryDalt.grid(row=1,column=2, ipadx=5, ipady=5, padx=5, pady=5)
		Label(root, text='Caixa inferior').grid(row=2,column=1, ipadx=5, ipady=5, padx=5, pady=5)
		self.EntryBaix = Entry(root)
		self.EntryBaix.grid(row=2,column=2, ipadx=5, ipady=5, padx=5, pady=5)
		Label(root, text='---------------').grid(row=3,column=1, columnspan=2, ipadx=5, ipady=5, padx=5, pady=5)
		Button1 = Button(root, text="Passa de dalt a baix", command=self.Mostra1)
		Button1.grid(row=4,column=1, ipadx=5, ipady=5, padx=5, pady=5)
		Button2 = Button(root, text="Passa de baix a dalt", command=self.Mostra2)
		Button2.grid(row=4,column=2, ipadx=5, ipady=5, padx=5, pady=5)
		
	# de dalt a baix.
	# no passa si no hi ha res a la caixa de dalt.
	def Mostra1(self):
		sEntryDalt = self.EntryDalt.get()

		if sEntryDalt == '':
			tkMessageBox.showinfo("Avís!", "No hi ha text a la caixa superior")
		else:
			# passa de dalt a baix
			self.EntryBaix.delete(0, END)		
			self.EntryDalt.delete(0, END)		
			self.EntryBaix.insert(0,sEntryDalt)

	# de baix a dalt
	# no passa si no hi ha text a la caixa de baix
	def Mostra2(self):
		sEntryBaix = self.EntryBaix.get()

		if sEntryBaix == '':
			tkMessageBox.showinfo("Avís!", "No hi ha text a la caixa inferior")
		else:
			# passa de baix a dalt
			self.EntryDalt.delete(0, END)		
			self.EntryBaix.delete(0, END)		
			self.EntryDalt.insert(0,sEntryBaix)

if __name__ == "__main__":
	root = Tk()
	root.title("Prova TK")
	App(root)	
	root.mainloop()

Uns comentaris al codi anterior,

Primer de tot, la importació de les llibreries: he utilitzat Tkinter i tkMessageBox. tkMessageBox em proporciona un popup de missatges que es pot configurar per a que sigui  d'informació, d'avís, d'error, amb botons d'OK, Cancel, Sí, no... tkMessageBox no és Tkinter, tot i que també es recolza en Tk per a proporcionar el widget de caixa de missatges.

Segon. M'en vaig al final, al main:


if __name__ == "__main__":
	root = Tk()
	root.title("Prova Tkinter")
	App(root)	
	root.mainloop()


Primer de tot, cal dir que una interface amb Tk es pot conceptualitzar com un arbre de widgets, en els que uns són fills (estan inclosos) en d'altres, fins arribar a un primer nivell on tindrem un widget arrel. El primer de tot, doncs, és obtenir  el widget arrel, amb


	root = Tk()

Ara  ja puc afegir (incloure) widgets  fills.

Un cop tinc el widget arrel, el puc configurar. Per exemple, li dono un títol "Prova Tkinter"

	root.title("Prova Tkinter")


A continuació, fent us d'una classe que anomeno App creo la interface.

	App(root)


Finalment, inicio l'interface  invocant el "bucle d'esdeveniments"

	root.mainloop()


Ara és el moment d'analitzar la classe App.

A la classe App he fet que el constructor sigui l'encarregat de col·locar els diferents widgtets a la finestra.
A més, he creat  dos mètodes Mostra1 i Mostra2 que seran la resposta als esdeveniments de prémer el botó esquerra i dret respectivament.

Vet aquí el constructor:

	def __init__(self, root):
		Label(root, text='Caixa superior').grid(row=1,column=1, ipadx=5, ipady=5, padx=5, pady=5)
		self.EntryDalt = Entry(root)
		self.EntryDalt.grid(row=1,column=2, ipadx=5, ipady=5, padx=5, pady=5)
		Label(root, text='Caixa inferior').grid(row=2,column=1, ipadx=5, ipady=5, padx=5, pady=5)
		self.EntryBaix = Entry(root)
		self.EntryBaix.grid(row=2,column=2, ipadx=5, ipady=5, padx=5, pady=5)
		Label(root, text='---------------').grid(row=3,column=1, columnspan=2, ipadx=5, ipady=5, padx=5, pady=5)
		Button1 = Button(root, text="Passa de dalt a baix", command=self.Mostra1)
		Button1.grid(row=4,column=1, ipadx=5, ipady=5, padx=5, pady=5)
		Button2 = Button(root, text="Passa de baix a dalt", command=self.Mostra2)
		Button2.grid(row=4,column=2, ipadx=5, ipady=5, padx=5, pady=5)


El que obtinc és

Els constructors dels widgets Label, Entry i Button prenen un primer paràmetre que és el widget pare del que pengen. Labels i Buttons, a més, prenen una paràmetre de configuració que, en aquest cas es refereix al text a mostrar en Labels i Buttons; els buttons, a més, prenent un tercer paràmetre de configuració que fa apunta al mètode que respondrà a l'esdeveniment per defecte del botó, que és fer-hi clic.

Com és que cada widget va al lloc que li correspon? la clau està al mètode grid() El mètode grid ens permet accedir al gestor de finestra Grid. Hi han d'altres tipus de gestor, com el gestor de finestra Pack, o el gestor de Finestra Place.

Grid és molt senzill de fer servir. La forma com ubica els widgets em recorda una mica a la maquetació amb taules en HTML. El mètode grid rep un conjunt de paràmetres. Per exemple:


Label(root, text='Caixa superior').grid(row=1,column=1, ipadx=5, ipady=5, padx=5, pady=5)


Grid conceptualitza la finestra com una graella rectangular de cel·les.  Cada cel·la pot acollir un widget. La posició d'una cel·la ve definida per la fila/columna. Les files es numeren de dalt a baix començant per l'1. Les columnes d'esquerra a dreta, començant per l'1. Puc agrupar cel·les adjacents en horitzontal i/o vertical.

Tenint en compte això, els paràmetres del Label "Caixa superior" s'expliquen fàcilment: posa el label a la cel·la de la fila 1, columna 1. L'ample de la cel·la s'ajusta automàticament al contingut. Tanmateix, per a fer que la interfase "respiri" dono un marge interior ("padding") entre el límit de la cel·la i la caixa d'entrada de 5píxels (ipadx=5, ipady=5) i un d'exterior entre cel·les, també de 5 píxels (padx=5, pady=5).

Amb la resta de widgets funciona igual. El resultat és que la meva finestra, conceptualment és una graella de dues columnes per quatre files.

A la fila 3 hi ha un exemple del que deia d'agrupar cel·les adjacents

Label(root, text='---------------').grid(row=3,column=1, columnspan=2, ipadx=5, ipady=5, padx=5, pady=5)

en aquest cas, el paràmetre columnspan em diu que la cel·la ocupa dues cel·les en horitzontal. Hi ha un paràmetre rowspan que funciona de la mateixa forma agrupant cel·les en vertical.

Per acabar, els mètodes que responen als esdeveniments Mostra1 i Mostra2 són gairebé idèntics,


	# de dalt a baix.
	# no passa si no hi ha res a la caixa de dalt.
	def Mostra1(self):
		sEntryDalt = self.EntryDalt.get()

		if sEntryDalt == '':
			tkMessageBox.showinfo("Avís!", "No hi ha text a la caixa superior")
		else:
			# passa de dalt a baix
			self.EntryBaix.delete(0, END)		
			self.EntryDalt.delete(0, END)		
			self.EntryBaix.insert(0,sEntryDalt)

	# de baix a dalt
	# no passa si no hi ha text a la caixa de baix
	def Mostra2(self):
		sEntryBaix = self.EntryBaix.get()

		if sEntryBaix == '':
			tkMessageBox.showinfo("Avís!", "No hi ha text a la caixa inferior")
		else:
			# passa de baix a dalt
			self.EntryDalt.delete(0, END)		
			self.EntryBaix.delete(0, END)		
			self.EntryDalt.insert(0,sEntryBaix)


A Mostra1, per exemple, primer de tot, s'obté el contingut del EntryDalt amb el mètode get.

		sEntryDalt = self.EntryDalt.get()


Si el valor obtingut és nul, mostra l'avís

		if sEntryDalt == '':
			tkMessageBox.showinfo("Avís!", "No hi ha text a la caixa superior")

En cas contrari, primer esborra les Entry (mètode delete(0,END))

		else:
			# passa de baix a dalt
			self.EntryDalt.delete(0, END)		
			self.EntryBaix.delete(0, END)	

I, finalment, emplena la caixa inferior

			self.EntryDalt.insert(0,sEntryBaix)


I fins aquí aquest senzill experiment per a prendre contacte amb Tkinter.

Per a més informació, podeu consultar:

La pàgina de Tkinter, http://www.python.org/topics/tkinter/doc.html
Thinking in Tkinter, http://www.ferg.org/thinking_in_tkinter/index.html
Tkinter: GUI programming with Python, http://infohost.nmt.edu/tcc/help/lang/python/tkinter.html
Python. GUI Programming (Tkinter), http://www.tutorialspoint.com/python/python_gui_programming.htm