lunes, 10 de noviembre de 2014

El patrón de diseño singleton en Harbour



Dejo aquí el diseño singleton en Harbour.

Fuente: Wikipedia
"El patrón de diseño singleton (instancia única) está diseñado para restringir la creación de objetos pertenecientes a una clase o el valor de un tipo a un único objeto.
Su intención consiste en garantizar que una clase sólo tenga una instancia y proporcionar un punto de acceso global a ella."


#include "hbclass.ch"

CLASS HARBOURSingleton
      CLASSDATA Self
      DATA cText

      METHOD getInstance
      METHOD InitClass

END CLASS

METHOD InitClass CLASS HARBOURSingleton
  if ::Self = NIL
     ::Self := Self
  endif  
RETURN Self

METHOD getInstance CLASS HARBOURSingleton
RETURN ::Self


Ejemplo de uso:

/* Test Class SingleTon */

Function Main()
   Local  oSingleton , oSingleton_TWO

   oSingleton := HARBOURSingleton():getInstance()
 

  ? "instance one" , oSingleton:cText := "Singleton"

  oSingleton_TWO := HARBOURSingleton():getInstance()
  oSingleton:cText := "Change"

  ? "instance two",  oSingleton_TWO:cText, oSingleton:cText

RETURN NIL

viernes, 7 de noviembre de 2014

git, ignorar mayúsculas / minúsculas



Para sistemas que no distingues entre A y a, no digo nombres que después todo se sabe ;-)

git config --unset-all core.ignorecase 
git config --system core.ignorecase true

miércoles, 27 de agosto de 2014

Git zippatch, creando un nuevo comando.



Podemos obtener a través del comando git archive, una copia de nuestro repositorio en un zip.
Esto es genial.

Ahora , vamos a rizar el rizo, un poquito.

Tenemos una conexión a un ftp que tiene nuestra web. Por desgracia, no tenemos git para poder aplicar los parches, y tampoco es un repositorio, por lo tanto, lo único que nos queda es subir los ficheros que hemos modificado/creado desde nuestro repositorio.

Una de las cosas que odiamos los programadores es perder el tiempo en cosas banales.
No me dirás que subir 40 ficheros en diferentes rutas es una tarea agradable, si es así, este post no es para tí. ;-)

La solución sería que alguna cosa que creará un zip con solamente los cambios de la rama donde estemos, y con la misma estructura de directorios.

Git es una caja de sorpresas, en un par de pasos;

1) Creamos un fichero, zippatch.sh, ;
     git archive -o update.zip HEAD $(git diff --name-only HEAD^) -v
     y lo colocamos , en el repositorio o en una ruta que este en el PATH del sistema, por ejemplo, en /git/bin/

2) En nuestro .gitconfig global, creamos un nuevo alias;
[alias]
        zippatch = "!sh zippatch.sh"

Eso es todo. Ahora desde la linea de comando, ejecutamos, podemos ver algo como;

$git zippath 
file.c
dir1/
dir1/readme.MD

Esto nos creará un fichero llamado update.zip,  que contendrá SOLO los ficheros que se modificaron en el último commit. 



sábado, 16 de agosto de 2014

GitBlit. Nuestro github personal.


Hemos actualizado la versión GitBit de la 1.1.0 a la 1.6.0 y las mejoras son espectaculares.
https://dev.gitblit.com/

Podemos ver las mejoras en los siguientes puntos:

  • Mejora claridad en la portada.
  • Accesos directos a nuestros Marcadores, repositorio, actividad y Proyectos.
  • Tickets, que podemos gestionarlo como una rama de nuestro proyecto.
  • Fork de repositorios
  • Documentación a través de README.md , HOME.md
  • Reflujo.
La actualización a sido muy simple, manteniendo todos nuestros users y repositorios sin problemas,siguiendo las instrucciones;

Como nosotros tenemos un directorio diferente, lo único que hice fue apuntar directamente el directorio de trabajo y dejar el resto como estaba;
git.repositoriesFolder=/opt/gitRepos

Lo que más nos ha gustado, es que podemos abrir un ticket , y asociarlo a una rama, comentarlo, etc..
Para ello, se tiene que modificar el fichero de configuración, y determinar que tipo de sistema de tickets queremos más info en  https://dev.gitblit.com/doc/gitblit.git/master/src!site!tickets_overview.mkd






lunes, 7 de abril de 2014

Escritorio de mi GNU/Linux




















