miércoles, 28 de diciembre de 2011

Visor de imágenes para xkcd

Una de mis tiras cómicas favoritas es xkcd, y desde ya hace tiempo quería descargarme todas las tiras para tenerlas en el disco duro. Si han leido xkcd seguro sabran de los textos adicionales que Randall Munroe pone en el atributo title de las imágenes, este texto es complementario a la tira y en muchas ocasiones mas gracioso que la tira misma.

Así que si descargaba las tiras tenia que hacerlo con todo y titulo. Tuve un tiempo libre esta navidad y me puse a recolectar información de como hacerlo, lo primero era descargar las imágenes y guardar el texto del tooltip en los metadatos de la imagen, para esto me fue de mucha ayuda este articulo donde se explica como hacerlo usando la libreria PIL.

Lo siguiente era crear un visor de imágenes que mostrara los tooltip, algo complicado y mas teniendo en cuenta que esta es la segunda interfaz gráfica "seria" que hago, pero encontré muy buenos recursos como este libro online sobre tkinter con muy buenos ejemplos, ademas de un script que implementa un tooltip que me cayo de perlas.

Luego de varias horas de pelearme acomodando los widget (con pack y grid), he aquí el visor de imágenes junto con todas las tiras hasta la fecha (1-995).
Si bajan el código fuente necesitaran instalar las siguientes librerías:
  • PIL (en Windows y Linux)
  • ImageTk (solo en Linux: python-imaging-tk)
  • Tkinter (solo en Linux)
Si tienen alguna sugerencia no duden en dejarla en los comentarios, siempre es bueno saber en que mejorar.

lunes, 26 de diciembre de 2011

Actualización: canvas-event-js 0.2

Después de meses sin actualizar, he aquí algunas cosas nuevas que pueden encontrar en canvas-event:
Ideas que tengo pero no he implementado:
  • Drag-n-Drop live para evitar llamar drag en cada objeto creado, ya que demasiados callback pueden poner lenta las aplicaciones.
  • moveUp, moveDown, toFront, toBack: para mover objetos entre capas (como en PowerPoint). 
Todo esto lo pueden encontrar en el repositorio de github a donde he movido el proyecto, donde ademas pueden dejar sus sugerencias y reportes de errores, su ayuda es muy importante y siempre bien recibida.

P.D.: Has creado alguna aplicación usando canvas-event, deja el link en los comentarios (simple curiosidad *¬*)

viernes, 23 de diciembre de 2011

Creando un blog con django - parte 3

En la parte anterior dejamos la base de datos lista, es momento de crear las plantillas para visualizar estos datos.
Lo primero que haremos es configurar el directorio donde estaran guardas las plantillas. Creamos el directorio templates en la raíz del proyecto y editamos el archivo settings.py.
.....
import os

TEMPLATE_DIRS = (
    os.path.join(os.path.dirname(__file__) ,"templates"),
)
....
Así el script puede encontrar el directorio templates aun cuando cambiemos de ubicación la carpeta del proyecto. Ahora editamos el archivo urls.py para incluir la url a la pagina.
....
urlpatterns = patterns('',
    (r'^$', 'blog.views.index'),
    ....
)
Con esto le decimos a django que toda petición hecha a la raíz del blog sera manejada por la función index ubicada en el archivo views, la cual luce así:
from django.shortcuts import render_to_response

def index(request):
  return render_to_response("index.html")
Al visitar la raíz del sitio, django llamara la función index pasando como argumento un objeto request que contiene información sobre la solicitud (datos POST y GET entre otros). La función index realizar algún proceso y retornar un objeto response.

Si iniciamos el servidor de desarrollo y entramos a la dirección localhost:8000 veremos un mensaje de error como el de la imagen ya que la plantilla index.html aun no existe, así que manos a la obra.
<!DOCTYPE HTML>
<html lang="es-ES">
<head>
 <title>Mi Blog</title>
</head>
<body>
<!-- hasta aquí siempre es igual -->
<h1>Hola Django</h1>
</body>
No muy útil, pero por el momento es todo lo que necesitamos. Imagine que creamos otras plantillas para post individuales, contacto, faq, quejas... debemos repetir la cabecera en cada una de ellas.

El sistema de plantillas de django permite crear plantillas base con bloques que luego pueden ser reemplazados por las plantillas que heredan de ésta; para tal efecto creamos la plantilla base.html:
<!DOCTYPE HTML>
<html lang="es-ES">
<head>
 <title>Mi Blog</title>
