8 de diciembre de 2017

Extraer el oro del XSource - Parte 2

Artículo original: Mining for Gold in XSource - Part 2
http://msdn.microsoft.com/library/en-us/dnfoxtk04/html/ft04k8.asp
Autor: Doug Hennig
Traducido por: Ana María Bisbé York


En la primera parte de esta serie, Doug Hennig examinó algunos componentes interesantes en XSource, el código fuente de la mayoría de herramientas "Xbase" que vienen con VFP.  El artículo de este mes continúa escarbando por el código que usted puede utilizar en sus propias aplicaciones.

Como he descrito en el artículo del mes pasado, el código fuente para casi todas las herramientas XBase viene con VFP en un archivo .ZIP: XSource.ZIP en la carpeta Tools\XSource debajo de la carpeta raíz de VFP. Al descomprimir este archivo obtenemos como resultado una carpeta llamada VFPSource que queda debajo de Tools\XSource que contiene el código fuente para las herramientas XBase. En la Parte 1, he comentado sobre algunas de las imágenes que se incluyen en el XSource y componentes que puede utilizar para guardar la configuración de los recursos de VFP y para crear menús contextuales orientados a objetos. Este mes, voy a mostrar otros componentes que puede utilizar en sus propias aplicaciones.

Mostrar archivos de video.

Cuando elimina o mueve un grupo de archivos, Explorador de Windows despliega un diálogo muy agradable que muestra una versión animada de qué es lo que se está haciendo. Esto no hace que el proceso sea más rápido; pero al menos permite conocer que el sistema no se ha quedado parado mientras procesa la acción.

Hace varias versiones, VFP salió con archivos animados en la carpeta Graphics\Videos. Puede utilizar estos videos para representar el mismo tipo de diálogo de proceso que tiene Explorador de Windows. La clase cAnimation en FoxRef.VCX (en la carpeta FoxRef XSource) hace esta labor de forma sencilla. Arrastre simplemente la clase a un formulario y llame a su método Load, pasando el nombre del archivo de video a mostrar. Si desea cargar el archivo; pero no ejecutarlo inmediatamente, establezca lAutoPlay = .F., llame a Load para que cargue el archivo y luego llame a Play cuando esté listo para ejecutar el video. La clase cAnimation es una subclase de Microsoft Animation Control, así que es necesario distribuir este control ActiveX (MSCOMCT2.OCX) con su aplicación.

Para hacerlo aun más fácil de usar, he creado una subclase de cAnimation llamada SFAnimation en SFXSource.VCX. La subclase tiene una propiedad cFileName que contiene el nombre del archivo de video, y su método Init carga al Load, pasándole This.cFileName. Entonces, simplemente arrastro el control SFAnimation en un formulario, establezco cFileName y ya está listo.

Para ver un ejemplo de este control, ejecute TestAnimation.SCX, incluido en el archivo Download de este mes. Ejecuta seis archivos de video que vienen con VFP, mostrando animaciones similares a las que ve en el diálogo del Explorador de Windows. La figura 1 muestra la apariencia de este formulario durante la ejecución.

Figura 1

Diálogo de progreso

Mostrar un  video animado no siempre brinda al usuario suficiente información sobre el progreso de un proceso de larga duración. Muchas veces, los usuarios desean verificar el progreso de procesos de múltiples pasos o desean ver un termómetro que indique cuán largo ha sido un proceso. La clase cProgressForm de FoxToolbox.VCX en la carpeta ToolBox XSource brinda una barra de progreso muy agradable que puede utilizar para una tarea de este tipo ( Existe también una versión de esta clase en FoxRef.VCX en la carpeta FoxRef XSource; pero la versión ToolBox tiene un poco más de posibilidades.)

Como puede ver en la figura 2, cProgressForm tiene varias posibilidades:

  • Una imagen animada - el PC con la lente de aumento - que indica que algo está ocurriendo (no es un archivo de video, es un GIF animado).
  • Una descripción general del proceso.
  • Un termómetro que indica el progreso del proceso.
  • Un mensaje de estado que muestra qué paso del proceso se está ejecutando actualmente.
  • Un botón cancelar, que permite al usuario terminar el proceso.

Figura 2