Este es mi escritorio actual, y así se va a quedar.

Los gráficos de la derecha se montan a través de conky. Muy interesante es la herramienta conky-manager, que se encarga de todo ;-) , que en mi época lo hacía uno mismo a mano.





jueves, 27 de marzo de 2014

A PC muerto, PC puesto


Mi nueva máquina, un capricho.

La compre en  http://www.framoiz.com/ , decir de verdad que el trato y el favor de cambiarme la torre sin problemas ha sido espectacular.

Al final me decidí por un ;

  • AMD FX-8350 8 núcleos 
  • 16 GIGAS de RAM 1600
  • 120 Disco SSD Samsung
  • 1 Tera Disco Seagate Barracuda
  • La placa 990XDA-UD3, que merece un poco de atención.
  • nVidia GT 750 2Gigas
Como no, Ubuntu 13.10 con GnomeShell. De verdad, cada vez que uso GnomeShell, más me gusta.
Realmente es espectacular y lo limpio que se ve, un gran trabajo de los chicos de Gnome.

Este es escritorio actual, simple y limpio;




La odisea a sido la torre. La que pillé al principio, tenía 4 ventiladores, 2 por la parte de arriba.
Demasiado ruido, y no podían controlarse. Al final, al día siguiente, me hice con otra torre, un pelín más cara, pero con control de los ventiladores, además, los puedo parar manualmente, importante para noches de programación ;-)


Lo de la placa Gigabyte 990XDA-UD3 ha sido de traca y sigue siendo, aunque no tengo USB2, los USB3 funcionan , pero el colmo fue el tema de la red.

Instalo Ubuntu, ni rastro de ella. Probé LiveCD de Debian, LinuxMint, y tampoco. Estuve retocando parámetros de la bios, fuera todo rastro del engendro del EUFI, etc..., mirando por Internet , y nada.

Al final me cansé y me dió por buscar el driver. Lo encontré, y lo único que hice es ejecutar el autorun.sh como root.

Aqui está http://r8168dl.appspot.com/,  o buscar r8168-8.038.00.tar.bz2.
Eso elimina el r8169 que no funciona, y coloca este que va como la seda.

Ahora queda averiguar el tema de los puertos USB2.

Lo que si es impresionante es Steam de Valve. ES ALUCINANTE!! 
Espero que la nena no se percate de ello, sino, me quedo sin ordenador ;-)

video


También el Nexus 4 lo pincho y todo disponible sin hacer nada. Realmente estoy muy muy contento.
Ahora a meterle todo el tema de programación, máquinas virtuales, etc...



sábado, 22 de marzo de 2014

NAS, muerte HP y resurrección del S.O. del pingüino


Hace unos días, para el día del padre, me regalé yo mismo, que mejor que uno mismo para saber que es lo que uno necesita ;-), un NAS Synologic DS114.

La verdad es que la máquina es una pasada. La idea era apagar el PC, que lo tenia 24 horas encendido, haciendo de servidor de MySql, Servidor de DNLA, Git server, etc..

Así que decido dar el paso, y traspasar los BD de MySql al NAS con MariaDB, crear el repo Git y enchufar un disco externo para la biblioteca multimedia.

¡¡OLE OLE OLE!!

Lo tengo todo listo, y decido apagar la máquina HP Pavilion
La verdad que esta máquina a estado a la altura, excepto un cambio de fuente de alimentación, y una tarjeta un poco más potente. Me ha durado más de 7 años, y ha estado prácticamente los 3 últimos años sin apagarse.
A sido la compañera en la cual he desarrollado cantidad de software y en la que los primeros años la dediqué exclusivamente a MediaCenter. Descanse en paz. R.I.P

Pero ¿ que pasa ahora con todos mis proyectos en mi Ubuntu 10.04 ? No quiero cambiar de distro, porque sería demasiado costoso el cambio para adaptar el software que tengo creado.
Así que miro una máquina que tenia para hacer una recreativa, le quito el disco duro y le pongo el del HP, arranco, y:

¡¡El poder del pingüino!! 

Me dice que la tarjeta Nvidia no se encuentra, que si quiero usar una estandard o poner los que se detectan ahora, le digo que si, y ya estoy otra vez funcionando! He perdido solo un minuto.

Y aquí estoy escribiendo este post desde la máquina provisional.

