Desarrollando una extensión para F-spot

Me he decidido a desarrollar una extensión para f-spot, me gusta la filosofía de este estupendo gestor fotografico pero hecho en falta alguna funcionalidad, en concreto me voy a centrar en un buscador que muestre las fotos que no han sido exportadas nunca o que no han sido exportadas mediante alguna extensión de exportación en concreto. Iré explicando los detalles del proceso en este post ¿sere capaz de programar esta extensión? ¿llegará a tener calidad suficiente como para que la añadan al core del programa? ¿servira este post como guía para otros desarrolladores?… Todas estas dudas se iran solventando en futuros episodios, así que no te pierdas el próximo en este mismo canal y a la misma hora.

Pongamos las manos en la masa

Empecemos por leer la documetnación existente sobre el tema, en la web de F-spot se encuentra una guía básica sobre el desarrollo de extensiones. Tras la lectura parece muy simple desarrollar una extensión, pero la verdad es la documentación es demasiado básica y tan solo muestra la esctructura que deben tener las extensiones pero no documenta nada más, así que habrá que descargar el código mediante subversión, buscar extensiones que nos sirvan como referencia:

cd src/

svn co http://svn.gnome.org/svn/f-spot/trunk f-spot

La primera duda que me surge, a parte del pequeño inconveniente de no tener ni la más remota idea sobre c#, es en qué menú debe ir mi extensión, al ser de busqueda me gustaría que apareciese en el menú buscar pero en la documentación no aparece nada acerca de la estrucura de menús, ante esto podría comenzar a buscar en el código fuente o intentarlo directamente. Personalmente me gusta intentar deducir las cosas y aprender de forma empirica siempre que no esté en entornos productivos así que voy a probar modificando una extensión, y la elegida es la que busca fotos duplicadas mediante su hash.

cp -r f-spot/extensions/Tools/HashJob/ f-spot/extensions/Tools/UnexportedFinder

cd f-spot/extensions/Tools/UnexportedFinder/

mv HashJob.addin.xml UnexportedFinder.addin.xml

Como veis he decidido llamar a la extension UnexportedFinder (buscador de no exportadas). Ahora lo primero que haré es editar el archivo que contiene el «manifiesto» y realizar los cambios necesarios, el archivo resultante es:

<Addin namespace="FSpot"
	id="UnexportedFinder"
	version="0.0.0.1"
	description="Search for pictures not exported for a given exporter extension"
	author="Abdul Pallare"
	url="http://f-spot.org/Extensions"
	name="Unexported Finder"
	category="Tools">
	<Dependencies>
		<Addin id="Core" version="0.5.0.0"/>
	</Dependencies>
	<Extension path = "/FSpot/Menus/Find">
		<Command id = "FindUnexported" _label = "Search unexported"
command_type = "UnexportedFinderExtension.UnexportedFinder" />
	</Extension>
</Addin>

No estoy seguro de:

  1. Que la versión del core sea correcta
  2. Que el path del menú sea /FSpot/Menus/Find
  3. Por lo que he visto «command_type» hace referencia al namespace y la clase que se utilizará.

Ahora hay que crear la clase que se definirá en el archivo UnexportedFinder.cs. Abro el archivo con mi queridisimo vi y le inserto el siguiente código, que no es más que una copia del código de ejemplo que se encuentra en la web de F-spot con los nombres adaptados a esta extensión

//UnexportedFinder.cs

using FSpot;
using FSpot.Extensions;
using System;

namespace UnexportedFinderExtension
{
	public class Dump : IExporter
	{
		public void Run (IBrowsableCollection photos)
		{
			foreach (IBrowsableItem photo in photos.Items)
			{
				Console.WriteLine (photo.Name);
			}
		}
	}
}

Bien si intentamos impotar esto como extensión probablemente fallé ya que no encontrará ni el metodo ni la clase especificados en el parametro «command_find» pero me voy a arriesgar y poner así a prueba la calidad del propio código de F-spot.

