divendres, 15 de juliol del 2011

Taules amb cantonades rodones (reprise)

En un post anterior vaig explicar com  fer caixes amb els cantons rodons amb HTML.

En aquella ocasió la solució es basava en taules HTML aplicant estils a etiquetes <table> <tr> i <td>.

En ocasions , un enfocament com l'anterior pot ser necessari, perquè hi han un munt de pàgines web que fan servir, o han fet servir, les taules per a maquetar-ne la presentació, i pot ser que ens calgui retocar-les.

Des de fa temps, però, la solució amb taules es considera inadequada. Les taules HTML només haurien de fer-se servir per presentar dades en format tabular, i formatar-les amb css.

Per a maquetar, la recomanació general és fer servir capes (etiquetes <div>). Per al cas concret de taules amb cantons rodons, vet aquí solucions possibles.

Si em cal crear els gràfics corresponents a les caixes amb els cantons rodons, faig servir la combinació de Inkscape i Gimp. Inkscape permet crear caixes amb cantons rodons, especificant mides i radis dels cantons,  aplicar diferents efectes i exportar les caixes a format png. Després, per retallar les diferents cantonades, les o tapes, o els costats, es pot fer servir el Gimp, per exemple.

OpenOffice.org/LibreOffice Draw també permet crear caixes amb els cantons rodons amb l'eina de dibuix caixa i fent, després Format-Posició i Mida i ajustant el radi a la pestanya Inclinació i Radi de la Cantonada. La pega de fer servir aquesta eina és que no disposa de la unitat "píxel", d'us comú al disseny web. Si la fem servir segurament ens caldrà fer una conversió prèvia a les unitats de treball OOo/LO. Per exemple, fer servir punts, la conversió és 3pt aprox. igual a  4px.

El cas trivial és que coneixem d'antuvi la mida que tindrà la taula. En aquest cas, n'hi ha prou amb fer-servir la caixa de background d'un div:

<div class="caixa">Compte amb no sortir-se'n dels
marges pre-establerts.<br>
<ul>
  <li>ítem 1</li>
  <li>ítem 2</li>
  <li>ítem 3</li>
  <li>ítem 4</li>
  <li>ítem 5</li>
  <li>ítem 6</li>
  <li>ítem 7</li>
</ul>
</div>

i tindré la següent definició per a la class caixa:

