Ir al contenido principal

Ralsina.Me — El sitio web de Roberto Alsina

Sobre Japón, Dios y Lilita

Cla­ro que si yo fue­ra ja­po­nés y en­ci­ma de to­do eso tu­vie­ra que ver co­mo al­gu­nos pe­lo­tu­dos di­cen que dios me es­tá cas­ti­gan­do por­que (lo que sea que al pe­lo­tu­do no le gus­ta de Ja­pó­n), es­ta­ría ten­ta­do de bus­car al su­so­di­cho gan­so y ... bue­no, con­si­de­ran­do que los ja­po­ne­ses es­tán de­mos­tran­do ser una gen­te muy ra­zo­na­ble y ci­vi­li­za­da, di­ga­mos que de­cir­le al­go, muy edu­ca­da­men­te.

Por otro la­do, no soy ja­po­né­s, por lo que me sien­to li­bre de ex­pli­car en de­ta­lle por­qué los que di­cen "en una de esas es <al­go> cas­ti­gan­do a | di­cien­do­le a Ja­pón <al­gu­na co­sa>" son un des­per­di­cio de oxí­geno.

Me voy a en­fo­car en un ejem­plo, por­que es una pe­lo­tu­da re­li­gio­sa en es­pe­cia­l: la can­di­da­ta a pre­si­den­te Li­li­ta Ca­rrió.

Acá es­tán las de­cla­ra­cio­nes::

"Dios nos es­tá di­cien­do que de­be­mos cui­dar el pla­ne­ta, que no si­ga­mos des­tru­yen­do la tie­rra, que vi­va­mos en la ver­da­d, en la de­cen­cia, en la jus­ti­cia, que no use­mos la tec­no­lo­gía, aun­que sea de ma­ne­ra pa­cí­fi­ca. Hay que leer los sig­nos de los tiem­po­s"

Vea­mos de a pe­da­ci­to­s, co­mo hu­bie­ra di­cho Ja­ck el des­tri­pa­dor si en rea­li­dad hu­bie­ra si­do Ja­ck el des­cuar­ti­za­do­r:

"Dios nos es­tá di­cien­do que de­be­mos cui­dar el pla­ne­ta"

De­bo con­fe­sar mi con­fu­sión al ver que un ser to­do­po­de­ro­so se ha­ce en­ten­der me­nos que mi ne­ne de ca­si 4. Cuan­do quie­re ju­gar a la pe­lo­ta vie­ne con la pe­lo­ta y me di­ce "pa, quie­ro ju­gar a la pe­lo­ta".

Por otro la­do, al pa­re­cer dio­s, pa­ra de­cir­nos que cui­de­mos el pla­ne­ta y de­je­mos de usar tec­no­lo­gía pre­fie­re pro­vo­car una se­rie de ca­tás­tro­fes en las an­tí­po­da­s, traer las no­ti­cias por in­ter­net (un mi­la­gro tec­no­ló­gi­co) pa­ra que las vea Li­li­ta, in­ter­pre­te la ver­da­de­ra in­ten­ción de dio­s, y me las cuen­te por ra­dio (o­tro mi­la­gro tec­no­ló­gi­co más vie­jo­).

Eso tie­ne sen­ti­do pa­ra al­guien? O sea, por qué dios no di­ce las co­sas de­re­cho vie­jo, cla­ri­to? Por­que pa­ra los re­li­gio­so­s, lo di­ver­ti­do es ad­vi­na­r. Son co­mo sacer­do­tes ro­ma­nos tra­tan­do de ver la vo­lun­tad de Jú­pi­ter en las en­tra­ñas de un ani­ma­l, pe­ro usan la vi­da y el su­fri­mien­to de la gen­te.

Uy, mi­rá, gen­te su­frien­do en Ja­pó­n, dios quie­re que no use­mos la Wi­i!