gmcs -t:library UnexportedFinder.cs -pkg:f-spot -resource:UnexportedFinder.addin.xml

Vaya pues ha funcionado, hay que tener en cuenta que tanto la tecnología usada (MonoAddins) como el propio sistema de extensioned de F-spot están en desarrollo, por lo tanto no hay que extrañarse demasiado. Bien una vez probado que compila voy a denominar a la clase correctamente de forma que el archivo .cs quedará tal que

//UnexportedFinder.cs

using FSpot;
using FSpot.Extensions;
using System;

namespace UnexportedFinderExtension
{
	public class UnexportedFinder : IExporter
	{
		public void Run (IBrowsableCollection photos)
		{
			foreach (IBrowsableItem photo in photos.Items)
			{
				Console.WriteLine (photo.Name);
			}
		}
	}
}

Tras volver a compilarlo se instala la extensión

cp UnexportedFinder.dll ~/.gnome2/f-spot/addins

Este comando copia la libreria recien creada al directorio .gnome2/f-spot/addins de nuestra $HOME. Tan solo falta ver el resultado… Ouch!! funciona a medias, la extensión aparece en el gestor de extensiones y está activada, pero no hay ninguna opción nueva en el menú Buscar esto se puede deber a que no tenga el texto especificado en el atributo _label del manifiesto o a que no el path del menú no sea correcto. Para salir de dudas lo primero es ejecutar F-spot desde la linea de comandos y estudiar la salida

abdul@greytet:~$ f-spot
[Info 21:57:53.944] Initializing DBus
[Info 21:57:54.107] Initializing Mono.Addins
[Info 21:57:54.367] Starting new FSpot server
Extension node not found or not extensible: /FSpot/Menus/Find
[Info 21:57:55.908] Starting DBusService
[Info 21:57:55.917] Starting BeagleService
[Info 21:57:55.917] Hack for gnome-settings-daemon engaged

(f-spot:27433): GdkPixbuf-WARNING **: GdkPixbufLoader finalized without calling gdk_pixbuf_loader_close() – this is not allowed. You must explicitly end the data stream to the loader before dropping the last reference.

Como se puede observar no existe el path /FSpot/Menus/Find o este no es extensible. Aunque esto me da algunas pistas

  1. No era correcta la suposición de que el path hace referencia a los menús
  2. F-spot organiza las extensiones por nodos y el path hace referencia a dichos nodos
  3. Parece que no podré insertar la extensión donde me plazca

Tras haber realizado la prueba pertinente con el path /FSpot/Menus/Find y para no perder tiempo buscando como funcionan los nodos de extensiones, la voy a añadir al menú Herramientas y espero profundizar más en la materia a medida que vaya estudiando el código para realizar la clase que osquetará el funcionamiento de esta extensión. Así que modificaré el atributo path del manifiesto para que refleje los cambios deseados

<Extension path = «/FSpot/Menus/Tools»>

Compilar y volver a copiar la libreria y…ahora sí!! aparece un nuevo elemento en el menú Herramientas que se llama Search unexported tal y como se ha indicado en el atributo _label del manifiesto. Pero al seleccionarlo lo único que sucede que que el F-spot se va al carajo carajote 😉 nada raro ya que no tengo ni flawers de lo que estoy programando, bueno ahora empieza la odisea, aprender c# mediante el estudio del código jjj ahora biene lo bueno…

Investigando el código

Para empezar voy a investigar como se ataca a la base de datos, sabiendo que:

  1. El formato utilizado es sqlite
  2. La base de datos se encuentra en ~/.gnome2/f-spot/photos.db

Es de suponer que F-spot provea metodos de acceso a la base de datos así que debemos encontrarlos, empezaré haciendo una búsqueda del texto photos.db en el código fuente