Ahora a buscar máquina, y la verdad que viendo precios, me decanto por un AMD FX-8350,  un micro de 8 núcleos, pero con un coste inferior que por ejemplo un i5 4670K. 
La gráfica estoy dudando, seguramente seguiré con nVidia por su soporte a Linux, ya que las ATI no tenía muy buenas críticas.

Intentaremos pillar 16GB de RAM, a costa de disco , porque en el NAS tengo 5 Teras, no me preocupa el espacio en la máquina.

A ver si puedo meterle un SSD , para el sistema operativo y hacer un /home aparte en un disco rigido.







martes, 18 de marzo de 2014

Python. Usando TreeStore. PyGtk


En esta 4 entrega, recordad una vez más de hacer un pull del repositorio, veremos como montar en la parte derecha de nuestra pantalla, las bases de datos de nuestro servidor, y dentro de cada base de datos, las tablas correspondientes y dentro , la estructura de cada uno de ellos.

Para usar la vista de arbol desde Glade, simplemente cogemos el objeto;

 self.view_tree = self.glade.get_object('treeview_tables')


Ahora, vamos a montar lo que habíamos comentado a través del método mount_treeview()

    def mount_treeview(self):
        pbd_bd = gtk.gdk.pixbuf_new_from_file("./images/bd.png")
        pbd_table = gtk.gdk.pixbuf_new_from_file("./images/table.png")
        pbd_field = gtk.gdk.pixbuf_new_from_file("./images/field.png")

He visto muchos ejemplos, en que la gente le da por cargar y guardar en el modelo de datos la imagen por cada fila. PITTTTT ERROR!!! INEFICAZ!
No hace falta, hombre. Se abre una sola vez, y se pasa esa imagen que será común a todas la filas. Eso significa , que si las imágenes pesan en memoria 30KB cada una, el consumo será, independientemente de las filas, 90K.
Si cargáramos por cada fila una imagen, pues multiplicar;
total memoria en imagen = filas * ( 3 * 30KB )

En fin, siempre viene bien recordad que la optimizaciones pequeñas, al final son las que marcan la diferencia entre un gran programa y un programa que se arrastra por el escritorio. ;-)

Vamos a ir comentando aunque el código habla por si solo.
Lo primero, es obtener las BD que tenemos en nuestro servidor de MySQL;


        #Preguntamos por la BDs
        sql = "Select schema_name From `INFORMATION_SCHEMA`.`SCHEMATA`"
        cursor = self.db.cursor()
        try:
            cursor.execute(sql)
            result_db = cursor.fetchall()
        except MySQLdb.Error, e:
            self.status_setText( "Error %d: %s" % (e.args[0], e.args[1]) )
            return

En la variable result_db, tenemos nuestras BD.
Ahora de lo que se trata es de empezar a rellenar nuestro TreeStore, que como vemos, estará formado por un tipo Pixbuf y un string;

        self.treestore = gtk.TreeStore(gtk.gdk.Pixbuf, str)

Ahora, lo que vamos hacer es recorrer nuestras bases de datos, y por cada una de ellos vamos a meterle las tablas. 

¿ Y como se hace ? pues a través de un iterator. 

Explicar que es un iterator me puede llevar bastante tiempo, así que vamos a suponer que tú eres un creyente, no importa de que religión o secta, la cuestión es que tienes que tener fe en lo que ves ;-)

Después, por ti mismo, busca documentación sobre ello, o ya reportaré alguna documentación que vi en su momento.

Bueno, como la fe en lo que ves, la introducción del primer grupo, en este caso las bases de datos, verás que el paso de parámetros es None, por la simple razón que es la raiz.

La llamada self.treestore.append nos devuelve un iterator a la vez que introduce el elemento de la base de datos y la imagen, [pbd_bd, bd[0]

Este indica el camino al padre.  ( Tú ves rezando ;-) )

        for bd in result_db:
            iter = self.treestore.append(None,[pbd_bd, bd[0]] )
            cursor1 = self.db.cursor()
            try:
                cursor1.execute( "show tables from " + bd[0])
                result_table = cursor1.fetchall()
            except MySQLdb.Error, e:
                self.status_setText( "Error %d: %s" % (e.args[0], e.args[1]) )
                return

Ahora, recorremos las tablas, que obtuvimos anteriormente con show tables from + la base de dato actual

