Introducción | Herramientas | Tutorial: Añadir un nuevo conector | Configuracion Archivo XML
Ciertas aplicaciones pueden requerir capacidades más allá del visor estándar de OpenEV o pueden requerir ocultar algo de la funcionalidad existente donde no se necesita. Mientras que la herencia estándar del Python es a veces la mejor ruta, OpenEV proporciona algunos mecanismos para modificar su aspecto y funcionalidad, sin reescribir la principal aplicación o el código de las ventanas.
Los archivos de configuración de XML pueden ser usados para seleccionar cual de las entradas de los iconos del menú será mostrada en OpenEV, cuales son lanzadas por la aplicación principal y la cubierta del Python en OpenEV; y sí puede o no, ser mostrada en la barra del área de historia y progreso de la cubierta del python de OpenEV. Los nombres de las entradas del menú y de los archivos usados del icono, pueden también alterarse a través de los archivos de la configuración. Los archivos de la configuración de XML no se pueden utilizar para crear nuevos servicios repetidos, ellos pueden dictar solamente la manera en que esos servicios repetidos son presentados y referenciados.
La funcionalidad de OpenEV puede ser extendida con la clase de Tool_GViewApp. En el caso más general, esta clase contiene solamente una referencia al caso actual de OpenEV (GViewApp) y un sistema de entrada para agregar al menú y al icono de barras de las ventanas de la vista y de la cubierta del python. Cada menú o entrada del pymenu se define por una secuencia automática que describe la localización del menú (Ej. “Herramienta del análisis de Tools/ROI”), una posición del número entero relacionado con los artículos existentes dentro del mismo menú (ej. 2 pondrían “la herramienta del análisis del ROI” después de la segunda entrada, dentro del menú de herramientas) y un servicio repetido.
La referencia al uso principal permite la interacción de la herramienta con vistas actuales, capas, y edición de la barra de herramientas. Cada entrada del icono es definida por un icono de archivo, un texto aludido, una posición automática, un servicio repetido, temas de ayuda (no es usado actualmente), y texto. Todos, excepto los servicios repetidos pueden modificarse usando XML, aunque el mecanismo es un poco diferente a los ítems de la ventana estándar porque el servicio repetido debe ser localizado a través de la información en el archivo de XML, siempre y cuando el código de servicio repetido de la herramienta no haga parte de la clase de gviewwindow.
OpenEV le permite agregar enlaces a sus propias funciones a través de la aplicación principal con la herramienta de clase de GViewApp almacenada en gviewapp.py. La clase base es muy simple, ésta guarda referencia a la aplicación principal (también se puede encontrar la aplicación principal importando el gview, donde se almacena como gview.app, pero la referencia se almacena en cada herramienta, según la conveniencia). Los objetos almacenados en la vista del menú pyshell y el icono de entradas son agregados por la herramienta.class Tool_GViewApp: # Abstract base class to derive tools from def __init__(self,app=None): self.app = app self.menu_entries = Tool_GViewAppMenuEntries() self.icon_entries = Tool_GViewAppIconEntries() self.pymenu_entries = Tool_GViewAppMenuEntries() self.pyicon_entries = Tool_GViewAppIconEntries()La entrada de información del menú es almacenada en un diccionario, con el camino de entrada como una llave, la posición y el retorno como una tupla (objetos que representan una fila de una tabla de datos) para el valor.class Tool_GViewAppMenuEntries: # Class to store entries to be added to openev's menu def __init__(self): self.entries = {} def set_entry(self,item,position=0,callback=None,accelerator=None): # item = a string describing menu location # position = default location in the menu (integer): Ignored if an # xml menu entry is specified for the tool. Note: # when used, the position refers to position in the # lowest level menu. Eg. if a menu entry is # 'File/menu1/entryN', position refer's to entryN's # position within menu1, not menu1's position in # File. For more flexibility, use the xml form of # configuration. # callback = callback # accelerator = shortcut key if (type(item) == type('')): if (type(position) == type(0)): self.entries[item] = (position,callback, accelerator) else: raise AttributeError,"position should be an integer" else: raise AttributeError,"Menu entry item must be a string"La información de un icono es almacenada en una lista, cada ítem en la lista contiene un archivo de icono, pista, posición, retorno e información en temas de ayuda. El archivo de icono debe ser especificado por completo a menos que esté localizado en las herramientas OpenEV o en el directorio de imágenes, en el caso de que el nombre de base esté solo, puede ser especificado.class Tool_GViewAppIconEntries: # Class to store entries to be added to openev's menu def __init__(self): self.entries = [] def set_entry(self,iconfile,hint_text,position=0,callback=None,help_topic=None,label=None,icontype='xpm'): # iconfile=icon filename (xpm case), or some other string not yet defined # (pixmap/widget case- not yet supported- may never be) # hint_text=tooltip text to use # position = default location in the icon bar (integer) # callback = callback # help topic = html help file (not yet used by anything) # label = some gtk think- not sure what this does # icontype = 'xpm' (later may allow 'pixmap' or 'widget', but not yet) if (type(iconfile) == type('')): import os if os.path.isfile(iconfile): fullfilename=iconfile elif os.path.isfile(os.path.join(gview.home_dir,'tools',iconfile)): fullfilename=os.path.join(gview.home_dir,'tools',iconfile) elif os.path.isfile(os.path.join(gview.home_dir,'pics',iconfile)): fullfilename=os.path.join(gview.home_dir,'pics',iconfile) else: txt = "Cannot find file "+iconfile+'. Either the full\n' txt = txt+"path must be specified, or "+iconfile+ " must be\n" txt = txt+"placed in the tools or pics directory." raise AttributeError,txt # On nt, path separators need to be trapped and doubled to avoid # being interpreted as an escape before special characters. if os.name == 'nt': import string fullfilename=string.replace(fullfilename,"\\","\\\\") if (type(position) == type(0)): self.entries.append((fullfilename,label,hint_text,position,callback,help_topic,icontype)) else: raise AttributeError,"position should be an integer" else: txt = "Cannot find file "+iconfile+'. Either the full\n' txt = txt+"path must be specified, or "+iconfile+ " must be\n" txt = txt+"placed in the tools or pics directory." raise AttributeError,txtUn Ejemplo: La Herramienta General ROI
En la práctica, el usuario define la clase adquirida desde la herramienta de aplicación GView (por lo menos tiene los mismos componentes básicos definidos), y agrega al menú de entradas y los retornos asociados estas entradas. La clase de Herramientas Generales ROI, en toolexample.py (Herramientas de ejemplo) muestra cómo funciona. Ésta es una herramienta de demostración que agrega 'Tools/ROI Analysis Tool' (Herramienta de análisis) una entrada al menú. Cuando se señala, ésta activa el editor de la barra de herramientas 'Draw ROI' method (Método de dibujo) y se muestra un diálogo (Véase abajo). Cuando el usuario selecciona una región, ROI actualiza el diálogo mostrando la selección. Si presiona “analizar” conseguirá imprimir estos valores en la ventana final.
En la función de instalación Herramientas Generales ROI:
- Seleccione gviewapp.Tool_GViewApp's_ inicio_ función. Esto crea la referencia a la aplicación principal y un sistema vacío de las entradas al menú.
- Cree una clase StoredROIPOI (acopio) para almacenar la región de interés anteriormente seleccionada (usado para evitar fallas de la herramienta si 'Draw ROI' method (método del dibujo) se ha desactivado en la edición de la barra de herramientas).
- Seleccione la función init_dialog para crear el diálogo que se muestra cuando “se selecciona la entrada del menú de la herramienta de análisis de Tools/ROI”. Se oculta este diálogo hasta que la entrada al menú es seleccionada.
- Seleccione la función del menú inicial para programar la entrada al menú. El llamado asociado muestra el diálogo creado en el paso del diálogo _ inicial.
- Seleccione init_connections para configurar las conexiones de la señal entre la herramienta y su diálogo (tal como la señal “analyze-pressed” es emitida por el diálogo) y entre la herramienta y su principal aplicación (tal como la señal “ROI-cambiante” señal emitida por la herramienta del ROI en edición de la barra de herramientas).
Un Ejemplo más avanzado: La Herramienta de Histograma
La herramienta histograma hereda el diálogo y las conexiones de herramientas generales ROI, pero en lugar de fijar únicamente los límites de la región seleccionada, extrae los valores contenidos en ésta y forma un histograma de ellos. El diálogo de la herramienta ha sido extendido para mostrar esto. Esta herramienta está disponible de SourceForge CVS (openev/tools/gvrastertools.py), pero todavía no se incluye en distribuciones pre-construidas del OpenEV, pues aún se encuentra en desarrollo y no está estable para todas las plataformas y tipos de datos.
Registro de una Herramienta.
Dos mecanismos son proporcionados para registrar herramientas por los usuarios:
Para escribir un conector OpenEV GUI comprensivo, usted debe estar familiarizado con GTK+ usando Python (PyGTK). Los módulos OpenEV deben estar ubicados en el subdirectorio de herramientas para ser cargados automáticamente.
- Auto-Registro OpenEV buscará automáticamente a través de todos los py-archivos en su directorio de herramientas y cargará cualquier herramienta enumerada en la lista global de herramientas, variando para cada módulo.
- El argumento de línea de comando OpenEV permite especificar un archivo de texto, que enumere los módulos para importar y cargar herramientas en la línea de comando. Por ejemplo, comenzando desde arriba con OpenEV con el comando “openev-t (trayectoria llena o relativa) /tool_example.txt”, donde tool_example.txt contiene:
MODULE_NAME = toolexample TOOL_NAME=GeneralROITool TOOL_NAME=GeneralPOIToolResultará en OpenEV cargando las herramientas generales ROI y las herramientas generales POI desde toolexample.py. Observe que la variable de PYTHONPATH debe incluir el directorio de toolexample.py's para que funcione.
Escribirá un módulo simple GUI para automatizar tareas de rápida transformación (Fast Fourier Transform task) Como primer intento hará un ítem en el menú para desarrollar un FFT.
# OpenEV module fft.py from gtk import * import gview import GtkExtra import gviewapp import gdalnumeric import FFT class FFTTool(gviewapp.Tool_GViewApp): def __init__(self,app=None): gviewapp.Tool_GViewApp.__init__(self,app) self.init_menu() def init_menu(self): self.menu_entries.set_entry("Image processing/FFT",2,self.calculate_fft) def calculate_fft(self,*args): layer = gview.app.sel_manager.get_active_layer() ds = layer.get_parent().get_dataset() data = gdalnumeric.DatasetReadAsArray(ds) data_tr = FFT.fft2d(data) array_name = gdalnumeric.GetArrayFilename(data_tr) gview.app.file_open_by_name(array_name) TOOL_LIST = ['FFTTool']Éste módulo agrega al menú un nuevo nivel con un sólo ítem "FFT", "El Procesador de Imagen". Guarde el texto en archivo / herramientas y reinicie OpenEV. Cargue una imagen y haga clic en el nuevo ítem del menú. La imagen transformada aparece en una nueva capa (usted puede necesitar hacer clic en "rellenar todas las capas" oprímalo para ver).Observe que con este acceso usted puede extender fácilmente la funcionalidad de OpenEV. Puede escribir guiones para las tareas desarrolladas con frecuencia (como calibración o NDVI, descripción en la sección de cobertura de python) y los ubica en el menú o en la barra de tareas.
El trabajo de escritura más simple funciona, pero podrá ir más allá y hará una caja de diálogo que le permitirá seleccionar qué tipo de transformación (adelante o inversa) deberá llevar a cabo y donde se deberán ubicar los resultados (en una nueva capa de la misma vista o creará una nueva vista y ubicará los resultados ahí)
# OpenEV module fft2.py from gtk import * import gview import GtkExtra import gviewapp import gdalnumeric import FFT class FFT2Tool(gviewapp.Tool_GViewApp): def __init__(self,app=None): gviewapp.Tool_GViewApp.__init__(self,app) self.init_menu() def launch_dialog(self,*args): self.win = FFTDialog() self.win.show() def init_menu(self): self.menu_entries.set_entry("Image processing/FFT2",2,self.launch_dialog) class FFTDialog(GtkWindow): def __init__(self,app=None): GtkWindow.__init__(self) self.set_title('Fast Fourier Transform') self.create_gui() self.show() def show(self): GtkWindow.show_all(self) def close(self, *args): self.destroy() def create_gui(self): self.box1 = GtkVBox(spacing = 10) self.box1.set_border_width(10) self.add(self.box1) self.box1.show() self.switch_forward = GtkRadioButton(None, "Forward") self.box1.pack_start(self.switch_forward) self.switch_forward.show() self.switch_inverse = GtkRadioButton(self.switch_forward, "Inverse") self.box1.pack_start(self.switch_inverse) self.switch_inverse.show() self.separator = GtkHSeparator() self.box1.pack_start(self.separator, expand=FALSE) self.switch_new_view = GtkCheckButton("Create new view") self.box1.pack_start(self.switch_new_view) self.switch_new_view.show() self.separator = GtkHSeparator() self.box1.pack_start(self.separator, expand=FALSE) self.box2 = GtkHBox(spacing=10) self.box1.pack_start(self.box2, expand=FALSE) self.box2.show() self.execute_btn = GtkButton("Ok") self.execute_btn.connect("clicked", self.execute_cb) self.box2.pack_start(self.execute_btn) self.execute_btn.set_flags(CAN_DEFAULT) self.execute_btn.grab_default() self.close_btn = GtkButton("Cancel") self.close_btn.connect("clicked", self.close) self.box2.pack_start(self.close_btn) def execute_cb( self, *args ): layer = gview.app.sel_manager.get_active_layer() ds = layer.get_parent().get_dataset() data = gdalnumeric.DatasetReadAsArray(ds) if self.switch_forward.get_active(): data_tr = FFT.fft2d(data) else: data_tr = FFT.inverse_fft2d(data) array_name = gdalnumeric.GetArrayFilename(data_tr) if self.switch_new_view.get_active(): gview.app.new_view() gview.app.file_open_by_name(array_name) else: gview.app.file_open_by_name(array_name) TOOL_LIST = ['FFT2Tool']Como puede ver, la parte más complicada del trabajo es crear controles de GUI.Los archivos de configuración XML están ubicados en el directorio del “xmlconfig” de OpenEV. Parte de la vista de archivos XML del menú se muestra abajo. Adjuntando la entrada a GViewAppMenu muestra que se trata de un archivo de configuración del menú de la vista principal, esta entrada podría ser GViewAppIconBar la ventana principal para la barra de iconos o GViewAppPyshell para la cobertura Python.
Los ítems de entrada mostrados abajo, que tienen llamadas definidas en gvviewwindow.py., son utilizados para indicar la ubicación estándar en la ventana de vista. Éste indicará cómo la entrada aparecerá en el menú. Indica cuál llamado del gvviewwindow se va a utilizar. La entrada “archivo/rfl1” demuestra cómo una discusión simple (interna o secuencia) se puede pasar al servicio repetido. Los ítems
se usan en la entrada de herramienta siguiendo la trayectoria del menú. La posición de la entrada del menú será determinada por la posición de la entrada en el archivo de XML. Por ejemplo, la herramienta GDALTool (la herramienta exportación Tool_Export.py) define una entrada “File/Export” del menú. En el caso mostrado abajo, la entrada aparecería entre “Import” y “Print” en el menú del “ file”. El ítem
se utiliza para redefinir los nombres de la trayectoria y seleccionarlos entre entradas, mostrando los casos donde una herramienta define más de una entrada al menú. Cada debe definir la herramienta de la antigua trayectoria al menú (herramienta por defecto), correspondiendo al llamado que se utilizará y una ruta nueva del menú. La vieja trayectoria del menú se utiliza como una tecla al diccionario de los menu_entries de la herramienta para localizar el llamado deseado. Por ejemplo, el ítem
que se muestra abajo indica que el llamado de RenderTestTool que corresponde a su entrada “Render Test”, bajo el menú “Tools”, debe aparecer en esta vista como una entrada “Render Test” bajo el menú de “File”, entre los ítems “Print” y el separador. Si RenderTestTool especifica otras entradas del menú, tendrían que aparecer como ítems separados del para aparecer en el menú de la vista.
<GViewAppMenu> <Menu> <tools>All</tools> <entry> <path>'File/Import'</path> <callback>self.file_import_cb</callback> </entry> <simpletoolentry> <name>GDALTool</name> </simpletoolentry> <entry> ... <path>'File/Print'</path> <callback>self.print_cb</callback> </entry> <complextoolentry> <name>RenderTestTool</name> <oldpath>'Tools/Render Test'</oldpath> <newpath>'File/Render Test'</newpath> </complextoolentry> <entry> <path type="separator">'File/'</path> </entry> <entry> <path>'File/rfl1'</path> <callback>self.rfl_cb</callback> <arguments> <arg>1</arg> </arguments> </entry> ... </Menu> </GViewAppMenu>En el archivo XML de la barra de iconos, los parámetros de entrada definidos son: el archivo que se utiliza con el icono (), el texto tooltip ( ) y el callback. La posición en la barra de iconos está determinada por la posición de entrada en el archivo. Como en el archivo XML de la barra de iconos, las entradas de herramienta se especifican con los ítems
, , and . Las entradas de barra de iconos se guardan internamente como una lista y no como un diccionario en caso de que una aplicación quiera usar el mismo archivo de icono dos veces, con un texto tooltip diferente para diferenciar entre callbacks, de tal manera que se usa un índice para especificar cuál de las entradas de herramientas de iconos utilizar. El ítem puede usarse para anular el archive de icono, la pista, ayuda (que no es usada actualmente por OpenEV para los iconos) y la posición de un ítem. Un ejemplo de un archivo icono XML para ver ventanas se ofrece abajo (…indica donde se han eliminado secciones para brevedad):
<GViewAppIconBar> <Iconbar> <icon> <xpm>'openfile.xpm'</xpm> <hint>'Open and Display Raster/Vector File'</hint> <callback>self.file_open_cb</callback> </icon> <icon> <xpm>'print.xpm'</xpm> <hint>'Print Current View'</hint> <callback>self.print_cb</callback> <help>'gvprint.html'</help> </icon> ... <complextoolentry> <name>ShapesGridTool</name> <hint>Newhint</hint> <index>0</index> </complextoolentry> ... </Iconbar> </GViewAppIconBar>El menú Python Shell y las entradas de iconos se combinan en un sólo archivo que también indica otras opciones en el despliegue pyshell, como si un área de historia y barra de progreso están presentes. Las especificaciones del menú y de icono son análogas a las especificaciones de vista del menú y de iconos. El área del mensaje y el aviso siempre estarán presentes. Un ejemplo de un archivo de configuración Python Shell XML es dado a continuación:
<GViewAppPyshell> <Menu> <tools>All</tools> <entry> <path>'File/Preferences'</path> <callback>self.preferences_cb</callback> </entry> <entry> <path>'File/Quit'</path> <callback>self.close_cb</callback> <accelerator>control+D</accelerator> </entry> <entry> <path>'Help/Help'</path> <callback>self.launch_help_cb</callback> </entry> </Menu> <Iconbar> <tools>Some</tools> <complextoolentry> <name>MyTool</name> <hint>MyToolHint</hint> <index>0</index> </complextoolentry> </Iconbar> <History> </History> <Progress> </Progress> </GViewAppPyshell>En el caso de arriba, una entrada vacía (Ej.) indica que la entrada debe estar presente con los ajustes automáticos. Si una entrada no está presente (Ej. Toda la entrada del <Menu>… </Menu> fue eliminada), ese componente no estará presente. Si el componente está presente, sólo las subentradas especificadas serán utilizadas. El archivo de arriba especifica un Python Shell con un menú con todas las entradas del menú de herramientas, más las entradas para la ayuda callback ( Help->Help), una entrada (File-> Quit), y una entrada para especificar las trayectorias para buscar los módulos del python (Archivo->Preferencias). La barra de iconos contendría solamente una entrada - la primera entrada del icono especificada en la herramienta MyTool, con su pista de texto substituida por MyToolHint. La barra de historia y la barra de progreso estarían presentes con sus ajustes automáticos (actualmente no hay opciones para alterarles – éstos están o no presentes). La barra del progreso no se utiliza automáticamente aunque esté presente, sin embargo, las funciones que tomarán mucho tiempo podrán ser actualizadas importando gview y ubicándolo en la siguiente línea dentro de la función, en donde se quiera actualizar la barra de progreso: gview.app.shell.show_progress(i,msg)Donde i es un número entre 0 y 100 (el progreso para mostrar en la barra), y msg es una secuencia para mostrar en la sección del mensaje.