No so­lo va eso en contra de to­da la doc­tri­na cris­tia­na, des­de la vir­tud de la ca­ri­dad (si dios lo hi­zo pa­ra de­cir­nos al­go, por de­fi­ni­ción se lo me­re­cían) a la prohi­bi­ción de adi­vi­nar la in­ten­ción de dios ob­ser­van­do por­ten­tos (sí, es­tá prohi­bi­do pa­ra vo­s, mo­na­gui­llo, ha­blá con un cu­ra).

"[­Dios nos es­tá di­cien­do] que no si­ga­mos des­tru­yen­do la tie­rra"

Ah, bue­no! Ok! La pr­óxi­ma, oja­lá en­cuen­tre una ma­ne­ra más su­til que rom­per me­dio país y (o­ja­lá que no, no creo que pa­se) con­ta­mi­nar ra­diac­ti­va­men­te una zo­na del mis­mo.

"[­Dios nos es­tá di­cien­do] que vi­va­mos en la ver­da­d"

Da­le. Yo em­pie­zo por no creer en un dios que (de ver­da­d) no exis­te. Cuan­do me al­can­ces se­gui­mos ha­blan­do.

"[­Dios nos es­tá di­cien­do] que no use­mos la tec­no­lo­gía, aun­que sea de ma­ne­ra pa­cí­fi­ca."

Me en­can­ta­ría que Li­li­ta de­ja­ra de usar tec­no­lo­gía por­que sig­ni­fi­ca­ría que no ten­dría que ver sus es­tu­pi­de­ces nun­ca má­s. Por otro la­do, sino tu­vié­ra­mos tec­no­lo­gía ni si­quie­ra nos hu­bié­ra­mos en­te­ra­do de lo de Ja­pón to­da­vía. Su­pon­go que tal vez qui­so de­ci­r, o di­jo "tec­no­lo­gía nu­clea­r" y es­tá saca­do de con­tex­to.

Por otro la­do, nú­me­ro de muer­tos por tec­no­lo­gía nu­clear des­de 1950: 1000? 10000?

Nú­me­ro de muer­tos por te­rre­mo­tos y tsu­na­mi en los úl­ti­mos 5 año­s: 100000? 200000?

Sí aca­bo de in­ven­tar esos nú­me­ro­s, pe­ro creo que es­tán mas bien que ma­l. En­ton­ces bá­si­ca­men­te, dios ma­tó más gen­te es­ta se­ma­na pa­ra que no use­mos ener­gía nu­clear de los­que la ener­gía nu­clear ma­tó en 50 año­s. No es exac­ta­men­te una es­tra­te­gia co­mu­ni­ca­cio­nal efec­ti­va.

"Hay que leer los sig­nos de los tiem­po­s"

Ok, leé el "sign of The Ti­me­s":

The New York Times

No vo­ten a es­ta pe­lo­tu­da re­li­gio­sa. Es pe­li­gro­sa y pro­ba­ble­men­te ten­ga al­gún pro­ble­ma men­ta­l.

En 128 líneas de código entra exactamente ESTE browser.

Por su­pues­to, po­dría ha­cer má­s, pe­ro has­ta yo ten­go mis stan­dar­d­s!

  • No usar ;

  • No usar if whate­ve­r: f()

Sal­vo eso, hi­ce al­gu­nos tru­cos su­cio­s, pe­ro, en es­te mo­men­to, es un bro­w­ser bas­tan­te com­ple­to en 127 lí­neas de có­di­go se­gún sloc­coun­t, así que ya ju­gué su­fi­cien­te y ma­ña­na ten­go tra­ba­jo que ha­ce­r.

Pe­ro an­tes, con­si­de­re­mos co­mo se im­ple­men­ta­ron al­gu­nos fea­tu­res (voy a cor­tar las lí­neas pa­ra que la pá­gi­na que­de ra­zo­na­ble­men­te an­gos­ta), y vea­mos tam­bién las ver­sio­nes "nor­ma­le­s" de lo mis­mo. La ver­sión "nor­ma­l" no es­tá pro­ba­da, avi­sen si es­tá ro­ta ;-)

Es­to noes al­go que de­ba apren­der­se. De he­cho es ca­si un tra­ta­do en co­mo no ha­cer las co­sas. Es el có­di­go me­nos pi­tó­ni­co y me­nos cla­ro que vas a ver es­ta se­ma­na.

