Ir al contenido principal

Ralsina.Me — El sitio web de Roberto Alsina

Publicaciones sobre python (publicaciones antiguas, página 53)

More fun with rst2pdf, poppler and PyQt

És­te es un vi­deo de co­mo se ve aho­ra, des­pués de afei­tar al­gu­nos yaks (per­dón por mi in­glé­s, Es­cri­bo mu­cho in­glé­s, pe­ro nun­ca ha­blo in­glé­s. Y per­dón por no ha­cer una ver­sión en cas­te­lla­no­):

Co­mo pue­den ve­r, la apli­ca­ción bá­si­ca es­tá más o me­nos com­ple­ta, aún si le fal­tan las "a­me­ni­da­des" que ha­rían que val­ga la pe­na usar­la (co­mo bus­ca­r? Y guar­da­r? ;-)

Tie­ne un gran pro­ble­ma: no pue­do pu­bli­car­la to­da­vía.

¿Por­qué? Por­que ne­ce­si­to usar po­ppler des­de Py­Q­t, y el có­di­go que en­contré pa­ra ha­cer­lo no tie­ne li­cen­cia (ver el có­di­go).

Es­toy tr­tan­do de con­tac­tar al au­tor (Ra­jeev J Se­bas­tian), así que tal vez ha­ya no­ve­da­des pron­to.

Ape­nas se acla­re ese pun­to, el wi­dget PDF es un post en sí mis­mo, por­que es­tá bue­no, si lo pue­do de­cir yo mis­mo.

EX­TRA: Ese bin­ding aho­ra es­tá ba­jo li­cen­cia MI­T, gra­cias a Ra­jeev Se­bas­tian!

Afeitando Yaks: septiembre 22, 2009

Es­te yak es­tá que­dan­do bo­ni­to.

Pa­ra mi se­gun­da pi­la de pe­lo de yak: con­ver­tir QPlain­TextE­dit en un wi­dget de edi­ción acep­ta­ble pa­ra pro­gra­ma­do­res. A me­di­da que el tra­ba­jo avan­za­ba en mi edi­tor de rs­t2­pdf (de pa­so: ne­ce­si­to un nom­bre!), se vol­vió evi­den­te que la par­te de la in­ter­fa­ce en la que el usua­rio pa­sa más tiem­po es un sim­ple par de edi­to­res de tex­to.

Qt vie­ne con un wi­dget pa­ra eso, por su­pues­to, lla­ma­do QPlain­TextE­dit. Sin em­bar­go, es mu­y, muy ma­lo pa­ra pr­pgra­ma­do­res.

És­tos son mis re­que­ri­mien­tos mí­ni­mo­s:

  1. Re­s­al­­ta­­do de si­n­­ta­­xis pa­­ra dos len­­gua­­je­s: res­­tru­c­­tu­­red text y ja­­va­s­­cri­p­­t. Es­­te yak ya es­­tá afei­­­ta­­do.

  2. Nú­­me­­ros de lí­­nea.

  3. Re­s­al­­tar la lí­­nea ac­­tua­­l.

  4. Re­s­al­­tar erro­­­res cuan­­do tie­­ne sen­­ti­­do (por eje­m­­plo, en el sty­­le­s­hee­­t)

Una for­ma de lo­grar es­to se­ría des­car­tar QPlain­TextE­dit y usar QS­ciS­cin­ti­lla que es la ba­se pa­ra el edi­tor de có­di­go en eric y (en otra ver­sió­n) pa­ra sci­te.

Sin em­bar­go, su­fro un bug en QS­ciS­cin­ti­lla, don­de no pue­do es­cri­bir ¡ca­rac­te­res con acen­to­!. Sin eso el cas­te­llano es im­po­si­ble, y el bug pa­re­ce te­ner co­mo dos año­s, así que ... no va.

¿En­ton­ce­s, lo­gré esos fea­tu­res? ¡Sí!