Cuando instancia cProgressForm, puede pasarle opcionalmente la descripción del proceso (algo como "Recuperando datos") para establecer el valor de Caption de la etiqueta Description. Puede además, establecer la descripción llamando al método SetDescription. En la medida que va progresando el proceso, puede querer actualizar el Caption de la etiqueta Status debajo del termómetro para indicar en qué paso se encuentra, pase la cadena deseada al método SetStatus.

Si desea que el termómetro muestre valores absolutos, tales como registro actual de todos los registros existentes, en lugar del porcentaje de ejecución, establezca el valor máximo para el termómetro llamando a SetMax. Si no quiere que se muestre el botón Cancel, establezca la propiedad lShowCancel a .F.. Si no desea que se muestre el termómetro, establezca la propiedad lShowThermometer a .F.

Para actualizar el termómetro, llame al método SetProgress, pase el valor al termómetro (ya sea como un valor absoluto si llama a SetMax o como un valor porcentual, por ejemplo, 50 cuando el proceso está cumplido a la mitad) y una cadena opcional para utilizar como el Caption para la etiqueta Status, SetProgress devuelve .T. si el proceso debe continuar o .F. si se debe detener, debido a que el usuario ha presionado el botón cancelar.

cProgressForm tiene un par de comportamientos que yo deseaba modificar. Primero, debemos manualmente llamar al método Show para mostrar el formulario. Yo preferiría que lo mostrara automáticamente si es necesario la primera vez que es llamado SetProgress. Segundo, establece una propiedad de usuario nSecond a SECONDS() en el Init. Esta propiedad puede utilizarse para determinar el tiempo total requerido para un proceso; pero, estableciéndolo en el Init puede ser muy pronto. Entonces, he creado una subclase llamada SFProgressForm en SFXSource.VCX. El método Init utiliza DODEFAULT() y establece entonces nSeconds a 0. El método SetProgress simplemente hace Visible = .T. si el formulario no es visible y nSeconds a SECONDS() si fue 0, y utiliza luego DODEFAULT() para el comportamiento habitual.

Uno de los archivos que se incluyen en el archivo Download, TestProgress.PRG, es un ejemplo de que busca los archivos en la carpeta FFC en la carpeta Home de VFP para localizar cadenas (constantes con "_LOC" en el nombre). Esto utiliza SetMax para especificar la cantidad total para procesar, y actualizar el medidor de progreso después de que cada archivo ha sido buscado. Quite los comentarios al código estableciendo lShowCancel o lShowThermometer a .F. para ver que cómo se ve la barra de progreso en cada caso.

* Determinar cuántos ficheros deseamos procesar.
lnFiles = adir(laFiles, home() + 'ffc\*.*')
* Instanciar el diálogo de progreso y dar valor a algunas propiedades.
loProgress = newobject('cProgressForm', 'SFXSource.vcx')
loProgress.SetDescription('Buscando las cadenas localizadas ')
loProgress.SetMax(lnFiles)
*loProgress.lShowCancel = .F.
*loProgress.lShowThermometer = .F.
* Buscar los archivos según las cadenas localizadas,
* actualizando el medidor de progreso según vamos.
lnFound = 0
for lnI = 1 to lnFiles
  lcFile = laFiles[lnI, 1]
  lcFullFile = forcepath(lcFile, home() + 'ffc')
  lcExt = justext(lcFile)
  do case
    case inlist(lcExt, 'H', 'PRG')
      lcContents = filetostr(lcFullFile)
      lnFound = lnFound + occurs('_LOC', ;
        upper(lcContents))
    case lcExt = 'VCX'
      use (lcFullFile) again shared
      lnFound = lnFound + occurs('_LOC', upper(METHODS))
  endcase
  if not loProgress.SetProgress(lnI, 'Verificando ' + ;
    lcFile + '...')
    exit
  endif not loProgress.SetProgress ...
next lnI
* Mostrar los resultados
messagebox(transform(lnFound) + 'instancias encontradas ' + ;
  'en ' + transform(seconds() - loProgress.nSeconds) + ;
  ' segundos.')

Vea que cProgressForm utiliza el control ActiveX Microsoft ProgressBar, entonces necesitará distribuir MSCOMCT.OCX con su aplicación. Además, utiliza un archivo GIF animado, FindComp.GIF en la carpeta BitMaps de la carpeta Toolbox XSource, entonces este archivo sería agregado a su proyecto cuando lo genera.

Barra Outlook

