Contenidos
Para poder seguir este ejercicio es interesante:
Que se conozca gvSIG a nivel de usuario.
Que se tengan conocimientos de programación orientada a objetos, mejor si se conoce el lenguaje de programación python.
Que se haya seguido la documentación básica sobre scripting que podemos encontrar en la web de gvSIG y se este mínimamente familiarizado con el editor de scripts.
http://downloads.gvsig.org/download/web/es/build/html/scripting_devel_guide/2.3/index.html
Muy importante haberle echado un vistazo a la sección de “Introducción”
http://downloads.gvsig.org/download/web/es/build/html/scripting_devel_guide/2.3/introduccion.html
A la hora de plantearme un ejercicio de desarrollo como este, siempre me surge la duda de si debo centrarme en el acceso y manipulación de datos, o hacer algo que interactúe más con gvSIG. El acceso y manipulación de datos es bastante sencillo y hay muchos ejemplos en la documentación de gvSIG. Así que en esta ocasión me he decidido por centrarme en algo que interactúe con gvSIG. El problema es que gvSIG es más grande de lo que parece a primera vista y decidir qué hacer no es nada simple. El “qué hacer” tendría que cubrir los siguientes puntos:
Con esto en mente, y después de darle algunas vueltas decidí preparar una pequeña funcionalidad que consiste en:
“Desarrollar una herramienta de información rápida que muestre en un ‘tip’ sobre el mapa la información que configuremos para cada capa, y que ese ‘tip’ se vaya actualizando tal como vayamos moviendo el cursor del ratón sobre el mapa”.
Para esto, ¿ qué es lo que veremos ?
No vamos a hacerlo todo de golpe. Iremos haciendo pequeños pedacitos de código que podamos ir probando para al final cuando los juntemos tengamos nuestra funcionalidad completada.
Vamos a dividir el ejercicio en cinco partes:
Antes de empezar es conveniente que nos acostumbremos a que cuando vayamos a realizar desarrollos con scripting que se integren con gvSIG arranquemos gvSIG desde la consola. Muchos mensajes que pongamos y esperemos que salgan en la consola de scripting no saldrán en esta, y sí en la del sistema.
Vamos a crear un script que nos presente un tip en la vista. Asumiremos que hay una capa en concreto cargada en la vista, la capa “manzanas_pob” que viene con los scripts de ejemplo de gvSIG. No añadiremos ningún botón ni entrada de menú, y haremos que el ‘tip’ muestre la cantidad de población que hay en cada manzana sobre la que dejemos el puntero del ratón.
Lo primero que haremos será cargar la capa en la vista. Para ello, en el editor de scripts iremos a la pestaña “projects” y “Sistema”, y allí a “org.gvsig.scripting.mainplugin/data” y seleccionaremos “manzanas_pob.shp”. Se nos activará la opción de menú “Herramientas->Load layer”. Le daremos para cargar la capa en la vista corriente.
Sacaremos la tabla de atributos de la capa y observaremos que tiene un campo “pop_total”. Será el valor de este campo el que mostraremos en nuestro ‘tip’.
Antes de empezar a ver código veamos un poco las partes de gvSIG que vamos a necesitar.
Solo algunas ideas generales de forma muy rápida, solo para ver donde encaja lo que vamos a desarrollar:
En gvSIG tenemos “el proyecto”. Este tiene, por un lado la clase Project que representa y mantiene los datos del proyecto, y por otro ProjectWindow, que representa la ventana que tiene asociado el proyecto.
Un proyecto tiene documentos. Que pueden ser de varios tipos. En el caso que nos interesa, de tipo ViewDocument, que representa a la vista. Y que tiene asociado una clase IView que representa a la ventana de la vista. Esta ventana, IView, tiene un ToC, un localizador y un mapa. Estos están representados por la clase MapControl, para el localizador y el mapa, y una clase TOC para el ToC. Ahora mismo lo que nos interesa es el mapa, el MapControl.
Lo que nos interesa es cómo se añaden herramientas al MapControl. Una “herramienta” está formada por dos componentes:
Normalmente cuando vayamos a desarrollar una herramienta, implementaremos un listener y lo asociaremos al behavior que nos interese.
En nuestro caso, usaremos el behavior MouseMovementBehavior, que lleva asociado un listener de tipo PointListener. Así, tendremos que implementar un PointListener que, con la información del punto que recibe, se encargue de calcular el valor del tip que queremos presentar y asociarlo al MapControl.
Para implementar esto crearemos dos clases:
La clase QuickInfo. Será una clase que almacena la información sobre la capa con la que trabaja, así como que información de la capa queremos presentar, es decir, que atributo vamos a presentar en el tip.
Además de la información con la que va a trabajar nuestra herramienta también incluirá algunas operaciones más como:
La clase QuickInfoListener, que responda a los eventos del usuario y fija el tip que el MapControl deberá mostrar.
Vamos a ver cómo quedan estas clases, primero la clase QuickInfo:
class QuickInfo(object):
def __init__(self):
self.__behavior = None
self.__layer = None
def getTooltipValue(self, point, projection):
try:
fieldName = "pob_total"
store = self.__layer.getFeatureStore()
query = store.createFeatureQuery()
# Haremos un filtro espacial para localizar los
# registros que intersecten con el punto sobre el que
# esta el ratón.
query.setFilter(SpatialEvaluatorsFactory.getInstance().intersects(point,
projection,store))
# Con que nos devuelva la primera linea es suficiente.
query.setLimit(1)
query.retrievesAllAttributes();
l = store.getFeatures(query,100)
if len(l) < 1:
return ""
return str(l[0].get(fieldName))
except Exception, ex:
print str(ex)
def setTool(self, mapControl):
#
# Nos quedamos con nuestra capa de manzanas_pob
self.__layer = mapControl.getMapContext().getLayers().getLayer("manzanas_pob")
if not mapControl.hasTool("quickinfo"):
#
# Creamos nuestro "tool" asociando el MouseMovementBehavior con nuestro
# QuickInfoListener.
self.__behavior = MouseMovementBehavior(QuickInfoListener(mapControl, self))
self.__behavior.setMapControl(mapControl)
#
# Le añadimos al MapControl la nueva "tool".
mapControl.addBehavior("quickinfo", self.__behavior)
#
# Activamos la tool.
mapControl.setTool("quickinfo")
De la clase esta solo resaltar un par de cosas:
el método getTooltipValue. Revive el punto del sobre el que está el puntero del ratón., y el sistema de coordenadas en que está ese punto; normalmente será el sistema de coordenadas de la vista. Lo necesitamos por qué puede tener que reproyectarse el punto para compararlo con la información de la capa.
¿Qué hace este método?
Se limita a crear una consulta, un FeatureQuery, filtrando por una condición espacial, recoger los resultados de esta consulta, y recuperar el valor del atributo que nos interesa.
Para hacer la consulta de espacial, utiliza la clase SpatialEvaluatorsFactory, que nos permite automatizar la creación del query.
El método setTool. Que crea una tool nueva, la asigna al MapControl que recibimos, y la activa.
Veamos como sería la clase QuickInfoListener:
class QuickInfoListener(AbstractPointListener):
def __init__(self, mapControl, quickinfo):
AbstractPointListener.__init__(self)
self.mapControl = mapControl
self.quickinfo = quickinfo
self.projection = self.mapControl.getProjection()
def point(self, event):
p = event.getMapPoint()
tip = self.quickinfo.getTooltipValue(p,self.projection)
self.mapControl.setToolTipText(unicode(tip, 'utf-8'))
Como veis es sorprendentemente “corta”. Cuando la creamos le pasamos una instancia de QuickInfo, y en su método point se limita a recoger el punto sobre el que está el puntero del ratón, pasárselo al método getTooltipValue y con el valor obtenido lo asigna como el tooltip del MapControl (no olvidemos que un MapControl extiende de un JComponent de java que tiene métodos para gestionar el manejo de tooltips).
Hasta aquí bastante sencillo. Ya solo nos quedaría por ver cómo lo probamos. Podría ser algo como:
viewDoc = gvsig.currentView()
viewPanel = viewDoc.getWindowOfView()
mapControl = viewPanel.getMapControl()
quickInfo = QuickInfo()
quickInfo.setTool(mapControl)
La idea sería juntar todo esto en un script, pondríamos nuestras dos clase, y el código anterior en la función “main”. Tendríamos un script quickinfo con algo como:
# encoding: utf-8
import gvsig
from org.gvsig.fmap.mapcontrol.tools.Behavior import MouseMovementBehavior
from org.gvsig.fmap.mapcontrol.tools.Listeners import AbstractPointListener
from org.gvsig.fmap.mapcontext.layers.vectorial import SpatialEvaluatorsFactory
class QuickInfo(object):
def __init__(self):
self.__behavior = None
self.__layer = None
def getTooltipValue(self, point, projection):
try:
fieldName = "pob_total"
store = self.__layer.getFeatureStore()
query = store.createFeatureQuery()
# Haremos un filtro espacial para localizar los
# registros que intersecten con el punto sobre el que
# esta el ratón.
query.setFilter(SpatialEvaluatorsFactory.getInstance().intersects(point,
projection,store))
# Con que nos devuelva la primera linea es suficiente.
query.setLimit(1)
query.retrievesAllAttributes();
l = store.getFeatures(query,100)
if len(l) < 1:
return ""
return str(l[0].get(fieldName))
except Exception, ex:
print str(ex)
def setTool(self, mapControl):
#
# Nos quedamos con nuestra capa de manzanas_pob
self.__layer = mapControl.getMapContext().getLayers().getLayer("manzanas_pob")
if not mapControl.hasTool("quickinfo"):
#
# Creamos nuestro "tool" asociando el MouseMovementBehavior con nuestro
# QuickInfoListener.
self.__behavior = MouseMovementBehavior(QuickInfoListener(mapControl, self))
self.__behavior.setMapControl(mapControl)
#
# Le añadimos al MapControl la nueva "tool".
mapControl.addBehavior("quickinfo", self.__behavior)
#
# Activamos la tool.
mapControl.setTool("quickinfo")
class QuickInfoListener(AbstractPointListener):
def __init__(self, mapControl, quickinfo):
AbstractPointListener.__init__(self)
self.mapControl = mapControl
self.quickinfo = quickinfo
self.projection = self.mapControl.getProjection()
def point(self, event):
p = event.getMapPoint()
tip = self.quickinfo.getTooltipValue(p,self.projection)
self.mapControl.setToolTipText(unicode(tip, 'utf-8'))
def main(*args):
viewDoc = gvsig.currentView()
viewPanel = viewDoc.getWindowOfView()
mapControl = viewPanel.getMapControl()
quickInfo = QuickInfo()
quickInfo.setTool(mapControl)
Para probarlo cargaremos la capa de “manzanas_pob” en la vista, lanzaremos nuestro script, y pasaremos el ratón por la vista a ver que sucede.
Añadir botones o entradas de menú en gvSIG involucra dos entidades.
Por un lado necesitaremos disponer de una “Extension”. Esta “extension” se encarga principalmente de proporcionar el código asociado a nuestra herramienta, así como mecanismos para determinar si nuestra herramienta debe estar visible y activa.
Por otro lado deberemos registrar una “ActionInfo”. Esta se encarga de aglutinar en un solo punto la información que pueda ser necesaria para asociar una acción a un botón, o entrada de menú. Tiene un rotulo, un acelerador, un icono, un tooltip, una descripción, y una “extension”. De forma que con todo esto podemos, simplemente, asignarla a un botón y este ya tiene toda la información necesaria para presentar el botón y ejecutar la acción cuando se haga clic sobre el. Además la acción controla si el usuario actual tiene o no permisos para ejecutarla.
gvSIG mantiene un registro de todas las acciones disponibles. Lo primero será registrar nuestra nueva acción, y luego ya la asignaremos a los menús o botones.
Para crear nuestra acción, lo primero será implementar una “Extension”. En nuestro caso implementaremos una clase QuickinfoExtension que podría ser algo como:
class QuickinfoExtension(ScriptingExtension):
def __init__(self):
pass
def isVisible(self):
return True
def isEnabled(self):
layer = currentLayer()
if not self.isLayerValid(layer):
return False
return True
def execute(self,actionCommand, *args):
actionCommand = actionCommand.lower()
if actionCommand == "settool-quickinfo":
layer = currentLayer()
if not self.isLayerValid(layer):
return
viewPanel = currentView().getWindowOfView()
mapControl = viewPanel.getMapControl()
quickInfo = QuickInfo()
quickInfo.setTool(mapControl)
def isLayerValid(self, layer):
if layer == None:
return False
if layer.getName() != "manzanas_pob":
return False
return True
Cosas a comentar. Básicamente debemos implementar tres métodos:
Tanto el método isEnabled como execute, lo primero que hacen es comprobar que nuestra capa, “manzanas_pob” esté activa en la vista corriente. Si no lo está, no se habilita nuestra herramienta ni se ejecuta nada.
Nuestra herramienta, QuickInfo, requería para poder activarla, un MapControl, así que, una vez hechas las verificaciones oportunas, obtendremos el MapControl asociado al mapa de la vista, crearemos nuestra herramienta y le diremos que se añada al MapControl:
viewPanel = currentView().getWindowOfView()
mapControl = viewPanel.getMapControl()
quickInfo = QuickInfo()
quickInfo.setTool(mapControl)
Bien, pues esto sería la parte de crear nuestra “extension”, ahora tendremos que crear nuestro “acción”.
Para crear nuestra acción, acudiremos al ActionInfoManager, que es el encargado de crear acciones y mantener el registro de ellas. Obtendremos el manager con:
actionManager = PluginsLocator.getActionInfoManager()
La acción lleva asociado un icono, normalmente para presentarlo en el botón o junto a la entrada de menú. Antes de crear la acción deberemos cargar y registrar nuestro icono en el tema de iconos de gvSIG. Lo haremos con:
iconTheme = ToolsSwingLocator.getIconThemeManager().getCurrent()
quickinfo_icon = File(join(dirname(__file__),"images","quickinfo.png")).toURI().toURL()
iconTheme.registerDefault("scripting.quickinfo", "action", "tools-quickinfo",
None, quickinfo_icon)
Luego ya podremos crear y registrar nuestra acción:
quickinfo_extension = QuickinfoExtension()
quickinfo_action = actionManager.createAction(
quickinfo_extension,
"tools-quickinfo", # Action name
"Show quick info", # Text
"settool-quickinfo", # Action command
"tools-quickinfo", # Icon name
None, # Accelerator
1009000000, # Position
"Show quick info" # Tooltip
)
quickinfo_action = actionManager.registerAction(quickinfo_action)
Y una vez ya tenemos registrada nuestra acción podemos ver de añadirla a las barras de botones o entradas de menús con:
# Añadimos la entrada "Quickinfo" en el menú herramientas
application.addMenu(quickinfo_action, "tools/Quickinfo")
# Añadimos el la acción como un botón en la barra de herramientas "Quickinfo".
application.addSelectableTool(quickinfo_action, "Quickinfo")
Todo este proceso de crear nuestra acción y registrarla en menús o botones, lo meteremos en un método “selfRegister”, (el nombre no es importante) para poder invocarlo desde donde lo necesitemos.
Si metemos todo en un modulo “actions”, este podría quedar algo como:
# encoding: utf-8
import gvsig
import os.path
from os.path import join, dirname
from gvsig import currentView
from gvsig import currentLayer
from java.io import File
from org.gvsig.app import ApplicationLocator
from org.gvsig.andami import PluginsLocator
from org.gvsig.scripting.app.extension import ScriptingExtension
from org.gvsig.tools.swing.api import ToolsSwingLocator
from quickinfo import QuickInfo
class QuickinfoExtension(ScriptingExtension):
def __init__(self):
pass
def isVisible(self):
return True
def isLayerValid(self, layer):
if layer == None:
print "### QuickinfoExtension.isLayerValid: None, return False"
return False
if layer.getName() != "manzanas_pob":
return False
return True
def isEnabled(self):
layer = currentLayer()
if not self.isLayerValid(layer):
return False
return True
def execute(self,actionCommand, *args):
actionCommand = actionCommand.lower()
if actionCommand == "settool-quickinfo":
print "### QuickinfoExtension.execute(%s)" % repr(actionCommand)
layer = currentLayer()
if not self.isLayerValid(layer):
return
viewPanel = currentView().getWindowOfView()
mapControl = viewPanel.getMapControl()
quickInfo = QuickInfo()
quickInfo.setTool(mapControl)
def selfRegister():
application = ApplicationLocator.getManager()
actionManager = PluginsLocator.getActionInfoManager()
iconTheme = ToolsSwingLocator.getIconThemeManager().getCurrent()
quickinfo_icon = File(join(dirname(__file__),"images","quickinfo.png")).toURI().toURL()
iconTheme.registerDefault("scripting.quickinfo", "action", "tools-quickinfo",
None, quickinfo_icon)
quickinfo_extension = QuickinfoExtension()
quickinfo_action = actionManager.createAction(
quickinfo_extension,
"tools-quickinfo", # Action name
"Show quick info", # Text
"settool-quickinfo", # Action command
"tools-quickinfo", # Icon name
None, # Accelerator
1009000000, # Position
"Show quick info" # Tooltip
)
quickinfo_action = actionManager.registerAction(quickinfo_action)
# Añadimos la entrada "Quickinfo" en el menú herramientas
application.addMenu(quickinfo_action, "tools/Quickinfo")
# Añadimos el la acción como un botón en la barra de herramientas "Quickinfo".
application.addSelectableTool(quickinfo_action, "Quickinfo")
def main(*args):
selfRegister()
He añadido un “main” para que podamos probar que se añade nuestra herramienta como toca.
Recordemos cual sería la funcionalidad de nuestro panel.
Se trata de un panel que nos permita seleccionar el campo que queremos mostrar en el ‘tip’ de nuestra herramienta. Más tarde usaremos este panel para añadirlo a las propiedades de la capa. El panel va a ser muy sencillo, tan solo con una etiqueta y un desplegable con los campos de la capa.
No voy a contar mucho sobre la creación de interfaces gráficas con scripting, de esto ya hay bastante en la documentación de la web:
Así que me voy a centrar solo en uno de tres detalles.
Uno, botones de aceptar y cancelar. Es muy corriente que en los ejemplos que veamos sobre creación de interfaces gráficos encontremos que se suele añadir botones como “Aceptar”, “Cerrar”, “Aplicar” o “Cancelar” a nuestros paneles. A priori parece lógico, de hecho lo que suele llamar la atención es cuando te dicen que mejor si no los incluyes. Pues bien, a pesar de ser bastante didáctico incluir ese tipo de botones en nuestros paneles, lo mejor es no hacerlo. En nuestro caso si los incluimos obtendríamos un efecto nada deseado.
En gvSIG hay herramientas para presentar de forma uniforme un panel, añadiéndole estos botones, un rotulo de cabecera y hasta algún icono si lo consideramos oportuno. Ahora veremos cómo hacerlo.
Dos, es recomendable que nuestro panel exponga métodos para poder acceder a los elementos que este recoge sin que quien los usa se vincule a si estos valores están en una caja de texto, un desplegable o una tabla. Este panel debe proporcionar al usuario una forma de seleccionar qué campo de la tabla queremos usar para mostrar en el tip, así que le añadiremos un método “getFieldName” para poder obtener el valor que selecciono el usuario.
Tres, ¿ cómo y dónde podemos guardar los datos introducidos por el usuario ? Pues a priori puede parecer mas complicado de lo que es. La capa implementa el interface ExtendedPropertiesSupport que nos provee de dos métodos:
Conviene que los valores sean valores numéricos o de cadena, y no objetos complejos. No es que no puedan almacenarse objetos complejos, pero para los valores simples gvSIG se encarga de persistirlos automáticamente cuando se guarda el proyecto, mientras que para objetos complejos deberíamos hacer algo más de faena para que se persistieran.
Para crear un “panel”, deberemos hacer básicamente dos cosas:
Crearemos un modulo “quickinfopanel”, y en él meteremos la clase:
class QuickinfoPanel(FormPanel):
pass
Y luego con el Abeille crearemos en interface de usuario, que podría ser algo como:
Tan solo una etiqueta y un JComboBox. Lo más importante es el nombre que le demos al JComboBox, en nuestro caso cboFields.
La clase QuickinfoPanel podría quedar algo como:
class QuickinfoPanel(FormPanel):
def __init__(self, layer=None):
FormPanel.__init__(self,getResource(__file__,"quickinfopanel.xml"))
self.setLayer(layer)
def setLayer(self, layer):
self.__layer = layer
if layer==None:
self.cboFields.removeAllItems()
else:
self.fillCombo(
self.cboFields,
self.__layer.getFeatureStore().getDefaultFeatureType()
)
def getLayer(self):
return self.__layer
def getFieldName(self):
name = self.cboFields.getSelectedItem()
if name == None:
return None
name = name.strip()
if name == "":
return None
return name
def fillCombo(self, combo, featureType):
combo.removeAllItems()
combo.addItem(" ")
for attr in featureType:
combo.addItem(attr.getName())
x = self.__layer.getProperty("quickinfo.fieldname")
if x in ("", None):
combo.setSelectedIndex(0)
else:
combo.setSelectedItem(x)
def save(self):
self.__layer.setProperty(
"quickinfo.fieldname",
self.getFieldName()
)
Tan solo cinco métodos. Un set/getLayer para asignarle la layer con la que debe trabajar, el getFieldName que ya hemos comentado antes, un fillCombo que se encargaría de rellenar el desplegable con los valores de los campos que tiene la capa con la que estemos trabajando. Y un save que se encargaría de guardar el campo seleccionado por el usuario como un valor asociado a la capa.
El panel, con esto, ya tiene todo lo que nos puede interesar, veamos ahora cómo podríamos hacer para mostrarlo:
def main(*args):
viewDoc = gvsig.currentView()
layer = viewDoc.getLayer("manzanas_pob")
panel = QuickinfoPanel(layer)
winmgr = ToolsSwingLocator.getWindowManager();
dialog = winmgr.createDialog(
panel.asJComponent(),
"Quickinfo test",
"Quickinfo information",
winmgr.BUTTONS_OK_CANCEL
)
dialog.show(winmgr.MODE.DIALOG)
if dialog.getAction()==winmgr.BUTTON_OK:
print "Ok"
print "Show field: ", repr(panel.getFieldName())
panel.save()
else:
print "Cancel"
Todo junto nuestro “quickinfopanel” quedaría algo como:
# encoding: utf-8
import gvsig
from gvsig import getResource
from gvsig.libs.formpanel import FormPanel
from org.gvsig.tools.swing.api import ToolsSwingLocator
class QuickinfoPanel(FormPanel):
def __init__(self, layer=None):
FormPanel.__init__(self,getResource(__file__,"quickinfopanel.xml"))
self.setLayer(layer)
def setLayer(self, layer):
self.__layer = layer
if layer==None:
self.cboFields.removeAllItems()
else:
self.fillCombo(
self.cboFields,
self.__layer.getFeatureStore().getDefaultFeatureType()
)
def getLayer(self):
return self.__layer
def getFieldName(self):
name = self.cboFields.getSelectedItem()
if name == None:
return None
name = name.strip()
if name == "":
return None
return name
def fillCombo(self, combo, featureType):
combo.removeAllItems()
combo.addItem(" ")
for attr in featureType:
combo.addItem(attr.getName())
x = self.__layer.getProperty("quickinfo.fieldname")
if x in ("", None):
combo.setSelectedIndex(0)
else:
combo.setSelectedItem(x)
def save(self):
self.__layer.setProperty(
"quickinfo.fieldname",
self.getFieldName()
)
def main(*args):
viewDoc = gvsig.currentView()
layer = viewDoc.getLayer("manzanas_pob")
panel = QuickinfoPanel(layer)
winmgr = ToolsSwingLocator.getWindowManager();
dialog = winmgr.createDialog(
panel.asJComponent(),
"Quickinfo test",
"Quickinfo information",
winmgr.BUTTONS_OK_CANCEL
)
dialog.show(winmgr.MODE.DIALOG)
if dialog.getAction()==winmgr.BUTTON_OK:
print "Ok"
print "Show field: ", repr(panel.getFieldName())
panel.save()
else:
print "Cancel"
Vamos a ver ahora cómo podemos hacer para añadir nuestro panel a las pestañas de propiedades de una capa. Para dar soporte a esto, en gvSIG tenemos tres clases:
Con esta idea en mente, deberemos crear dos clases, QuickinfoPropertyPageFactory, y PropertiesPage, y registrar nuestra factoría en el PropertiesPageManager. Aunque parezca complicado, vamos a ver como es relativamente simple. La factoría será algo como:
class QuickinfoPropertyPageFactory(PropertiesPageFactory):
def __init__(self):
pass
def getName(self):
return "Quickinfo"
def getGroupID(self):
return ViewDocument.LAYER_PROPERTIES_PAGE_GROUP
def isVisible(self, layer):
if isinstance(layer,VectorLayer):
return True
return False
def create(self, layer):
if not isinstance(layer,VectorLayer):
return None
return QuickinfoPropertyPage(layer)
Solo tiene cuatro métodos, pero todos ellos son importantes:
Visto en que consistiría la factoría, veamos que sería nuestra página de propiedades:
class QuickinfoPropertyPage(PropertiesPage):
def __init__(self, layer=None):
self.__panel = QuickinfoPanel(layer)
def getTitle(self):
return "Quickinfo"
def asJComponent(self):
return self.__panel.asJComponent()
def getPriority(self):
return 1
def whenAccept(self):
self.__panel.save()
return True
def whenApply(self):
return self.whenAccept()
def whenCancel(self):
return True
Vemos que también tiene poquitos métodos y de una implementación bastante simple. Vamos a repasarlos uno a uno:
Vistas ya las dos clases, solo queda ver como las registraríamos para que gvSIG las use. Muy simple, nos limitaríamos a obtener el PropertiesPageManager y registrar en el nuestra factoría:
def selfRegister():
propertiesPageManager = MapControlLocator.getPropertiesPageManager()
propertiesPageManager.registerFactory(QuickinfoPropertyPageFactory())
def main(*args):
selfRegister()
Igual que ya hicimos antes, meteremos el registro en un método selfRegister para facilitarnos usarlo mas tarde.
Con esto crearemos un modulo “quickinfopropertypage” en el que lo meteremos todo, quedando algo como:
# encoding: utf-8
import gvsig
from org.gvsig.propertypage import PropertiesPage
from org.gvsig.fmap.mapcontext.layers.vectorial import VectorLayer
from org.gvsig.app.project.documents.view import ViewDocument
from org.gvsig.propertypage import PropertiesPageFactory
from org.gvsig.fmap.mapcontrol import MapControlLocator
from quickinfopanel import QuickinfoPanel
class QuickinfoPropertyPage(PropertiesPage):
def __init__(self, layer=None):
self.__panel = QuickinfoPanel(layer)
def getTitle(self):
return "Quickinfo"
def asJComponent(self):
return self.__panel.asJComponent()
def getPriority(self):
return 1
def whenAccept(self):
self.__panel.save()
return True
def whenApply(self):
return self.whenAccept()
def whenCancel(self):
return True
class QuickinfoPropertyPageFactory(PropertiesPageFactory):
def __init__(self):
pass
def getName(self):
return "Quickinfo"
def getGroupID(self):
return ViewDocument.LAYER_PROPERTIES_PAGE_GROUP
def isVisible(self, layer):
if isinstance(layer,VectorLayer):
return True
return False
def create(self, layer):
if not isinstance(layer,VectorLayer):
return None
return QuickinfoPropertyPage(layer)
def selfRegister():
propertiesPageManager = MapControlLocator.getPropertiesPageManager()
propertiesPageManager.registerFactory(QuickinfoPropertyPageFactory())
def main(*args):
selfRegister()
Para terminar de armarlo todo, vamos a volver sobre algunos de los módulos que hemos ido haciendo para tocar algunas cositas en ellos.
En el modulo “quickinfo” vamos a cambiar un par de cosas.
Por un lado en el método getTooltipValue de la clase QuickInfo. Al principio comprobaremos si la capa tiene asignada la propiedad “quickinfo,fieldname”. Si la tiene recuperaremos de ella el campo que vamos a mostrar, y si no, no saldremos devolviendo un None.
def getTooltipValue(self, point, projection):
try:
fieldName = self.__layer.getProperty("quickinfo.fieldname")
if fieldName in ("", None):
print '### QuickInfo.getTooltipValue: %s return ""' % repr(fieldName)
return ""
store = self.__layer.getFeatureStore()
...
if len(l) < 1:
return ""
return str(l[0].get(fieldName))
Por otro lado, el método setTool que usamos para activar la tool en la vista, en lugar de trabajar con una capa fija, trabajará con la capa activa, pero solo si tiene asignada la propiedad “quickinfo.fieldname”.
def setTool(self, mapControl):
actives = mapControl.getMapContext().getLayers().getActives()
if len(actives)!=1:
# Solo activamos la herramienta si hay una sola capa activa
return
fieldName = actives[0].getProperty("quickinfo.fieldname")
if fieldName in ("", None):
# Si la capa activa no tiene configurado el campo a mostrar
# tampoco activamos la herramienta
return
self.__layer = actives[0]
if not mapControl.hasTool("quickinfo"):
...
En el módulo “actions” vamos a cambiar un par de cosas, similares a las que hemos cambiado en el “quickinfo”. Modificaremos el método de utilidad que añadimos isLayerValid, para que en lugar de comprobar si la capa es una en concreto verifique que la capa tiene en sus propiedades el valor “quickinfo.fieldname”:
...
def isLayerValid(self, layer):
if layer == None:
return False
fieldName = layer.getProperty("quickinfo.fieldname")
if fieldName in ("", None):
# Si la capa no tiene configurado el campo a mostrar
# no activamos la herramienta
return False
return True
...
Con estos cambios ya casi lo tendríamos. Nos faltará solo una cosa...
¿ Quién llamará y cuándo a los métodos *selfRegistry* para que nuestra herramienta se integre en gvSIG ?
Pues en gvSIG podemos crear scripts que se ejecuten en el arranque de la aplicación de forma automática. Crearemos uno de estos scripts y en el llamaremos a las dos funciones selfRegister que hemos implementado.
Para que un script se ejecute en el arranque de gvSIG, bastará con que se llame de una forma especial, “autorun”. Así que simplemente crearemos nuestro script autorun con el siguiente código:
# encoding: utf-8
import gvsig
import actions
import quickinfopropertypage
def main(*args):
actions.selfRegister()
quickinfopropertypage.selfRegister()
Es interesante tener en cuenta, que podemos deshabilitar un script para que no se ejecute, esto es útil con los scripts autorun ya que nos permite hacer pruebas sin que se nos carguen todas ellas en el arranque de gvSIG.
Un comentario más... tener en cuenta que parte de las cosas que estamos implementando quedan registradas y cargadas en memoria en gvSIG al ejecutarse. Muchas veces no basta con corregir y volver a lanzar nuestro script, por que lo que cargamos en la ejecución anterior aun está cargado. En estos casos será necesario cerrar gvSIG y volver a arrancar para cerciorarnos que nuestras modificaciones son correctas. En general, siempre es una buena practica cerrar gvSIG y volverlo a arrancar cuando tengamos el trabajo concluido o casi concluido para cerciorarnos que funciona correctamente; no vaya a ser que algo de lo que ejecutamos anteriormente esté haciendo funcionar las cosas y al re-arrancar gvSIG no vayan.