.caixa {
  border-style: none;
  margin: 0px;
  padding: 5px;
  width: 401px;
  background-image: url(http://localhost/albert/prova01/img/quadre401x201px.gif);
  background-repeat: no-repeat;
  height: 201px;
  font-family: Arial,Helvetica,sans-serif;
  color: #000099;
  top: 0px;
  right: 0px;
  bottom: 0px;
  left: 0px;
}

On la imatge és aquesta:


Pot ser útil  crear caixes que es redimensionin automàticament:

Aquesta és una caixa que pot  créixer en vertical. Cal fer tres capes: una capa per la tapa superior, una capa central, i una capa per a la tapa inferior.

<div class="topbox">&nbsp;</div>

<div class="midbox">exemple de caixa que port créixer en
vertical. Només cal anar afegint text per veure com creix sense haver
de fer res més.<br>
</div>

<div class="bottombox">&nbsp;</div>

Les css corresponents:

.topbox {
  background-image: url(img/top-quadre301x12px.png);
  width: 301px;
  height: 12px;
  background-repeat: no-repeat;
}
.midbox {
  padding: 5px;
  background-image: url(img/bg-quadre301x1px.png);
  width: 301px;
  font-family: Arial,Helvetica,sans-serif;
  font-size: 25px;
  background-repeat: repeat-y;
}
.bottombox {
  width: 301px;
  background-repeat: no-repeat;
  background-image: url(img/bottom-quadre301x12px.png);
  height: 12px;

Les mides de les imatges han de coincidir amb les dels div.

Les imatges de les tapes superior i inferior:

i la imatge central que és d'1 pixel d'amplada i que aconsegueix emplenar la capa en vertical amb el background-repeat: repeat-y
 

De forma similar, es pot crear una caixa capaç d'adaptar-se, fins un tamany màxim, a divrerents llargades horitzontals. Em cal una tapa a l'esquerra, una capa central i la tapa de la dreta. Aleshores cal indicar que la imatge central ha de repetir-se en horitzontal.
Com cal posar les capes una a continuació de l'altre, cal indicar que "floten":

<div class="leftbox">&nbsp;</div>

<div class="midbox2">exemple de caixa que pot créixer en
horitzontal fins un màxim de 900px. Cal vigilar perquè tindrà un màxim
de línies útils.<br>
</div>

<div class="rightbox">&nbsp;</div>

Els css corresponents són:

.leftbox {
  background-image: url(img/left-quadre12x151px.png);
  background-repeat: no-repeat;
  height: 151px;
  width: 12px;
  float: left;
}
.midbox2 {
  padding: 5px;
  background-repeat: repeat-x;
  float: left;
  font-family: Arial,Helvetica,sans-serif;
  background-image: url(img/bg-quadre1x151px.png);
  height: 151px;
  max-width: 900px;
  font-size: 40px;
}
.rightbox {
  background-image: url(img/right-quadre12x151px.png);
  background-repeat: no-repeat;
  width: 12px;
  height: 151px;
  float: left;
}

Les imatges són:




En aquest últim cas,  caldrà tenir en compte que les dimensions de la pantalla poden provocar que alguna capa passi a la fila inferior, desmuntant l'efecte de caixa. Es pot prendre la precaució d'agrupar les tres capes anteriors en una capa de dimensions suficients i que forçaria l'aparició de la barra d'scroll horitzontal si calgués.

Finalment, pot ser interessant disposar d'un mètode general per a fer caixes de mida ajustable.

El següent és una tècnica adaptada de http://www.neuroticweb.com/recursos/css-rounded-box/. Amb aquesta tècnica només cal fixar la mida horitzontal.

Com en els casos anteriors tindrem una tapa superior, una capa central i una capa de tapa inferior.  Però les tapes tindran, a més sengles capes addicionals per a fer cada una la cantonada dreta corresponent.

Vet aquí el codi. Podem ampliar el text o posar i treure <br> per veure'n els resultats.

<div class="rbbox">

    <div class="rbltop">
        <div class="rbrtop">&nbsp;</div>
    </div>

    <div class="rbcontent">
        <p>aquesta és una prova de caixa que s'adapta de forma<br>
        automàtica a la mida del seu contingut.<br>
        Adaptat de 

        <a href="http://www.neuroticweb.com/recursos/css-rounded-box/">
        http://www.neuroticweb.com/recursos/css-rounded-box/</a></p>
    </div>

    <div class="rblbot">
        <div class="rbrbot">&nbsp;</div>
    </div>
</div>


Els css són aquests:

.rbbox { background: url(img/rbbg.gif) repeat; width: 300px; margin: 0 100px; }
.rbltop { background: url(img/rbtl.gif) no-repeat top left; }
.rbrtop { background: url(img/rbtr.gif) no-repeat top right; }
.rblbot { background: url(img/rbbl.gif) no-repeat bottom left; }
.rbrbot { background: url(img/rbbr.gif) no-repeat bottom right; }
.rbcontent { margin: 5px 5px; }


I les imatges:






Finalment, dir que de tècniques per a fer cantonades rodones amb HTML i CSS  n'hi han moltes. A aquest enllaç (http://www.devwebpro.com/25-rounded-corners-techniques-with-css/) en podem trobar un bon grapat.

dimecres, 29 de juny del 2011

Com fer servir wget per a descarregar llocs web sencers

Un "truc" amb Linux. Com descarregar-se una web sencera?

En general, la resposta és amb una aranya web (o web crawler): un programa que es dedica a recorrer els enllaços d'una pàgina web de forma recursiva fins a un determinat nivell de profunditat.

Ara bé, a Linux hi ha una petita aplicació de línia de comandes que permet fer exactament això mateix: wget

Per a descarregar una web "sencera" podem fer-ho amb:

wget -r -l 0 http://www.undomini.cat/

Si fem wget --help a un terminal obtenim la llista d'opcions. En particular es veu que:

Descàrrega recursiva:
-r, --recursive       baixa de forma recursiva.
-l, --level=NOMBRE    nivell màxim de recursió (inf o 0 per infinit)

Al manual de GNU Wget trobem tota la informació sobre aquest programa.

diumenge, 26 de juny del 2011

X Jornades de Programari Lliure

Els propers 30 de juny i 1 de juliol es celebraran les X Jornades de Programari Lliure a l'Escola Tècnica Superior d'Enginyers Industrials de Barcelona. Vet aquí la informació que publiquen a la web:


X Jornades de programari Lliure

JPL - La teva trobada anual - Free Software, Open Source i Coneixement Lliure.

Informació general

Benvinguts a la desena edició de les Jornades de Programari Lliure, les X-JPL. Fidels al compromís dels organitzadors per fer d'aquestes jornades les jornades de tothom, en aquesta edició l'esdeveniment se celebrarà a l'Escola Tècnica Superior d'Enginyeria Industrial de Barcelona de la  Universitat Politècnica de Catalunya.
Les JPL tenen com a principal objectiu la difusió del programari lliure. Es debat com potenciar el seu ús a la societat en general i a la recerca, desenvolupament i docència en particular, així com donar a conèixer les activitats on intervé el programari lliure que actualment es porten a terme a les diverses universitats catalanes i també a altres sectors del país inclosa l'administració.

Desè aniversari

En ocasió d'aquests 10 anys d'existència, la temàtica de les jornades girarà al voltant de 2 eixos bàsics:
  • Repàs històric: d'alguna manera o altra volem oferir un balanç de les fites més rellevants assolides pel Programari Lliure en aquest període
  • Tendències de futur en el programari lliure: Portarem reconeguts experts en la matèria per tal que ens ajudin a discernir l'esdevenidor del programari lliure en els seus diversos àmbits (escriptori, dispositius mòbils, bases de dades, ofimàtica, temes legals, societat, educació, etc.)

Inscripció

Per assistir a les jornades és imprescindible inscriure-us-hi tot i que el registre és gratuït. Per formalitzar-lo només cal que us doneu d'alta en aquest portal a través d'aquest formulari (a partir de l'1 de juny). Recordeu que cal que passeu pel taulell de recepció per tal d'acreditar-vos.

Dates importants

  • Jornades: 30 de juny i 1 de juliol de 2011.

Si voleu estar informats

En aquests moments el programa detallat d'activitats està en preparació. Si voleu estar al dia de les darreres novetats que es vagin produïnt, teniu a la vostra disposició els següents mecanismes:
  • Us podeu subscriure (recomanable) a la llista de distribució de les jornades. Des de la llista us mantindrem informats de les darreres novetats referents a les jornades. Aquesta llista és moderada i de baix tràfic (vuit missatges l'any com a molt).
  • Podeu estar subscrits a les notícies d'aquest portal mitjançant RSS.

Informació de contacte

Envieu un correu a la secretaria de les jornades.

Organització

La X edició de les JPL està organitzada per la  Càtedra de Programari Lliure de la UPC. Els membres de l'organització podeu trobar-los aqui.

divendres, 24 de juny del 2011

CSS Box model. quirks mode vs. w3c mode

Recentment he estat involucrat en la creació de pàgines web amb crossbrowsing per a Internet Explorer 6,7,8 i FireFox 3,3.6 i 4.

Per crossbrowsing s'entén la creació de pàgines web que es vegin igual amb tots els navegadors.

És, doncs,  l'art d'eliminar les diferències de visualització que hi han entre dieferents navegadors, entre versions d'un mateix navegador o entre un mateix navegador  a diferents sistemes operatius.

Per exemple: les diferències de visualització entre Internet Explorer, FireFox i Chrome; les diferències de visualització entre Internet Explorer 6, 7,8 i 9; o les diferències de visualització entre Windows i Unix.


Un dels aspectes que dificulten el crossbrowsing és el model de caixes del CSS.

El model de caixes aplica a les capes i a un  munt d'etiquetes. El model de caixes especifica els marges, el padding o espai entre els límits  de la caixa i l'espai útil de la caixa, especifica la forma i mida de les vores de la caixa...

 
El model de caixes està definit en una especificació del WWW Consortium i l'honoren tots els navegadors importants, si més no en les seves versions actuals. És l'standard model. Ara bé, les versions antigues de l'Internet Explorer feien servir un model de caixes propi. I les versions modernes han optat per mantenir aquest model per a totes aquelles pàgines HTML que no tinguin DOCTYPE, o que el DOCTYPE defineixi la pàgina com HTML 3, com HTML 4 Transitional, o es tracti d'un document de framesets. El que es coneix com Quirqs mode.

Això ens parla de la importància de donar un DOCTYPE adequat a les pàgines que tinguem que crear.

A Internet n'hi han un munt de pàgines que provoquen que l'Internet Explorer les visualitzi en quirks mode.

Per això, cal tenir present les diferències entre el model de caixes del quirks mode i el model de caixes estàndar.

La següent imatge, agafada de http://www.456bereastreet.com/archive/200612/internet_explorer_and_the_css_box_model/, il·lustra aquestes diferències. Aquesta és una imatge clau que cal tenir present a l'hora de desenvolupar

La tècnica de desenvolupament tenint el cross-browsing en ment seguiria els següents principis:

1. desenvolupar per a un navegador base, per exemple Firefox.
2. separar tots els estils en un document css.
3. examinar el resultat en els navegadors per als que es vol garantir el crossbrowsing, per exemple Internet Explorer
4. fent us de condicionals. En el cas de Internet Explorer es disposa de les etiquetes <!--[IF IE nn ]>   .....    <![END IF] --> (amb nn versió de IE).

 Un bon motiu per a desenvolupar primer amb FireFox és que després és molt senzill associar uns CSS específics per al cross browsing amb IE fent servir les etiquetes condicionals de l'IE. En canvi, si es pren IE com navegador base, aleshores cal utilitzar condicionals amb javascript per a associar CSS a Firefox.

El codi dels condicionals amb FireFox quedaria així

<link rel="stylesheet" type="text/css" href="/css/ff_css.css"/>
<!--[IF IE 7 ]>
<link rel="stylesheet" type="text/css" href="/css/ie7_css.css"/>
<![END IF] -->
<!--[IF IE 8 ]>
<link rel="stylesheet" type="text/css" href="/css/ie8_css.css"/>
<![END IF] -->

diumenge, 29 de maig del 2011

Parseig d'XML amb C# i DOM (Mono)

Una de les guerres religioses més interessants que hi han hagut en els darrers temps en el món del programari lliure ha estat entre els partidaris i detractors de Mono.

Mono és una implementació lliure de .Net per a màquines GNU/Linux basada en l'estàndar ECMA. La batalla al voltant d'aques projecte és perquè part de les eines que s'han implementat podrien no respectar les llicències de Microsoft. Es tem que, en un moment donat, els de Redmond pordrien denunciar aquesta possible violació de llicències i provocar que tot allò que estigues desenvolupat a partir d'aquest programari no lliure de Mono quedés en una situació difícil.


Els defensors de Mono diuen que les parts conflictives són molt poques i estan ben localitzades i que programar sobre Mono no presenta perill.

No prendré partit. En tot cas, des del meu punt de vista, Mono ofereix una plataforma  alternativa als desenvolupadors de .Net. Una plataforma basada en programari i sistemes operatius lliures, amb l'extraordinari estalvi de cost de llicències que això suposa.

Per mi, doncs, Mono és una plataforma a tenir en compte pels desenvolupadors. I no només. Amb Mono sobre Linux i Apache es poden executar aplicacions  CMS com dotNetNuke, o  MojoPortal. EL trio Linux,Apache,Mono ofereix, doncs, una alternativa per al hosting amb ASP.

Mono ens porporciona eines tan interessants com els compiladors de C# i  VB.NET que poden compilar a Linux nadiu però també al bytecode del CLI i, per tant, poden produir executables aptes per Windows.

En aquest post presento com fer servir un fitxer XML com a fitxer de configuració.  NET no té, com Java, una classe per a fitxers de properties de Unix, ni fitxers .ini de Windows. És dir, fitxers amb files  nom=valor. La filosofia NET recomana fer servir XML per a les configuracions.

NET és eficient amb l'XML. Per als programadors de NET  provinents de Java l'anàlisi de fitxers XML té dos noms principals associats: DOM i SAX.

De forma molt resumida: DOM permet analitzar fitxers més aviat petits i es basa en carregar el fitxer en memòria i exposar-lo a les aplicacions com un arbre d'objectes que ens representen els elements i els atributs.

SAX, per la seva banda, és un parser que processa  un flux XML i que respon, amb funcions de callback, quan es produeixen determinats esdeveniments, per exemple l'aparició d'una etiqueta concreta o una ordre de processament. SAX és lleuger i  adequat per al processat de grans fluxos, o grans fitxers d'XML.

Doncs bé. L'API estàndar de SAX no està al framework de .NET. Existeix, això sí, una API pròpia que és, conceptualment, equivalent a SAX (diguem que el parseig basat en XmlTextReader). I també es pot trobar la implementació de SAX per a .NET, com una descàrrega externa.

En canvi, l'API estàndar DOM està implementada al framework de .NET. El que faré serà aprofitar l'API DOM de NET per a fer servir un fitxer  XML com a fitxer de configuració.

El fitxer XML és aquest:

<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE claus> 
<claus>
<clau nom="Nom1" valor="Valor1" />
<clau nom="Nom2" valor="Valor2" />
</claus>

Primer de tot, amb el MonoDevelop (instal·lable des del Centre de programari de l'Ubuntu) crearé una nova Solució del tipus aplicació de consola amb  C#.
Afegeixo la referència System.Xml i creo dues classes C#

KeyValuePair és, com el seu nom indica, una estructura de dades clau-valor

using System;
using System.Collections.Generic;
using System.Text;

namespace provaproperties
{
    class KeyValuePair
    {
        string sKey;
        string sValue;

        public KeyValuePair(string sKey, string sValue)
        {
            this.sKey = sKey;
            this.sValue = sValue;
        }

        public string getKey()
        {
            return sKey;
        }

        public string getValue()
        {
            return sValue;
        }

    } // de la class
} // del namespace

La classe Config, carrega un fitxer XML de configuració, l'analitza amb XML i el fa servir per carregar parells nom valor en una classe de col·lecció ArrayList. A més, proporciona un mètode per obtenir un valor de la llista a partir d'una clau.

Un incís: el que acabo d'implementar aquí és un diccionari. NET proporciona la classe Dictionary a System.Collections.Generic. L'objectiu del post era presentar l'ús de DOM, no implementar un diccionari. Si el que ens cal és un diccionari, la millor opció, evidentment, és fer  servir la classe del framework.

La classe config:

using System;
using System.Collections;
using System.Collections.Generic;
using System.Xml;
using System.Text;

namespace provaproperties
{
    class Config
    {
        ArrayList arrKeyValuePairs = null;
       
        public static void Main (string[] args)
        {
            Console.WriteLine ("Anàlisi de fitxer de configuració");
            Config cf = new Config("./config.xml");
            Console.WriteLine("clau: Nom1; valor: {0}", cf.getValue("Nom1"));
            Console.WriteLine("clau: Nom2; valor: {0}", cf.getValue("Nom2"));
            Console.WriteLine("clau: Nom3; valor: {0}", cf.getValue("Nom3"));   
        }
       
       
        public Config(string sXmlConfigFile)
        {
            arrKeyValuePairs = new ArrayList();
           
            /* XML de configuracions
            * <?xml version="1.0" encoding="utf-8" ?>
            * <!DOCTYPE claus>
            * <claus>
            *   <clau nom="Nom1" valor="Valor1" />
            *   <clau nom="Nom2" valor="Valor2" />
            *   ...
            * </claus>
            */

            string sClau = "";
            string sValor = "";
           
            // DOM parsing
            XmlDocument xdoc = new XmlDocument();
            xdoc.Load(sXmlConfigFile);
            XmlNodeList xnl = xdoc.GetElementsByTagName("clau");
            foreach (XmlNode node in xnl)
            {
                XmlAttributeCollection attrColl = node.Attributes;
                foreach( XmlAttribute attr in attrColl)
                {
                    string sName = attr.Name;
                    string sValue = attr.Value;

                    if (sName == "nom")
                    {
                        sClau = sValue;
                    }

                    if (sName == "valor")
                    {
                        sValor = sValue;
                    }
                }

                // afegeix les claus a l'Array
                arrKeyValuePairs.Add(new KeyValuePair(sClau, sValor));
            }
        }


        public string getValue(string sClau)
        {
            // si no el troba, torna "".
           
            string sValue = "";

            foreach (KeyValuePair item in arrKeyValuePairs)
            {
                if (item.getKey() == sClau)
                {
                    sValue = item.getValue();
                    break;
                }
            }
           
            return sValue;
        }
       
    } // de la class
}  // del namespace


I compilem. L'execució del programa proporciona l'efecte esperat:

albert@atenea:~/wk-mono/prova-properties/prova-properties/bin/Debug$ ./prova-properties.exe
Anàlisi de fitxer de configuració
clau: Nom1; valor: Valor1
clau: Nom2; valor: Valor2 
clau: Nom3; valor:

Encara un altre incís: si la intenció és fer servir l'XML com a fitxer de configuració mitjançant DOM, aleshores, el fet de carregar una ArrayList amb els parells nom valor resulta, si més no, redundant.


Una solució sense redundàncies seria crear un mètode que obtingués el valor corresponent a la clau cercada directament a partir del Document XML parsejat. Per exemple, una cosa com la següent. Un detall interessant, la doble iteració sobre els atributs. És necessària perquè l'ordre de la col·lecció d'atributs no està determinat i, si fes una sola passada, podria ser que el valor aparegués abans del nom, i no sabria identificar-lo. Vet aquí el codi:

using System;
using System.Collections;
using System.Collections.Generic;
using System.Xml;
using System.Text;

namespace provaproperties
{
    class XmlConfig
    {
        XmlDocument xdoc = null;
       
        public static void Main (string[] args)
        {
            Console.WriteLine ("Anàlisi de fitxer de configuració - millorat");
            Config cf = new Config("./config.xml");
            Console.WriteLine("clau: Nom1; valor: {0}", cf.getValue("Nom1"));
            Console.WriteLine("clau: Nom2; valor: {0}", cf.getValue("Nom2"));
            Console.WriteLine("clau: Nom3; valor: {0}", cf.getValue("Nom3"));   
        }
       
       
        public XmlConfig(string sXmlConfigFile)
        {
           
            /* XML de configuracions
            * <?xml version="1.0" encoding="utf-8" ?>
            * <!DOCTYPE claus>
            * <claus>
            *   <clau nom="Nom1" valor="Valor1" />
            *   <clau nom="Nom2" valor="Valor2" />
            *   ...
            * </claus>
            */
           
            // DOM parsing
            XmlDocument xdoc = new XmlDocument();
            xdoc.Load(sXmlConfigFile);
        }

        public string getValue(string sClau)
        {
            // si no el troba, torna "".
            string sFoundValue = "";

            // obté la llista de nodes
            XmlNodeList xnl = xdoc.GetElementsByTagName("clau");
           
            // itera pels nodes
            foreach (XmlNode node in xnl)
            {
                // obté els atributs del node
                XmlAttributeCollection attrColl = node.Attributes;
                Boolean boolTrobat = false;
               
                // itera pels atributs del node
                foreach( XmlAttribute attr in attrColl)
                {
                    // nom de l'atribut
                    string sName = attr.Name;
                    // valor de l'atribut
                    string sValue = attr.Value;

                    // si el nom de l'atribut és nom
                    if (sName == "nom")
                    {
                        // aleshores, si el valor coincideix amb la clau buscada
                        if (sValue == sClau)
                        {
                            // és que l'ha trobat
                            boolTrobat = true;
                        }
                    }
                }
                   
                // si l'ha trobat, aleshores busca el valor
                if (boolTrobat) {
                    // itera pels atributs del node un altre cop
                    foreach( XmlAttribute attr in attrColl)
                    {
                        // nom de l'atribut
                        string sName = attr.Name;
                        // valor de l'atribut
                        string sValue = attr.Value;
   
                        // si el nom de l'atribut és valor
                        if (sName == "valor")
                        {
                            // aleshores, ja el tenim
                            sFoundValue = sValue;
                            break;
                       
                        }
                    }
                }   
            } // del foreach
           
            return sFoundValue;
       
        } // del mètode       
    } // de la class
}  // del namespace


Com abans, compilem i executem. El resultat és l'esperat:

albert@atenea:~/wk-mono/prova-properties/prova-properties/bin/Debug$ ./prova-properties.exe
Anàlisi de fitxer de configuració - millorat
clau: Nom1; valor: Valor1
clau: Nom2; valor: Valor2
clau: Nom3; valor:

diumenge, 22 de maig del 2011

API C per a SQLite3

En aquest post presento un exemple de com fer servir l'API de C per a SQLite3 a Ubuntu 10.04.

L'API de C per a SQLite3 ens proporciona una interfície eficient per a programar aplicacions amb aquesta base de dades.

Per una banda crearé una base de dades nova. Fem servir l'SQLite Database Browser (es pot instal·lar des del Centre de Programari de l'Ubuntu).


Creo la 'taula1' amb tres columnes: id, del tipus integer; i valor i traduccio, les dues del tipus varchar.


Informo la taula amb algunes dades de prova:



Desenvolupo l'exemple fent servir la IDE Anjuta (si cal, es pot instal·lar des del centre de programari de l'Ubuntu). Creo un projecte C nou del tipus genèric (mínim).
Fins i tot sent un projecte "mínim" la quantitat de fitxers que genera Anjuta és important. Tanmateix, només ens caldrà modificar-ne dos: el main.c i el Makefile.am.


Li dono la ubicació del projecte i al fitxer main.c hi posem el següent codi


/*
 * main.c
 * Copyright (C) albert 2011 <stsoftlliure@gmail.com>
 *
 * c-sqlite is free software: you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * c-sqlite is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <stdio.h>
#include <sqlite3.h>
int main(void) {
    /* variables */
    sqlite3 *conn;
    sqlite3_stmt *stmtResultSet;
    int iError=0;
    int iComptador=0;
    const char *tail;
   
    /* obre  la connexió a la bd*/
    /* int sqlite3_open(
    /*     const char *filename,   /* Database filename (UTF-8) */
    /*     sqlite3 **ppDb          /* OUT: SQLite db handle */
    /* );
    /*****/
    iError = sqlite3_open("/home/albert/databases/sqlite3/prova-sqlite2/prova2.db3",
                          &conn);
    if (iError) {
        puts("No pot obrir la base de dades");
        exit(0);
    }
   
    /* crea una instrucció per a executar-la, en aquest cas un update */
    /* int sqlite3_exec(
    /*     sqlite3*,                                  /* An open database */
    /*     const char *sql,                           /* SQL to be evaluated */
    /*     int (*callback)(void*,int,char**,char**),  /* Callback function */
    /*     void *,                                    /* 1st argument to callback */
    /*     char **errmsg                              /* Error msg written here */
    /* );
    /*****/
    iError = sqlite3_exec(conn,
                          "update taula1 set traduccio=\'nova traducció\' where id=5",
                          0,0,0);
    if (iError) {
        puts("Error en fer update");
        exit(0);
    }
   
    /* fa una query i obté un "resultset" */
    /*
    /* int sqlite3_prepare_v2(
    /*     sqlite3 *db,            /* Database handle */
    /*    const char *zSql,       /* SQL statement, UTF-8 encoded */
    /*    int nByte,              /* Maximum length of zSql in bytes. */
    /*    sqlite3_stmt **ppStmt,  /* OUT: Statement handle */
    /*    const char **pzTail     /* OUT: Pointer to unused portion of zSql */
    /* );
    /*****/
    iError = sqlite3_prepare_v2(conn,
                                "select id, valor, traduccio from taula1 order by id",
                                1000,
                                &stmtResultSet,
                                &tail);
    if (iError != SQLITE_OK) {
        puts("No pot obtenir dades");
        exit(0);
    }
   
    puts("---------------------------------------");
   
    /* mostra els resultats obtinguts iterant pas a pas pel pel "ResultSet" */
    /*
        int sqlite3_step(sqlite3_stmt*);
        After a prepared statement has been prepared using either sqlite3_prepare_v2() 
        this function must be called one or more times to evaluate the statement.
        If the SQL statement being executed returns any data,
        then SQLITE_ROW is returned each time a new row of data is ready
        for processing by the caller.
        The values may be accessed using the column access functions.
        sqlite3_step() is called again to retrieve the next row of data.
       
        Result Values From A Query:
        int sqlite3_column_int(sqlite3_stmt*, int iCol);
        const unsigned char *sqlite3_column_text(sqlite3_stmt*, int iCol);
    */                   
    while(sqlite3_step(stmtResultSet) == SQLITE_ROW) {
        /* en la fila actual, obté els valors de les columnes */
        printf("%d |", sqlite3_column_int(stmtResultSet, 0)); /* id,  decimal */
        printf("%s |", sqlite3_column_text(stmtResultSet, 1)); /* valor, varchar */
        printf("%s \n", sqlite3_column_text(stmtResultSet, 2)); /* traducció, varchar */
        iComptador++;
    }
    printf("Total de registres: %d\n", iComptador);
   
    /* tanca el resultset */
    /*
    /* int sqlite3_finalize(sqlite3_stmt *pStmt);
    /* The sqlite3_finalize() function is called to delete a prepared statement.
    /*****/
    sqlite3_finalize(stmtResultSet);
   
    /* tanca la connexió */
    /*
    /* int sqlite3_close(sqlite3 *);
    /* The sqlite3_close() routine is the destructor for the sqlite3 object.
    /*****/
    sqlite3_close(conn);
   
    return (0);
}


Per a poder compilar el codi anterior, és necessari enllaçar-lo amb la llibreria de l'SQLite3. Per a fer això, modifico {nom-projecte}_LDFLAGS = -lsqlite3.
Com a alternativa, també hauria pogut generar un makefile particular.



Inclús en tindríem prou amb una instrucció del tipus:
gcc ./main.c -o prova-csqlite.exe -lsqlite3

Finalment, podem compilar i muntar amb l'Anjuta; i executem:

EXECUTING:
/home/albert/wk-c/prova-c-sqlite/c_sqlite
----------------------------------------------
1 |valor1 |traducció 1
2 |valor2 |traducció 2
3 |valor3 |traducció 3
4 |valor4 |traducció 4
5 |valor5 |nova traducció
Total de registres: 5

----------------------------------------------
Program exited successfully with errcode (0)
Press the Enter key to close this terminal ...


Com sempre, diposar de la documentació és clau per a poder reeixir en els desenvolupaments. La documentació està disponible al lloc de SQLite a Internet. D'aquesta altre adreça en podem descarregar un zip amb la documentació.

diumenge, 15 de maig del 2011

Una aproximació a Tcl

Fa molt de temps que es fan aplicacions amb interfases gràfiques a Linux. Un dels llenguatges veterans per a la construcció d'interfases gràfiques és el Tcl quan s'acompanya del toolkit gràfic Tk. El Tcl/Tk.

En propietat, el llenguatge és Tcl. Tk és un toolkit, una afegit, una extensió, que permet crear finestres, posar botons, camps de text, llistes... els widgets típics d'una aplicació de finestres fent servir el llenguatge Tcl o altres llenguates.

Entre aquests altres llenguatges que poden fer servir TK hi ha Python. Python proporciona el mòdul TkInter que permet construir interfases gràfiques fent servir widgets de forma similar a com es fa amb Tcl.

Tcl té algunes característiques que el fan molt interessant:
- és interpretat, per tant, fàcil de desenvolupar i distribuir. L'intèrpret de tcl és tclsh; A més, l'interpret per a desenvolupar aplicacions amb Tk: wish
- interacció amb les comandes de la shell: la comanda exec permet executar instruccions de la shell
- tipus de dades únic: cadena de text; estructures de dades de llistes i arrays associatius.
- estructures de control: if-else, foreach,
- procediments
- extensió senzilla del llenguatge amb C. Càrrega dinàmica de llibreries d'extensió amb la comanda load.
- la substitució de cadenes en temps d'execució és la clau per a l'utilització productiva de Tcl. Tenir clar quan utilitzar "", {}, [], ()... i les diferents substitucions que es produeixen amb unes o altres és la clau de l'èxit.


A continuació, alguns exemples amb tcl. Els poso en un script de la forma habitual a Unix.

#!/usr/bin/tclsh

#analitza els arguments d'entrada i els mostra amb foreach
puts $argv
puts "Número d'arguments: $argc"
foreach arg $argv {
puts "Argument: $arg"
}

# fa càlculs aritmètics
set a 10
set b 25
puts "$a * $b = [expr $a * $b]"

#llistes associatives que simulen arrays
set mat(1) 4
set mat(2) 3
puts "mat(1): $mat(1)"
puts "mat(2): $mat(2)"

# els índexos són cadenes.
set telefon(personal) 123456789
set telefon(movil) 666666666
set telefon(feina) 987654321
puts "telefons disponibles: [array names telefon]"
puts "Mostra tot: [array get telefon]"

# entrada senzilla per teclat
puts "quin vols veure?"
gets stdin nom
# passa a majúscules
puts "Has triat: [string toupper $nom]"
# el mostra
puts "Telefon de $nom: $telefon($nom)"

#treball amb llistes
set progs [list man gedit thunderbird firefox ls cat wc dd dc]
puts "llista: $progs"
puts "llarg de la llista: [llength $progs]"
#index de la llista comença amb 0
puts "element 3: [lindex $progs 3]"
puts "elements 2 a 6: [lrange $progs 2 6]"
puts "afegeix element al final";
set progs [lappend progs tclsh]
puts "llista: $progs"
puts "afegeix element enmig de la llista";
set progs [linsert $progs 3 wish]
puts "llista: $progs"


# obrir fitxers per llegir i esciure
#obre un fitxer per a escriure-hi (obté descriptor de fitxer per a escriptura "w")
set fdw [open prova-tcl.txt w]
puts $fdw "Escriu una línia"
puts $fdw "Escriu una altre línia"
puts $fdw "Escriu la línia del final"
close $fdw

#Llegeix el fitxer (obté descriptor de fitxer per a lectura "r")
set fdr [open prova-tcl.txt r]
while {![eof $fdr]} {
gets $fdr linea
puts $linea
}
close $fdr

En fi, tot allò que podem esperar d'un llenguatge d'script i potser alguna cosa més.

Hi ha gran quantitat de documentació en línia sobre Tcl.  Vet aquí uns quants enllaços útils:

El manual de referència de Tcl

Tcl developer site
Curs de Tcl a la Universitat de Chicago
Tclers wiki

I també uns manuals en format PDF per a descarregar i tenir-los a ma.

Tcl/Tk quick start
Tcl/Tk reference guide


Per a més endavant, un post sobre Tk.