Aun cuando ha sido liberado hace muchos años, Outlook ha influido en la apariencia y el comportamiento de otras aplicaciones. Una de las interfaces qué más ha sido copiada ha sido la de Outlook  Estos controles tienen un conjunto de categorías plegables, cada una de las cuales contienen elementos individuales que pueden seleccionar que determina qué aparece a la derecha del Outlook.

Existen controles ActiveX que se pueden adquirir que simulan la apariencia de la barra Outlook, algunos de los controles trabajan con VFP y muchos otros no lo hacen. Sin embargo, existe una aplicación que viene con VFP que utiliza un control similar, y tenemos el código fuente en el XSource: el Toolbox.

El  Toolbox se agregó en VFP 8 como una vía para propiciar una interfaz similar a Visual Studio.NET para seleccionar controles y otros elementos frecuentemente utilizados. Debido a que no tiene apariencia exacta a Outlook, yo llamaría al control que mantiene las categoría y elementos debajo de cada uno del "toolbox".

Como puede ver en la Figura 3,  la caja de herramientas (toolbox) consta de cuatro áreas de interés (sin contar el texto de ayuda al final). Una categoría representa un conjunto de elementos. Las categorías son como las barras en el toolbox. Solamente una categoría se expande en cada momento, el resto aparece como barra de categoría. Al hacer Clic en una barra, esta se expande, lo que provoca que la que estaba actualmente expandida se contrae.

Figura 3

Una herramienta es un elemento dentro de una categoría. Las herramientas tienen una imagen, un nombre, y un ToolTip. Puede realizar varias acciones en la herramienta: hacer clic, hacer doble clic, arrastrar, etc. Además, dentro de una categoría hay botones de desplazamiento hacia arriba y abajo. No necesita hacer clic sobre estos botones, simplemente desplace el puntero del ratón sobre un botón por un segundo y comenzará a desplazar la lista en la dirección indicada.

Las clases que proporcionan el toolbox están en FoxToolbox.VCX y _Toolbox.VCX en la carpeta Toolbox XSource. La clase de interés en FoxToolbox.VCX  es ToolContainer. Contiene los controles y la lógica para una simple categoría. Un formulario que muestra una caja de herramientas va a contener tantos objetos ToolContainer como categorías haya que mostrar.

La propiedad oToolItem de cada ToolContainer tiene una referencia a un objeto _Category ( o uno de sus subclases, las diferentes subclases son utilizadas por tipos diferentes de categorías en la aplicación VFP Toolbox) desde _Toolbox.VCX, _Category tiene propiedades y métodos para la categoría. Para nuestro uso, _Category no es realmente importante; pero algunos métodos de ToolContainer requieren de que exista.

Cada ToolContainer también mantiene una colección de herramientas, cada una de las cuales es representada por un objeto _Tool (o una de sus subclases, subclases diferentes son utilizadas para diferentes tipos de herramientas en la aplicación Toolbox de VFP) desde _Toolbox.VCX. _Tool tiene propiedades y métodos para una herramienta.

El formulario principal en la aplicación Toolbox se basa en la clase ToolboxForm en FoxToolbox.VCX. De inicio consideré subclasear este formulario; pero desafortunadamente es demasiado dependiente de las tareas relacionadas con la aplicación Toolbox en lugar de un toolbox genérico. Entonces, en su lugar, he creado SFToolboxForm (en SFXSource.VCX) como la base para formularios con una barra de herramientas.

El método ShowToolbox, llamado desde el Init, es el responsable de crear el toolbox.

local loCategories, ;
  lnI, ;
  loCategory, ;
  lcCategory, ;
  loContainer, ;
  loTools, ;
  lnJ, ;
  loTool
