Personal Website

My Web: MindEchoes.com

Wednesday, June 27, 2012

Tuesday, June 19, 2012

Vistazo al Code Completion de NINJA-IDE

Code Completion para NINJA-IDE es una de las features importantes de esta versión, ya que queremos lograr algo que sea muy muy preciso para Python, todavia nos faltan resolver variasssss cosas en las que estamos trabajando para cerrar en estos dias, pero aca hay un pequeño preview de algunas de las cosas que sabe resolver ahora:


Friday, June 15, 2012

Escribiendo un Unity Lens para NINJA-IDE

Este es un Unity Lens MUY SIMPLE, la parte mas complicada del codigo pasa en realidad por actualizar el descriptor de NINJA-IDE para que sepa la informacion del Plugin descargado y pueda eliminarse correctamente despues desde la UI si fuera necesario.

Basicamente lo que hace este Lens, es permitirle al usuario ingresar las palabras a buscar, y el Lens (o el scope del Lens) se encargara de buscar esas palabras entre el nombre, descripcion y tags de los plugins subidos a la pagina de NINJA-IDE, retornando como resultado aquellos plugins que contengan alguna de las palabras buscadas, tambien se puede ingresar el nombre de algun usuario y ver los plugins de ese usuario, y al elegir alguno de los resultados de la Dash, el Plugin sera instalado en el sistema de la misma forma que lo hariamos desde NINJA-IDE.




En este Lens, estamos capturando la emision de la uri para manejarla nosotros y descargar el Plugin sin que la dash haga nada con eso, ya que es un uso muy especifico que se quiere hacer con el resultado.

El codigo de este Lens se encuentra en:
https://github.com/diegosarmentero/Experiments/tree/master/lenses/ninjaplugins

Para probarlo deberia ser tan simple como tener instalado Quickly
($ sudo apt-get install quickly)

Y adentro de la carpeta del Lens hacer:

$ sudo quickly install
$ quickly run

Aclaracion: Si la carpeta donde se descargan los plugins de NINJA-IDE no existe (porque ninja no esta instalado o nunca fue ejecutado), el Lens va a permitir hacer las busquedas pero no va a descargar ningun Plugin.

Friday, June 8, 2012

Viaje a BsAs y UbuConLA

El fin de semana pasado viaje a BsAs para participar de la UbuConLA y de paso aproveche para hacer turismo a full por buenos aires :D

Como siempre estuvo re bueno juntarse con gente de la comunidad Python y del Software Libre, siempre hay gente muy copada y temas de conversacion interesantes!

El Viernes fui un rato a la UbuConLA, despues me retire a buscar al nuevo miembro de la familia :D


Despues paseamos un rato por unas comiquerias:



Y luego al hotel!! :D
Tengo que reconocer que me sentia como Mi Pobre Angelito 2 en ese hotel jejeje




Y aca Filly se hacia la que estaba haciendo cosas interesantes:


Y para terminar el Viernes, fuimos a comer a un lugar reee copado!!
Muy buena la comida!


 Algunas fotos de la UbuConLA:




El sabado a la noche despues de la UbuConLA, nos fuimos varios de los que estabamos ahi a comer a Orsai Bar (pude conocerlo :D... pero bastante regulares las pizzas)


Y despues mas tarde fui a una fiesta que se llamaba: "Headshot Party", pasaban musica de anime, habia pistas para bailar, arriba se podia jugar al Mortal Kombat y otros juegos (en los que soy sumamente horrible), repartian cosas luminosas, habia minas que bailaban colgadas de una tela en una parte... bastante completo.


Y para finalizar, el domingo fui a conocer el Jardin Japones y comimos ahi (muy rica la comida tambien :P)








Y despues del Jardin Japones fuimos al Museo que habia una exposicion de Cultura Oriental, pero ahi no pude sacar fotos porque no me dejaron...

Ahora a esperar con ansias el siguiente viaje que es PyCamp!!!

Haciendo un Navegador Web Desktop con Scroll Kinetico

Este es un post que tenia pendiente hace rato, la idea es mostrar como en una aplicación desktop podemos tener un Navegador Web donde podamos scrollear apretando con el mouse y moviendo el puntero en alguna dirección, de la misma forma que hacemos tradicionalmente con el dedo en los navegadores de los celulares, y tener ese efecto de Scroll Kinetico, donde el scroll de la página parece tener cierta inercia.

DISCLAIMER: No critiquen mi código en C++ :P, no programo seguido en este lenguaje.

Vamos a empezar de lo general a lo particular.
Primero paso, el punto de entrada de nuestro programa:


#include <QtGui/QApplication>
#include "mainwindow.h"

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();

    return a.exec();
}


Ahora vamos a diseñar la ventana principal, que basicamente va a consistir en un layout con un Widget para renderizar la página web, y una barra de progreso para la carga de la página (podriamos hacerlo más completo, con la barra de direcciones, etc, pero no es la idea de este post, para eso ver: http://www.diegosarmentero.com/2010/09/navegador-web-en-menos-de-100-lineas-de.html)

"mainwindow.h"


#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QtGui>
#include <QtCore>
#include <QtWebKit>
#include "grabdragscroll.h"

class QWidget;
class QVBoxLayout;
class QWebView;
class QProgressBar;
class GrabDragScroll;

class MainWindow : public QWidget
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);