Pero, ahora, a la hora de introducir en el TreeStore, vamos a indicar a que Padre, a través del iterator creado anteriormente, y que este, a su vez, nos devuelve otro iterator, iterchild, que lo usaremos posteriormente ;


            for table in result_table:
                iterchild = self.treestore.append(iter,[pbd_table, table[0] ] )
                cursor2 = self.db.cursor()
                try:
                  cursor2.execute( "show columns from " + bd[0] + "." + table[0])
                    result_field = cursor2.fetchall()
                except MySQLdb.Error, e:
                   self.status_setText( "Error %d: %s" % (e.args[0], e.args[1]) )
                   return

Bien, y para acabar, vamos a introducir la estructura en su tabla:

                for field in result_field:
                    self.treestore.append(iterchild,[pbd_field, field[0] ] )

Pues lo único que queda, es crear un par de columnas;

        #Create Columns from names fields
        column = gtk.TreeViewColumn( "", gtk.CellRendererPixbuf(), pixbuf=0)
        self.view_tree.append_column( column )
        column = gtk.TreeViewColumn( "Database", gtk.CellRendererText(), text=1 )
        self.view_tree.append_column( column )


Y asociar nuestro TreeStore a nuestra vista;
        self.view_tree.set_model( self.treestore )

El resultado, en la parte derecha de la imagen;



Ahora va tomando una forma más que elegante, para las pocas líneas que hemos picado ;-)

Os aseguro, que si no fuese por Glade, el picar hubiese sido muy tedioso.

En la próxima, veremos de incorporar una pequeña historia sobre las query que hemos ido realizando.

Ah, se me olvidaba, si pruebas USE BD_QUE_QUIERO, algo hace ;-)



domingo, 16 de marzo de 2014

Python. MVC y MySQL ( PyGtk )




En esta tercera entrega de nuestro aprendizaje de Python con PyGtk, vamos a ver como haciendo nuestras Querys de MySQL se verán reflejadas en nuestra vista.

Antes de continuar, hacer un git pull del proyecto ubicado en github, para ver los nuevos cambios.

Las mejoras que tenemos en esta revisión, son;

  • Usamos una statusbar para dar información y mostrar también errores de MySQL
  • Crearemos a través de un TextView, un modelo de datos ListStore y columnas dinámicamente.
  • Controlamos la salida de la aplicación, preguntado si queremos salir.
  • Pulsando la tecla F5 en la ventana, lanzará la consulta que tengamos en ese momento en el TextView
  • Los eventos saltan ahora a la propia clase, abandonando la clase Handler(), por ser más sencillo la gestión. De todas maneras no la quito del código, para que se vea que podemos elegir el sistema que más nos guste.
Casi todo el trabajo se realiza en el método setQuery(), y es lo que veremos a continuación.

Obtener la sentencia que hemos escrito en el textview;

        textbuffer = self.textview_sql.get_buffer()
        self.cSql = textbuffer.get_text(*textbuffer.get_bounds())


Aquí de lo que se trata es de eliminar las columnas de la antigua sentencia ejecutada y limpiar el modelo de datos asociado a la vista;


        # remove columns the old view
        nOld_Fields = self.getTotalColumns()
        if nOld_Fields != 0:
            for column in self.view_lista.get_columns():
                self.view_lista.remove_column( column )


        #Clear model data of view
        oModel = self.view_lista.get_model()
        if oModel != None:
            oModel.clear()
        self.view_lista.set_model()

Ahora, ejecutamos la sentencia en MySQL, también podríamos adaptarlo fácilmente a otro motor, como Sqlite o PostGres, y en caso de error, lo mostramos en la statusbar;

        #Execute Sql
        cursor = self.db.cursor()
        try:
            cursor.execute(self.cSql)
            result = cursor.fetchall()
        except MySQLdb.Error, e:
            self.status_setText( "Error %d: %s" % (e.args[0], e.args[1]) )
            return

Ahora lo que vamos a realizar es muy simple, obtenemos la cantidad de campos y sus nombres, para crear las columnas correspondientes, que lo logramos a través del método AddListColumn()

        num_fields = len(cursor.description)
        field_names = [i[0] for i in cursor.description] # Name of fields

        #Create Columns from names fields
        i = 0
        for nombre in field_names:
            self.AddListColumn(nombre, i)
            i = i + 1

Una vez que tenemos nuestras columnas creadas, lo que nos resta es crear el modelo de datos y rellenarlo con nuestra Query y asignarlo a la vista;

        #Create model dinamic of types str for view
        ListStore = gtk.ListStore(*([str] * num_fields))
        for value in result:
            ListStore.append(value)
        self.view_lista.set_model(ListStore)