Es cor­to, es ex­pre­si­vo, pe­ro es feo feo.

Voy a co­men­tar so­bre es­ta ver­sión.

Soporte deProxy

Un bro­w­ser no es gran co­sa si no se pue­de usar con pro­x­y. Por suer­te el sta­ck de red de Qt tie­ne buen so­por­te de pro­x­y. El chis­te es con­fi­gu­rar­lo.

De Vicenzo soporta proxies HTTP y SOCKS parseando la variable de entorno http_proxy y seteando el proxy a nivel aplicación en Qt:

 proxy_url = QtCore.QUrl(os.environ.get('http_proxy', ''))
 QtNetwork.QNetworkProxy.setApplicationProxy(QtNetwork.QNetworkProxy(\
 QtNetwork.QNetworkProxy.HttpProxy if unicode(proxy_url.scheme()).startswith('http')\
 else QtNetwork.QNetworkProxy.Socks5Proxy, proxy_url.host(),\
 proxy_url.port(), proxy_url.userName(), proxy_url.password())) if\
'http_proxy' in os.environ else None

Co­mo es la ver­sión nor­mal de esa co­sa?

if 'http_proxy' in os.environ:
    proxy_url = QtCore.QUrl(os.environ['http_proxy'])
    if unicode(proxy_url.scheme()).starstswith('http'):
        protocol = QtNetwork.QNetworkProxy.HttpProxy
    else:
        protocol = QtNetwork.QNetworkProxy.Socks5Proxy
    QtNetwork.QNetworkProxy.setApplicationProxy(
        QtNetwork.QNetworkProxy(
            protocol,
            proxy_url.host(),
            proxy_url.port(),
            proxy_url.userName(),
            proxy_url.password()))

Los abu­sos prin­ci­pa­les contra py­thon son el uso del ope­ra­dor ter­na­rio pa­ra ha­cer un if de una lí­nea (y ani­dar­lo) y el lar­go de lí­nea.

Cookies Persistentes

Es­to es ne­ce­sa­rio por­que que­rés per­ma­ne­cer lo­guea­do en los si­tios de una se­sión a otra. Pa­ra es­to, pri­me­ro tu­ve que ha­cer un pe­que­ño me­ca­nis­mo de per­sis­ten­cia, y guar­da­r/­leer los cookies de ahí.

Acá está como hice la persistencia (settings is una instancia de QSettings global):

def put(self, key, value):
    "Persist an object somewhere under a given key"
    settings.setValue(key, json.dumps(value))
    settings.sync()

def get(self, key, default=None):
    "Get the object stored under 'key' in persistent storage, or the default value"
    v = settings.value(key)
    return json.loads(unicode(v.toString())) if v.isValid() else default

No es có­di­go muy ra­ro, sal­vo por usar el ope­ra­dor ter­na­rio al fi­na­l. El uso de json me ase­gu­ra que mien­tras me­ta co­sas ra­zo­na­ble­s, voy a ob­te­ner lo mis­mo de vuel­ta, con el mis­mo ti­po, sin ne­ce­si­dad de con­ver­tir­lo o lla­mar mé­to­dos es­pe­cia­le­s.

¿Entonces, como guardo/leo los cookies? Primero se necesita acceder el "cookie jar". No encontré si hay uno global o por view, así que creé un QNetworkCookieJar en la línea 24 y la asigno a cada página en la línea 107.

# Save the cookies, in the window's closeEvent
self.put("cookiejar", [str(c.toRawForm()) for c in self.cookies.allCookies()])

# Restore the cookies, in the window's __init__
self.cookies.setAllCookies([QtNetwork.QNetworkCookie.parseCookies(c)[0]\
for c in self.get("cookiejar", [])])

Confieso mi crimen de usar comprensiones de listas cuando la herramienta correcta era un for.

Uso el mis­mo tru­co al res­tau­rar los ta­bs abier­to­s, con el mo­co agre­ga­do de usar una com­pren­sión de lis­ta y des­car­tar el re­sul­ta­do:

