Es mostren els missatges amb l'etiqueta de comentaris linux. Mostrar tots els missatges
Es mostren els missatges amb l'etiqueta de comentaris linux. Mostrar tots els missatges

diumenge, 11 de desembre del 2011

Lubuntu a un Hercules eCafe 800

En un post anterior vam instal·lar Ubuntu de la darrera versió Netbook Remix en un petit Hercules eCafe 800.La maquineta ha funcionat prou bé però ha estat evident, des del primer dia, que Gnome li anava gran.

La solució ha estat posar una distribució més lleugera. En aquest cas la Lubuntu. Lubuntu és una distribució que fa servir LXDE com a entorn d'escriptori i OpenBox com a gestor de finestres, amb la intenció de minimitzar el consum de recursos que calen. Vegeu també http://ca.wikipedia.org/wiki/Lubuntu. Per cert, està disponible en català.

La instal·lació no ha tingut complicacions: he descarregat la iso de http://cdimages.ubuntu.com/lubuntu/releases/11.10/release/ i he creat un pendrive d'instal·lació.

Per a fer la instal·lació m'ha estat útil connectar un monitor extern, degut a que la petita pantalla de l'eCafe no era suficient per als continguts que s'estaven mostrant. Aquest problema ja el vaig trobar en l'anterior instal·lació de Ubuntu (Gnome), i es resol igual: un cop feta la instal·lació cal afegir el fitxer xorg.conf a /etc/X11.

Un cop feta la instal·lació i fet l'ajust de pantalla, ja es pot treballar amb el netbook. Ens adonarem d'alguns canvis, per exemple en el programari instal·lat. Per mi el més cridaner ha estat trobar Chromium, el navegador open-source que ha servit de base per a Chrome, en comptes de Firefox. I també trobar Synaptic, en comptes del "Centre de Programari de l'Ubuntu".

La millora en la reproducció de vídeos i música és evident. També en la reproducció de vídeos online.

Pegues, de moment, poques. Li he instal·lat Thunderbird, LibreOffice, Skype... i el netbook els mou de forma que són útils. No està gens malament per a aquesta petita maquina.

Potser encara posaré Lubuntu a l'ordinador de sobretaula.

diumenge, 20 de novembre del 2011

Groovy i HSQLDB

Groovy aporta classes per al treball amb bases de dades que permeten un increment de la productivitat en el desenvolupament de codi d'aplicacions que en facin us.

Dit d'una altre forma: Groovy permet simplificar el codi de les aplicacions de bases de dades.  Res millor que un exemple.

En l'exercici que es proposa faré us de la base de dades HSQLDB, que és el motor de base de dades que es fa servir, per exemple,  a les suites ofimàtiques OpenOffice.org i LibreOffice.