Pues esto es todo en esta tercera entrega, ahora podemos empezar a ver resultados visibles ;-)

Para la próxima, usaré un TreeStore, para montar las estructuras de nuestras tablas que tengamos en la base de datos, además, incorporaremos alguna que otra imagen en la vista, para que quede más chulo.

Dejo unas imágenes con los resultados del código a ver que os parecen.









sábado, 15 de marzo de 2014

Python. Conexión de señales( PyGtk )




Seguimos con nuestro particular estudio de Python y nuestro pequeño programa.

Hasta ahora, hemos visto como hacer uso de Glade, y vemos como conectamos las respectivas señales.

   #Create object gtk.Window , from load window the glade
   self.window = self.glade.get_object('consultas')
   # Example connect manual signal.
   self.window.connect("destroy", self.destroy)

Si le echamos un vistazo al commit 78f6437,podemos observamos los cambios que teníamos anteriormente, git diff 753f400 78f6437 , observaremos como podemos determinar en Glade donde tiene que saltar.

En la imagen de tinnydb.ui puedes ver donde tienes que declarar que señal 
vamos a controlar.


En este caso, vamos a controlar el evento delete-event
En Gtk, este evento se dispara ANTES del destroy, y por ejemplo, podemos usarlo para preguntar si queremos salir de la aplicación o no.
Eso se consigue simplemente deteniendo la propagación de la señal al sistema de eventos de GTK, o por el contrario dejamos a Gtk que siga su curso, en este caso, sería el salto a la señal destroy.

¿ Pero no habíamos conectado manualmente el destroy ? Cierto. Podemos conectarlo combinándolo glade, además, las señales que conectamos se suman a las existen no se eliminan, aunque yo jamás lo he realizado, la teoría es simple: Para un mismo evento de Gtk, se puede disparar una o varias señales.
( Un poco de teoría de Gtk tampoco viene mal ;-) )

Bueno, seguimos. ¿ Como le decimos que use las señales definidas en Glade ?

# From Glade, signal delete_event the window consultas,  at class Handler.
  self.glade.connect_signals( Handler() )
Ya está, muy simple. Esto lo que hace es que decirme que clase va a tratar los eventos.

Creamos la clase que quiero que procese las señales;

class Handler():
    def delete_event(self, widget,data=None):
        print( "Call from Glade." )
        # TIP:if you return 0 , destroy, but if you return 1, stop , not kill program
        # TODO: Here create dialog ask if I want exit program
        return 0

Esto lo he realizado a propósito para que veáis lo que podemos hacer.
Podía hacer realizado;
  self.glade.connect_signals( self )
Ahora es nuestra propia clase principal, lo que tendrá un método llamado delete_event que gestiona esa señal.

También se puede conectar señales de esta manera;

 #Create our dictionay and connect it
dic = { "delete_event" : self.delete_event,
 "destroy" : gtk.main_quit }
self.window.signal_autoconnect(dic)

Hasta aquí un poquito de PyGtk , Glade y sus señales.




jueves, 13 de marzo de 2014

Un nuevo proyecto, TinnyDb.



Como me gusta aprender Python, siempre me ha gustado compartir con la gente lo que voy descubriendo y de paso mostrar mis avances desde el inicio, de esta manera, otro pueda ir cogiendo el hilo poco a poco.

En este paso voy a realizar un pequeño programa para hacer consultas a MySQL a través de PyGtk.
Para ello, las pantallas estarán realizadas con Glade.

De esta manera, podemos ver un poco de todo, Python , POO, Gtk , Glade, conexión con MySQL, como montar las vistas, etc..

En esta entrada, veremos como descargar el código desde GitHub.
Si no usas Git como control de versiones, es una buena oportunidad de empezar.

No voy a explicar como instalar Python , PyGtk o las librerías de MySQL, porque hay excelentes post que explican como hacerlo, es cuestión de buscar por la web.

No sé cuanto tiempo ni el grado de complejidad que va a requerirme llevar a cabo el proyecto, lo que sí sé seguro, es que nos vamos a divertir ;-)

Para empezar a trastear  e ir abriendo boca, tenemos un poco de código, es muy simple, un par de clases con muy poquitas lineas, puedes descargarlo desde github:

git clone https://github.com/rafathefull/tinnydb.git