with This
  * Tomar una colección de categorías y procesar cada una.
  loCategories = .GetCategories()
  .nCategories = loCategories.Count
  for lnI = 1 to .nCategories
    loCategory = loCategories.Item(lnI)
    * Crear un objeto ToolContainer y establecer sus propiedades.
    lcCategory = 'Category' + loCategory.cID
    .NewObject(lcCategory, 'ToolContainer', ;
      'FoxToolbox.vcx')
    loContainer = evaluate('.' + lcCategory)
    with loContainer
      .SetFont(This.FontName, This.FontSize, '')
      .oToolItem = newobject('_Category', ;
        '_Toolbox.vcx')
      .CategoryID = loCategory.cID
      .Caption = loCategory.cName
      .Width = This.nToolboxWidth
      .Visible = .T.
    endwith
    * Si esta es la primera categoría, fija la altura estándar
    * para las categorías según la altura de esta y hace que sea 
    * la abierta de forma predeterminada
    if lnI = 1
      .nStandardHeight = loContainer.Height
      .cCurrentCategory = loCategory.cID
    endif lnI = 1
    * Toma una colección de herramientas para la categoría actual. 
    * Para cada una, enlaza su método OnClick con nuestro método
    * ToolSelected. La clase toolbox espera su propiedad
    * oEngine referencie un objeto con una propiedad
    * DblClickToOpen, entonces, crea una. Agrega el objeto herramienta
    * al contenedor de categorías.
    loTools = .GetToolsForCategory(loCategory.cID)
    for lnJ = 1 to loTools.Count
      loTool = loTools.Item(lnJ)
      bindevent(loTool, 'OnClick', This, 'ToolSelected')
      loTool.oEngine = createobject('Empty')
      addproperty(loTool.oEngine, 'DblClickToOpen', .F.)
      loContainer.AddTool(loTool)
    next lnJ
  next lnI
  * Ajusta los tamaños de las categorías.
  .ResizeToolbox()
endwith

Este código comienza llamando al método GetCategories para devolver una colección de objetos con información sobre cada categoría. GetCategories es abstracto en esta clase porque el mecanismo exacto para llenar la colección puede variar. El código entonces puede ir a través de la colección, agregando un objeto ToolContainer al formulario de cada categoría. El ancho del ToolContainer se establece en la propiedad nToolboxWidth del formulario, para asegurarse de establecer esta propiedad en una subclase o formulario creado desde esta clase.

Solamente para la primera categoría, el código establece la propiedad nStandardHeight, que contiene el estándar (esto es, cerrado) altura para cada categoría, para la altura del contenedor actual. Además establece cCurrentCategory al ID para esta categoría de tal forma que se abrirá automáticamente en el código que explicaré más tarde.

Luego, el código llama al método GetToolsForCategory, el que es abstracto en esta clase, para devolver una colección de objetos_Tool que representan las herramientas para la categoría especificada. Cuando un usuario hace clic en una herramienta, el toolbox llama al método OnClick en el objeto herramienta apropiado. Decidí controlar los clics en el nivel formulario, entonces, el código utiliza BINDEVENT() para enlazar el método OnClick de cada objeto herramienta del método ToolSelected del formulario. Debido a que varios métodos toolbox esperan que la propiedad oEngine de un objeto herramienta referencie un objeto con una propiedad DblClickToOpen, el código crea un objeto. El objeto herramienta es agregado a la colección de herramientas en la categoría contenedor.

Finalmente, ShowToolbox llama al método Resize Toolbox para abrir la categoría seleccionada (en este caso, la primera) y cierra las otras. Este código no se muestra aquí por razones de espacio, examínelo con toda libertad.

El método SetCategory es llamado desde el código toolbox cuando el usuario hace clic sobre una categoría. Todo este código establece cCurrentCategory al ID para la categoría seleccionada y llama a ResizeToolbox, para controlar la nueva selección.

SFToolboxForm contiene otros tres métodos abstractos: OnRightClick, llamado cuando el usuario hace clic derecho sobre una herramienta, ResetHelpFile, la que establece el archivo Help a uno predeterminado, y SetHelpText, el que, en la aplicación Toolbox, además alguna información acerca de la herramienta seleccionada dentro del área Help al final del formulario. Estos métodos son necesarios debido a que algunos métodos del toolbox lo llaman. Aunque ellos no hacen nada en esta clase, puede implementar el comportamiento apropiado en una subclase.

TestToolbox.SCX es un formulario de ejemplo basados en SFToolboxForm. Esto utiliza una caja de herramientas para mostrar los diferentes módulos en una aplicación contable. Lea tres módulos desde una tabla MODULES, el que contiene registros para las categorías (tales como "Cuentas admisibles" y "Entrada de órdenes") y el módulo debajo de cada uno (como "Usuarios" y "Ordenes") Tiene el siguiente código en GetCategories, el método llamado por ShowToolbox para llenar una colección de categorías.

local loCollection, ;
  loCategory
loCollection = createobject('Collection')
select NAME, ID ;
  from MODULES ;
  where PARENTID = 0 and ACTIVE ;
  into cursor _CATEGORIES
