Thursday, November 5, 2009

Recapitulemos

Creo que podemos hacer un breve resumen de las cosas que tenemos hasta ahora. Por ejemplo, seguiremos pensando en usar patrones, pues eso nunca fué mala idea. Así pues, el MVC se seguirá respetando. Con lo que las entidades "inspector" y "skin" (por lo menos, lo que representan) seguirán estando presentes.

Twin Panel seguirá siendo un sistema plugable, por lo que no podemos dejar de usar una factoría, aunque su estructura cambie bastante.

Otra cuestión interesante que discutir, y que siempre ha sido bastante difusa, es la relación entre los métodos de acceso (los antes llamados "methods"), y los dominios; conceptos que siempre han dado que hablar.

Creo que una muy buena manera de ver los componentes y sus relaciones es utilizando un diagrama de secuencia. Voy a intentar esbozar uno, y con la ayuda de todos, lo vamos mejorando, ¿vale? :p Intentaré ser lo más genérico posible, puesto que todavía no hay nada determinado.
  1. Un usuario desea explorar su carpeta personal. Para ello, lanzaría TP de una forma parecida a esta:  $ tp file:///home/user
  2. tp (el binario/script que se ejecute) deberá analizar la URL, convertirla a la estructura que sea necesario, y pedir a "alguien" que se encargue de instanciar/despertar/... los componentes que entrarán en juego para la tarea. Lo podemos llamar "factoría", con comillas, pues puede que me cuele. 
  3. La "factoría" determina los componentes a usar, siempre que no se hayan especificado antes (como opciones de configuración, u otra cosa). Esta se encargará de que esos componentes estén accesibles, bien instanciándolos, bien despertándolos, etc. 
  4. Alguien deberá encargarse de configurar estos componentes. Por ejemplo, al "inspector" deberá decirsele la URL que está inspeccionando, o al skin dónde debe pintar.
Mmm, creo que faltan muchas cosas intermedias, pero seguramente que en estas cuatro básicas, ya he metido la pata...

Bueno, empieza la audiencia :D

Wednesday, November 4, 2009

Casos de uso

En vista de los cambios que pronto afrontaremos, debemos tener siempre presente aquello para lo que, en principio, queremos usar Twin Panel. Este post intentará recoger los casos de uso, para así capturar requisitos y poder emprender el diseño.

Twin Panel debe ser una herramienta que permita explorar conjuntos de objetos. Casi cualquier cosa puede ser tratada como un objeto, desde un fichero hasta una máquina, o un mensaje. Dada esta pluralidad, es muy difícil concretar todos los casos de uso. Si encuentras alguno que falta, puedes comentarlo y se tendrá en cuenta.

Consideramos "contenedor" al elemento del sistema que posee al resto de elementos, siendo posible que estos, a su vez, sean "contenedores". Para evitar duplicar el texto, definimos los grupos de acciones permitidas:
  • Acciones Mínimas: listar elementos del "contenedor".
  • Acciones Básicas: las acciones mínimas, más: copiar, cortar, pegar, crear y eliminar elementos, entre nodos del mismo "contenedor".
  • Acciones Ampliadas: acciones básicas entre nodos de "contenedores" diferentes (compatibles).