Podemos observar en el fichero login.py, la pantalla que ilustra este post, y que está contenido en el fichero tinnydb.ui que lo puedes abrir con Glade.

Para ejecutarlo, simplemente : python main.py

De momento, os dejo el código que he realizado en un par de horas, funcionando con conexión a datos.

En el siguiente entrega, explicaremos un poco como la clase login se conecta a MySQL y el uso de Glade desde Python a través de PyGtk.




domingo, 9 de marzo de 2014

Maneras de hacer en PyGTK( Python ) vs T-GTK ( Harbour )


El haber usado Glade para el diseño del GUI para mi programa de Maquinas-Herramientas, me ha va permitir aprender Python con PyGtk, muchos más rápido, porque las pantallas ya las tengo creadas.

De esta manera, todo el interface es reusable 100%.

Siempre he pensado que aprender un nuevo lenguaje haciendo simples funciones que muestran como hacer el factorial de un número es súper aburrido.

¿ A quién le resulta divertido "ver", de hecho no vas a ver un carajo, como funciona una función recursiva? ¿ De verdad te vas a sentir realizado viendo un puñado de números sin sentido ? ;-)

Por eso, voy a intentar portar mi programa a Python para aprender de él, y de paso, ver como evoluciona, y sentirme satisfecho de ver ventanas, botones, carga de datos en una vista, etc..

¿ A que suena mejor ? Como no, comparar un triste número en un interprete a ver una ventana que responde a tus acciones no tiene color ;-)

Esta es el código de la ventana de Login usando T-GTK


    SET RESOURCES pGlade FROM FILE "./gui/pol.ui"

    DEFINE WINDOW oWnd ID "inicio" RESOURCE pGlade

            DEFINE ENTRY VAR s_cUser      ID "entry_user"     RESOURCE pGlade
            DEFINE ENTRY VAR s_cPass      ID "entry_pass"     RESOURCE pGlade
            DEFINE ENTRY VAR s_cServer    ID "entry_server"   RESOURCE pGlade
            DEFINE SPIN  VAR s_nPort      ID "spin_port"      RESOURCE pGlade
            DEFINE ENTRY VAR s_cDBName    ID "entry_bd"       RESOURCE pGlade
            DEFINE LABEL oLabelInf        ID "information"    RESOURCE pGlade

            DEFINE BUTTON oBtn ID "btn_access" RESOURCE pGlade;
                          ACTION ( oBtn:Disable(),;
                                   IF( !empty( oServer := Connect_Db( oLabelInf )),( oWnd:End() ), ),;
                                   oBtn:Enable() )

            DEFINE BUTTON ID "btn_cancel" RESOURCE pGlade ACTION ( oWnd:End() )

    ACTIVATE WINDOW oWnd INITIATE


En PyGTK;

__author__ = 'rafa'

import pygtk
pygtk.require('2.0')
import gtk
import MySQLdb
import os

class Login:

    def __init__(self):

        """
        Ventana de acceso a la BD
        """

        self.conectado = False

        self.gladefile = "./gui/pol.ui"
        self.glade = gtk.Builder()
        self.glade.add_from_file(self.gladefile)

        #accediendo a los controles
        self.window = self.glade.get_object('inicio')
        self.window.connect("destroy", self.destroy)

        self.btn_access = self.glade.get_object('btn_access')

        # este salta a un method
        self.btn_access.connect("clicked", self.Conect_Db, None)

        self.btn_cancel = self.glade.get_object('btn_cancel')
        self.btn_cancel.connect("clicked", self.destroy )

        self.label = self.glade.get_object('information')

        self.entry_user   = self.glade.get_object('entry_user')
        self.entry_user.connect("key-press-event", self.on_key_press_event )

        self.entry_pass   = self.glade.get_object('entry_pass')
        self.entry_pass.connect("key-press-event", self.on_key_press_event )

        self.entry_server = self.glade.get_object('entry_server')
        self.entry_bd     = self.glade.get_object('entry_bd')
        self.spin_port    = self.glade.get_object('spin_port')

    def on_key_press_event(self, widget, event):
        keyname = gtk.gdk.keyval_name(event.keyval)

        if keyname == "Return" or keyname == "KP_Enter":
            widget.get_toplevel().child_focus(gtk.DIR_TAB_FORWARD)

    # Cuando destruimos la pantalla, si no estamos conectado, salimos de la aplicacion.
    def destroy(self, widget, data=None):
        if not self.conectado:
            gtk.main_quit()
            os.sys.exit (1)
        else:
            gtk.main_quit()

    def main(self):
        gtk.main()

        return 0