private:
    QWebView* web;
    QVBoxLayout* v_box;
    QProgressBar* progress;
    GrabDragScroll* grab;
};

#endif // MAINWINDOW_H


Y el código de la clase:


#include "mainwindow.h"

MainWindow::MainWindow(QWidget *parent) :
    QWidget(parent)
{
    v_box = new QVBoxLayout(this);

    this->web = new QWebView(this);
    this->web->load(QUrl("http://diegosarmentero.com.ar"));

    this->grab = new GrabDragScroll(this);
    this->grab->installWidget(this->web, true);
    this->progress = new QProgressBar(this);
    this->v_box->addWidget(this->web);
    this->v_box->addWidget(this->progress);

    this->connect(this->web, SIGNAL(loadProgress(int)), 
                  this->progress, SLOT(setValue(int)));
    this->connect(this->web, SIGNAL(loadFinished(bool)), 
                  this->progress, SLOT(hide()));
    this->connect(this->web, SIGNAL(loadStarted()), 
                  this->progress, SLOT(show()));
}


Como podemos ver en la linea:

    this->grab->installWidget(this->web, true);

Se esta llamando a la función "installWidget" de GrabDragScroll, esta función recibe 2 atributos, el primero es el widget web sobre el cual se va a instalar el Scroll Kinetico, y el segundo argumento es para decirle a GrabDragScroll si queremos que escondas las barras de desplazamiento del Widget Web para que la única opción de scroll sea el mouse.

Lo que va a ser nuestra clase GrabDragScroll es instalar un eventFilter sobre el Widget Web, para que todos los eventos de ese Widget pasen primero por esta clase, y podamos tomar aquellos eventos que son de mouse para hacer el efecto de Scroll kinetico, y dejar pasar el resto de los eventos para que sigan su comportamiento normal.

La siguiente parte no la voy a explicar en detalle :P porque es medio vueltero de explicar y es más fácil entender el código, básicamente lo que hace es fijarse en que estado me encuentro:

STATE { STEADY, PRESSED, MANUAL_SCROLL, AUTO_SCROLL, STOP };

Y en base al estado y el tipo de evento que se produjo con el mouse pasa a un estado u otro y hace los calculos que sean necesarios del offset del mouse para poder animar el scroll.

Se puede ver más abajo que tenemos un método "timerEvent" que se utiliza para hacer la animación de ir desacelerando el movimiento de la página, y por último tenemos los métodos "scrollOffset" y "setScrollOffset" que ejecutan funciones javascript sobre el widget web para obtener la posición en la que se encuentra el scroll o setear una posición del scroll. El efecto de scroll kinetico basicamente consiste en ir seteando distintas posiciones del scroll e ir ampliando/disminuyendo la diferencia entre asignaciones dependiendo del impulso que se le da con el cursor (el cual consiste en calcular la posición cuando se estaba en estado STEADY y la distancia de la posición final del mouse al hacer release del click):


#include "grabdragscroll.h"

struct ScrollData
{
    enum STATE { STEADY, PRESSED, MANUAL_SCROLL, AUTO_SCROLL, 
                 STOP };
    int state;
    QWebView *widget;
    QPoint pressPos;
    QPoint offset;
    QPoint dragPos;
    QPoint speed;
};

GrabDragScroll::GrabDragScroll(QObject *parent) :
    QObject(parent)
{
    this->data = new ScrollData;
    this->data->state = ScrollData::STEADY;
    this->data->pressPos = QPoint(0, 0);
    this->data->offset = QPoint(0, 0);
    this->data->dragPos = QPoint(0, 0);
    this->data->speed = QPoint(0, 0);

    this->timer = new QBasicTimer;
}

void GrabDragScroll::installWidget(QWebView* widget, 
                                   bool withoutBars)
{
    if(withoutBars){
        QWebFrame* frame = widget->page()->mainFrame();
        frame->setScrollBarPolicy(
                    Qt::Vertical, Qt::ScrollBarAlwaysOff);
        frame->setScrollBarPolicy(
                    Qt::Horizontal, Qt::ScrollBarAlwaysOff);
    }
    widget->installEventFilter(this);
    data->widget = widget;
}