# get("tabs") is a list of URLs
[self.addTab(QtCore.QUrl(u)) for u in self.get("tabs", [])]

Propiedades y Señales al crear un objeto

Es­te fea­tu­re es­tá en ver­sio­nes re­cien­tes de Py­Q­t: si pa­sás nom­bres de pro­pie­da­des co­mo ar­gu­men­tos con nom­bre, se les asig­na el va­lo­r. Si pa­sás una se­ñal co­mo ar­gu­men­to con nom­bre, se co­nec­tan al va­lo­r.

Es un fea­tu­re ex­ce­len­te, que te ayu­da a crear có­di­go cla­ro, lo­cal y con­ci­so, y me en­can­ta te­ner­lo. Pe­ro si te que­rés ir a la ban­qui­na, es man­da­da a ha­ce­r.

Es­to es­tá por to­dos la­dos en De Vi­cen­zo, és­te es só­lo un ejem­plo (sí, es una so­la lí­nea):

QtWebKit.QWebView.__init__(self, loadProgress=lambda v:\
(self.pbar.show(), self.pbar.setValue(v)) if self.amCurrent() else\
None, loadFinished=self.pbar.hide, loadStarted=lambda:\
self.pbar.show() if self.amCurrent() else None, titleChanged=lambda\
t: container.tabs.setTabText(container.tabs.indexOf(self), t) or\
(container.setWindowTitle(t) if self.amCurrent() else None))

Por adon­de em­pie­zo­...

Hay expresiones lambda usadas para definir los callbacks en el lugar en vez de conectarse con una función o método "de verdad".

Hya lamb­das con el ope­ra­dor ter­na­rio:

loadStarted=lambda:\
    self.pbar.show() if self.amCurrent() else None

Hay lambdas que usan or o una tupla para engañar al intérprete y que haga más de una cosa en un solo lambda!

loadProgress=lambda v:\
(self.pbar.show(), self.pbar.setValue(v)) if self.amCurrent() else\
None

No voy ni a in­ten­tar des­en­re­dar es­to con fi­nes edu­ca­ti­vo­s, pe­ro di­ga­mos que esa lí­nea con­tie­ne co­sas que de­be­rían ser 3 mé­to­dos se­pa­ra­do­s, y de­be­ría es­tar re­par­ti­da en 6 lí­neas o ma­s.

Download Manager

Lla­mar­lo un ma­na­ger es exa­ge­rar por­que no se pue­de pa­rar una des­car­ga des­pués que em­pie­za, pe­ro bue­no, te de­ja ba­jar co­sas y se­guir bro­w­sean­do, y te da un re­por­te de pro­gre­so!

Primero, en la línea 16 creé un diccionario bars para llevar registro de los downloads.

Des­pué­s, te­nía que de­le­gar el con­te­ni­do no so­por­ta­do al mé­to­do in­di­ca­do, y eso se ha­ce en las lí­neas 108 and 109

Básicamente, con eso cada vez que hacés click en algo que WebKit no puede manejar, se llama al método fetch con el pedido de red como argumento.

def fetch(self, reply):
    destination = QtGui.QFileDialog.getSaveFileName(self, \
        "Save File", os.path.expanduser(os.path.join('~',\
            unicode(reply.url().path()).split('/')[-1])))
    if destination:
        bar = QtGui.QProgressBar(format='%p% - ' +
            os.path.basename(unicode(destination)))
        self.statusBar().addPermanentWidget(bar)
        reply.downloadProgress.connect(self.progress)
        reply.finished.connect(self.finished)
        self.bars[unicode(reply.url().toString())] = [bar, reply,\
            unicode(destination)]

No hay mu­cho golf acá sal­vo las lí­neas lar­ga­s, pe­ro una vez que me­tés en­ters es la ma­ne­ra ob­via de ha­cer­lo:

  • Pe­­dí un no­m­­bre de ar­­chi­­vo

  • Creás un pro­­­gress­­ba­­r, lo po­­­nés en el sta­­tus­­ba­­r, y lo co­­­ne­c­­tas a las se­­ña­­les de pro­­­gre­­so de la des­­ca­r­­ga.