</head>
<body>
{% block content %}
{% endblock %}
</body>
Y editamos la plantilla index.html para que extienda base.html:
{% extends "base.html" %}
{% block content %}
<h1>Hola Django</h1>
{% endblock %}
Ok, teniendo una idea general del sistema de plantillas, seguimos con la pagina inicial del blog empezando por editar el archivo views.py.
from django.shorcuts import render_to_response
from blog.models import Post

def index(request):
    # recuperamos 5 primeras entradas
    posts = Post.objects.filter()[:5]
    return render_to_response("index.html", locals())
Cada vez que visitemos a la raíz del blog se consultaran los primeros 5 post y se pasaran a la plantilla index.
{# plantilla index.html #}
{% extends "base.html" %}

{% block content %}
{% for post in posts %}
    <h1><a href="/{{ post.id }}">{{ post.title }}</a></h1>
<p>{{ post.pub_date}}</p>
{{ post.body }}
    {# esto no es muy eficiente* #}
    {% for tag in post.tags.filter %}
        <a href="/tags/{{ tag.tag }}">{{ tag.tag }}</a>
    {% endfor %}
{% endfor %}
{% endblock %}
* Extraer las etiquetas de esta forma no es eficiente ya que al llamar a filter (5 veces) se hace una nueva consulta a la base de datos, algunas soluciones se pueden encontrar aquí.

Con esto la pagina principal esta lista. Sigamos con la pagina individual, veremos que al trabajar con django se sigue mas o menos el mismo patrón:
# archivo urls.py
urlpatterns = patterns('',
    (r'^$', 'blog.views.index'),
    # expresión regular que captura el numero después de post/
    (r'^post/(?P<id>\d+)$', 'blog.views.single'),
)
# Archivo views.py
...
# la funcion recibe el numero capturado
def single(request, id=0):
    # recuperar post según id
    post = Post.objects.get(id=int(id))

    return render_to_response("single.html", locals())
{# plantilla single.html #}
{% block content %}
    <h1><a href="/{{ post.id }}">{{ post.title }}</a></h1>
    <p>{{ post.pub_date}}</p>
    {{ post.body }}
    {# esto no es muy eficiente* #}
    {% for tag in post.tags.filter %}
        <a href="/tags/{{ tag.tag }}">{{ tag.tag }}</a>
    {% endfor %}
{% endblock %}
Notan algo raro? el código de single.html e index.html luce muy similar; podemos eliminar el código repetido moviéndolo a otra plantilla (article.html) que luego incluimos en estas.
{# plantilla article.html #}
</div>
    <h1><a href="/{{ post.id }}">{{ post.title }}</a></h1>
    <p>{{ post.pub_date}}</p>
    {{ post.body }}
    {# esto no es muy eficiente* #}
    {% for tag in post.tags.filter %}
        <a href="/tags/{{ tag.tag }}">{{ tag.tag }}</a>
    {% endfor %}
</div>
Ahora editamos las plantillas index.html y single.html para que utilicen article.html.
{# plantilla inde.html #}
{% extends 'base.html' %}

{% block main_content %}
    {% for post in posts %}
        {# pasamos la variable post como argumento #}
        {% include 'include/article.html' with post=post %}
    {% endfor %}

{% endblock %}
{# plantilla single.html #}
{% extends 'base.html' %}

{% block main_content %}
      {% include 'include/article.html' %}
{% endblock %}
Con esto damos por terminado esta parte. No se olviden de comentar.

P.D.: Procurare tener la próxima parte mas rápido.

viernes, 9 de diciembre de 2011

Actualización mget

Debido al rediseño de megaupload mget ha dejado de funcionar, el problema se debe específicamente a que el link de descarga ya no se identifica con el id downloadlink sino con la clase download_regular_usual, ademas el tiempo de espera ahora es de 60 segundos.

Por el momento no tengo como arreglar la versión para windows pero la de linux ya esta lista, solo hay que reemplazar el archivo antiguo y dar permisos de ejecución.

sábado, 26 de noviembre de 2011

Creando un blog con django - parte 2


Llego el momento de crear los modelos, pero antes analicemos los datos que necesitamos guardar. Nuestro blog tendrá post, comentarios y tags, los que a su vez se componen de:
  • Post: fecha, titulo, contenido y etiquetas.
  • Etiqueta: nombre.
  • Comentario: autor, fecha, contenido.
Ahora bien, una etiqueta puede estar relacionada con varios post, así como un post tener varias etiqueta, por lo que tienen una relación many to many. Los comentarios por otro lado solo pertenecen a un post por lo que necesitan una clave foránea que lo relacione con el respectivo post.

Teniendo claro el modelo de la base de datos, es hora de escribir el código necesario para crear las tablas, afortunadamente django nos facilita la tarea. Editamos el archivo models.py ubicado en la carpeta de la aplicación:
# 1
from django.db import models

# 2
class Tag(models.Model):
    # 3
    tag = models.CharField(max_length=100, unique=True, db_index=True)
    # 4
    def __unicode__(self):
        return self.tag
Veamos que hicimos:
  1. Importamos el modulo models.
  2. Creamos una subclase de models.Model, 
  3. Cada columna de la bd es representada por un atributo, en este caso tag es de tipo caracter (CharField), existen otros tipos de campo según el valor almacenar. Los argumentos describen las propiedades del campo como la cantidad de caracteres que puede contener (max_length), que no se permiten valores repetidos (unique), ademas de hacer que se indexe (db_index) ya que es comun hacer búsquedas como: buscar todos los post con tag django.
  4. el método __unicode__ permite dar un nombre mas significativo a las instancias, este es utilizarlo en el sitio administrativo.
Sabiendo esto es fácil crear las demás tablas:
# post
class Post(models.Model):
    # la relación many to many entre Post y Tag
    tags = models.ManyToManyField(Tag, blank=True)
    title = models.CharField(max_length=200)
    body = models.TextField()
    # fecha de publicación, se agrega fecha automáticamente
    pub_date = models.DateField(auto_now_add=True)

    def __unicode__(self):
        return self.title

class Comment(models.Model):
    # Cada comentario pertenece a un post
    post = models.ForeignKey(Post)
    # nombre es opcional (blank) y por defecto es Anonimus (default)
    name = models.CharField(max_length=100, blank=True, default="Anonimus")
    # el cuerpo del comentario
    body = models.TextField(verbose_name="comment")
    # fecha de publicación, se agrega fecha automáticamente
    date = models.DateField(auto_now_add=True)

    def __unicode__(self):
        return "Por " + self.name + " en " + self.post.title
La clave foránea y relación many to many se crea en una simple linea de código. Ahora sincronizamos nuevamente y entramos al interprete de python para introducir información en la base de datos:
$ python manage.py syncdb
$ python manage.py shell
# importamos los modelos
from blog.models import Tag, Post

# aun no hay tags así que creamos una
django = Tag(tag="django")
django.save()

# creamos un post
new_post = Post(title="mi primer post", body="El primer post que realizo")
new_post.save()
# agregamos un tag y volvemos a guardar
new_post.tags.add(django)
new_post.save()
Como vemos, para introducir un registro en la base de datos creamos una instancia de la clase adecuada y seguidamente llamamos el método save, ¿pero como recuperamos un registro? existen tres métodos para consultar la base de datos:
  • get: retorna un solo registro, lanza una excepción si la consulta retorna cero o mas de un registro.
  • filter: retorna un array con los registros que coinciden la consulta o un array vacío.
  • exclude: similar a filter pero retorna los registros que no cumplen con la consulta.
Estos métodos se acceden a través del atributo objects que poseen las subclases de models.Model.
# recuperar todos los post
Post.objects.filter()

# recuperar los primeros 10
Post.objects.filter()[:10]
Para refinar la búsqueda usamos argumentos que corresponden con los campos de la tabla:
# post segun titulo
Post.objects.filter(title="mi primer post")
Los campos que son claves foraneas (Post.tags, Comment.post) son algo especial: podemos seguir la relación escribiendo el nombre de la columna seguido de dos guiones bajos (_), pudiendo acceder el modelo al que apunta dicha relación. veamos un ejemplo:
# post con tag django
Post.objects.filter(tags__tag="django")
Al usar tags__ ya no estamos mas en Post sino en Tag, ahora podemos consultar los campos de esta tabla (tag). otro ejemplo:
# comentarios de posts con tag django
Comment.objects.filter(post__tags__tag="django")
Seguimos la relación a Post (post__) y consultamos la columna tags__ lo que nos lleva a Tag donde finalmente consultamos el atributo tag o_O.

Con esto tenemos lista la base de datos, en la próxima parte veremos como crear plantillas para visualizar estos datos.

Muchas gracias por su atención, no olviden dejar sus sugerencias, inquietudes y amenazas en los comentarios, hasta la próxima.

viernes, 25 de noviembre de 2011

Extrae imágenes de archivos PowerPoint


Versión corta: pues que escribí una aplicación para extraer imágenes de archivos PowerPoint y la pueden descargar de aquí.

Versión larga:

Nuca he entendido esa manía de algunas personas de estar enviando PowerPoints a diestra y siniestra por lo que todo el que llega termina en la papelera sin leerlo XD.

Un conocido al que si le encantan me pidió un programa para extraerles las imágenes. Encontré dos pero ninguno funciono, así que escribí un script en python que pueden encontrar en github (advertencia: visualizar el código puede causar sangrado de ojos). Ya estando en ello le cree interfaz gráfica (la primera que hago). Eso sí, es algo.... feeeeaaaa, pero en el proceso puede recuperar imágenes porno aprender algo sobre threads, instaladores y otras cosas de las que escribiré mas adelante, por el momento he aquí el cuerpo del delito: PowerPoint Extractor.


Eso es todo. no olviden seguirme en twitter y/o suscribirse al feed del blog.

Actualización: En github pueden encontrar un modulo para dicho fin que funciona tanto en Windows como en Linux.

jueves, 24 de noviembre de 2011

Creando un blog con django - parte 1

the killer framework
Primero quiero dejar claro que no soy un experto en django, así que si cometo algún error no duden en hacérmelo saber.

Este tutorial se divide en varias partes:
  1. instalación y configuración
  2. creación de modelos
  3. creación de vistas
  4. formularios
Instalación y configuración

Procedemos a descargar la ultima versión de django e instalarla como dice la pagina oficial:
$ wget http://www.djangoproject.com/download/1.3.1/tarball/
$ tar xzvf Django-1.3.1.tar.gz
$ cd Django-1.3.1
$ sudo python setup.py install
Ahora haciendo uso del script de administración creamos nuestro proyecto:
$ django-admin startproject tutorial
Esto crea el directorio tutorial, con los siguientes archivos:
  • manage.py: script para administrar el proyecto (sincronizar bd, crear aplicaciones, iniciar servidor...)
  • urls.py: las urls admitidas.
  • settings.py: configuración del proyecto (aplicaciones instaladas, directorios de plantillas, bd...)
Lo primero que haremos es crear nuestra aplicación (blog) usando el script manage.py
$ python manage.py startapp blog
Este comando crea el directorio blog con varios archivos en su interior, los cuales analizaremos en otra ocasión. Para terminar editamos el archivo settings.py e incluir la configuración de la base de datos y agregamos nuestra aplicación a la lista de aplicaciones instaladas:
DATABASES = {
'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': 'myblog',
        # lo demás no es necesario para una bd sqlite
        'USER': '', 
        'PASSWORD': '',
        'HOST': '',
        'PORT': '',
    }
}
...
...
INSTALLED_APPS = (
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.sites',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'blog', # Nuestra aplicación
)
Sincronizamos la base de datos para crear las tablas necesarias:
$ python manage.py syncdb
Se pedirán algunos datos para crear la cuenta de administrador. Luego iniciamos el servidor de desarrollo con:
$ python manage.py runserver
Y listo, si entramos a la dirección localhost:8000 veremos una flamante pagina web confirmando que django esta correctamente configurado y listo para iniciar a desarrollar nuestra aplicación.

Eso fue todo por ahora, muchas gracias por su atención y no olviden estar pasando para la segunda parte.

miércoles, 16 de noviembre de 2011

Ejemplo método find de canvas-event


Varias personas han estado buscando como usar el método find de canvas-event, por lo que dejo un ejemplo de su uso mientras termino la documentación.

Interfaz del programa Kmines
Usando el método find, es posible seleccionar un grupo de objetos y aplicarles un método o manejador de evento. para esto debemos usar un selector como:
  • tipo: Corresponde con el nombre del método usado para crear el objeto (circle, rect, line...).
  • id: Un nombre que podemos dar a un objeto con el fin de diferenciarlo de otros del mismo tipo.
Imagine estar creando un buscaminas, el campo esta lleno con varios rectángulos: unos son minas y otros no. Todos los rectángulos tienen el mismo color, pero solo los rectángulos mina explotan y terminan el juego, mientras los demás aumentan el puntaje del jugador.

Nota Al igual que en css, el símbolo # indica que mina y safe son id.
for (var i = 0; i < 5; i++) {
        ce.rect(i*10, 0, 10).setId("mina");
    }
    
    for (var i = 0; i < 5; i++) {
        ce.rect(i*10; 10; 10).setId("safe");
    }
    
    // selector tipo: todos los rectángulos, tanto #mina como #safe
    ce.find("rect").attr("fill", "gray");
    
    // selector id: solo rectángulos #mina
    ce.find("#mina").click(function(c, e) {
        alert("Boom!");
        alert("Game Over");
    });
    
    // selector id: solo rectángulos #safe
    ce.find("#safe").click(function(c, e) {
        puntos += 1;
    });
Por otro lado el método getAll (solo en repo de github) retorna un array con los objetos que coinciden con el selector:
// encontrar y retornar los objetos rect dibujados hasta el momento
var rects = ce.findAll("rect");
// rects ahora es igual a [Rect, Rect, Rect...]
    
// obtener el primer circulo dibujado
var first_circle = ce.getAll("circle")[0];
Si tienen dudas o sugerencia pueden dejarlas en los comentarios, prometo tenerlas en cuenta.

martes, 15 de noviembre de 2011

Lecciones aprendidas

Andando por hacker news (hago muy frecuente), encontré un interesante desafió de instagram, que consiste en crear un programa que tome una imagen cortada en tiras y la "reconstruya". leyendo los comentarios parece que para otros era algo trivial, pero para mi era todo un reto y una oportunidad de poner en practica conceptos aprendidos en alguna lecturas.

Durante el proceso de resolver este problema, aprendí varias cosas:

Estudiar siempre nuevos algoritmos, herramienta, librerías..., no intentando memorizar o convertirse en un "experto". Basta con entender los conceptos aplicados y hacer algunas practicas. lo esencial es tener la capacidad de asociar estos conocimientos con nuevos problemas, ya luego podremos retornar y re-estudiarlos en el momento que los necesitemos.

Nunca compararnos con otros, al compararnos con otra persona pueden ocurrir dos cosas:
  • Nos frustramos pensando que no podremos llegar a ser tan buenos como EL(LA).
  • Dejemos de avanzar al creernos superior a otros y pensar que con lo que sabemos es suficiente, recordemos que somos novatoz.
Por otro lado al comparamos con nosotros mismos, veremos lo que realmente hemos avanzado: hace unos dos años no entendía ni el funcionamiento de un loop while, y hoy puede resolver un puzzle que aunque trivial para otros, para mi era como escalar el Everest.

Esto nos lleva a otro punto: Asumir retos, si nos quedamos auto contemplando lo bien que hacemos una tarea, no avanzamos, debemos desafiarnos. puede que fracasemos, pero esto no quiere decir que no aprendimos. Siempre aprendemos y lo hacemos mas de los errores que de los triunfos, si fracasamos, creemos una lista con las causas y usémosla como temas a estudiar.

Bueno eso es todo (creo), has tenido una "revelación" como esta. compártela los comentarios.

martes, 1 de noviembre de 2011

ConceptMapp - webapp para crear mapas conceptuales

Después de varios meses de posponerlo, por fin esta disponible mi primera aplicación web: ConceptMapp, una herramienta para la creación de mapas conceptuales completamente gratis.

La idea nació luego de estar experimentando con el elemento canvas, entonces me acorde de Cmaptools, uno de los mejores programas que he utilizado para crear mapas conceptuales, pero que a mi parecer tiene los siguientes inconvenientes:
  • Consume muchos recursos.
  • No es muy común: no se puede esperar encontrarlo instalado en otros computadores diferentes al personal, lo que impide realizar cambios cuando mas se necesitan.
Con ConceptMapp intento resolver estos problema, permitiendo acceder y editar mapas conceptuales desde cualquier computador con un navegador web moderno (a.k.a no ie <= 8) y una conexión a Internet, ademas de ser liviano*.

Si les parece útil, encuentran algún error o tienen sugerencias pueden dejarlas en los comentarios o usando el formulario de contacto.

* Probado en un PC de: 1 GB de RAM con procesador de 1.8 GHz.

viernes, 23 de septiembre de 2011

mget ahora para windows

Actualización: se incluyeron las dependencias del wget que faltaban, que pena u_u.

Un amigo me pidió que le pasara el programa con el que descargo de megaupload, pero en cuanto le dije que tenia que:
  1. Descargar e instalar python 2.6.
  2. Descargar e instalar wget para Windows.
  3. Poner los directorios de wget y python en el PATH de Windows.
En seguida se desanimo (no se por que XD), así que me pase toda una tarde mejorando un poco el programa e intentando convertirlo en un ejecutable usando pyinstaller y he aquí el resultado:
Basta con descomprimir los archivos en la carpeta windows y a descargar de megaupload. Lo probé en varias maquinas con Windows XP y funciono correctamente, pero si tienen algún problema no duden en dejarlo en los comentarios.

Su uso es muy sencillo, abren la consola de comandos y:
mget -c url-con-link-de-megaupload

mget -i archivo-con-links

mget link1 link2 link3 ...
Para una próxima explico como fue lo de convertir el script en ejecutable, algo muy util para compartir los programas que hagamos con amigos.

Pueden seguirme en twitter o agregarme en Google+

viernes, 9 de septiembre de 2011

Me decepciona el estudio

Viendo a mi hermano desesperado intentando grabarse un montón de formulas porque el día siguiente tenia una evaluación, me dí de cuenta que el sistema de "enseñanza" es una perdida de tiempo, los profesores no se preocupan por estimular a los estudiantes a aprender, sino por embutirles información. La única "motivación" que se tiene es la de pasar al siguiente curso.

Recuerdo cuando estaba estudiando que aunque era muy bueno para el álgebra y la geometría, realmente no le veía mucha utilidad practica y luego de pasar la nota dejaba de estudiar, ya que no me interesaba profundizar el tema.

Creo que la educación mejoraría si en lugar de hacer que los alumnos memoricen una gran cantidad de información, se hace que éstos ganen interés en el tema a estudiar.

Por ejemplo: matemáticas - promedios. En la escuela vi promedios alrededor del 6 grado. fuera de hacer muchos ejercicios como el promedio de edades, precios... cosas que a alguien de esa edad no le hacen mucha gracia. pero que tal si se muestra algo interesante (para un niño). por ejemplo como gracias a los promedios se puede convertir una fotografía a color en una a blanco y negro.
tal vez alguien diga que es algo muy complicado para explicar a un estudiante de 6 grado, pero el objetivo es hacer que éste diga: "WOW las matemáticas son una bacaneria" y entonces se interese realmente en aprender y no solo en ganar el año.

miércoles, 24 de agosto de 2011

Rehacer - Deshacer en javascript

En este tutorial intentaré explicar la forma de implementar la funcionalidad de deshacer y rehacer, una característica muy usada en los programas de escritorio y que muchas aplicaciones web no tienen (y hace mucha falta ya que errar es de humanos).
Descargar código
La idea consiste en tener objetos que representen las acciones (mover, eliminar, resaltar...), los cuales deben guardar el estado anterior del objeto afectado.
ChangeColorCommand = function(obj, color) {
  this.obj = obj;
  this.new_color = color;

  // guardar estado anterior
  this.prev_color = obj.style.backgroundColor;
}
		
ChangeColorCommand.prototype.execute = function() {
  this.obj.style.backgroundColor = this.new_color;
}
		
ChangeColorCommand.prototype.undo = function() {
  this.obj.style.backgroundColor = this.prev_color;
}
Cada objeto cuenta con dos métodos
  • execute: ejecuta la acción (dah)
  • undo: deshace la acción valiéndose de los datos guardados previamente en el constructor.
Las acciones son administradas por un objeto que mantiene dos pilas de acciones: las que se pueden deshacer y las que se pueden rehacer:
  • cuando ejecuta una acción, la guarda en undo_stack y borra toda acción que se podía rehacer
  • cuando se deshace algo, saca el ultimo objeto de la pila undo_stack, ejecuta el método undo y guarda la acción en la pila redo_stack.
CommandManager = function(max_undo) {
  // máxima cantidad de acciones guardadas
  max_undo = max_undo || 30;  

  // pilas de acciones
  this.undo_stack = [];
  this.redo_stack = [];

  // ejecutar comando cmd
  this.executeCommand = function(cmd){
    cmd.execute();

    // si se sobrepasa cantidad de acciones
    // eliminar primer elemento
    if (this.undo_stack.length &amp;gt;= max_undo) {
      this.undo_stack.shift();
    }
    this.undo_stack.push(cmd);
    this.redo_stack = [];
  }

  // deshacer acción 
  this.undoCommand = function() {
    var cmd = this.undo_stack.pop();
    
    // si existe acción
    if ( cmd ) {
      cmd.undo();
      this.redo_stack.push(cmd);
    }
  }
}
Para realizar una acción creamos una instancia del objeto apropiado y la pasamos al CommandManager (previamente creado).
var UndoRedo = new CommandManager(),
    box = document.getElementById("box");

// cambiar color a rojo
UndoRedo.executeCommand(new ChangeColorCommand(box, "red"));

// cambiar color a verde
UndoRedo.executeCommand(new ChangeColorCommand(box, "green"));

// cambiar de verde a rojo
UndoRedo.undoCommand();

// cambiar de rojo a blanco
UndoRedo.undoCommand();
Ahora solo resta crear mas acciones (mover, negrita, itálica...) dependiendo de la aplicación que estemos desarrollando.

Eso es todo, espero les haya servido; si tienen alguna duda o critica no duden en dejarla en los comentarios.

miércoles, 13 de julio de 2011

Objetivos

Luego de participado en el campus party, me di cuenta de mi total desconocimiento en temas importantes en la computación, por ejemplo en el desafió de desarrollo con solo saber un algoritmo de búsqueda, habría logrado un mejor puesto. Así que a partir de ahora pienso estudiar - y practicar - algunos temas mas "profundos", en lugar de solo centrarme en el desarrollo web y uso de frameworks, así como por el momento centrarme en aprender python y dejar de lado C.

viernes, 24 de junio de 2011

Rumbo a campus party

¿Como? ¿que voy a campus party gratis?
Pues eso, por fin puedo ir a campus party y lo mejor de todo: gratis. iré en representación del SENA Vichada a hacer una presentación de canvas-event, la noticia me sorprendio tanto que solo lo creí hasta el día que fui a comprar los boletos de avión.

P.D.: Estaré publicando en twitter lo que me parezcan importantes.

miércoles, 15 de junio de 2011

No soy picado ni raro, soy introvertido

Andando por la web me encontré este interesante articulo, en donde se desmienten algunos de los mitos que se tienen sobre las personas introvertidas, está en perfecto ingles, pero luego de algo de búsqueda encontré una traducción decente.

La verdad me parece muy acertado todo, pero resalto los siguientes puntos:

Mito #1 - A los introvertidos no les gusta hablar
Esto no es cierto. Es solo que los introvertidos no hablan a menos que tengan algo que decir. Odian la charla. Haz que un introvertido se ponga a hablar acerca de algo que le interesa, y no se callará por días

Mito #2 - Los introvertidos son tímidos
La timidez no tiene nada que ver con ser un introvertido. A los introvertidos no necesariamente les asusta la gente. Lo que necesitan es una razón para interactuar. No interactúan solo por interactuar. Si quieres hablar con un introvertido, solo empieza a hablar. No te preocupes por ser educado.

Mito #6 - Los introvertidos siempre quieren estar a solas
Los introvertidos están perfectamente cómodos con sus propios pensamientos. Piensan mucho. Sueñan despiertos. Les gusta tener problemas en los cuales trabajar, rompecabezas que resolver. Pero también pueden sentirse increiblemente solos si no tienen a nadie con quién compartir sus descubrimientos. Ansían una conexión auténtica y sincera con UNA PERSONA a la vez

Mito #7 - Los introvertidos son extraños
Los introvertidos suelen ser individualistas. No siguen a la multitud. Preferirían ser valorados por sus maneras originales de vivir. Piensan por sí mismos y, por ello, frecuentemente desafían las normas. No toman la mayoría de las decisiones basadas en lo que es popular o está de moda.

Mito #9 - Los introvertidos no saben cómo relajarse y divertirse
Los introvertidos típicamente se relajan en casa o en la naturaleza, no en lugares públicos concurridos. Los introvertidos no son buscadores de emociones fuertes ni adictos a la adrenalina. Si hay mucha charla y ruido, se encierran. Sus cerebros son muy sensibles al neurotransmisor llamado dopamina. Los introvertidos y los extrovertidos tienen diferentes vías neuronales dominantes. Simplemente consúltalo.

Mito #10 - Los introvertidos pueden arreglarse a sí mismos y volverse extrovertidos
... hay todavía abundantes técnicas que un extrovertido puede aprender para interactuar con un introvertido. (Sí, puse al revés esos dos términos a propósito para mostrarte cuán parcializada está nuestra sociedad). Los introvertidos no pueden "arreglarse a si mismos" y merecen respeto por su temperamento natural y contribuciones a la raza humana...

Como ven, los introvertidos no somos picados o raros por el hecho de no hablar mucho ni salir de rumba, es simplemente que como a todos, hay cosas que no nos llenan.

jueves, 28 de abril de 2011

Personas que me desagradan

El titulo del post puede parecer fuerte, pero esto tengo de decirlo o sino exploto, hay cierto tipo de personas que me desagradan y últimamente me encuentro con muchos de ellos:
  • Los que no aceptan estar equivocados: esas personas que ni aun con pruebas en la mano de que lo que están diciendo es un completo disparate aceptan la corrección, realmente no entiendo a este tipo de personas ya que equivocarse es necesario para aprender y mejorar.
  • Los que menosprecian a los demás: esas personas que creen que son mas que los demás, especialmente los que porque saben algo tratan a los demás como estúpidos que no pueden llegar a tener el "vasto" conocimiento que ellos poseen, tal vez no se acuerdan que ellos en un momento también fueron novatoz.
  • Los que quieren que les den todo mascadito: esas personas que quieren las respuestas mas no aprender, no estoy en contra de los que preguntan, estoy en contra de los que quieren que les hagan el trabajo, por ejemplo: "necesito hacer X pero no se, alguien puede darme el código para poder hacerlo", se ve que al tipo le importa un comino aprender,  y: "no se realizar X, alguien podría decirme como lo puedo realizar", se nota que quiere aprender.
Listo, lo dije, Ahora me siento mas liberado.

viernes, 15 de abril de 2011

Beta 2 de canvas-event disponible

Ya esta disponible la beta 2 de canvas-event, esta revisión cuenta con algunas mejoras y corrección de errores como:
  1. Posibilidad de adicionar una función a varios eventos simultáneamente
  2. Las figuras dibujadas dentro de un manejador de eventos ahora si se visualizan
  3. Los Objetos Line ahora también pueden ser arrastrados
  4. Los bordes redondeados ahora también funcionan en Opera
Espero que les sea tan útil como lo ha sido para mi.

lunes, 24 de enero de 2011

script: descarga automática de Megaupload

Aprovechando que estoy aprendiendo a programar en bash y que megaupload ahora no requiere llenar ningún captcha para realizar las descargas, decidí rescribir el anterior script (escrito en python) y agregarle nuevas características.

Ahora es posible indicar una URL (con la opcion -c) y el script extrae los links de Megaupload que encuentre, me parece una funcionalidad muy conveniente ya que con el anterior script era necesario copiar a mano todos los links a un archivo de texto, lo cual puede ser muy tedioso.

Otra funcionalidad es la de usar la dirección ip de los servidores para realizar las descargas, esto ayuda a saltarse algunos proxies que están configurados para bloquear paginas por nombres de dominio en lugar de direcciones ip XD.

El script ha sido probado con Ubuntu 10.10 que es el OS que uso actualmente, pero no creo que tengan problemas usándolo en otra distro, siempre y cuando tenga instalado wget que es el programa que usa para realizar las descargas.

Pueden descargarlo del siguiente enlace:
Luego le damos permisos de ejecución y lo copian en el directorio bin:
novatoz@sney2002:~$ chmod +x mget
novatoz@sney2002:~$ sudo cp mget /bin/
Espero les sea de utilidad, y si tienen alguna inquietud o sugerencia no duden en dejarla en los comentarios.

update: corregido error que impedía descargar archivos cuyos nombres contuvieran  caracteres no ascii, los cuales están codificados usando html entities, ejemplo:
# este link no apunta a ningún archivo
http://www465.megaupload.com/files/f4f4ab2163558f1f/Capacitaci&#00243;n.mp4
# el link es valido al cambiar &#00243; por ó
http://www465.megaupload.com/files/f4f4ab2163558f1f/Capacitación.mp4
ahora es necesario tener instalado w3m

lunes, 3 de enero de 2011

script bash para dividir archivos

Si usas linux y quieres dividir un archivo grande para luego unirlo en otra maquina usando cat o hjsplit este script puede servirte; para dividir el archivo podemos usar el comando split, pero hjsplit espera que los archivos estén numerados desde el 001 y split genera archivos empezando desde el 000.

solucion?, dividir el archivo y luego renombrar las partes generadas.

1. para saber cuantos archivos se generan, podemos dividir el archivo y ver en cuantas partes se divide usando la opcion verbose y wc:
parts=`split -d -b 50M -a 3 --verbose archivo-entrada archivo-salida | wc -l`
2. conociendo cuantos archivos se crearon, podemos renombrar los archivos empesando desde al ultimo:
while [ $parts -gt 0 ]; do
    # sufijo del archivo
    old_suf=`printf "%03d" "$(($parts-1))`
    # sufijo nuevo
    new_suf=`printf "%03d" "$parts"`
    #renombramos archivo
    mv "$1.$old_suf" "$1.$new_suf"
    # disminuimos $parts
    parts=$((parts-1))
done
el script completo lo pueden descargar desde el siguiente enlacce:
Solo tienen que dejarlo en la carpeta de nautilus-scripts (~/.gnome2/nautilus-scripts), darle permisos de ejecución y listo :)

P.D: este es el segundo script en bash de mas de 3 lineas que hago, así que si encuentran algún error me avisan para ir mejorando u_u
 
© 2009 NovatoZ. All Rights Reserved | Powered by Blogger
Design by psdvibe | Bloggerized By LawnyDesignz