bool GrabDragScroll::eventFilter(QObject *obj, QEvent *event)
{
    if(!(obj->isWidgetType())){
        return false;
    }

    QEvent::Type eventType = event->type();
    if(eventType != QEvent::MouseButtonPress &&
       eventType != QEvent::MouseButtonRelease &&
       eventType != QEvent::MouseMove){
        return false;
    }

    QMouseEvent* mouseEvent = dynamic_cast<QMouseEvent*>(
                                            event);
    if(this->data->pressPos.x() == mouseEvent->pos().x() &&
       this->data->pressPos.y() == mouseEvent->pos().y()){
        this->data->state = ScrollData::STEADY;
        return false;
    }
    bool consumed = false;

    if(this->data->state == ScrollData::STEADY &&
       eventType == QEvent::MouseButtonPress &&
       mouseEvent->buttons() == Qt::LeftButton){
            consumed = false;
            data->state = ScrollData::PRESSED;
            data->pressPos = QPoint(mouseEvent->pos());
            data->offset = this->scrollOffset(data->widget);

    }else if(data->state == ScrollData::PRESSED){
        if(eventType == QEvent::MouseButtonRelease){
            consumed = true;
            data->state = ScrollData::STEADY;
            QMouseEvent* event1 = new QMouseEvent(
                        QEvent::MouseButtonPress,
                        data->pressPos, Qt::LeftButton,
                        Qt::LeftButton, Qt::NoModifier);
            QMouseEvent* event2 = new QMouseEvent(*event1);

        }else if(eventType == QEvent::MouseMove){
            consumed = true;
            data->state = ScrollData::MANUAL_SCROLL;
            data->dragPos = QCursor::pos();
            if(!this->timer->isActive()){
                this->timer->start(20, this);
            }
        }

    }else if(data->state == ScrollData::MANUAL_SCROLL){
        if(eventType == QEvent::MouseMove){
            consumed = true;
            const QPoint pos = mouseEvent->pos();
            const QPoint delta = pos - data->pressPos;
            this->setScrollOffset(this->data->widget,
                                  this->data->offset - delta);

        }else if(eventType == QEvent::MouseButtonRelease){
            consumed = true;
            data->state = ScrollData::AUTO_SCROLL;
        }

    }else if(data->state == ScrollData::AUTO_SCROLL){
        if(eventType == QEvent::MouseButtonPress){
            consumed = true;
            data->offset = this->scrollOffset(data->widget);
            data->state = ScrollData::STOP;
            data->speed = QPoint(0, 0);
        }else if(eventType == QEvent::MouseButtonRelease){
            consumed = true;
            data->state = ScrollData::STEADY;
            data->speed = QPoint(0, 0);
        }

    }else if(data->state == ScrollData::STOP){
        if(eventType == QEvent::MouseButtonRelease){
            consumed = true;
            data->state = ScrollData::STEADY;
        }else if(eventType == QEvent::MouseMove){
            consumed = false;
            data->state = ScrollData::MANUAL_SCROLL;
            const QPoint pos = mouseEvent->pos();
            this->setScrollOffset(this->data->widget, 
                                  this->data->offset);
        }
    }

    return consumed;
}

void GrabDragScroll::timerEvent(QTimerEvent *event)
{
    if(data->state == ScrollData::MANUAL_SCROLL){
        QPoint cursorPos = QCursor::pos();
        data->speed = cursorPos - data->dragPos;
        data->dragPos = cursorPos;
    }else if(data->state == ScrollData::AUTO_SCROLL){
        data->speed = this->deaccelerate(this->data->speed);
        QPoint p = this->scrollOffset(data->widget);
        this->setScrollOffset(
                    this->data->widget, p - data->speed);
        if(data->speed == QPoint(0, 0)){
            data->state = ScrollData::STEADY;
        }
    }

    QObject::timerEvent(event);
}

QPoint GrabDragScroll::scrollOffset(
        const QWebView* view) const {
    int x, y = 0;
    QWebFrame* frame = view->page()->mainFrame();
    x = frame->evaluateJavaScript("window.scrollX").toInt();
    y = frame->evaluateJavaScript("window.scrollY").toInt();

    return QPoint(x, y);
}

void GrabDragScroll::setScrollOffset(QWebView* view, 
                                     const QPoint& p)
{
    QWebFrame* frame = view->page()->mainFrame();
    frame->evaluateJavaScript(
     QString("window.scrollTo(%1,%2);").arg(p.x()).arg(p.y()));
}

QPoint GrabDragScroll::deaccelerate(const QPoint &speed, 
                                    const int a, 
                                    const int maxVal)
{
    int x = qBound<int>(-maxVal, speed.x(), maxVal);
    int y = qBound<int>(-maxVal, speed.y(), maxVal);
    if(x > 0){
        x = qMax<int>(0, x - a);
    }else if(x < 0){
        x = qMin<int>(0, x + a);
    }
    if(y > 0){
        y = qMax(0, y - a);
    }else if(y < 0){
        y = qMin(0, y + a);
    }

    return QPoint(x, y);
}


Un pequeño problema que tuve la primera vez que lo hice, fue que como estamos capturando los eventos de mouse, no se podia navegar porque los clicks nunca llegaban a los links, y un navegador que no te deja navegar no tenia mucho chiste, así que como se puede ver al principio del método "eventFilter", agregue que si la posición del mouse es igual a la posición anterior y se estaba en un estado de STEADY, se deja pasar el evento hacia arriba para que lo agarre el widget web.

El código completo de todo el ejemplo puede encontrarse en:
https://github.com/diegosarmentero/Experiments/tree/master/KineticScroll

Para ejecutarlo, deberia ser tan simple como abrir el proyecto con Qt Creator y compilarlo y ejecutarlo desde ahí (y sino siempre esta la consola y qmake).

Espero no haber dejado nada sin explicar... y cualquier cosa el código es bastante corto y simple :D