En parte , la ventaja de T-GTK, en este caso es de Harbour y su maravilloso Preprocesado , que nos permite crear comandos, "ocultando " el uso de clases, aunque por debajo, el trabajo realizado es similar a la librería de PyGtk.

Pero, he aquí donde la potencia de Glade:

        SET RESOURCES pGlade FROM FILE "./gui/pol.ui"
        DEFINE WINDOW oWnd ID "inicio" RESOURCE pGlade

en PyGtk;

        self.gladefile = "./gui/pol.ui"
        self.glade = gtk.Builder()
        self.glade.add_from_file(self.gladefile)

        #accediendo a los controles
        self.window = self.glade.get_object('inicio')
        self.window.connect("destroy", self.destroy)

En Harbour escribimos muchísimo menos para obtener lo mismo, gracias a su potente preprocesado.
Pero lo importante, es que convertir un programa en otro a través de Glade, es de una gran ayuda.

O por decirlo de otra manera, un diseñador puede hacer las pantallas, mientras el programador lo dota de funcionalidad.

Esta manera , la de separar GUI y funcionalidad, me encanta. ;-)


sábado, 8 de marzo de 2014

Ubuntu 10.04 y Git


Si usas Ubuntu 10.04 todavía, como yo, seguramente la última versión que vas a tener en el sistema de git será la  1.7.0.4.

Y eso es un problema si quieres usar PyCharm con git, pues simplemente no te dejará usar repositorios de git desde el entorno de programación

. Pues tan simple como esto:

sudo apt-get install python-software-properties
sudo add-apt-repository ppa:git-core/ppa
sudo apt-get update
sudo apt-get install git

Ahora dispones en el sistema de la versión 1.9.0 ;-)
Seguimos jugando con Python....


viernes, 7 de marzo de 2014

Software Maquinas-Herramientas

Después de más de 2 años, sacrificando fin de semanas y tiempo libre, he podido pasar a una versión totalmente funcional.

El Sofware es capaz de cálcular :

  • Tornillos Sin Fin 
  • Corona
  • Metrico ISO
  • Fresas-Madres
  • Husillos
  • Engranajes Rectos y Helicoidales
  • Noyos

Las pantallas de cálculo son similares, por ejemplo, las del tornillo SINFIN y su CORONA;


A parte de calcular , también dispone de la realización de la orden de fabricación:

Y toda la gestión de albaranes con su correspondiente facturación;

La verdad, es que estoy muy satisfecho con el resultado final, a costado, pero al menos, objetivo cumplido. Video del estado de como estaba de avanzado del software y ver más o menos como funciona el programa en si, tanto en GNU/Linux como en Windows.





martes, 4 de marzo de 2014

Python, enamorado de la simplicidad.


Estaba yo trasteando con Python, y estaba traspasando unas clases de cálculos de mecánica y me he encontrado que he implementado esta linea de Python, la cual es asombrosamente simple y potente;

  def aproxima( self, aMatriz, nNumber):
      """
        Devuelve el valor que sea el más aproximado dentro del array a nNumber
      """
      return min( aMatriz, key=lambda x:abs( x - nNumber ) )

Tenía una rutina en Harbour , hace lo típico, recorre el array, me guardo el valor de la posición que cumple y después devuelvo el elemento de ese array según la posición, lo cual, después de 25 lineas más o menos de código fuente, obtenía el premio ;-)

Pero la linea en Python hace lo mismo, en UNA SOLA LINEA!
Me he quedado prendado. 

Aunque a veces su simplicidad y el 'paso de todo', como esto;

   class coche():
         pass
 
   coche = coche()

   coche.color = 'rojo'
   coche.puertas = 5


Pero esa simplicidad, a veces, te puede volver loco ;-)

Ah! Se me olvidaba, muy importante , TODO el código ordenado!!!

ODIO el tener que estar repasando códigos de otros y cada cual usa la indentación como le da la gana, y era reacio a su uso al principio, pero si usas código de otros, lo agradeces de todo corazón!!! ;-)

sábado, 22 de febrero de 2014

HARBOUR:Acelerando al viejo SET FILTER TO


Estaba yo con mis cosas, y repasando la lista de correo de harbour, leí que alguien ponía una función, llamada OrdWildSeek

