26 maggio, 2007

Aggiornamenti sul blog

Se non scrivo da un po su questo blog non è per pigrizia o perché ho deciso di abbandonarlo ma perché martedì mi sono laureato e da allora non ho proprio acceso il pc. Sto dedicando tutto il mio tempo a suonare e a godermi qualche giorno di meritato riposo. Credo che continuerò ancora per qualche tempo, ma prima o poi mi deciderò a riprendere le mie normali attività. Rimanete connessi!

14 maggio, 2007

Python Cairo e le glass window

Chi non conosce il lavoro di njpatel? Ormai i suoi avant window navigator e affinity sono noti ai più. Le sue idee sono innovative è apprezzate da molti bloggers. In questo post non voglio solo esaltare le sue produzioni (anche perché arriverei molto in ritardo in questo) ma voglio spingermi un po oltre per spiegare il modo di operare per ottenere risultati visivi simili ma utilizzando il nostro amato pitone.

A chi non sapesse a cosa mi riferisco, rimando a questo post sul blog di njpatel. Notate le trasperenze della finestra e gli altri widget inseriti normalmente al suo interno.

Come faccio ad ottenere un risultato simile in python?


#!/usr/bin/env python

import gtk
import cairo

if gtk.pygtk_version < (2, 10, 0):
print 'Questo esempio ha bisogno delle PyGtk >= 2.10'
raise SystemExit

def hex2float(hex):
ret = []
for i in range(4):
ic = int(hex[i])
ret.append( ic / 255.0 )
return ret


class TransparentWindow(gtk.Window):
__gsignals__ = {
'expose-event': 'override',
'screen-changed': 'override',
}

def __init__(self):
gtk.Window.__init__(self)

# Indichiamo alle GTK che vogliammo disegnare noi lo sfondo.
self.set_app_paintable(True)

# non vogliamo le decorazioni del window manager
self.set_decorated(False)

# usiamo il segnale button-press-event per permettere
# il "click e trascina" sulla finestra. Ricordiamo
# che non stiamo utilizzando i classici decoratori
# del window manager e non gestendo qeusto segnale
# non sarebbe possibile spostare la finestra
self.add_events(gtk.gdk.BUTTON_PRESS_MASK)
self.connect('button-press-event', self.on_button_press)

# Inizializziamo lo schermo
self.do_screen_changed()

def on_button_press(self, widget, event):
self.begin_move_drag(
event.button,
int(event.x_root),
int(event.y_root),
event.time)

def render_rect(self, cr, x, y, w, h, o):
# Crea un rettangolo con i bordi arrotondati
x0 = x
y0 = y
rect_width = w
rect_height = h
radius = 10 + o

x1 = x0 + rect_width
y1 = y0 + rect_height
cr.move_to(x0, y0 + radius)
cr.curve_to(x0, y0, x0, y0, x0 + radius, y0)
cr.line_to(x1 - radius, y0)
cr.curve_to(x1, y0, x1, y0, x1, y0 + radius)
cr.line_to(x1 , y1)
cr.line_to (x0 , y1)
cr.close_path()

def do_expose_event(self, event):
cr = self.window.cairo_create()

if self.supports_alpha:
cr.set_source_rgba(1.0, 1.0, 1.0, 0.0)
else:
cr.set_source_rgb(1.0, 1.0, 1.0)

cr.set_operator(cairo.OPERATOR_SOURCE)
cr.paint()


(width, height) = self.get_size()
cr.move_to(0, 0)
cr.set_line_width(1.0)

cr.set_operator(cairo.OPERATOR_OVER)

pat = cairo.LinearGradient(0.0, 0.0, 0.0, height)

ex_list = [0xA1, 0xA8, 0xBB, 0xEC]
col = hex2float(ex_list)
pat.add_color_stop_rgba(0.0, col[0], col[1], col[2], col[3])

ex_list = [0x14, 0x1E, 0x3C, 0xF3]
col = hex2float(ex_list)
pat.add_color_stop_rgba(1.0, col[0], col[1], col[2], col[3])

self.render_rect(cr, 0, 0, width, height, 10)
cr.set_source(pat)
cr.fill()

# bordo luminoso
ex_list = [0xFF, 0xFF, 0xFF, 0x4e]
col = hex2float(ex_list)
cr.set_source_rgba(col[0], col[1], col[2], col[3])
self.render_rect(cr, 1.5, 1.5, width - 3 , height - 3, 10)
cr.stroke()

# bordo
ex_list = [0x00, 0x15, 0x1F, 0xe0]
col = hex2float(ex_list)
cr.set_source_rgba(col[0], col[1], col[2], col[3])
self.render_rect(cr, 0.5, 0.5, width - 1 , height - 1, 10)
cr.stroke()