grep -lr photos.db ~/src/f-spot/*

docs/C/.svn/text-base/f-spot.xml.svn-base
docs/C/f-spot.xml
docs/de/.svn/text-base/de.po.svn-base
docs/de/de.po
docs/de/figures/.svn/text-base/de.po.svn-base
docs/de/figures/de.po
docs/es/.svn/text-base/es.po.svn-base
docs/es/es.po
docs/fr/.svn/text-base/fr.po.svn-base
docs/fr/fr.po
docs/it/.svn/text-base/it.po.svn-base
docs/it/it.po
docs/oc/.svn/text-base/oc.po.svn-base
docs/oc/oc.po
docs/ru/.svn/text-base/ru.po.svn-base
docs/ru/ru.po
docs/sv/.svn/text-base/sv.po.svn-base
docs/sv/sv.po
grep: dpap-sharp/dpap-client/dpap-sharp.dll: No existe el fichero ó directorio
grep: dpap-sharp/dpap-server/dpap-sharp.dll: No existe el fichero ó directorio
extensions/Tools/ChangePhotoPath/.svn/text-base/ChangePhotoPathGui.cs.svn-base
extensions/Tools/ChangePhotoPath/.svn/text-base/ChangePhotoPath.addin.xml.svn-base
extensions/Tools/ChangePhotoPath/.svn/text-base/ChangePhotoPathController.cs.svn-base
extensions/Tools/ChangePhotoPath/ChangePhotoPath.addin.xml
extensions/Tools/ChangePhotoPath/ChangePhotoPathGui.cs
extensions/Tools/ChangePhotoPath/ChangePhotoPathController.cs
extensions/Tools/MergeDb/.svn/text-base/MergeDb.cs.svn-base
extensions/Tools/MergeDb/MergeDb.cs
src/.svn/text-base/Core.cs.svn-base
src/Core.cs
tools/.svn/text-base/f-spot-sqlite-upgrade.svn-base
tools/f-spot-sqlite-upgrade

La opción -l le indica a grep que tan solo muestre el nombre del archivo que contenga el texto espeficicado en lugar de mostrar cada linea en la que aparece el texto. Del listado optenido llaman la atención los archivos

src/Core.cs

tools/f-spot-sqlite-upgrade

El archivo f-spot-sqlite-upgrade tan solo me llama la atención como curiosidad pero doy por sentado que el código que busco se encuentra en el archivo src/Core.cs que parece alojar la clase principal del programa. Tras un breve vistazo al archivo f-spot-sqlite-upgrade saco en claro que

  • Es un shell script que actualiza la base de datos de SQLite v2 a SQLite v3
  • SQLite proporciona el comando .dump para realizar copias de seguridad, así que es recomendable usarlo en lugar de copiar el archivo desde el sistema operativo.

Veamos ahora el archivo objeto de nuestro estudio Core.cs, mirando por encima veo que la clase utilizada para crear objetos para la gestión de la base de datos es Db así que debería existir un archivo llamado Db.cs si Mono sigue la filosofía de Java ya que este obliga a que las clases se definan en archivos que se llaman exactamente igual que la clase más la extensión pertinente, .class en Java .cs en Mono.

find ./ -name Db*
./src/.svn/text-base/Db.cs.svn-base
./src/Core/.svn/text-base/DbItem.cs.svn-base
./src/Core/DbItem.cs
./src/Db.cs
./src/Utils/.svn/text-base/DbUtils.cs.svn-base
./src/Utils/DbUtils.cs

Efectivamente hechemos un vistazo al fichero src/Db.cs. Lo primero que me llama la atención es que a diferencia de Java, Mono permite definir más de una clase en el mismo archivo. A parte de esto se constata que esta es la clase buscada y que esta a su vez utiliza la clase Banshee.Database del proyecto Banshee. No recuerdo haber visto que se importe la clase Db en el archivo Core.cs así que debe estar contenida en otro import, esto hace que me pregunte si yo necesitaré importarla o basta con lo que he importado. Para probarlo voy a crear el objeto db en la clase UnexportedFinder y compilar de nuevo a ver si falla

//UnexportedFinder.cs

using FSpot;
using FSpot.Extensions;
using System;

namespace UnexportedFinderExtension
{
	public class UnexportedFinder : IExporter
	{
		private Db db;
		public void Run (IBrowsableCollection photos)
		{
			foreach (IBrowsableItem photo in photos.Items)
			{
				Console.WriteLine (photo.Name);
			}
		}
	}
}

gmcs -t:library UnexportedFinder.cs -pkg:f-spot -resource:UnexportedFinder.addin.xml

UnexportedFinder.cs(11,28): warning CS0169: The private field `UnexportedFinderExtension.UnexportedFinder.db’ is never used
Compilation succeeded – 1 warning(s)

Y ZAS! compila a la perfección dando tan solo un aviso de que no estamos utilizando la variable db. Genial, ya tenemos objeto de base de datos ahora a inicializarlo y ejecutar alguna consulta de prueba. Pero antes voy a instalar Monodevelop para acelerar el estudio del código fuente. Una vez instalado Monodevelop hay que abrir el archivo de proyecto que es f-spot.mds esto cargará todos los objetos definidos y ahora podemos navegar de una forma sencilla y eficaz las clases, sus metodos y dependencias. Al hechar un vistazo a la clase Db se saca en claro que

  • La base de datos se cachea y se trabaja sobre la caché
  • La clase Db no proporciona metodos de consulta directa a la base de datos
  • Se accede a la base de datos a través de clases que gestionan cada una de las tablas denominadas NombredetablaStore

La siguiente clase a estudiar es ExportStore que es la que gestiona la tabla de exportaciones. En esta clase ya sí que se encuentran metodos que realizan consultas SQL a la base de datos, por lo que veo se accede a la base de datos mediante un referencia directa a la clase Database (que imagino se encuentra en la librería de Banshee) y hay varios métodos para acceder a la base de datos que son ExecuteNonQuery, Query y Execute. Aún no sé en que radica su diferencia pero en el método LoadAllItems se llama al metodo Database.Query para obtener todas las imágenes exportadas, y como el método que estoy desarrollando debe realizar una consulta del mismo tipo voy a copiar este método en mi clase y modificaré la consulta. Es interesante poder exportar todas las versiones de una foto así que debería seleccionar el identificador de foto y de version que no existan en la tabla exports, pero como no soy ningún experto en SQL y esa consulta es algo compleja, de momento seleccionare el identificador de cada foto que no exista en la tabla exports y más adelante me pelearé con la consulta óptima, este es el método resultante

private void GetUnexportedItems (string export_type)
{
	SqliteDataReader reader = Database.Query("select id from photos where id not in
	(select image_id from exports where export_type not like "%"+export_type+"%" );");

	while (reader.Read ()) {
		Console.WriteLine (reader["id"]);
	}

	reader.Close ();
}

Compilar y…

gmcs -t:library UnexportedFinder.cs -pkg:f-spot -resource:UnexportedFinder.addin.xml
UnexportedFinder.cs(15,25): error CS0246: The type or namespace name `SqliteDataReader’ could not be found. Are you missing a using directive or an assembly reference?
Compilation failed: 1 error(s), 0 warnings

Ouucchh falla, bueno veamos que me he dejado por importar, aha en la clase ExportStore se importa el paquete Mono.Data.SqliteClient así que añadiré la siguiente linea en la sección de importación

using Mono.Data.SqliteClient;

Y vuelta a compilar y vuelve a fallar

gmcs -t:library UnexportedFinder.cs -pkg:f-spot -resource:UnexportedFinder.addin.xml
UnexportedFinder.cs(4,12): error CS0234: The type or namespace name `Data’ does not exist in the namespace `Mono’. Are you missing an assembly reference?
Compilation failed: 1 error(s), 0 warnings

¿Tendré que instalar alguna librería de mono? hecharé un vistazo a los paquetes de Ubuntu…efectivamente existe el paquete libmono-sqlite2.0-cil pero ya lo tengo instalado y mirando el contenido del paquete veo que crea la carpeta /usr/lib/mono/2.0/Mono.Data.SqliteClient.dll que contiene el paquete que está fallando así que parece que falte esta librería en el path en el que mono buca las librerías, pero me extraña ya que es donde están todas las librerías de mono así que hecharé un vistazo a la documentación

zless /usr/share/doc/libmono-sqlite2.0-cil/README.gz

Nada interesante aquí mmm… a seguir buscando, al hechar un vistazo al Makefile.am de la extensión HashJob veo una sección que llama la atención

REFS =
-r:Mono.Posix
-r:$(top_builddir)/src/f-spot.exe
-r:$(top_builddir)/src/FSpot.Core.dll
-r:$(top_builddir)/src/FSpot.Utils.dll
-r:$(top_builddir)/src/FSpot.Query.dll
-r:Mono.Data.SqliteClient

así que al parecer hay que referenciar a los namespace que utiliza nuestro código, esto quiere decir que no hay un path donde buscará por defecto, seguramente exista algo parecido al classpath de Java pero no voy a buscar esto ahora. Así que pruebo de nuevo y me da un error

gmcs -t:library UnexportedFinder.cs -pkg:f-spot -resource:UnexportedFinder.addin.xml -r:Mono.Data.SqliteClient

UnexportedFinder.cs(20,74): error CS0103: The name `Database’ does not exist in the current context

Compilation failed: 1 error(s), 0 warnings

Pero ya no se queja sobre Sqlite bien! un pasito más avanzado ahora que puñetas pasará…busco alguna librería que haga referencia a Banshee.dll o algo por el estilo locate Banshee, find ./ -name Banshee* y cosas por el estilo pero no aparece nada, vuelvo a hechar un vistazo a la clase Db y veo que contiene un miembro público llamado Database así que como ya tengo un objeto de tipo Db inicializado en el código cambio la linea para que quede tal que así

SqliteDataReader reader = db.Database.Query…

Vuelvo a compilar y… Voila!! yeah ahora sí compiló a la perfección, copiar la librería al directorio pertinente

cp UnexportedFinder.dll ~/.gnome2/f-spot/addins

y ejecuto f-spot desde un terminal ya que el código imprime el id de las fotos mediante un Console.WriteLine que imprime por el standart output del sistema, y esto no es el entorno gráfico jjj. Bueno pues el mismo resultado de antes, la aplicación inicia bien pero al seleccionar la extensión peta. El cariñoso mensaje que nos escupe el terminal reza así

Exception in Gtk# callback delegate
Note: Applications can use GLib.ExceptionManager.UnhandledException to handle the exception.
System.InvalidCastException: Cannot cast from source type to destination type.
at FSpot.Extensions.CommandMenuItemNode.OnActivated (System.Object o, System.EventArgs e) [0x00000]
at GLib.Signal.voidObjectCallback (IntPtr handle, IntPtr data) [0x00000]
at GLib.ExceptionManager.RaiseUnhandledException(System.Exception e, Boolean is_terminal)
at GLib.Signal.voidObjectCallback(IntPtr handle, IntPtr data)
at GLib.Signal.voidObjectCallback(IntPtr , IntPtr )
at Gtk.Application.gtk_main()
at Gtk.Application.gtk_main()
at Gtk.Application.Run()
at Gnome.Program.Run()
at FSpot.Driver.Main(System.String[] args)

La verdad es que no me he preocupado por saber si le puedo pasar un int al Console.WriteLine. Tras un grep WriteLine en el directorio raíz de la aplicación veo rápidamente que esta función se parece a la función printf de, osea que los parametros se dividen en la cadena de texto que imprimirá seguido de tantas variables como se hayan referenciado en la cadena, pero al parecer en este caso al menos no es necesario indicar de qué tipo será cada una de las variables, al contrario que en c. La linea modificada queda así

Console.WriteLine («El id de la foto es: {0}»,reader[«id»]);

Vaya esta modificación no produce cambio alguno en la ejecución y mirando detalladamente la salida, se ve que el error viene dado por un System.InvalidCastException en el método FSpot.Extensions.CommandMenuItemNode.OnActivated (System.Object o, System.EventArgs e) osea que el error se produce en el mismo f-spot y no en la clase desarrollada… bueno parece que he topado con un bug de F-spot estoy preguntando en el canal irc oficial, he buscado en el bugzilla pero no he encontrado nada. Bueno finalmente falsa alarma, he hechado un vistazo más detallado a la extensión Hashjob y he visto dos formas distintas de solucionar el error la primera sería implementar un método que gestione el click en el menú, la definición de la función seria

void HandleResponse (object obj, ResponseArgs args)

La segunda es eredar de ICommand en lugar de IExporter e implementar su metodo Run y dado que esta clase no va a exportar ni usará colecciones es más correcto heredar de esta clase, además he visto como utilizar el objeto Database sin necesidad de crear un nuevo objeto Db, esto es más elegante ya que se evita el acceso a la base de datos de dos objetos diferentes y se minimiza la posibilidad de herrores, los cambios realizados son

public class UnexportedFinder : ICommand

…..

SqliteDataReader reader = FSpot.Core.Database.Database.Query

…..

public void Run (object o, EventArgs e)

Una vez compilado y copiada la librería…Great!!! it WORKS!!! ahora sí que sí al seleccionar la extensión en el menú la queridisima terminal escupe unas lienas preciosas al estilo

El id de la foto es: 1
El id de la foto es: 2
El id de la foto es: 3
El id de la foto es: 4
El id de la foto es: 5
El id de la foto es: 6
El id de la foto es: 7
El id de la foto es: 8
El id de la foto es: 9
El id de la foto es: 10
El id de la foto es: 11
El id de la foto es: 12
…..

yeah!! justo lo esperado. Ahora al paso siguiente, ¿como hago que se visualicen estas fotos en la pantalla de F-spot? Bueno esto lo dejaré para la siguiente entrega, ahora es hora de cocinar una rica y merecida cena mmmmm 😛

Bueno pues aquí estoy de nuevo, ultimamente no he podido dedicar mucho tiempo al post pero en algún rato yendo de aca para allá he podido seguir mirando código y demás. La verdad es que esperaba que el paso del resultado de la consulta sería más sencillo pero aún no lo he conseguido. En este tiempo he probado: depurar con Monodevelop pero vi en la web que en la versión actual no funciona el addin que lo realiza, instalar eclipse con plugins para c# pero en un vistazo rápido tampoco vi como depurar así que no me quedó otra que seguir el flujo del código. La verdad es que el código es dificil de seguir ya que los comentarios brillan por su ausencia y por lo que se ve en los existentes ni siquiera los desarrolladores conocen bien el código ya que se encuentran comentarios del tipo «esto parece servir para…» despues de unos paseos por el código (que describiré a continuación), cambios en la clase que estoy desarrollando y tras la no ver una salida clara por donde sacar a luz el resultado de la consulta me conecté al chat oficial a ver si alguien conocía el camino a tomar, pero tras varios intentos no respondieron ni tan siquiera al saludo…»uh! parece un proyecto muy individualizado, bueno probaré con la lista de correo!» me dije y siguiendo las ordenes de las neuronas que se activarón por las alturas de mi ser fuí, me apunte a la lista de correo y envié el mensaje pero una semana y pico después no ha habido respuesta alguna, así que parece que hay poca colaboración o poca gente que conozca bién el código y los que lo conocen no son muy colaborativos 🙁 bueno habrá que seguir probando…

Entonces si tengo que estudiar el código ¿Por donde empiezo? Al ser un lenguaje basado en c el punto de entrada a la ejecución del programa debe ser la función main y mirando los archivos se encuentra el archivo main.cs que contiene la clase Driver, la cual a su vez contiene el método estático Main que devuelve un entero tras su ejecución, así que ya tengo por donde empezar :). La primera idea que me viene a la cabeza es utilizar el método View de la clase Core al cual se le pasa como argumento una lista de uris (los uris son rutas hacia archivos ya sea en el disco duro, en una camara, en internet…) para este fin se utiliza el objeto UriList y como las uris están en la base de datos y ya tengo acceso a la misma no es dificil obtenerlas. Tras relizar los cambios pertinentes en el código obtengo el primer resultado en el que veo las imágenes que me devuelve la consulta pero no el esperado, se abre en modo visualización de archivos, en una ventana aparte que muestra las fotos de una en una :(. En esta clase no se encuntra nada más que mirar y el objeto que usa principalmente es de tipo Core…a la clase Core pués. Esta clase hereda de la interfaz ICore, una interfaz es una definición de métodos que deben ser programados por las clases que deriven de ella de forma obligatoria, sirve para utilizar metodos comunes en clases que aunque siguen una lógica de métodos iguales el código necesario para llevarlos a cabo puede diverger mucho, de esta forma las clases tendrán metodos comunes pero cada una se encaga de las especificaciones necesarias para su correcto funcionamiento y podrán intercambiar información perfectamente. La clase Core define el objeto organizer(privado) y MainWindow(público), son de tipo MainWindow que es la clase encargada de la gestión gráfica del programa, también define un objeto de tipo Db para la gestión de la base de datos. En esta clase se encuentran unos cuantos métodos interesantes:

  • public static ICore FindInstance: Sirve para saber si ya se extá ejecutando F-spot y si es así devuelve una referencia al objeto ICore principal de la misma
  • RegisterServer: Registra una instancia principal de F-spot
  • public void Import: Importa imágenes al catálogo ejecutando el metodo Execute de un objeto de tipo ImportCommand
  • Organize: Es el metodo encargado de mostrar la ventana principal mediate la orden MainWindow.Window.Present ()
  • View: Este método sirve para mostrar una lista de imágenes en una ventana de modo visualización, se usa para ver archivos que no se han importado a la colección
  • ShowSlides: Muestra las imágenes en modo presentación
  • Register: Registra una ventana en la instancia principal de F-spot, por ejemplo una de slideshow

Bueno esta clase gestiona toda la ejecución del programa pero no se encarga de mostrar las fotos, proxima parada MainWindow jeje.

En esta clase se encuentran las definiciones de todos los objetos gráficos mediante GTK, hay muchas cosas interesantes, pero las que me interesan a mi son:

  • public static MainWindow Toplevel: Objeto estático de tipo MainWindow y público, servirá para acceder a los métodos y propiedades de la ventana principal
  • Db db: Acceso a base de datos
  • FindBar find_bar: Barra de búsqueda puede ser interesante saber como funciona e imitar su funcionamiento
  • query = new FSpot.PhotoQuery (db.Photos): Esta parece ser la propiedad a modificar pero es privada
  • photo_view = new PhotoView (query): Aquí parece agrupar los elementos devueltos por la consulta en objetos apropiados para visualizarlos
  • photo_box.Add (photo_view): Y aquí los agrega al objeto encargado de mostrarlos
  • public void SetViewMode (ModeType value): Método que cambia el modo de visión entre iconos y foto
  • JumpTo (photo_view.Item.Index): Esta instrucción demuestra que las fotos que se mustrán están contenidas en la propiedad photo_view que es de tipo PhotView
  • public PhotoQuery Query: Propiedad de solo lectura que devuelve una copia del atributo privado query
  • public Gtk.Window GetToplevel (object sender): Devuelve la ventana toplevel si se pasa Null como parámetro
  • public void UpdateQuery (): Creo que es el método a ejecutar tras un cambio en el atributo query
  • query_widget.Include (tag_selection_widget.TagHighlight): Incluye un tag en la barra de búsqueda, puede ser interesante seguir com funciona ya que cambia las imágenes que se muestran y por tanto la consulta principal
  • void PopulateExtendableMenus: Aquí se averigua cuales son los menús extendibles, en caso de querer que la extensión salga en otro menú que no sea el menú Tools tendré que tocar esta parte del código

Aunque en esta clase hay muchas cosas interesantes, no existe ningún metodo para modificar la propiedad query así que no es posible modificar la consulta desde aquí, pero tengo ganas de incarle el diente a la clase PhotoView a ver como funciona. Pues mira tu por donde que aquí se define una propiedad muy interesante:

public IBrowsableCollection Query {
get { return query; }
set { query = value; }
}

Aquí si se modifica lo que parece ser la consulta que se mostrará, esta propiedad es pública y ofrece la posibilidad de establecer su valor mmm interesante, ahora a ver si se puede acceder a la propiedad photo_view de la ventana principal. He buscado todos los objetos de tipo PhotoView en la clase MainWindow y tan solo hay uno que sea público pero es tan solo de lectura:

public PhotoView PhotoView {
get { return photo_view; }
}

Bueno lo dejo por hoy que es hora de cenar 🙂

Uff cuanto tiempo sin dedicarme a estos menesteres… Pero tras obtener respuesta de los miembros del equipo de desarrollo de F-spot constaté que de momento no hay manera alguna de modificar la consulta que construye la ventana principal, por lo tanto no se puede cambiar las fotos que se mostrarán y la única alternativa para continuar con este desarrollo sería la de crear una nueva ventana del tipo MainWindow, y aunque no me gusta esta solución es la única plausible si quiero evitar tocar el núcleo del código forzando así a tener que recompilar la aplicación a cualquiera que quiera usar esta extensión. Hoy tampoco le puedo dedicar tiempo a esto pero en breve realizaré los últimos test y acabaré con este post

3 comentarios en “Desarrollando una extensión para F-spot

  1. abdul Autor

    Hola pipiten, perdona que no te haya contestado antes, pero estoy bastante liado ultimamente, de hecho tengo pendiente acabar con este post… ¿Sigues con este problema? La verdad es que a mi no me ha pasado nunca algo parecido, por lo que veo en el mensaje de error estás definiendo un submenú, no sé si el sistema de plugins acepta esto, yo me he encontrado con limitaciones que han parado mi desarrollo ya que, de momento no es posible modificar la query de la ventana principal. Siento no poder darte una respuesta concreta pero si quieres puedes enviarme la extensión y la pruebo en mi f-spot a ver si me sucede lo mismo

    Saludos y grácias por tus comentarios

    Responder
  2. pipiten

    Hola,

    Hubiera preferido escribirte un correo, pero no lo encontre en tu info de contacto.
    Primero que todo gracias por este «paso a paso».., no hay casi documentacion para desarrollar addins en F-spot.
    Justamente yo estoy haciendo un par de addins tb.., pero tengo un problema que no encontre info en ningun lugar.., capaz te ah pasado, y seria bueno comentarlo.
    Compilo el addin.., lo instalo copiando el dll ( con el xml como recurso embebido ), y la primera vez que corro el f-spot funciona bien, detecta el addin, aparece en el Menu de Herramientas el label elegido, etc etc.., corre perfecto.
    Pero a partir de la 2da vez que corra f-spot, ya no figura el label en el menu de Herramientas, pero aun sigue en el gestor de Extensiones, y esta activado (tengo que volver a copiar el dll para que lo tome nuevamente).
    Ahora.., la primera vez en correrlo, aparece este warning:

    «WARNING: The add-in ‘FSpot.cwPlugin,0.1’ is trying to extend ‘/FSpot/Menus/Tools’, but there isn’t any compatible add-in defining this extension point
    WARNING: The add-in ‘FSpot.cwPlugin,0.1’ is trying to extend ‘/FSpot/Menus/Tools/cwPlugin’, but there isn’t any compatible add-in defining this extension point»

    Te ah pasado algo parecido?. Te comento igual esto me ah pasado con varias versiones.., ahora estoy probando con la 0.5.0.3. El xml esta bien formateado.., tal cual los ejemplos, o incluso al tuyo :S.

    Bueno.., mil gracias desde ya.., disculpa las molestias.

    Un saludo.

    Responder

Responder a pipiten Cancelar la respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.