La lista de casos de uso es la siguiente:
  • Exploración local de ficheros. Es el caso más obvio. Deber permitir explorar cualquier sistema de ficheros local (obviamente, que esté soportado por el kernel). Debe soportar las Acciones Ampliadas. El mecanismo de acceso podría ser GVFS.
  • Exploración remota de ficheros. La extensión al caso anterior. Los mecanismos de acceso pueden ser variados: SSH, FTP, Ice... Debe soportar las Acciones Ampliadas
  • Explorar el conjunto de máquinas de una red. Deberían poderse listar tanto las máquinas (con sus nombres, IP's u otros identificadores), como los servicios que proveen. El conjunto de acciones permitido serían las Acciones Mínimas.
  • Visualizar los elementos que componen un documento web. Dependiendo del método de acceso, debería soportarse el conjunto de Acciones Básicas o Acciones Ampliadas.
Podríamos tener descripciones similares para los siguientes casos:
  • Objetos Ice
  • Elementos de UPnP
  • Servidor POP3
  • Dispositivos Bluetooth
  • Nodos ZigBee
  • Redes y AP's WiFi
  • Procesos del sistema
  • Puertos abiertos con sus servicios asociados
  • Bibliotecas multimedia (picassa, flickr, youtube...)
  • Entornos sensibles a localización (GIS)
  • Dispositivos X10 de un entorno
  • ...
Examinando estos casos, surgen algunas cuestiones que tratar:
  1. Debemos definir un mecanismo preciso para la interacción entre diferentes mecanismos de acceso a estos "contenedores" (por ejemplo, para copiar un fichero desde un SF remoto a uno local, usando SSH), algo que todavía no se había abordado.
  2. Debemos ser muy estrictos con las interfaces que cada "plugin" debe cumplir. Uno de los problemas que presenta la versión actual es que no hay un conjunto bien definido de interfaces, lo que permite registrar plugins incompletos que desestabilizan el sistema.
  3. Es necesario disponer de una buena documentación desde el principio. Si un desarrollador hace un plugin, pero no dispone de toda la documentación necesaria, perderá mucho tiempo, hará cosas duplicadas, y posiblemente causará otros problemas al resto del sistema.
  4. Pruebas. He aprendido (por fin :D) que es un aspecto clave a la hora de desarrollar software. Como dice incansablemente David: "las pruebas merecen la pena", y yo añado "sin pruebas, se pierde tiempo y es muy difícil abordar problemas complejos (sin causar otros)".
Con el nuevo diseño que se está planteando, algunas de estas cosas se resuelven. Por ejemplo, el uso de interfaces; utilizando Zeroc-Ice, se fuerza la utilización de interfaces.

Sigamos discutiendo aspectos del diseño.

Requisitos nuevos flamantes

Con vistas en el nuevo TP estamos acordando algunos requisitos bastante llamativos en relación a la implementación actual. A saber:
  • El núcleo de la aplicación van a ser los inspectores y los skins. La interfaz principal como tal va a perder algo de protagonismo. De hecho, la idea es poder manipular skins e inspectores como componentes de modo que pueda haber otras formas de cargarlos, incluso como programas individuales.
  • Queremos evitar la dependencia de GTK. Vamos a usar GTK como interfaz pero queremos poder usar otras cosas. Es decir, queremos poder hacer skins web o de consola, por ejemplo. También queremos que un cambio en GTK no sea demasiado traumático ni afecte a la implementación de los inspectores, solo a los skins. Esto tiene fuertes y traumáticas implicaciones:
    • No podremos usar los stores de GTK como almacén para los modelos de los inspectores. Tendremos que definir una interfaz para acceder a los datos del modelo. Esto supondrá varias repercusiones en el rendimiento porque implica copiar (o indireccionar) el contenedor de datos del modelo a la vista.
    • No podemos usar el UIManager para gestionar las "Actions" que pueden ofrecer los inspectores. Sin embargo, este sistema es muy flexible y quizá habría que copiarlo. De hecho ya lo copiamos en su momento sin saberlo.
  • Y agarraos los machos! Queremos que esos "componentes" sean distribuidos. Es decir, algo como lo que GNOME intentó con BONOBO, pero nosotros lo vamos a hacer bien :-S Y claro, para eso vamos a usar Ice. Cada skin e inspector posible ES un servicio. Y nuestra amada/odiada factoría es substituida (en parte) por IceBox. Él va a instanciar y eliminar skins y inspectores.
Lo más llamativo puede ser lo de usar Ice, aunque no es tan sorprendente. Paco lo dijo desde el principio de los tiempos, a magmax también le convencía y a mi me llamaba la idea sobre todo por el tema de imponer interfaces férreas gracias al slice, aunque siempre me ha preocupado la penalización que van a tener las invocaciones remotas (aunque sean locales) dado que es «otra capa de mierda» y me temo que se hará cierta esa frase que dice

«Cualquier problema en ciencias de la computación puede ser solucionado con otra capa de indirección… pero usualmente creará otro problema» ― David Wheeler

Pero ciertamente tiene muchas ventajas que espero que podamos explotar:
  • Hacer componentes en muchos y variados lenguajes. En particular esto nos permite prototipar en Python (aprovechando el código actual) y después las partes que sean estables, probadas y que sean críticas en cuanto a rendimiento se pueden pasar a C++.
  • Manipular inspectores remotos o locales de forma transparente, de modo que se puedan interponer «inspectores-caché» o hacer aplicaciones GUI puras para dispositivos móviles.
  • El modelo de datos puede estar perfectamente establecido por medio de un pequeño conjunto de interfaces, que como veremos se parece mucho a DUO, pues sigue el mismo principio: son interfaces de acceso puro a los datos, la semántica de los mismos la pone el que los interpreta.
  • Es posible utilizar inspectores ya creados por el sistema u otro usuario.
  • Hay muchas otras cosas que se pueden delegar a Ice:
    • Persistencia de la configuración
    • Gestión de la instanciación/liberación de los componentes
    • Instalación/actualización de componentes. El sistema de plugins ahora es IceBox.
Pero usar Ice también plantea muchos problemas que tendremos que ir viendo y que trataremos en siguientes posts. Este es más que nada para que lo vayáis digiriendo. ;-)