ex_list = [0xFF, 0xFF, 0xFF, 0xFF]
col = hex2float(ex_list)
cr.set_source_rgba(col[0], col[1], col[2], col[3])
self.render_rect(cr, 0, 0, width , height, 10)
cr.stroke()

pat = cairo.LinearGradient(0.0, 0.0, 0.0, height)
cr.set_source(pat)
ex_list = [0xFF, 0xFF, 0xFF, 0xbb]
col = hex2float(ex_list)
pat.add_color_stop_rgba(0.0, col[0], col[1], col[2], col[3])

ex_list = [0x00, 0x00, 0x10, 0xaa]
col = hex2float(ex_list)
pat.add_color_stop_rgba(0.2, col[0], col[1], col[2], col[3])
self.render_rect(cr, 0, 0, width, 20, 10)
cr.fill()

# chiediamo esplicitamente ai figli di disegnarsi
# Se non lo facessimo non sarebbero visualizzati
# i widgets aggiunti alla TransparentWindow
children = self.get_children()
for c in children:
self.propagate_expose(c, event)

def do_screen_changed(self, old_screen=None):
screen = self.get_screen()
if self.is_composited():
print 'Il tuo schermo supporta il canale alpha!'
colormap = screen.get_rgba_colormap()
self.supports_alpha = True
else:
print 'Il tuo schermo non supporta il canale alpha! Il composite\
manager e\' attivo?'
colormap = screen.get_rgb_colormap()
self.supports_alpha = False
self.set_colormap(colormap)

class CoolAlpha:
def __init__(self):
window = TransparentWindow()
window.resize(400, 400)
eb = gtk.EventBox()
eb.set_visible_window(False)
eb.set_border_width(12)
window.add(eb)

main_box = gtk.VBox(False, 12)
eb.add(main_box)

hbox = gtk.HBox(False, 6)
main_box.pack_start(hbox, False)

button = gtk.Button()
button.set_relief(gtk.RELIEF_NONE)
button.connect("clicked", gtk.main_quit)
icon_box = gtk.HBox(False, 0)

image = gtk.Image()
image.set_from_stock(gtk.STOCK_CLOSE,
gtk.ICON_SIZE_MENU)
icon_box.pack_start(image, False)
button.add(icon_box)
hbox.pack_end(button, False)

sw = gtk.ScrolledWindow()
sw.set_property("border-width", 20)
sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
self.textview = gtk.TextView()
self.textview.set_editable(False)
self.textview.set_cursor_visible(False)
sw.add(self.textview)
main_box.pack_start(sw, True)

window.show_all()

def set_text(self, text):
buf = self.textview.get_buffer()
buf.set_text(text)

if __name__ == '__main__':
a = CoolAlpha()
a.set_text("prova")
gtk.main()

Questo esempio è in gran parte una traduzione del codice in linguaggio c di affinity di njpatel con qualche piccolo ritocco da parte mia come l'inclusione del pulsante di chiusura in alto a destra e la textview centrale. Eseguendo l'esempio otterrete:

In genere le gtk non vengono usate per costruire finestre troppo "esotiche" e c'è un buon motivo per questo, ovvero mantenere la consistenza delle applicazioni sul desktop. Quindi nonostante queste finestre possono apparire molto belle a vedersi, il mio consiglio e di non abusarne ma utilizzarle in modo da ottenere una buona integrazione con il resto del desktop per non fare apparire la nostra applicazione come un "alieno".

13 maggio, 2007

Vmware Server su Ubuntu Feisty

Vi piace provare le nuove distro magari in beta o volete fare sperimentazioni di programmazione in rete ma non avete a disposizione i pc per farlo? La soluzione c'è e si chiama virtualizzazione. Vmware server è un software gratuito (non open source purtroppo) che dalla versione feisty è impacchettato e disponibile nei repository di software commerciale di canonical.

Installazione
Per installare vmware server è necessario prima di tutto abilitare i repository "non liberi" di canonical:

deb http://archive.canonical.com/ubuntu feisty-commercial main"
Per farlo è possibile utilizzare synaptic o editare manualmente il file /etc/apt/sources.list
A questo punto è possibile installare il software usanto il comando:
sudo apt-get install vmware-server
L'installazione vi richiederà un codice di attivazione che potete ottenere gratuitamente sul sito di vmware

10 maggio, 2007

Baby e ybab: uno script python per usare i dizionari babylon

E' da tanto che non scrivo? Lo so ma fra qualche giorno se tutto procede come previsto dovrei laurearmi quindi il tempo da dedicare al blog è praticamente inesistente. Appena tutto sarà finito tornerò a sviluppare cGmail e a postarvi gli studi su python e tutto il resto. Per ora, per non lasciarvi proprio a bocca asciutta, vi propongo un piccolo "bocconcino" che ho sviluppato qualche tempo fa e che potrebbe risultare utile (o quantomeno potrebbe essere uno spunto) a qualcuno di voi. Si tratta di un semplice script (indovinate in che linguaggio? . . . Bravi :) ) che interroga il database dei dizionari babylon, si presenta come Firefox 2 e poi interpreta l'html per tirare fuori le informazioni sul lemma ricercato.