Acá es­tá el vi­deo (sí, me vol­ví adic­to a ha­cer es­to­s, ya que qt-­reord­m­y­desk­top lo ha­ce de­ma­sia­do fá­cil ;-):

La ba­se pa­ra es­to es el ejem­plo Co­de Edi­tor que vie­ne con Qt mis­mo, más un po­qui­to de tra­ba­jo.

Pri­me­ro, por­té Co­de Edi­tor de C++ a Py­tho­n, que fué fá­cil y lle­vó so­lo unos mi­nu­to­s. Eso se en­car­gó de los pun­tos 2 y 3.

En­ton­ces en­chu­fé el re­sal­ta­do de sin­ta­xis, que era el pun­to 1.

¿Y que pa­só con la va­li­da­ción de ja­vas­cript en tiem­po rea­l? Fá­cil usan­do sim­ple­j­so­n. Hay que co­rrer es­to ca­da vez que se quie­re va­li­dar (yo lo co­rro en ca­da te­cla que se pre­sio­na).

Hay que reem­pla­zar se­l­f.ui.s­ty­le.­to­Plain­Text por el nom­bre de tu wi­dge­t, por su­pues­to:

def validateStyle(self):
    style=unicode(self.ui.style.toPlainText())
    #no point in validating an empty string
    if not style.strip():
        return
    pos=None
    try:
        json.loads(style)
    except ValueError, e:
        s=str(e)
        print s
        if s == 'No JSON object could be decoded':
            pos=0
        elif s.startswith('Expecting '):
            pos=int(s.split(' ')[-1][:-1])
        else:
            print 'UNKNOWN ERROR'

    # This makes a red bar appear in the line
    # containing position pos
    self.ui.style.highlightError(pos)

highlightError(pos) almacena pos en el Code Editor, qie dibuja una barra roja en esa linea, de la misma forma que resalta la linea actual.

Y eso es to­do. És­te es el có­di­go pa­ra co­dee­di­to­r.­py

Afeitando Yaks: septiembre 21, 2009

¡Y mis yaks son muy pe­lu­dos!

Em­pe­cé tra­tan­do de ha­cer un edi­tor de ho­jas de es­ti­lo pa­ra rs­t2­pdf (ver aquí).

Una co­sa lle­va a otra, y aho­ra ten­go por lo me­nos tres mi­ni-­pro­yec­tos in­te­re­san­tes por cul­pa de ése.

El de ho­y: (a­b)u­sar pyg­men­ts co­mo re­sal­ta­dor ge­né­ri­co de sin­ta­xis en una in­ter­fa­ce Qt.

¿Por­qué pyg­men­ts? Por­que es el úni­co re­sal­ta­dor de reS­truc­tu­red Text que en­contré. ¡E­so es pro­ba­ble­men­te por­que el reSt es muy di­fí­cil de re­sal­ta­r!

Has­ta adon­de sé es­ta es la pri­me­ra vez que al­guien hi­zo an­dar pyg­men­ts pa­ra es­to, en una ven­ta­na edi­ta­ble. Y hay muy bue­nos mo­ti­vos pa­ra eso:

  • Es pu­­ro py­­tho­­n, tal vez uno es­­pe­­ra que sea de­­ma­­sia­­do len­­to.

  • No ha­­ce le­­xing pa­r­­cial ni pro­­­gre­­si­­vo, hay que "lexea­­r" to­­­do el tex­­to jun­­to (de nue­­vo, eso pue­­de pa­­re­­cer muy len­­to)

  • Tie­­ne una API orien­­ta­­da a ar­­chi­­vo­­s, ge­­ne­­ra un ar­­chi­­vo con to­­­do el tex­­to fo­r­­ma­­tea­­do aden­­tro, y pa­­ra es­­ta cla­­se de uso se ne­­ce­­si­­ta ac­­ce­­der a da­­tos "del me­­dio" so­­­la­­men­­te.

Y, por su­pues­to, re­sul­ta que fun­cio­na bas­tan­te bien, co­mo se ve en es­te vi­deo:

Lec­ción del día: las com­pu­ta­do­ras aho­ra son rá­pi­das.

Acá es­tá el có­di­go pa­ra hi­gh­li­gh­te­r.­py con mu­chos co­men­ta­rio­s.

Se pue­de co­rrer y se ob­tie­ne la mis­ma de­mo que se ve en el vi­deo (s­al­vo el ti­peo ;-)

Jugando con poppler, PyQt y rst2pdf

Tam­bién pue­de lle­gar a ser úti­l, por ejem­plo, co­mo una for­ma de pro­bar cam­bios de sty­les­hee­t, ha­cien­do rs­t2­pdf más fá­cil de usar.

Des­pués de un par de ho­ras de ha­cking tran­qui­lo­... no es­tá na­da mal. Im­ple­men­té el (rús­ti­co) vi­sor PDF usan­do un bin­ding py­tho­n/­po­pple­r/­Qt que en­contré en google, la in­ter­fa­ce es Py­Q­t.

Acá es­tá el vi­deo:

Una no­ta: el vi­deo lo gra­bé usa­do qt-­re­cord­m­y­desk­to­p, y ese pro­gra­ma es ge­nia­l. Fue fa­ci­lí­si­mo de ha­ce­r.

No creo que fun­cio­ne muy bien con do­cu­men­tos lar­go­s, pe­ro el ma­nual de rs­t2­pdf (u­nas 25 pá­gi­na­s) se pro­ce­sa en 5 se­gun­do­s.

Una pregunta inocente...

To­do em­pe­zó cuan­do "Ga­li­leo Ga­li­lei" pre­gun­tó co­mo ha­cer una co­sa muy sim­ple. Él mos­tró es­te có­di­go:

age = int(raw_input('Cual es tu edad?'))
if age<18:
    print 'Sos menor'
else:
    print 'Sos mayor'

En rea­li­dad el ori­gi­na­l... era un po­co más gua­ran­go, pe­ro el có­di­go es bá­si­ca­men­te el mis­mo. has­ta ahí na­da ra­ro. Pe­ro en­ton­ces pre­gun­tó es­to:

Có­mo pue­do ha­cer que si el usua­rio en­tra al­go que no es un nú­me­ro, ha­ga al­go ti­po:

print 'No tengo poderes de supervaca'

o

print 'Error de tipeo'

Uno se ima­gi­na­ría que esa cla­se de pre­gun­ta pue­de pro­du­cir una o dos res­pues­ta­s. ¿No?

Efec­ti­va­men­te así es, y se ven las res­pues­tas de Fa­cun­do Ba­tis­ta o Eze­quiel.

Pe­ro­... que pa­sa­ría si que­re­mos se­guir pre­gun­tan­do cuan­do el usua­rio en­tra un no­-­nú­me­ro?

En­ton­ces ami­go­s... es una cues­tión de gus­to, y es to­do cul­pa de Juan Pe­dro Fi­sano­tti.

Acá es­tá mi idea:

while True:
    edad=raw_input('¿Cuantos años tenes?')
    if edad.isdigit():
        break
    print 'No ingresaste un numero!'

Sí, lo ad­mi­to, un po­co a la an­ti­gua. Y hu­bu gri­tos de "no, break es una por­que­ría, no es­tá bien", lo que lle­va a és­to, de Ma­nuel Aráoz

age = raw_input('Tu edad?')
while not age.isdigit():
    print "No es un número!"
    age = raw_input('Tu edad?')