Per a poder utilitzar HSQLDB des de Groovy cal afegir el hsqldb.jar a les llibreries que es carreguen a l'inici. En el cas que m'ocupa, un Groovy sobre Linux Ubuntu, cal revisar el fitxer $GROOVY_HOME/conf/groovy-starter.conf en el que es pot trobar el següent:

    # load user specific libraries
    load !{user.home}/.groovy/lib/*.jar

Tinc l'HSQLDB instal·lat a una carpeta apart. Per tant, per evitar duplicitats, en comptes de posar l'hsqldb.jar a $HOME/.groovy/lib, el que faig és posar en aquesta carpeta un enllaç simbòlic al jar que hi ha a la carpeta on està desplegat el motor de la base de dades.

A continuació, creo una base de dades del tipus fitxer i standalone. Cal dir que HSQLDB pot funcionar en "mode servidor" o  bé en "mode standalone". A més, també pot funcionar o bé mantenint les dades en un fitxer, o bé "mantenint" les dades en memòria (evidentment, només es "mantenen" mentre el procés és viu).

Per a crear la taula es pot fer servir, per exemple, l'OpenOffice.org/LibreOffice Base. En aquest cas caldria afegir hsqldb.jar al classpath de l'OOo/LO (eines - opcions - java)


i camí a les classes:



i, a continuació, crearia una base de dades del tipus Jdbc.



En el meu cas, la base de dades la fico a Documentes/databases/hsqldb/prova.db, per tant la URL JDBC que he de fer servir és:


jdbc:hsqldb:/home/albert/Documents/databases/hsqldb/prova.db

i el driver:

org.hsqldb.jdbc.JDBCDriver


(canviar aquesta imatge)

Això em permetrà fer servir l'assistent de l'OOo/LO per a definir la taula i els tipus de dades. Creo una taula diccionari amb tres columnes: id (enter), nom (varchar de 100) i valor (varchar de 100).


I  un cop creada la taula, la informo amb algunes dades de prova:



Probablement la classe més útil de Groovy per al treball amb bases de dades sigui groovy.sql.Sql.  Aquesta classe és una mena de navalla suïssa que proporciona una varietat de mètodes per a fer gairebé totes les tasques comuns amb taules.

La pàgina de la documentació de groovy.sql.Sql ens explica les possibilitats d'aquesta navalla suïssa.

Per a utilitzar-la  cal importar-la

import groovy.sql.Sql


A continuació puc configurar la connexió a la base de dades que he creat fa un moment:

def ds = [url:'jdbc:hsqldb:/home/albert/Documents/databases/hsqldb/prova.db', 
          user:'sa', 
          password:'', 
          driver:'org.hsqldb.jdbc.JDBCDriver']
def sql = Sql.newInstance(ds.url, ds.user, ds.password, ds.driver)


Consulto la taula que he creat amb el OOo/LO amb el potent mètode eachRow, adonem-nos com la fila en curs és accessible amb 'it':

sql.eachRow("select * from public.\"diccionari\" where \"id\" >= 3") {
    println "id = ${it.id}; nom = ${it.nom}; valor = ${it.valor}"
}


i el resultat és

albert@athena:~/Workspace/wk-groovy/prova-jdbc$ groovy provabd.groovy 
id = 3; nom = nom3; valor = valor3
id = 4; nom = nom4; valor = valor3
id = 5; nom = nom5; valor = valor5


Creo una nova taula i la informo amb dades. Cal tenir en compte algunes particularitats de l'SQL de  l'HSQLDB: els noms de les taules han d'anar precedides de l'esquema; els noms de taules, columnes i  d'altres objectes van entre cometes dobles; els literals van entre comentes simples.

sql.execute '''
create table public.\"traductor\" (
   \"id\" integer not null,
   \"catala\" varchar(100),
   \"castella\" varchar(100),
)
'''


Faig insert de dades amb diferents mètodes:

- fent servir la sintaxi amb '''
sql.execute '''
insert into public.\"traductor\"(\"id\",\"catala\",\"castella\") values (1, 'catala 1','castella 1') 
'''
println "inserta fila 1"

-  fent servir la sintaxi de preparedStatement de JDBC
def params = [2, 'catala 2', 'castella 2']
sql.execute("insert into public.\"traductor\" (\"id\", \"catala\", \"castella\") values (?, ?, ?)", params)
println "inserta fila 2"


params = [3, 'catala 3', 'castella 3']
sql.execute("insert into public.\"traductor\" (\"id\", \"catala\", \"castella\") values (?, ?, ?)", params)
println "inserta fila 3"

- fent servir la sintaxi GString
def map = [id:4, catala:'català 4', castella:'castellà 4']
sql.execute("insert into public.\"traductor\" (\"id\", \"catala\", \"castella\") values ($map.id, $map.catala, $map.castella)")
println "inserta fila 4"


Visualitzo els resultats amb eachRow, ara fent servir el paràmetre fila a la closure, en comptes d'it.

sql.eachRow("select * from public.\"traductor\"") {fila ->
    println "id = $fila.id; català = $fila.catala; castellà = $fila.castella"
}

Després d'aquestes accions, tinc les dues taules amb dades, com puc veure amb l'OOo/LO




He fet inserts, evidentment també es poden fer update, delete... fent servir execute, o millor  executeUpdate, que em retorna una llista de les files afectades.

eachRow admet paginació. He afegit algunes dades més a la taula traductor i la visualitzo, primer tota i després mostrant només quatre files a partir de la fila tres:

println "mostra la taula \"traductor\""
sql.eachRow("select * from public.\"traductor\"") {fila ->
    println "id = $fila.id; català = $fila.catala; castellà = $fila.castella"
}


println "mostra 4 files de la taula \"traductor\" a partir de la fila 3"
sql.eachRow("select * from public.\"traductor\"", 3, 4 ) {fila ->
    println "id = $fila.id; català = $fila.catala; castellà = $fila.castella"
}


El resultat és:
mostra la taula "traductor"

id = 1; català = catala 1; castellà = castella 1
id = 2; català = catala 2; castellà = castella 2
id = 3; català = catala 3; castellà = castella 3
id = 4; català = català 4; castellà = castellà 4
id = 5; català = catala 5; castellà = castella 5
id = 6; català = catala 6; castellà = castella 6
id = 7; català = catala 7; castellà = castella 7
id = 8; català = català 8; castellà = castellà 8
mostra 4 files de la taula "traductor" a partir de la fila 3
id = 3; català = catala 3; castellà = castella 3
id = 4; català = català 4; castellà = castellà 4
id = 5; català = catala 5; castellà = castella 5
id = 6; català = catala 6; castellà = castella 6

La classe groovy.sql.Sql té altres mètodes que també permeten augmentar la productivitat del desenvolupament, que s'afegeixen al fet que els scripts en Groovy no requereixen compilació. 

groovy.sql.Sql també té mètodes que retornen ResultSet de jdbc, permetent el tractament típic de Java.

El package groovy.sql també inclou la classe  DataSet que deriva de groovy.sql.Sql i que permet fer queries a la base de dades fent servir operadors i noms de camps de Groovy en comptes de crides a API JDBC i noms de taules i columnes. Un petit exemple:

Primer de tot, cal importar DataSet
import groovy.sql.DataSet

i ja el puc fer servir:

primer mostro el contingut de la taula amb eachRow d'Sql i després fa la query amb DataSet

println "mostra la taula \"traductor\""
sql.eachRow("select * from public.\"traductor\"") {fila ->
    println "id = $fila.id; català = $fila.catala; castellà = $fila.castella"
}

println "mostra valors amb id entre 3 i 7 fent servir la classe DataSet"
def traduccions = sql.dataSet("public.\"traductor\"")
def filtrat = traduccions.findAll {it."\"id\"" >= 3 && it."\"id\"" <= 7 }
filtrat.each { fila ->
    println "id = $fila.id; català = $fila.catala; castellà = $fila.castella"
}

El resultat és el següent

mostra la taula "traductor"
id = 1; català = catala 1; castellà = castella 1
id = 2; català = catala 2; castellà = castella 2
id = 3; català = catala 3; castellà = castella 3
id = 4; català = català 4; castellà = castellà 4
id = 5; català = catala 5; castellà = castella 5
id = 6; català = catala 6; castellà = castella 6
id = 7; català = catala 7; castellà = castella 7
id = 8; català = català 8; castellà = castellà 8

mostra valors amb id entre 3 i 7 fent servir la classe DataSet
id = 3; català = catala 3; castellà = castella 3
id = 4; català = català 4; castellà = castellà 4
id = 5; català = catala 5; castellà = castella 5
id = 6; català = catala 6; castellà = castella 6
id = 7; català = catala 7; castellà = castella 7

En aquest exemple les particularitats de HSQLDB pel que fa als noms de taules i columnes entre cometes  no permeten aprofitar la simplificació de fer servir els noms de les columnes com si fossin noms de camps Groovy de l'objecte it. Però, si més no, es permet veure l'ús de l'operador && per a construir el "where" del Dataset

I finalment, per acabar...

// tanca la connexió
sql.close()




divendres, 28 d’octubre del 2011

Com instal·lar XAMPP a Ubuntu.

A l'hora de fer presentacions i demos d'aplicacions web, pot ser molt útil disposar d'una versió de la demo en un servidor web en localhost, potser en una clau usb.

També pot ser molt útil un servidor web d'aquest tipus a l'hora de preparar sessions de formació.

És possible trobar per Internet diverses distribucions que d'aquest tipus que combinen Apache amb  MySQL i PHP, tant sobre Linux (LAMP), com sobre Windows (WAMP), per exemple XAMPP o MoWeS Portable.

MoWeS Portable empaqueta, a més de la plataforma AMP, altres aplicacions que utilitzen aquesta infraestructura, com Joomla! o Drupal. Ara bé, MoWeS Portable només està disponible per a Windows.

XAMPP, en canvi, s'ofereix per a Linux i Windows.

En el post d'avuí el que faig és instal·lar el XAMPP a un netbook Linux.

No cal dir que la plataforma AMP es pot instal·lar des del Centre de Programari de l'Ubuntu. Però la instal·lació de XAMPP és encara més senzilla.

Dit i fet. El primer pas és descarregar-se el paquet d'instal·lació. El  puc trobar en aquesta adreça:  http://sourceforge.net/projects/xampp/files/BETAS/xampp-linux-1.7.7.tar.gz/download. Són uns 77MB.

Aquesta versió incorpora:   Apache, MySQL, PHP amb PEAR, Perl, ProFTPD, phpMyAdmin, OpenSSL, GD, Freetype2, libjpeg, libpng, gdbm, zlib, expat, Sablotron, libxml, Ming, Webalizer, pdf class, ncurses, mod_perl, FreeTDS, gettext, mcrypt, mhash, eAccelerator, SQLite i IMAP C-Client.

En particular Apache 2.2.21, MySQL 5.5.16, PHP 5.3.8, i phpMyAdmin 3.4.5.

Per instal·lar  n'hi ha prou amb desempaquetar el tar.gz. Cal tenir en compte que als fitxers d'instal·lació s'assumeix que la home del XAMPP serà /opt/lampp. Això NO vol dir que només es pugui instal·lar en aquesta carpeta. Això l'únic que vol dir és que caldrà que /opt/lampp sigui la "home" del XAMPP i això es podrà aconseguir amb un enllaç simbòlic des de /opt/lampp al directori d'instal·lació que triï.

Si la carpeta de destinació del XAMPP es troba en una clau usb caldrà que aquesta clau estigui formatada amb un sistema de fitxers que permeti els enllaços simbòlics, per exemple  ext2,ext3 o ext4.

A l'hora de fer l'enllaç simbòlic caldrà tenir en compte el punt de muntatge del dispositiu. Per exemple, al meu netbook amb Ubuntu 11.04, el punt de muntatge és /media.

Per a poder instal·lar correctament el XAMPP caldran, a més, permisos de root. I també per a posar en marxa i aturar els diversos components de la distribució.

Tenint en compte tot l'anterior, instal·lo el XAMPP a la meva carpeta home, on he deixat el tar.gz:

sudo tar xvfz xampp-linux-1.7.7.tar.gz -C .

Amb la ordre anterior es crea una carpeta lampp amb els dversos components del XAMPP.

Ara creo l'enllaç simbòlic:

sudo ln -s /home/albert/lampp /opt/lampp

I engego per primer cop el servidor. Aquesta primera arrencada acaba d'instal·lar i configurar els components.

albert@athena:~$ sudo /opt/lampp/lampp start
Starting XAMPP for Linux 1.7.7...
XAMPP: Starting Apache with SSL (and PHP5)...
XAMPP: Starting MySQL...
XAMPP: Starting ProFTPD...
XAMPP for Linux started.
albert@athena:~$ 

I per revisar la nova instal·lació en tinc prou amb obrir un navegador i apuntar-lo al localhost:




El directori arrel del servidor web es troba a /opt/lampp/htdocs (en el meu cas, mercès a l'enllaç simbòlic, és /home/albert/lampp/htdocs).

Per aturar el XAMPP puc fer:


albert@athena:~$ sudo /opt/lampp/lampp stop
Stopping XAMPP for Linux 1.7.7...
XAMPP: Stopping Apache with SSL...
XAMPP: Stopping MySQL...
XAMPP: Stopping ProFTPD...
XAMPP stopped.
albert@athena:~$ 

I per a eliminar la instal·lació sense deixar rastres, amb el XAMPP aturat, n'hi ha prou amb fer:

sudo rm -r lampp

És possible engegar i aturar individualment els diversos components del XAMPP. Examinant el fitxer $HOME/lampp/lampp es poden  trobar les diverses opcions disponibles. La forma d'invocar-les, en general, és: 

sudo /opt/lampp/lampp opció

dimecres, 10 d’agost del 2011

Retardador de subtítols SubRip (again). BeanShell version.

El mateix retardador de subtítols del post anterior, ara fet amb BeanShell.

Suposant que guardo l'script al fitxer delayer.bsh i que tinc el fitxer prova.srt del post anterior, aleshores puc retardar l'aparició dels subtítols amb la següent línia de comandes:

./delayer.bsh prova.srt output.srt 10000

Vet aquí el codi:

#!/usr/bin/bsh
 

import java.text.DateFormat;
import java.text.SimpleDateFormat;

// obté els arguments
args = this.interpreter.get("bsh.args");

// verifica que té 3 arguments
if (args.length != 3 ) {
    sUsage = "Usage:\tdelayer filein.srt fileout.srt delay" +
             "\n\texample 1: delayer filein.srt fileout.srt 10000 --> delays 10 seconds" +
             "\n\texample 2: delayer filein.srt fileout.srt -10000 --> advances 10 seconds";

    print("incorrect number of arguments");
    print(sUsage);
    System.exit(1);
}

// calcula el delta
lDelta = Long.parseLong(args[2]);

// prepara el SimpleDateFormat
sdf = new SimpleDateFormat("HH:mm:ss,SSS");

// obre els fitxers args[0] per a lectura i args[1] per a escriptura
fFileIn = new FileReader(args[0]);
fFileOut = new FileWriter(args[1]);
brFileIn = new BufferedReader(fFileIn);
brFileOut = new BufferedWriter(fFileOut);

while((sLine = brFileIn.readLine()) != null) {
    sTokens= sLine.split("-->");   // mètode split() milor que StringTokenizer des de JDK1.4.
    if (sTokens.length == 2) {
    // si té dos elements, és la línia de temps

    // els "pela"
    sTempsInici = sTokens[0].trim();
    sTempsFi = sTokens[1].trim();

    // els converteix a Date
    dTempsInici = sdf.parse(sTempsInici);
    dTempsFi = sdf.parse(sTempsFi);

    // Els suma el retard
    dTempsIniciDelayed = new Date(dTempsInici.getTime() + lDelta);
    dTempsFiDelayed = new Date(dTempsFi.getTime() + lDelta);

    // converteix els Date a String amb el mateix SimpleDateFormat
    sTempsIniciDelayed = sdf.format(dTempsIniciDelayed);
    sTempsFiDelayed = sdf.format(dTempsFiDelayed);

    // Munta la línia
    sLine = sTempsIniciDelayed + " --> " + sTempsFiDelayed;
    }

    // escriu la línia
    brFileOut.write(sLine);
    // i salta de línia
    brFileOut.newLine();
}

// tanca fitxers i buffered readers / writers
brFileOut.close();
brFileIn.close();
fFileIn.close();
fFileOut.close();

// i acaba aquí


Molt poques coses a dir. Si de cas destacar que:

No he declarat els tipus de cap variable.

No ha calgut importar les classes de java.io ni java.util.En canvi sí les de java.text

Us de

// obté els arguments
args = this.interpreter.get("bsh.args");


per obtenir els arguments de la línia de comandes.

Evidentment, la versió amb Python i optparse era més potent funcionalment pel que fa al tractament dels arguments d'entrada. Ara bé, a la simplicitat en aquest cas juga a favor i fa que l'argument de retard negatiu es tracti de forma més natural que amb la solució Python.

Us del format HH:mm:ss,MMM (hores de  0 a 23:minuts:segons,milisegons) al SimpleDateFormat, tant per a parsejar strings com per a formatar dates.

I finalment, l'us d'Split, en comptes del clàssic StringTokenizer per a obtenir els temps d'inici i final de presentació dels subtítols.

Què és més eficient? Python o BeanShell? Per a aquest cas, ho deixo en empat. Per mi ha estat més fàcil BeanShell que Python, però crec que això respon al fet que estic molt més familiaritzat amb Java.

El proper pas podria ser escriure el mateix script en Groovy. Sospito que només caldrà tocar el pas d'arguments i poca cosa més.

dilluns, 8 d’agost del 2011

Un retardador de subtítols SubRip amb Python

EL cap de setmana passat em vaig dedicar a buscar subtítols en anglès per a pel·lícules en anglès. Va molt bé per a l'aprenentatge i la pràctica de l'idioma el poder veure la pel·lícula en versió original i, a l'hora, poder llegir el que estan dient en el mateix idioma. I després, tornar a veure la pel·lícula sense subtítols. Són tècniques d'aprenentatge.

El cas és que vaig trobar alguns subtítols però als posar-los al VLC amb la pel·lícula corresponent resultava que no estaven sincronitzats amb la imatge. El VLC permet adelantar o retardar el temps d'aparició dels subtítols, però és poc pràctic, ja que cal sincronitzar cada cop que obres la pel·lícula. O, almenys, no vaig veure com guardar la sincronització; i amb el reproductor de DVD del menjador és pitjor perquè no tinc la  possibilitat de sincronitzar.

Per tant, vaig decidir que la solució era fer un retardador per als subtítols i així generar fitxers de subtítols per a les pel·lícules que tinc i que estiguin correctament sincronitzats amb la imatge.


En el post d'avui, doncs,programo una petita aplicació amb python (versió 2.6.5, un pel antiga, però és la que tinc instal·lada a l'Ubuntu) que servirà per retardar o avançar el moment d'aparició de subtítols en un fitxer de subtítols del tipus SubRip (extensió .srt).

La WikiPedia diu del format SubRip qu és "probablement, el més senzill dels formats de subtítols". Els fitxers SubRip amb extensió .srt, són de text pla. El format de temps emprat és "hores:minuts:segons,milisegons". El separador decimal és una coma, en comptes d'un punt perquè l'especificació original és francesa. El salt de línia és, tot sovint,CR+LF. Els subtítols es numeren en seqüència, començant per 1. Els subtítols se separen per una línia en blanc.

En resum:

Número del subtítol
temps d'inici --> temps de fi
text del subtítol (una o més línies) 
Línia en blanc.

un exemple de fixer SubRip (prova.srt)

1
00:00:20,000 --> 00:00:24,400
Altocumulus clouds occur between six thousand

2
00:00:24,600 --> 00:00:27,800
and twenty thousand feet above ground level.

 Doncs bé, amb el següent script Python podré retardar o avançar l'aparició dels subtítols.

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

# L'objectiu del programa és retardar o adelantar el temps d'aparició del subtítol
# el programa pren tres arguments: fitxer original, fitxer de sortida i
# temps expressat en mili segons a avançar o retardar (un negatiu, vol dir avançar)
# El fitxer de sortida serà un fitxer .srt igual a l'original, però amb tots els
# temps retardats en la quantitat de mili segons indicada

import optparse 
from datetime import datetime
from datetime import timedelta

def main():
    # analitza els arguments rebuts
    usage = "\t%prog filein.srt fileout.srt delay"
    usage = usage + "\n\texample 1: %prog filein.srt fileout.srt 10000 --> delays 10 seconds"
    usage = usage + "\n\texample 2: %prog filein.srt fileout.srt -- -10000 --> advances 10 seconds"
    parser = optparse.OptionParser(usage)
    (options, args) = parser.parse_args()

 
    # if args != 3 (fitxer d'entrada, fitxer de sortida, delay) aleshores error
    if len(args) != 3:
        parser.error("incorrect number of arguments")

    # calcula el delta
        delta =  timedelta(milliseconds=int(args[2]))

    # obre el fitxer per llegir-lo
    f1 = open(args[0])
    f2 = open(args[1], "w")
    for line in f1:
            tokens = line.split(" --> ")
            if len(tokens) == 2:
            
        # strip() --> trim spaces
           
        TempsInici = datetime.strptime(tokens[0].strip(), "%H:%M:%S,%f") + delta
           
        TempsFi = datetime.strptime(tokens[1].strip(), "%H:%M:%S,%f") + delta

           
        # multilínia amb "\"
           
        line = \
           
        str(TempsInici.hour).zfill(2) + ":" + \
           
        str(TempsInici.minute).zfill(2) + ":" + \
           
        str(TempsInici.second).zfill(2) + "," + \
           
        str(int(TempsInici.microsecond / 1000)).zfill(3) + " --> " + \
           
        str(TempsFi.hour).zfill(2) + ":" + \
           
        str(TempsFi.minute).zfill(2) + ":" + \
           
        str(TempsFi.second).zfill(2) + "," + \
           
        str(int(TempsFi.microsecond/ 1000)).zfill(3) + "\r\n"
   
        
            f2.write(line)
    

    f1.close()
    f2.close()

#main
if __name__ == "__main__":
    main()


El programa és trivial, però en faré alguns comentaris.

Us d'optparse per a fer el tractament dels arguments d'entrada.  A partir de la versió 2.7 aquest mòdul es considera obsolet i cal fer servir argparse. Ara bé, la forma com funcionen tots dos mòduls és similar. A destacar com es fa el missatge d'us, i com es tornen en un diccionari les opcions (que en aquest cas, no n'hi han) i en un array els arguments posicionals (fitxer-d'entrada, fitxer de sortida, retard).

A destacar com passar arguments negatius (en aquest cas, això vol dir avançar l'aparició dels subtítols) fent servir  "--". El exemple que acompanya l'explicació de l'ús és aclaridor:

delayer.py entrada.srt sortida.srt -- -10000

Avança l'aparició dels subtítols 10 segons

En cas de no disposar dels arguments suficient, finalitza l'execució de l'script amb parser.error

Immediatament que ha tractat els arguments d'entrada, procedeix a calcular el retard amb:

delta =  timedelta(milliseconds=int(args[2]))

A continuació obre el fitxer original per a lectura i el fitxer de sortida.

La iteració línia a línia pel fitxer original es senzilla amb la potent sintaxi del for:

for line in f1:

Per a cada línia, verifica si es pot dividir amb -->. Les línies que tinguin --> seran les de temps i les que cal tractar.

El càlcul dels nous temps es fa amb el parell de línies

TempsInici = datetime.strptime(tokens[0].strip(), "%H:%M:%S,%f") + delta
TempsFi = datetime.strptime(tokens[1].strip(), "%H:%M:%S,%f") + delta

A destacar l'us de strptime  que converteix una cadena en un objecte datetime fent servir una cadena de format. El format "%H:%M:%S,%f" correspon a  hora:minut:segon,milisegons. El mètode strip(), per la seva part, elimina espais en blanc per davant i pel darrera de la cadena, com el trim de java.

Finalment, els camps útils dels datetime TempsInici i TempsFi es reordenen i s'ajunten en una cadena de text en la següent línia

line = \
str(TempsInici.hour).zfill(2) + ":" + \
str(TempsInici.minute).zfill(2) + ":" + \
str(TempsInici.second).zfill(2) + "," + \
str(int(TempsInici.microsecond / 1000)).zfill(3) + " --> " + \
str(TempsFi.hour).zfill(2) + ":" + \
str(TempsFi.minute).zfill(2) + ":" + \
str(TempsFi.second).zfill(2) + "," + \
str(int(TempsFi.microsecond/ 1000)).zfill(3) + "\r\n"



Aquesta instrucció llarga es divideix en vàries línies fent servir \.
Els camps numèrics es converteixen a cadena amb str().
El mètode de cadena zfill(longitud) zero fill posa zeros per l'esquerra fins a que la cadena té la longitud indicada.
Els datetime no tenen un camp millisecond, però sí que el tenen microsecond. aleshores divideixo aquest camp per 1000 i el converteixo a enter amb int(). Finalment afegeixo un CR+LF

Per acabar, fora de l'if, s'escriu al fitxer de sortida la línia. Si era una línia amb --> haurà estat processada, si no, es manté com a l'original.

Es tanquen els fitxers i s'invoca el main. Aquest if __name__ == "__main__":  és un "truc" de python que permet que aquest script pugui ser utilitzat com a llibreria des d'un altre script: en aquest cas, la variable __name__ prendria un valor diferent de __main__.

dissabte, 30 de juliol del 2011

El calendari en mode text de l'Ubuntu Linux

Hi han molts bons programes d'agenda i de calendari. Amb Linux (Ubuntu) n'hi ha un en mode text que ja ve de fàbrica: Es tracta del programa calendar.

calendar és un altre dels molts petits programes que es poden trobar als sistemes UNIX que fan una cosa senzilla, que la fan bé, i que es poden ajuntar amb altres petits programes per a realitzar tasques útils per a l'usuari. La tradicional i efectiva filosofia UNIX, ni més ni menys.

N'hi ha prou amb crear el fitxer calendar a la carpeta .calendar i afegir-hi línies amb les dates que ens interessin amb el format data <tabulació> descripció de la data. Finalment, si afegeixo calendar al fitxer .bashrc sempre que obri una finestra de terminal em revisarà el calendari.

Una altre forma d'utilitzar aquest petit programa podria ser invocant-lo des d'una aplicació que fem servir de forma habitual, potser una macro d'OpenOffice.org/LibreOffice o des d'un menú de llançament d'aplicacions fet amb zenity.

El que ve a continuació és la traducció lliure de la pàgina del manual de calendar.

Us:

calendar [-ab] [-A num] [-B num] [-l num] [-w num] [-f fitxer_de_calendari] [-t [[[cc]aa][mm]]dd]

Descripció:

Aquesta utilitat busca al directori actual, o al directori especificat a la variable d'entorn CALENDAR_DIR, un fitxer amb el nom calendar i mostra les línies que comencen amb la data d'avui i demà. Els divendres es mostren els events de tot el cap de setmana.

Les opcions són:

-A num  Mostra els propers esdeveniments d'avui i dels propers num dies. Per defecte és un dia.

-a      Processa els fitxers “calendar” de tots els usuaris i els envia un corru amb els resultats a cadascun d'ells. requereix privilegis de súper-usuari.

-B num  Mostra els fets esdevinguts entre d'avui i els passats num dies.

-b      Força mode de càlcul especial de dates per als calendaris KOI8.

-l num  Mostra els propers esdeveniments d'avui i dels propers num dies. Per defecte és un dia.

-w num  Mostra els propers esdeveniments d'avui i dels propers dos dies, però només si avui és divendres. És dir, mostra els esdeveniments del cap de setmana.

-f fitxer_de_calendari    fa servir fitxer_de_calendari en comptes del fitxer per defecte calendar.

-t [[[cc]aa][mm]]dd    fa servir la data especificada per cc, centúria; aa, any; mm, mes; dd, dia; en comptes de la data d'avui.

Les línies poden començar amb mes i dia. Si estan ben definides les característiques locals del sistema es poden fer servir els noms dels mesos i els dies de la setmana.

Un asterisc ('*') vol dir cada mes.
Un dia sense mes vol dir aquell dia cada setmana
Un mes sense dia vol dir el primer dia d'aquell mes.
Dos números volen dir mes i dia.
les línies que comencen amb un caràcter de tabulació es refereixen a l'última data entrada, permetent d'aquesta forma línies d'especificacions múltiples per a una data
Els dies de la setmana poden  anar seguits de “-4” ... “+5” (àlies last, first, second, third, fourth) per esdeveniments amb data variable del tipus “el darrer dilluns d'abril”.
El diumenge de pasqua és 'Easter' en anglès. Es pot posar un àlies fent Easter=àlies i fer servir l'àlies

Per conveni, les dates seguides d'un asterisc (‘*’) no són fixes, és dir, canvien d'any en any.

Les descripcions del dia inicien després del primer caràcter de tabulació de la línia. Si una línia no té caràcter de tabulació, no es mostra. Si el primer caràcter d'una línia és una tabulació aleshores es tracta de la continuació de la descripció prèvia.

El calendari és preprocessa amb el preprocessador de C, cpp, permetent la inclusió de fitxers compartits com poden ser, per exemple, les vacances de l'empresa, el calendari de reunions, o l'agenda de cursos.

Si el fitxer compartit no es referencia per una ubicació absoluta aleshores es mira primer al directori actual (o home) i després a /etc/calendar, i finalment a /usr/share/calendar.

Les línies buides o protegides amb comentaris amb la sintaxi de C son ignorades.

On busca el calendari?
calendar              fitxers al directori actual.
~/.calendar           directori ocult ubicat al directori de l'usuri.
~/.calendar/calendar  fitxers que fa servir si no hi un fitxer calendar al directori actual.
~/.calendar/nomail    si el fitxer nomail existeix, no s'envia el calendari del dia a aquel usuari (opció -a).

Els següents fitxers es poden trobar a /usr/share/calendar i contenen llistes d'efemèrides
calendar.all          fitxers calendar nacionals i internacionals.
calendar.birthday     dates de naixement i decés de personatges famosos.
calendar.christian    calendari cristià (però no està de més revisar-ne les dates).
calendar.computer     efemèrides rel·lacionades amb el món de la informàtica.
calendar.croatian     calendari croata.
calendar.discordian   el calendari de la discòrdia.
calendar.fictional    dates i efemèrides fantàstiques i de ficció .
calendar.french       calendari francès.
calendar.german       calendari alemany.
calendar.history      miscel·lània d'efemèrides.
calendar.holiday      altres festes.
calendar.judaic       calendari jueu.
calendar.music        dates del món del rock and roll.
calendar.openbsd      esdeveniments rel·lacionats amb el bsd.
calendar.pagan        festes paganes.
calendar.russian      calendari rus.
calendar.space        història de cursa espacial.
calendar.ushistory    esdeveniments històrics dels EUA.
calendar.usholiday    festes dels EUA.
calendar.world        esdeveniments mundials.

Exemple (\t és el caràcter de tabulació):

LANG=C
Easter=DiumengePasqua
#include <calendar.usholiday>
#include <calendar.birthday>

6/15\tJune 15 (Si és ambigu, es prem mes/dia).
Jun. 15\tJuny 15.
15 June\tJuny 15.
Thursday\tCada dijous.
June\tCada primer de juny.
15 *\tEl 16 de cada mes.

May Sun+2\tsegon diumenge de maig (Muttertag)
04/SunLast\túltim diumenge d'abril,
\tentra l'horari d'estiu a europa
Easter\tdiumenge de pasqua
DiumengePasqua\tdiumenge de pasqua (amb àlies)
Easter-2\tdivendres sant (2 dies abans de diumenge de pasqua)
DiumengePasqua\t-2\tdivendres sant(com abans, però amb àlies)
Paskha\tPasqua ortodoxa


Un altre exemple, més senzill:

#include <calendar.usholiday>
#include <calendar.russian>
#include <calendar.discordian>
#include <calendar.christian>
#include <calendar.computer>
#include <calendar.birthday>

07/31    últim dia de les vacances d'estiu 2011
08/01    Sant Tornem-Hi


El resultat avui és (30 de juliol):


dissabte, 16 de juliol del 2011

Fer servir zenity per a construir scripts bash amb interfície gràfica

La creació d'scripts de shell és una tasca habitual en l'administració i explotació de sistemes Unix. En algunes ocasions pot ser que calgui la interacció amb l'usuari. Per exemple, la presentació de menús d'opcions o l'usuari ha de triar què vol fer.

A Linux Ubuntu hom pot trobar eines com el programa Dialog que permet crear elements de interfície gràfica com caixes de text, llistes, botons de sel·lecció... en mode de text. Dialog es feia servir, per exemple, en els instal·ladors de moltes distribucions.

Una opció alternativa és fer servir Zenity. Zenity és la re-escriptura de GDialog, que al seu torn era la versió GNOME de Dialog. Zenity proporciona una forma senzilla de crear interfícies gràfiques per a scripts.

Zenity es pot instal·lar des del Centre de Programari de l'Ubuntu, si cal.

Anem a fer alguns experiments amb aquesta eina. Primer de tot cal conéixer la versió que tenim instal·lada i les opcions.

La versió s'obté amb zenity --version. En el meu cas és la 2.30.

Les diferents opcions s'obtenen amb: zenity --help-all a la línia d'ordres i el resultat és:

Forma d'ús:
  zenity [OPCIÓ...]

Opcions d'ajuda:
  -h, --help                            Mostra les opcions d'ajuda
  --help-all                            Mostra totes les opcions d'ajuda
  --help-general                        Mostra les opcions generals
  --help-calendar                       Mostra les opcions del calendari
  --help-entry                          Mostra les opcions de l'entrada de text
  --help-error                          Mostra les opcions d'error
  --help-info                           Mostra les opcions d'informació
  --help-file-selection                 Mostra les opcions del selector de fitxers
  --help-list                           Mostra les opcions de llistes
  --help-notification                   Mostra les opcions de la icona de notificació
  --help-progress                       Mostra les opcions del progrés
  --help-question                       Mostra les opcions de preguntes
  --help-warning                        Mostra les opcions d'avisos
  --help-scale                          Mostra les opcions de l'escala
  --help-text-info                      Mostra les opcions de text informatiu
  --help-misc                           Mostra les opcions miscel·lànies
  --help-gtk                            Mostra les opcions del GTK+

General options
  --title=TÍTOL                         Estableix el títol del diàleg
  --window-icon=CAMÍ D'ICONA            Estableix la icona de la finestra
  --width=AMPLADA                       Estableix l'amplada
  --height=ALÇADA                       Estableix l'alçada
  --timeout=TEMPS D'ESPERA              Estableix el temps d'espera del diàleg (en segons)

Calendar options
  --text=TEXT                           Estableix el text del diàleg
  --day=DIA                             Estableix el dia del calendari
  --month=MES                           Estableix el mes del calendari
  --year=ANY                            Estableix l'any del calendari
  --date-format=PATRÓ                   Estableix el format de la data de tornada

Text entry options
  --text=TEXT                           Estableix el text del diàleg
  --entry-text=TEXT                     Estableix el text de l'entrada
  --hide-text                           Amaga el text de l'entrada

Error options
  --text=TEXT                           Estableix el text del diàleg
  --no-wrap                             No habilites l'ajustament del text

Info options
  --text=TEXT                           Estableix el text del diàleg
  --no-wrap                             No habilites l'ajustament del text

File selection options
  --filename=NOM DE FITXER              Estableix el nom del fitxer
  --multiple                            Permet la selecció de múltiples fitxers
  --directory                           Activa la selecció de només directoris
  --save                                Activa el mode d'estalvi
  --separator=SEPARADOR                 Estableix el caràcter de separació de la sortida
  --confirm-overwrite                   Confirmeu la selecció del fitxer si el nom del fitxer ja existeix
  --file-filter=PATRÓ 1 PATRÓ 2 ...     Estableix un filtre per al nom de fitxer

List options
  --text=TEXT                           Estableix el text del diàleg
  --column=COLUMNA                      Estableix la capçalera de la columna
  --checklist                           Utilitza quadres de verificació per a la primera columna
  --radiolist                           Utilitza botons de grup per a la primera columna
  --separator=SEPARADOR                 Estableix el caràcter de separació de la sortida
  --multiple                            Permet la selecció de múltiples files
  --editable                            Permet canvis al text
  --print-column=NOMBRE                 Imprimeix una columna específica (el valor per defecte és 1. Es pot usar 'ALL' per a imprimir totes les columnes)
  --hide-column=NOMBRE                  Amaga una columna específica
  --hide-header                         Oculta les capçaleres de la columna

Notification icon options
  --text=TEXT                           Estableix el text de la notificació
  --listen                              Espera ordres de l'entrada estàndard

Progress options
  --text=TEXT                           Estableix el text del diàleg
  --percentage=PERCENTATGE              Estableix el percentatge inicial
  --pulsate                             Barra de progrés parpellejant
  --auto-close                          Tanca el diàleg quan s'arribi al 100%
  --auto-kill                           Mata el procés para si es prem el botó de cancel·lar

Question options
  --text=TEXT                           Estableix el text del diàleg
  --ok-label=TEXT                       Estableix l'etiqueta del botó D'acord
  --cancel-label=TEXT                   Estableix l'etiqueta del botó Cancel·la
  --no-wrap                             No habilites l'ajustament del text

Warning options
  --text=TEXT                           Estableix el text del diàleg
  --no-wrap                             No habilites l'ajustament del text

Scale options
  --text=TEXT                           Estableix el text del diàleg
  --value=VALOR                         Estableix un valor inicial
  --min-value=VALOR                     Estableix el valor mínim
  --max-value=VALOR                     Estableix el valor màxim
  --step=VALOR                          Estableix el valor dels augments
  --print-partial                       Imprimeix valors parcials
  --hide-value                          Amaga el valor

Text information options
  --filename=NOM DE FITXER              Obre un fitxer
  --editable                            Permet canvis al text

Miscellaneous options
  --about                               Quant a zenity
  --version                             Imprimeix la versió

Opcions del GTK+
  --class=CLASS                         Program class as used by the window manager
  --name=NAME                           Program name as used by the window manager
  --screen=SCREEN                       X screen to use
  --sync                                Make X calls synchronous
  --gtk-module=MODULES                  Load additional GTK+ modules
  --g-fatal-warnings                    Make all warnings fatal

Opcions de l'aplicació:
  --calendar                            Mostra el diàleg de calendari
  --entry                               Mostra el diàleg d'entrada de text
  --error                               Mostra el diàleg d'error
  --info                                Mostra el diàleg d'informació
  --file-selection                      Mostra el diàleg de selecció de fitxers
  --list                                Mostra el diàleg de llista
  --notification                        Mostra una notificació
  --progress                            Mostra el diàleg d'indicació de progrés
  --question                            Mostra el diàleg de pregunta
  --warning                             Mostra el diàleg d'avís
  --scale                               Mostra el diàleg d'escala
  --text-info                           Mostra el diàleg de text informatiu
  --display=DISPLAY                     X display to use


Anem a provar-los un per un:

Mostrar el diàleg de calendari:
zenity --calendar




Mostrar una caixa d'entrada de text
zenity --entry --text="Entra un text de prova"



El següent script mostra com obtenir el text introduït

#!/bin/bash

textPregunta="Text de la pregunta"
textPredet="Text predeterminat a l'entrada"
textTitol="Títol de la finestra"

resp=$(/usr/bin/zenity --entry --text="$textPregunta" --entry-text="$textPredet" --title="$textTitol" --width=400 --height=200)

echo "la meva resposta ha estat: $resp"



Mostrar una finestra de missatge d'error
zenity --error --text="Descripció de l'error"



Mostrar el diàleg de sel·lecció de fitxers
zenity --file-selection


Mostrar una finestra amb un text informatiu
zenity --info --text="Aquest és el text informatiu"


Mostrar una llista de sel·lecció
zenity --list --text="Llista de prova" \
       --column="ID" --column="Nom" --column="Valor" \
       "1" "nom1" "valor1" \
       "2" "nom2" "valor2" \
       "3" "nom3" "valor3" \
       "4" "nom4" "valor4"



Mostrar una icona de notificació al quadre del Gnome
zenity --notification  --text="Text de la notificació"



Mostrar una barra de progrés. El truc per a que funcioni és el tub (pipe) que alimenta la progressió de la barra.

(
for i in {1..100}
do
   echo $i
done
) | zenity --progress --text="Exemple de barra de progrés" --auto-close --percentage=0



Demanar a l'usuari si sí o si no a una pregunta.

if zenity --question --text="Vols continuar?" --ok-label="Sí, està molt interessant" --cancel-label="No, m'avorreixo com una ostra"
then
    zenity --info --text="Ok, doncs seguim."
else
    zenity --error --text="Cap problema, un altre dia serà."
fi

Mostrar un fitxer de text
zenity --text-info --filename="help.txt" --height=400 --width=500



Mostrar un text d'alerta. Adonem-nos que zenity ens proporciona caixes de notificació específiques de info, warning i error, que són els tres nivells que es fan servir més habitualment per a classificar per importància o gravetat els missatges de les aplicacions.
zenity --warning --text="Aquest és un text d'alerta"



Finalment, obtenir un valor numèric dins un rang
zenity --scale --text="Quants anys tens?" --min-value=0 --max-value=150