Entonces, por supuesto, está el slot progress que actualiza la barra:

progress = lambda self, received, total:\
    self.bars[unicode(self.sender().url().toString())][0]\
    .setValue(100. * received / total)

Sí, de­fi­ní un mé­to­do co­mo lamb­da pa­ra aho­rrar una lí­nea. [fa­ce­pal­m]

Y elslot finished para cuando termina el download:

def finished(self):
    reply = self.sender()
    url = unicode(reply.url().toString())
    bar, _, fname = self.bars[url]
    redirURL = unicode(reply.attribute(QtNetwork.QNetworkRequest.\
        RedirectionTargetAttribute).toString())
    del self.bars[url]
    bar.deleteLater()
    if redirURL and redirURL != url:
        return self.fetch(redirURL, fname)
    with open(fname, 'wb') as f:
        f.write(str(reply.readAll()))

has­ta so­por­ta re­di­rec­cio­nes co­rrec­ta­men­te! Más allá d eso, na­da más es­con­de la ba­rra, guar­da los da­to­s, fin del cuen­ti­to. La lí­nea lar­ga ni si­quie­ra es mi cul­pa!

Hay un pro­ble­ma en que el ar­chi­vo en­te­ro se man­tie­ne en me­mo­ria has­ta el fin de la des­car­ga. Si te ba­jás un DV­D, te va a do­le­r.

Usar el with ahorra una línea y no pierde un file handle, comparado con las alternativas.

Impresión

De nue­vo Qt me sal­va las pa­pa­s, por­que ha­cer es­to a ma­no de­be ser di­fí­ci­l. Sin em­bar­go, re­sul­ta que el so­por­te de im­pre­sió­n... es­tá he­cho. Qt, es­pe­cial­men­te usa­do vía Py­Qt es tan com­ple­to!

self.previewer = QtGui.QPrintPreviewDialog(\
    paintRequested=self.print_)
self.do_print = QtGui.QShortcut("Ctrl+p",\
    self, activated=self.previewer.exec_)

No ne­ce­si­té na­da de gol­f. Eso es exac­ta­men­te el có­di­go que se ne­ce­si­ta, y es la ma­ne­ra re­co­men­da­da de en­gan­char "C­tr­l+­p" con la im­pre­sión de la pá­gi­na.

Otros Trucos

No hay otros tru­co­s. To­do lo que que­da es crear wi­dge­ts, co­nec­tar unas co­sas con otra­s, y dis­fru­tar la in­creí­ble ex­pe­rien­ce de pro­gra­mar Py­Q­t, don­de po­dés es­cri­bir un web bro­w­ser en­te­ro (s­al­vo el mo­to­r) en 127 lí­neas de có­di­go.

El Conde de Montecristo

Cover for El Conde de Montecristo

Review:

Fi­nal­ly fin­ished. This is one *long* book (I read the unabridged ver­sion), and as usu­al, it's hard to read such an old book with­out the writ­ing style mak­ing it some­what hard­er.

A big part of this is be­cause the trans­la­tion avail­able at the guten­berg project is... aw­ful, prob­a­bly be­cause of its own age. I had read it maybe 10 years ago in a mod­ern span­ish trans­la­tion that was much su­pe­ri­or.

In any case, you can't un­der­stand, for ex­am­ple, Neal Stephen­son, with­out read­ing Du­mas first, and this one is prob­a­bly Du­mas' best work. It's one of those few se­lect books ev­ery­one thinks they have read but prob­a­bly has­n't.

For ex­am­ple, if I say "S­in­bad the Sailor", does it mean any­thing to you in the con­text of "The Count of Mon­te­cristo"? No? Then you have not read it. You may have read it on com­ic book for­m, or some ex­cerp­t, or maybe clif­f's notes, but you have not read the re­al thing.