Lo que lle­va a llan­tos de "Te­ner dos raw_i­n­put es feo­!", lo que a su vez pro­vo­ca es­to (nue­va­men­te, Ma­nuel Aráoz:

get_age = lambda: raw_input('Tu edad?')
age = get_age()
while not age.isdigit():
    print 'No es un numero!'
    age = get_age()

Acá Pa­tri­cio Mo­li­na pe­la la PEP 315.

Y en­ton­ces Ale­jan­dro San­tos di­ce al­go co­mo "Es­to es más fa­cil en C por­que po­de­mos asig­nar va­lo­res a edad en la con­di­ción del whi­le". Acuér­den­se de es­to.

Aho­ra Pa­blo Zi­llia­ni da su ver­sió­n, que, de­bo de­ci­r, es per­fec­ta en cier­ta for­ma:

age = reset = msg = 'Edad?: '
while not age.isdigit():
    age = raw_input(msg)
    msg = "%r no es un numero!, %s" % (age, reset)

print age

En­ton­ces Ga­briel Ge­ne­lli­na de­ci­de de­fen­der el uso de break pe­gán­do­nos a to­dos en la ca­be­za con Knu­th lo que de­be­ría te­ner un efec­to mu­cho más po­ten­te que men­cio­nar a Hi­tle­r.

Y va­mos lle­gan­do a aguas po­co na­ve­ga­ble­s. Acá es­tá la pro­pues­ta de news , que ad­mi­ro. A una res­pe­tuo­sa dis­tan­cia.

Pri­me­ro el có­di­go re­le­van­te:

edad = "0" # Entra igual la primera vez

while firstTrue (not edad.isdigit()):
    edad = raw_input ("¿Cuantos años tenes? ")
    if not edad.isdigit():
        print "No ingresaste un nro!"

¿Pe­ro qué, exac­ta­men­te, es firs­tTrue?

import inspect

def firstTrue(cond):
    """ devuelve True siempre la primera vez que se la ejecuta,
    las veces subsiguientes evalua la condicion """
    stack = inspect.stack()[1] # El stack del programa llamador
    line = stack[2] # Nro de linea desde la que llame a firstTrue
    del stack

    if not "line" in firstTrue.__dict__:
        # Primera vez que llamo a la funcion
        firstTrue.line = line
        return True
    elif firstTrue.line != line:
        # Llame a la funcion desde otro punto del programa
        firstTrue.line = line
    return True

    return cond

En­ton­ces yo men­ciono ge­ne­ra­do­res, lo que lle­va a es­to, por Clau­dio Frei­re, que ca­si fun­cio­na:

age = ''
def invalidAge():
    yield True
    while not age.isdigit():
        print "Not a number"
        yield True
    yield False

for i in invalidAge():
    age = raw_input("Age please: ")

print age

Y en­ton­ces Fa­bian Ga­lli­na es el se­gun­do en men­cio­nar que en C po­dés asig­nar en con­di­cio­nes.

No pien­so acep­tar­lo. C no pue­de ser más fá­cil pa­ra es­to!

Así que con una ayu­di­ta del cook­book...

edad=[1]

while not edad |asig| raw_input('Edad? '):
    print u'Poné un número!'

print u'Tenes %s años'%edad[0]

¿Pero qué es |asig|? ¡Qué buena pregunta!

class Infix:
    def __init__(self, function):
        self.function = function
    def __ror__(self, other):
        return Infix(lambda x, self=self, other=other: self.function(other, x))
    def __or__(self, other):
        return self.function(other)
    def __rlshift__(self, other):
        return Infix(lambda x, self=self, other=other: self.function(other, x))
    def __rshift__(self, other):
        return self.function(other)
    def __call__(self, value1, value2):
        return self.function(value1, value2)

def opasigna (x,y):
    x[0]=y
    return y.isdigit()

asig=Infix(opasigna)

Y en­ton­ce­s, Pa­blo pos­tea es­ta jo­yi­ta:

import inspect

def assign(var, value):
    stack = inspect.stack()[1][0]
    stack.f_locals [var] = value
    del stack
    return value

while not assign("edad", raw_input('Edad? ')).isdigit():
    print u'No es un numero!'

print u'Tenes %s años' % edad

Que es, me pa­re­ce a mí, lo me­nos tri­vial que se pue­de lle­gar con es­te pro­ble­ma. Cla­ro que el hi­lo no se mu­rió to­da­vía ;-)


Contents © 2000-2023 Roberto Alsina