Buscando más información sobre dicha función, me percaté enseguida, que haciendo un poco de malabares y con un indice CUSTOM, y sin apenas esfuerzos, podemos quitar nuestros SET FILTER, que por su utilidad estaban, pero que por su ineficacia no lo usaba prácticamente nadie.

Cada vez que he tenido que usar un LIKE en SQL, pasa por mi memoria el SET FILTER ;-)

¿ Y cual es la idea ? El idea es usar un sub-indice a partir del índice activo.
¿ Y como funciona ?

Pues a grandes rasgos y para que todo el mundo lo entienda;

  • SET FILTER TO , recurre TODO la DBF en busca de coincidencias.
    Ah!! y lo bonito que es ver que en el filtro hay solo 2 coincidencias separadas entre si por 200MB, el salto de un registro a otro es de "reír por no llorar" ;-)
  • OrWildSeek, recurre el FICHERO NTX ACTIVO, y nos va diciendo que registro cumple con el patrón que pasamos, y ahí es donde entramos nosotros con nuestro indices CUSTOMS


Ahora lo que más nos gusta, el código fuente.

Imagina que tenemos una tabla de clientes, y tenemos varios indices, y el 4, la expresión es nombre+apellidos.

Ahora, queremos obtener el nombre de SARAH. Pero no solo los que comienzan con SARAH, eso lo podemos solucionar rápidamente con un SCOPE, si no,  que forme parte de él, como;

  • ANGIE SARAH LOWE
  • SARAH CARMONA
  • JULIA SARAH SMITH
Eso se consigue , pasando el patrón "*"+Cadena+"?"

cNombre := "SARAH"

fast_filter( "*"+ alltrim( cNombre ) + "?", 4, 22, 28 )
Browse()


Ahora, ya estaría el filtro activado basado en subindices.

/* 
 Sustituto de SET FILTER
 cExpr  
    Expresion a buscar que forme parte del indice, podemos 
    poner *Texto? , para buscar palabra que forme parte

 nOrder
    Sobre que indice activo vamos a recorrer la lista.
    Por defecto es el indice activo.

 nRow, nCol 
     Posicion fila,columna para mostrar progreso. Se ejecuta en 
     un hilo aparte

 Return: Indice anterior

*/
function fast_filter( cExpr, nOrder, nRow, nCol )
      local PantaAnt := savescreen(0,0,MaxRow(),MaxCol())
      Local p := hb_threadStart( @Pajaritos(), nRow, nCol )
      Local nIndice := OrdNumber()

      DEFAULT nOrder TO OrdNumber()

      set order to nOrder
      INDEX ON &(IndexKey()) TAG _TEMP_ TO tHarbourt ;
               CUSTOM ADDITIVE MEMORY
       
      set order to nOrder
      go top

      DO WHILE OrdWildSeek( cExpr, .T. )
         OrdKeyAdd( "_TEMP_" )
      ENDDO
      OrdSetFocus( "_TEMP_" )
       
      hb_threadTerminateAll()
      restscreen(0 ,0 ,MaxRow(), MaxCol(), PantaAnt)

return nIndice

PROCEDURE Pajaritos( nRow, nCol )
        Local pajarito := "|",n := 0
        
        DEFAULT nRow := MaxRow(), nCol := 1

        DO WHILE .T.

           do case
              case n = 1 
                   pajarito = "|" 
              case n = 2 
                   pajarito = "/" 
              case n = 3  
                   pajarito = "-" 
              case n = 4 
                   pajarito = "\" 
              Otherwise 
                   n := 0
           end case

           DispOutAt( nRow, nCol, "Generando Consulta....[ " +  pajarito +" ]" ) //, "GR+/N" )
           ThreadSleep( 500 )
           n++
       ENDDO
RETURN


Creo que la función es muy simple y no necesita más explicación, el código habla por si mismo.
Lo único es que se crea un hilo para mostrar que se está haciendo alguna cosa.

Lo ideal sería que la función OrdWildSeek tuviese un codeblock para hacer llamada e ir mostrando un progreso, pero al no tenerlo, lo he ejecutado en un hilo aparte.

He de decir que me sorprende la velocidad, eso si, siempre teniendo en cuenta la cantidad de información a recorrer, avisados estáis.

Espero que os guste ;-)

GTXSII. Visual Basic .NET

Estamos trabajando muy duro en intentar en soportar más lenguajes de programación para nuestra librería GtxSII, para el Suministro Inmedi...