La primera conclusión es que ahora queremos dos cosas: Un programa para interacción entre colecciones de objetos cualesquiera (TP) y una plataforma de componentes (que no tiene nombre aún).

Saludos

    Wednesday, October 28, 2009

    Widgets personalizados en la barra de tareas

    Como ya sabéis (o deberíais) Twinpanel usa una encapsulación del ActionGroup de GTK (menu.ActionManager) dónde se pueden añadir de forma muy parecida a cómo se hace en GTK acciones que se mostrarán, dependiendo de la "location" especificada, en la barra de menú o en la de herramientas.

    El problema de esto es que GTK solo proporciona algunos tipos de acciones (gtk.Action y algunos más) que generan elementos en el menú de herramientas, lo que hace imposible en principio añadir un widget genérico en dicha barra.

    Para poder hacer esto se debe crear una especialización de gtk.Action (de forma que el ActionGroup de GTK pueda aceptarlo como acción), registrarlo en la factoría de tipos de GObject y, tras ello, decirle que tipo de widget debe representar. El widget a su vez debe ser una especialización de gtk.ToolItem.

    ¿Lioso? Bueno, no tanto. Como nosotros queremos añadir un widget personalizado tan solo deberíamos crear una especialización del gtk.ToolItem y, al ser estos también especialización de gtk.Container, añadir nuestro widget en ellos (y, por supuesto, no os olvidéis de hacer el "show" de nuestro widget).

    Con esto he creado un pequeño ejemplo que se puede ver en el fichero widget.py de la distribución "stable"de Twinpanel.

    Tenemos una clase "SpinAction", que hereda de gtk.Action y está registrada en la factoría de Gobject. La especialización de ToolItem en este caso es "SpinToolButton", que en su constructor crea un SpinButton y se lo añade y muestra. Además tiene una serie de métodos, propios de SpinButton, que nos permite acceder y modificar su valor, además de los callbacks que se conectan con los de SpinButton para emitir nuestras propias señales (que por supuesto están registradas en gobject unas líneas más abajo).

    Con esto simplemente tendremos que crear una instancia de SpinAction y añadirla a nuestro ActionManager con "add_real_action" (TODO: debe cambiarse dicho nombre para que sea "add_action", y cambiar el actual "add_action" a algo más apropiado).

    Lo mismo que con el ejemplo que acabo de explicar estoy intentando hacer con el MenuToolButton, para que podamos añadir una acción que defina un botón con menú asociado (al estilo de los que vemos todos los días en el navegador en los botones "Atrás" y "Adelante"). Lo malo de este tipo es que tiene un menú asociado, y añadirlo en tiempo de "diseño" de la interfaz parece que va a requerir algunos cambios en Twinpanel...