Como Hacer Una Actividad Sugar

Un programa Python autónomo para leer Etexts

El programa

Nuestro programa de ejemplo está basado en la primera Actividad que escribí, Read Etexts. Es un programa para leer e-books gratuitos.

La mejor y más antigua fuente de e-books gratuitos es un sitio web llamado Project Gutenberg (http://www.gutenberg.org/wiki/Main_Page). Ellos crean libros en formato de texto plano, en otras palabras, el tipo de archivo que podrías crear si escribieras un libro en Notepad y pulsaras la tecla Enter al final de cada línea. Ellos tienen miles de libros que no tienen derechos de autor, incluyendo algunos de los mejores jamás escritos. Antes de leer más, ve a ese sitio web y escoge un libro que te interese. Echa un vistazo a la lista "Top 100" para ver los libros y los autores más populares.

El programa que vamos a crear va a leer libros en formato de texto plano .

Existe un repositorio de Git que contiene todos los ejemplos de código de este libro. Una vez que hayas instalado Git puedes copiar el repositorio a tu computadora con este comando:

git clone git://git.sugarlabs.org/\
myo-sugar-activities-examples/mainline.git

El código para nuestro programa Python independiente se encuentra en el directorio Make_Standalone_Python en un archivo llamado ReadEtexts.py.  Se ve así:

#! /usr/bin/env python
import sys
import os
import zipfile
import pygtk
import gtk
import getopt
import pango

page=0
PAGE_SIZE = 45

class ReadEtexts():

    def keypress_cb(self, widget, event):
        "Respond when the user presses one of the arrow keys"
        keyname = gtk.gdk.keyval_name(event.keyval)
        if keyname == 'plus':
            self.font_increase()
            return True
        if keyname == 'minus':
            self.font_decrease()
            return True
        if keyname == 'Page_Up' :
            self.page_previous()
            return True
        if keyname == 'Page_Down':
            self.page_next()
            return True
        if keyname == 'Up' or keyname == 'KP_Up' \
                or keyname == 'KP_Left':
            self.scroll_up()
            return True
        if keyname == 'Down' or keyname == 'KP_Down' \
                or keyname == 'KP_Right':
            self.scroll_down()
            return True
        return False

    def page_previous(self):
        global page
        page=page-1
        if page < 0: page=0
        self.show_page(page)
        v_adjustment = \
            self.scrolled_window.get_vadjustment()
        v_adjustment.value = v_adjustment.upper - \
            v_adjustment.page_size

    def page_next(self):
        global page
        page=page+1
        if page >= len(self.page_index): page=0
        self.show_page(page)
        v_adjustment = \
            self.scrolled_window.get_vadjustment()
        v_adjustment.value = v_adjustment.lower

    def font_decrease(self):
        font_size = self.font_desc.get_size() / 1024
        font_size = font_size - 1
        if font_size < 1:
            font_size = 1
        self.font_desc.set_size(font_size * 1024)
        self.textview.modify_font(self.font_desc)

    def font_increase(self):
        font_size = self.font_desc.get_size() / 1024
        font_size = font_size + 1
        self.font_desc.set_size(font_size * 1024)
        self.textview.modify_font(self.font_desc)

    def scroll_down(self):
        v_adjustment = \
            self.scrolled_window.get_vadjustment()
        if v_adjustment.value == v_adjustment.upper - \
                v_adjustment.page_size:
            self.page_next()
            return
        if v_adjustment.value < v_adjustment.upper -\
            v_adjustment.page_size:
            new_value = v_adjustment.value + \
                v_adjustment.step_increment
            if new_value > v_adjustment.upper -\
                v_adjustment.page_size:
                new_value = v_adjustment.upper -\
                    v_adjustment.page_size
            v_adjustment.value = new_value

    def scroll_up(self):
        v_adjustment = \
            self.scrolled_window.get_vadjustment()
        if v_adjustment.value == v_adjustment.lower:
            self.page_previous()
            return
        if v_adjustment.value > v_adjustment.lower:
            new_value = v_adjustment.value - \
                v_adjustment.step_increment
            if new_value < v_adjustment.lower:
                new_value = v_adjustment.lower
            v_adjustment.value = new_value

    def show_page(self, page_number):
        global PAGE_SIZE, current_word
        position = self.page_index[page_number]
        self.etext_file.seek(position)
        linecount = 0
        label_text = '\n\n\n'
        textbuffer = self.textview.get_buffer()
        while linecount < PAGE_SIZE:
            line = self.etext_file.readline()
            label_text = label_text + unicode(line,
                'iso-8859-1')
            linecount = linecount + 1
        label_text = label_text + '\n\n\n'
        textbuffer.set_text(label_text)
        self.textview.set_buffer(textbuffer)

    def save_extracted_file(self, zipfile, filename):
        "Extract the file to a temp directory for viewing"
        filebytes = zipfile.read(filename)
        f = open("/tmp/" + filename, 'w')
        try:
            f.write(filebytes)
        finally:
            f.close

    def read_file(self, filename):
        "Read the Etext file"
        global PAGE_SIZE

        if zipfile.is_zipfile(filename):
            self.zf = zipfile.ZipFile(filename, 'r')
            self.book_files = self.zf.namelist()
            self.save_extracted_file(self.zf,
                self.book_files[0])
            currentFileName = "/tmp/" + self.book_files[0]
        else:
            currentFileName = filename

        self.etext_file = open(currentFileName,"r")
        self.page_index = [ 0 ]
        linecount = 0
        while self.etext_file:
            line = self.etext_file.readline()
            if not line:
                break
            linecount = linecount + 1
            if linecount >= PAGE_SIZE:
                position = self.etext_file.tell()
                self.page_index.append(position)
                linecount = 0
        if filename.endswith(".zip"):
            os.remove(currentFileName)

    def destroy_cb(self, widget, data=None):
        gtk.main_quit()

    def main(self, file_path):
        self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
        self.window.connect("destroy", self.destroy_cb)
        self.window.set_title("Read Etexts")
        self.window.set_size_request(640, 480)
        self.window.set_border_width(0)
        self.read_file(file_path)
        self.scrolled_window = gtk.ScrolledWindow(
            hadjustment=None, vadjustment=None)
        self.textview = gtk.TextView()
        self.textview.set_editable(False)
        self.textview.set_left_margin(50)
        self.textview.set_cursor_visible(False)
        self.textview.connect("key_press_event",
            self.keypress_cb)
        buffer = self.textview.get_buffer()
        self.font_desc = pango.FontDescription("sans 12")
        font_size = self.font_desc.get_size()
        self.textview.modify_font(self.font_desc)
        self.show_page(0)
        self.scrolled_window.add(self.textview)
        self.window.add(self.scrolled_window)
        self.textview.show()
        self.scrolled_window.show()
        v_adjustment = \
            self.scrolled_window.get_vadjustment()
        self.window.show()
        gtk.main()

if __name__ == "__main__":
    try:
        opts, args = getopt.getopt(sys.argv[1:], "")
        ReadEtexts().main(args[0])
    except getopt.error, msg:
        print msg
        print "This program has no options"
        sys.exit(2)

Ejecutar el programa

Para ejecutar el programa primero debes hacerlo ejecutable. Sólo tienes que hacer esto una vez:

chmod 755 ReadEtexts.py

Para este ejemplo he descargado el archivo de Pride and Prejudice. El programa trabaja con cualquiera de los formatos de texto plano, puede ser texto sin compresión o un archivo Zip. El archivo zip se llama 1342.zip, y podemos leer el libro ejecutando este archivo desde una terminal:

./ReadEtexts.py 1342.zip

Así es como se ve el programa en acción:


¿Cómo funciona el programa?

Este programa lee el archivo de texto que contiene el libro y lo divide en páginas de 45 líneas cada una. Tenemos que hacer esto, porque el componente gtk.TextView que usamos para ver el texto necesitaría una gran cantidad de memoria para desplazarse por todo el libro y bajaría el rendimiento. Una segunda razón es que queremos hacer la lectura del libro electrónico lo más parecida posible a la de un libro normal, y los libros normales tienen páginas. Si un profesor asigna la lectura de un libro podría decir leer las páginas 35 a 50 para mañana". Por último, queremos que este programa recuerde en qué página dejaste de leer y te lleve de vuelta a esa página la próxima vez que leas el libro. (El programa que tenemos aún no lo hace).

Para desplazarnos por el libro usamos acceso aleatorio para leer el archivo. Para entender lo que significa acceso aleatorio a un archivo, considera una cinta VHS y un DVD. Para llegar a una cierta escena en una cinta VHS tienes que pasar por todas las escenas que hay antes que ella, en orden. A pesar de que lo haces a gran velocidad, aún tienes que mirar todas para encontrar el lugar en que quieres empezar a ver. Este es el acceso secuencial. Por otro lado un DVD tiene paradas por capítulo y, posiblemente, un menú de capítulos. Usando el menú de capítulos puedes ver cualquier escena en la película de inmediato, y puedes saltar como quieras. Este es el acceso aleatorio o al azar, y el menú de capítulos es como un índice. Por supuesto, también puedes acceder al material en un DVD de forma secuencial .

Necesitamos acceso aleatorio para saltar a cualquier página que queramos, y necesitamos un índice para que sepamos dónde comienza cada página. Creamos el índice leyendo el archivo completo una línea a la vez. Cada 45 líneas creamos una nota con la cantidad de caracteres que se han introducido en el archivo y almacenamos esta información en una lista de Python. Después volvemos al principio del archivo y mostramos la primera página. Cuando el usuario del programa va a la página siguiente o anterior, averiguamos cuál será el nuevo número de página y buscamos esa página en el registro de la lista. Esto nos dice que la página comienza en el carácter 4200 del archivo. Nosotros usamos seek () en el archivo para ir a ese carácter y, a continuación, leemos 45 líneas partiendo de ese punto y las cargamos en TextView.

Cuando ejecutes este programa fíjate lo rápido que es. Los programas Python toman más tiempo para ejecutar una línea de código del que tomarían en un lenguaje compilado, pero eso no importa en este programa, ya que el trabajo pesado es realizado por TextView que fue creado en un lenguaje compilado. Las partes de Python no hacen demasiado, así que el programa no demora mucho tiempo en ejecutarlas.

Sugar utiliza mucho Python, no sólo para las Actividades, sino también para el entorno Sugar en sí mismo. Puedes leer en algún lado que usar tanto Python es un "desastre" para el rendimiento. No lo creas.

No hay lenguajes de programación lentos, sólo programadores lentos.

1  

  1. Traducido Santiago Zito Uruguay^


your comment:
name :
comment :

If you can't read the word, click here
word :