scan
  loCategory = createobject('Empty')
  addproperty(loCategory, 'cName', trim(NAME))
  addproperty(loCategory, 'cID', transform(ID))
  loCollection.Add(loCategory)
endscan
use
return loCollection

Este código llena una colección con objetos que tienen propiedades cName y cID desde las categorías en la tabla MODULES (aquellos registros con ParentID = 0).

GetToolsForCategory llamado además desde ShowToolbox, es responsable para llenar una colección de herramientas para la categoría especificada.

lparameters tcID
local loCollection, ;
  loCategory
loCollection = createobject('Collection')
select NAME, ID, CLASS, LIBRARY, IMAGEFILE ;
  from MODULES ;
  where PARENTID = val(tcID) and ACTIVE ;
  into cursor _MODULES
scan
  loModule = newobject('_Tool', '_Toolbox.vcx')
  with loModule
    .ToolName = trim(NAME)
    .UniqueID = transform(ID)
    .ParentID = tcID
    .ImageFile = textmerge(trim(IMAGEFILE))
    addproperty(loModule, 'cClass', trim(CLASS))
    addproperty(loModule, 'cLibrary', trim(LIBRARY))
  endwith
  loCollection.Add(loModule)
endscan
use
return loCollection

Este código selecciona los registros desde Modules que tienen ParentID conteniendo el ID de la categoría para llenar. Cada objeto en la colección es un objeto _Tool, con sus propiedades establecidas adecuadamente. Este código agrega además un par de propiedades de usuario: la clase y la librería para el contenedor de los controles a mostrar a la derecha del formulario cuando la herramienta es seleccionada.

Gracias a BINDEVENT(), el método ToolSelected es llamado cuando el usuario hace clic sobre la herramienta. ToolSelected agrega un objeto a la derecha del formulario empleando las propiedades cClass y cLibrary de la herramienta seleccionada para indicar que clase utiliza el objeto.

local llLockScreen, ;
  laEvents[1], ;
  loModule, ;
  lcClass, ;
  lcLibrary, ;
  llHaveIt, ;
  lnI, ;
  loControl, ;
  llDesiredModule
with This
  * Bloqueamos la pantalla y no vemos los cambios 
  * hasta que no hemos acabado.
  llLockScreen = .LockScreen
  .LockScreen = .T.
  * Tomamos la clase y la biblioteca que suponemos vamos a utilizar 
  * desde el módulo seleccionado.
  aevents(laEvents, 0)
  loModule = laEvents[1]
  lcClass = lower(loModule.cClass)
  lcLibrary = lower(loModule.cLibrary)
  * Ocultar todos los módulos, menos el seleccionado.
  llHaveIt = .F.
  for lnI = 1 to .ControlCount
    loControl = .Controls[lnI]
    if pemstatus(loControl, 'cAlias', 5)
      llDesiredModule = ;
        lower(loControl.Class) == lcClass
      llHaveIt = llHaveIt or llDesiredModule
      loControl.Visible = llDesiredModule
    endif pemstatus(loControl, 'cAlias', 5) ...
  next lnI
  * Instanciar la clase adecuada si lo necesitamos.
  if not llHaveIt
    try
      .NewObject(lcClass, lcClass, lcLibrary)
      loControl = evaluate('.' + lcClass)
      with loControl
        .Left = 210
        .Width = This.Width - .Left
        .Height = This.Height
        if version(5) >= 900
          .Anchor = 15
        else
          .OnResize()
        endif version(5) >= 900
        .Visible = .T.
        .SetupControls()
      endwith
    catch to loException
      messagebox('Este módulo no se ha definido.')
    endtry
  endif not llHaveIt
  * Refrescar la pantalla.
  .LockScreen = llLockScreen
endwith

La Figura 4 muestra que este formulario de la misma forma que se ejecuta.

Figura 4

Conclusiones

Mostrar archivos de video en formularios de VFP, mostrar un medidor de progreso durante un proceso largo y agregar una barra tipo Outlook a sus aplicaciones son tareas que se simplifican si se utilizan los componentes que vienen con VFP en XSource. En mi próximo artículo veremos como crear una zona con desplazamiento en un formulario y cuán fácil es crear generadores utilizando componentes XSource.

Descarga

Download 411HENNIG.ZIP

No hay comentarios. :

Publicar un comentario

Los comentarios son moderados, por lo que pueden demorar varias horas para su publicación.