Il codice python


#! /usr/bin/env python
import urllib2
import re
import sys
from sgmllib import SGMLParser

red='\033[0;31m'
RED='\033[1;31m'
blue='\033[0;34m'
BLUE='\033[1;34m'
cyan='\033[0;36m'
CYAN='\033[1;36m'
NC='\033[0m'

ROWLEN = 60

class Parser(SGMLParser):
def __init__(self):
SGMLParser.__init__(self)
self.must_print = False

def reset(self):
SGMLParser.reset(self)

def write(self, data):
sys.stdout.write(data)
sys.stdout.flush()

def start_div(self, attrs):
for attr, value in attrs:
if attr == "class":
if value == "term":
self.write(RED + '\n')
self.must_print = True
if value == "definition":
self.must_print = True

def end_div(self):
self.must_print = False
self.write(NC)

def handle_data(self, data):
if not self.must_print: return
if data.strip() == "": return
tmp = data.strip()
tmp = data.replace('\n', '')
count = len(tmp)
if count > ROWLEN:
pieces = count / ROWLEN
for i in range(pieces):
l = ROWLEN * i
u = ROWLEN * (i + 1)
self.write(tmp[l:u] + '\n')
else:
self.write(tmp + '\n')



def http_req(url, data):
req = urllib2.Request(url, data)
req.add_header('User-Agent', 'Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.4) Gecko/20061201 Firefox/2.0.0.4 (Ubuntu-feisty)')
req.add_header('Accept-Charset', 'ISO-8859-1,utf-8;q=0.7,*;q=0.7')
req.add_header('Accept', 'text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5')
stream = urllib2.urlopen(req)
out = stream.read()
stream.close()
return out


def usage():
print "Usage: baby parola\n";

def search(word, lang):

out = http_req('http://info.babylon.com/onlinebox.cgi',
'rt=ol&tid=pop&uil=EN&cid=CD1&term='+word+'&tl='+lang)
return out


if __name__ == "__main__":
import os
args = sys.argv[1:]
if args[0] == None:
usage()
sys.exit(1)
word = args[0]
out = ""
appname = os.path.basename(sys.argv[0])
print appname
if appname == "baby":
out = search(word, "EN")
elif appname == "ybab":
out = search(word, "IT")
p = Parser()
p.feed(out)
p.close()

Come lo installo?
Copiate ed incollate il codice in un file che chiamerete baby (Attenzione: il nome è importante). Date i permessi di esecuzione al file e copiatelo in una directory inclusa nel vostro path (ad esempio /usr/local/bin). A questo punto create un link simbolico al file con nome ybab. Esempio di tutti i passaggi (supponiamo che avete salvato lo script di cui sopra su un file di nome baby sul vostro desktop):

sudo cp ~/Desktop/baby /usr/local/bin
sudo chmod +x /usr/local/bin/baby
sudo ln -s /usr/local/bin/baby /usr/local/bin/ybab

Come si usa?
L'uso è semplicissimo. Invocando baby [lemma_da_cerare] verrà considerata come lingua target l'inglese, invocando invece ybab [lemma_da_cercare] verrà considerato come target l'italiano.

Esempio:

$ ybab glad
ybab

glad
agg.
felice, contento, lieto; gioioso
s.
gladiolo (fiore)

GLAD
CONTENTO. LIETO. FELICE. ALLIETARE. RALLEGRARE. GLADIOL
Purtroppo per alcuni lemmi l'output non è così pulito (ci sarebbe da migliorare il parser, suggerimenti sono ben accetti. Io non mi ci sono dedicato più di tanto)

Come funziona
Le informazioni che seguono sono piuttosto tecniche. Se vi interessa solo il programmino e non sapere come funziona potete tranquillamente evitare di proseguire nella lettura.

Lo script utilizza la urllib2 di python per fare una semplice richiesta al sito di babylon. Per conoscere l'esatto URL da interrogare e i parametri da passargli è necessario studiarsi un po il sito, conoscere un minimo di programmazione web, sapere come funzionano le form ed avere un po di pazienza. Una volta noto il meccanismo di interrogazione utilizzato dal sito in questione, il resto è "semplice". In questo caso l'output della richiesta http, viene "parsato" (passatemi il termine) dall'apposito parser che utilizza la libreria sgmllib. Un aspetto interessante dello script è il modo che utilizza per cambiare la sua funzionalità (in questo caso la lingua target). Ricordiamo che il primo parametro passato ad un programma è sempre il suo nome. Quindi distinguendo il nome del primo parametro è possibile effettuare operazioni diverse
. In questo caso se il programma viene invocato come baby viene utilizzata la lingua inglese, mentre invocato come ybab quella italiana.