I loved the at­ten­tion to de­tail­s, like con­coct­ing a rea­son­able way to sim­u­late a stock mar­ket cri­sis (a cute us­age of what's now called a "Man in the Mid­dle" se­cu­ri­ty ex­ploit!), or how a cer­tain char­ac­ter is al­ways de­scribed in ways that make you think she's a les­bian, but with­out ev­er re­al­ly say­ing it out loud.

All in al­l, a great book to have read. But I would rec­om­mend those who want to read it to in­ves­ti­gate and find a more mod­ern trans­la­tion than project Guten­berg's.

De Vicenzo: un mini browser más copado

Si no que­rés leer eso de nue­vo, la idea es ver cuán­to có­di­go fal­ta pa­ra con­ver­tir el mo­tor We­bKit de Qt en un bro­w­ser "en se­rio­".

Pa­ra ello, me pu­se una me­ta com­ple­ta­men­te ar­bi­tra­ria de 128 lí­neas de có­di­go. En es­te mo­men­to lo de­cla­ro fea­tu­re-­com­ple­te (pe­ro bu­gg­y).

Los nue­vos fea­tu­res so­n:

  • Ta­­bbed bro­­w­­sing (se pue­­de agre­­ga­­r/s­a­­car ta­bs)

  • Book­­ma­­rks (se pue­­den agre­­ga­­r/s­a­­car y ele­­gir de una lis­­ta)

Es­to es lo que ya fun­cio­na­ba:

  • Zoom in (C­­tr­­l++)

  • Zoom out (C­­tr­­l+-)

  • Re­set Zoom (C­­tr­­l+=)

  • Bus­­car (C­­tr­­l+­­F)

  • Es­­co­n­­der bús­­que­­da (Es­­c)

  • Bo­­­to­­­nes de atrá­s/a­­de­­lan­­te y re­­ca­r­­gar

  • En­­tra­­da de URL que coi­n­­ci­­de con la pá­­gi­­na + au­­to­­­co­m­­ple­­ta­­do des­­de la his­­to­­­ria + arre­­gla la URL pues­­ta a ma­no (a­­gre­­ga http://, esas co­­sas)

  • Plu­­gins (i­n­­cluí­­do fla­s­h, que hay que ba­­jar apa­r­­te ;-)

  • El tí­­tu­­lo de la ven­­ta­­na mues­­tra el tí­­tu­­lo de la pá­­gi­­na (sin pro­­­pa­­gan­­da del bro­­w­se­­r)

  • Ba­­rra de pro­­­gre­­so pa­­ra la ca­r­­ga de la pá­­gi­­na

  • Ba­­rra de es­­ta­­do que mues­­tra el des­­tino de los li­nks cuan­­do pa­sas el mou­­se

  • To­­­ma una URL en la lí­­nea de co­­­man­­do (o abre http://­­p­­y­­tho­­n.org

  • Mu­l­­ti­­pla­­ta­­fo­r­­ma (fun­­cio­­­na do­n­­de fun­­cio­­­na QtWe­­bKi­­t)

Y cuan­to có­di­go es eso? 87 LI­NEAS.

O si pre­fe­rís la ver­sión que cum­ple con la PE­P8: 115 LI­NEAS.

Me ata­jo an­tes que al­guien lo di­ga: sí, el mo­tor de ren­de­ring y el toolkit son enor­mes. Lo que es­cri­bí es el "ch­ro­me" al­re­de­dor de eso, igual que ha­cen Aro­ra, Rekon­q, Ga­leo­n, Epi­phan­y, y mu­chos otros bro­w­ser­s.

Es un ch­ro­me sim­ple y mi­ni­ma­lis­ta, pe­ro fun­cio­na bas­tan­te bien, creo yo.

Aquí es­tá el de­mo (bu­gg­y):

Mas o me­nos ha­ce lo que es­pe­ra­ba que se pue­die­ra lo­gra­r, pe­ro le fal­tan arre­glo­s.

Pa­ra ver el có­di­go, va­yan a su ho­me pa­ge: http://­de­vi­cen­zo­.­google­co­de.­com


Contents © 2000-2023 Roberto Alsina