29 de febrero de 2016

Quitar los acentos

Con esta simple rutina podemos quitar y reemplazar todos los acentos de una cadena

Ejemplo:

? QuitarAcentos("José María SÁNCHEZ")

*-------------------------------------
* FUNCTION QuitarAcentos(tcCadena)
*-------------------------------------
* Quita los acentos de una cadena
* RETORNA: Caracter
* USO: QuitarAcentos("Mamá")
*-------------------------------------
FUNCTION QuitarAcentos(tcCadena)
 RETURN CHRTRAN(tcCadena, "áéíóúÁÉÍÓÚ","aeiouAEIOU")
ENDFUNC
*-------------------------------------

Utilizando la función CHRTRAN() también podemos crear otras rutinas para quitar y/o reemplazar caracteres especiales de una cadena de texto.

Luis María Guayán

28 de febrero de 2016

Agregar controles a Grids

Los Grids de Visual FoxPro nos permiten incluir controles diferentes a texbox dentro de sus columnas, estos pueden ser chebox, combobox, editbox...

A continuación un tutorial animado, requiere tener instalado el Macromedia Flash.

Si no puede visualizar el tutorial haga clic aquí

Espero les sea de utilidad.

Esparta Palma

25 de febrero de 2016

Generador de imágenes Captcha con GDI+

Artículo original: Captcha Image Generator with GdiPlus-X
https://vfpimaging.blogspot.com/2012/03/captcha-image-generator-with-gdiplusx.html
Autor: Cesar Ch.
Traducido por: Ana María Bisbé York


Recientemente, estaba navegando por el Blog de Rick Stralh cuando encontré un escrito muy interesante, "A Captcha Image generator for FoxPro", en el cual el muestra cómo crear algunas imágenes "captcha".

Nota de la Traductora: CAPTCHA es el acrónimo de "Completely Automated Public Turing test to tell Computers and Humans Apart", y consiste en una verificación automatizada para determinar si el usuario que realiza la petición es humano o no.

Como ha dicho Rick Stralh en su escrito, "CAPTCHA muestra básicamente una imagen de verificación junto a un cuadro de texto que tiene que ser llenado para verificar la petición actual. No es un procedimiento infalible para la validación y tiene algunos problemas de accesibilidad; pero parece que es la solución más frecuente a este problema." La imagen generada contiene un texto aleatorio.

En este caso, Rick Stralh escogió crear una DLL en C++, y la llama desde VFP. Aquí mostraré cómo obtener los mismos resultados utilizando la nueva biblioteca GdiPlus-X.

El código que se muestra a continuación pudiera ser útil para otros propósitos también, porque se utilizaron algunas técnicas de dibujo muy interesantes.

Para obtener la cadena aleatoria, creé una función muy simple, que recibe como un parámetro de cantidad de caracteres que se crearán.

PROCEDURE CreateString(tnLength
  LOCAL lcText, lcChar, x, n
  lcText = ""
  FOR n = 1 TO tnLength
    x = INT(RAND() * 36)
    lcChar = IIF(x < 10, TRANSFORM(x), CHR(x + 55))
    lcText = lcText + lcChar
  ENDFOR
  RETURN lcText
ENDPROC

¡ A JUGAR !

IMPORTANTE:

Todos los ejemplos que se muestran emplean la nueva biblioteca GDIPlus-X, que está aun en versión ALPHA; pero es estable y fiable para hacer la mayoría de las tareas de GDI+. Descargue la última versión estable de Codeplex:

http://www.codeplex.com/Wiki/View.aspx?ProjectName=VFPX&title=GDIPlusX

EJEMPLO 1 - CREAR UN IMAGEN SENCILLA Y DIBUJE ALGÚN TEXTO

** Generador de imágenes Captcha 
** Crea una imagen muy sencilla que contiene una cadena
** Se basa en el código de Rick Strahl expuesto en su weblog
** http://www.west-wind.com/wconnect/weblog/ShowEntry.blog?id=556
** El siguiente ejemplo muestra como crear un sencillo Generador de imágenes Captcha 
LOCAL lcText AS Character 
lcText = CreateString(8) 
_SCREEN.AddProperty("System", NEWOBJECT("xfcSystem", ;
  LOCFILE("system.vcx","vcx")))

LOCAL loBmp AS xfcBitmap
LOCAL loFont AS xfcFont
LOCAL loSizeF AS xfcSizeF
LOCAL loScreenGfx AS xfcGraphics
LOCAL loGfx AS xfcGraphics 
WITH _SCREEN.System.Drawing
  loScreenGfx = .Graphics.FromHwnd(_Screen.HWnd)
  loFont = .Font.New("Tahoma",20) && Font Name, Size
  * Obtiene la altura y el ancho necesarios para la cadena
  loSizeF = loScreenGfx.MeasureString(lcText, loFont)
  * Crea un bitmap nuevo
  loBmp = .Bitmap.New(loSizeF.Width, loSizeF.Height)
  * Obtiene un objeto Graphics para dibujar
  loGfx = .Graphics.FromImage(loBmp)
  loGfx.Clear(.Color.White) 
  * Dibuja toda la cadena
  loGfx.DrawString(lcText, loFont, .Brushes.Black, 0, 0)
  * Guarda la imagen en disco
  loBmp.Save("c:\captcha1.png", .Imaging.ImageFormat.Png)
ENDWITH 
* Muestra la imagen en el explorador
RUN /n explorer.exe c:\captcha1.png
RETURN 

PROCEDURE CreateString(tnLength)
  LOCAL lcText, lcChar, x, n
  lcText = ""
  FOR n = 1 TO tnLength
    x = INT(RAND() * 36)
    lcChar = IIF(x < 10, TRANSFORM(x), CHR(x + 55))
    lcText = lcText + lcChar
  ENDFOR
  RETURN lcText
ENDPROC 

El ejemplo de arriba crea la imagen más sencilla, que contiene una cadena aleatoria dentro. para obtener el tamaño necesario para crear el Bitmap, en el primer momento, he creado un objeto Graphics desde _Screen. Como se puede ver a continuación, este objeto tiene solamente una tarea: permitirnos llamar a la función MeasureString para obtener el tamaño necesario para nuestro bitmap.

loScreenGfx = .Graphics.FromHwnd(_Screen.HWnd)
loFont = .Font.New("Tahoma",20) && FontName, FontSize
* Obtiene la altura y el ancho necesarios para la cadena
loSizeF = loScreenGfx.MeasureString(lcText, loFont)
* Crea un bitmap nuevo
loBmp = .Bitmap.New(loSizeF.Width, loSizeF.Height) 

EJEMPLO 2 - CREA UNA IMAGEN SENCILLA Y DIBUJA UN TEXTO SOBRE UN FONDO TRAMADO.

** Generador de imágenes Captcha 
** Crea una imagen muy sencilla que contiene una cadena con un fondo tramado
** Se basa en el código de Rick Strahl expuesto en su weblog
** http://www.west-wind.com/wconnect/weblog/ShowEntry.blog?id=556
** El siguiente ejemplo muestra como crear un sencillo Generador de imágenes Captcha 
LOCAL lcText AS Character 
lcText = CreateString(8) 
_SCREEN.AddProperty("System", NEWOBJECT("xfcSystem", ;
  LOCFILE("system.vcx","vcx")))

LOCAL loBmp AS xfcBitmap
LOCAL loFont AS xfcFont
LOCAL loSizeF AS xfcSizeF
LOCAL loScreenGfx AS xfcGraphics
LOCAL loGfx AS xfcGraphics 
WITH _SCREEN.System.Drawing
  loScreenGfx = .Graphics.FromHwnd(_Screen.HWnd)
  loFont = .Font.New("Tahoma",20) && Font Name, Size
  * Obtiene la altura y el ancho necesarios para la cadena
  loSizeF = loScreenGfx.MeasureString(lcText, loFont)
  * Crea un bitmap nuevo
  loBmp = .Bitmap.New(loSizeF.Width, loSizeF.Height)
  * Obtiene un objeto Graphics para dibujar
  loGfx = .Graphics.FromImage(loBmp)
  loGfx.Clear(.Color.White) 
  liRand = INT(RAND()*52) + 1 
  * Rellena el fondo del rectángulo con una brocha tramada de forma aleatoria
  loGfx.FillRectangle( ;
  .Drawing2D.HatchBrush.New(liRand, .Color.Black, .Color.White),;
  0,0,loBmp.Width,loBmp.Height)
  * Dibuja toda la cadena
  loGfx.DrawString(lcText, loFont, .Brushes.Black, 0, 0)
  * Guarda la imagen en disco
  loBmp.Save("c:\captcha2.png", .Imaging.ImageFormat.Png)
ENDWITH 
* Muestra la imagen en el explorador
RUN /n explorer.exe c:\captcha2.png
RETURN 

PROCEDURE CreateString(tnLength)
  LOCAL lcText, lcChar, x, n
  lcText = ""
  FOR n = 1 TO tnLength
    x = INT(RAND() * 36)
    lcChar = IIF(x < 10, TRANSFORM(x), CHR(x + 55))
    lcText = lcText + lcChar
  ENDFOR
  RETURN lcText
ENDPROC 

Este es exactamente el mismo ejemplo que utilizamos antes; pero agregando un fondo tramado. GDI+ ofrece 52 plantillas diferentes para brochas con tramas. En este ejemplo, preferí utilizar uno aleatorio. Verifíquelo y seleccione el que le guste más:

* Rellena el fondo del rectángulo con una brocha tramada de forma aleatoria
loGfx.FillRectangle( ;
.Drawing2D.HatchBrush.New(liRand, .Color.Black, .Color.White),;
0,0,loBmp.Width,loBmp.Height)

EJEMPLO 3 - DIBUJAR ALGÚN TEXTO CON UN FONDO COLOREADO UTILIZANDO COLORES ALEATORIOS

** Generador de imágenes Captcha 
** Crea una imagen muy sencilla que contiene una cadena 
** Cada carácter en color aleatorio diferente con un color de fondo aleatorio
** Se basa en el código de Rick Strahl expuesto en su weblog
** http://www.west-wind.com/wconnect/weblog/ShowEntry.blog?id=556
** El siguiente ejemplo muestra como crear un sencillo Generador de imágenes Captcha 
LOCAL lcText AS Character 
lcText = CreateString(8) 
_SCREEN.AddProperty("System", NEWOBJECT("xfcSystem", ;
  LOCFILE("system.vcx","vcx")))

LOCAL loBmp AS xfcBitmap
LOCAL loFont AS xfcFont
LOCAL loSizeF AS xfcSizeF
LOCAL loScreenGfx AS xfcGraphics
LOCAL loGfx AS xfcGraphics 
WITH _SCREEN.System.Drawing
  loScreenGfx = .Graphics.FromHwnd(_Screen.HWnd)
  loFont = .Font.New("Tahoma",20) && Font Name, Size
  * Obtiene la altura y el ancho necesarios para la cadena
  loSizeF = loScreenGfx.MeasureString(lcText, loFont)
  LOCAL lnWidth, lnHeight, lnCharWidth
  lnWidth = loSizeF.Width * 1.5 && Se estrecha al 50% para garantizar que quepan los caracteres rotados
  lnHeight = loSizeF.Height
  lnCharWidth = lnWidth / LEN(lcText) 
  * Crea un bitmap nuevo
  loBmp = .Bitmap.New(lnWidth, lnHeight)
  * Obtiene un objeto Graphics para dibujar
  loGfx = .Graphics.FromImage(loBmp)
  loGfx.Clear(.Color.White) 
  * Llena el fondo del rectángulo con una brocha sólida de color aleatorio
  * Necesitamos aquí un color pastel color, por lo que haremos que cada canal sea al menos 180 
  * Dibuja el rectángulo de fondo
  loGfx.FillRectangle( ;
  .SolidBrush.New(.Color.FromARGB(255, INT(RAND() * 76)+180, ;
    INT(RAND() * 76)+180, INT(RAND() * 76)+180)), ;
    0,0,loBmp.Width,loBmp.Height)
  * Dibuja cada carácter en un color aleatorio diferente
  LOCAL n, lcChar
  LOCAL loBrush AS xfcSolidBrush
  FOR n = 0 TO LEN(lcText)
    lcChar = SUBSTR(lcText, n, 1) 
    * Crea una brocha con un color aleatorio
    loBrush = .SolidBrush.New(.Color.FromRGB(INT(RAND() * 0xFFFFFF))) 
    * Dibuja el carácter
    loGfx.DrawString(lcChar, loFont, loBrush, (n-1) * lnCharWidth, 0)
  ENDFOR 
  * Guarda la imagen en disco
  loBmp.Save("c:\captcha3.png", .Imaging.ImageFormat.Png)
ENDWITH 
* Muestra la imagen en el explorador
RUN /n explorer.exe c:\captcha3.png
RETURN 

PROCEDURE CreateString(tnLength)
  LOCAL lcText, lcChar, x, n
  lcText = ""
  FOR n = 1 TO tnLength
    x = INT(RAND() * 36)
    lcChar = IIF(x < 10, TRANSFORM(x), CHR(x + 55))
    lcText = lcText + lcChar
  ENDFOR
 RETURN lcText
ENDPROC 

Esta vez decidí crear un fondo utilizando una brocha sólida con un valor aleatorio en BackColor. Quise asegurarme de obtener un color pastel, por que utilicé:

INT(RAND() * 76) + 180 para cada canal de color RGB. Esta forma, cada canal tendrá al menos el valor 180, asegurando que obtenga un color brillante.

También muy sencillo dibujar cada color en un color aleatorio. Solamente hice un lazo de la cadena y dibujé cada caracter en su posición proporcional. Este momento, deseo tener cualquier color aleatorio, por tanto podría ser cualquier valor entre 0 (RGB[0,0,0] - negro) y 0xFFFFFF (RGB[255,255,255] - blanco).

* Crea una brocha con un color aleatorio 
loBrush = .SolidBrush.New(.Color.FromRGB(INT(RAND() * 0xFFFFFF))) 

EJEMPLO 4 - DIBUJAR ALGÚN TEXTO EN UN FONDO CON TRAMA COLOREADA UTILIZANDO COLORES ALEATORIOS CON ROTACIÓN DE CARACTERES.

** Generador de imágenes Captcha 
** Crea una imagen muy sencilla que contiene una cadena 
** Cada carácter en color aleatorio diferente y una ligera rotación
** Se basa en el código de Rick Strahl expuesto en su weblog
** http://www.west-wind.com/wconnect/weblog/ShowEntry.blog?id=556
** El siguiente ejemplo muestra como crear un sencillo Generador de imágenes Captcha 
LOCAL lcText AS Character 
lcText = CreateString(8) 
_SCREEN.AddProperty("System", NEWOBJECT("xfcSystem", ;
  LOCFILE("system.vcx","vcx")))

LOCAL loBmp AS xfcBitmap
LOCAL loFont AS xfcFont
LOCAL loSizeF AS xfcSizeF
LOCAL loScreenGfx AS xfcGraphics
LOCAL loGfx AS xfcGraphics 
WITH _SCREEN.System.Drawing
  loScreenGfx = .Graphics.FromHwnd(_Screen.HWnd)
  loFont = .Font.New("Tahoma",20) && Font Name, Size
  * Obtiene la altura y el ancho necesarios para la cadena
  loSizeF = loScreenGfx.MeasureString(lcText, loFont)
  LOCAL lnWidth, lnHeight, lnCharWidth
  lnWidth = loSizeF.Width * 1.5 && Se estrecha al 50% para garantizar que quepan los caracteres rotados
  lnHeight = loSizeF.Height
  lnCharWidth = lnWidth / LEN(lcText) 
  * Crea un bitmap nuevo
  loBmp = .Bitmap.New(lnWidth, lnHeight)
  * Obtiene un objeto Graphics para dibujar
  loGfx = .Graphics.FromImage(loBmp)
  loGfx.Clear(.Color.White) 
  * Llena el fondo del rectángulo con una brocha sólida de colores pastel aleatorios
  loGfx.FillRectangle( ;
  .Drawing2D.HatchBrush.New(INT(RAND()*52), ;
    .Color.FromARGB(255, INT(RAND() * 76)+180, INT(RAND() * 76)+180, INT(RAND() * 76)+180), ;
    .Color.FromARGB(255, INT(RAND() * 76)+180, INT(RAND() * 76)+180, INT(RAND() * 76)+180)), ;
    0,0,loBmp.Width,loBmp.Height)
  * Dibuja cada carácter en un color aleatorio diferente 
  * y un ángulo aleatorio de rotación desde -40 a +40 grados
  LOCAL n, lcChar
  LOCAL loBrush AS xfcSolidBrush
  LOCAL loMatrix AS xfcMatrix 
  FOR n = 0 TO LEN(lcText)
    lcChar = SUBSTR(lcText, n, 1) 
    * Crea una brocha con un color aleatorio
    loBrush = .SolidBrush.New(.Color.FromRGB(INT(RAND() * 0xFFFFFF))) 
    * Crea una matriz para aplicar la rotación de los caracteres
    loMatrix = _Screen.System.Drawing.Drawing2D.Matrix.New() 
    * Calcula el ángulo aleatorio
    lnAngle = INT(RAND() * 80) - 40
    * Rota al centro de cada posición de carácter
    loMatrix.RotateAt(lnAngle, ;
      .Point.New((n-1) * lnCharWidth + (lnCharWidth / 2), lnHeight / 2)) 
    * Asocia la matriz al objeto Graphics 
    loGfx.Transform = loMatrix 
    * Dibuja el carácter
    loGfx.DrawString(lcChar, loFont, loBrush, (n-1) * lnCharWidth, 0)
  ENDFOR 
  * Guarda la imagen en disco
  loBmp.Save("c:\captcha4.png", .Imaging.ImageFormat.Png)
ENDWITH 
* Muestra la imagen en el explorador
RUN /n explorer.exe c:\captcha4.png
RETURN 

PROCEDURE CreateString(tnLength)
  LOCAL lcText, lcChar, x, n
  lcText = ""
  FOR n = 1 TO tnLength
    x = INT(RAND() * 36)
    lcChar = IIF(x < 10, TRANSFORM(x), CHR(x + 55))
    lcText = lcText + lcChar
  ENDFOR
  RETURN lcText
ENDPROC

Aquí hicimos lo mismo; pero utilizando una brocha con trama para el fondo, y aplicando una ligera rotación de cada carácter. La rotación de caracteres ya se ha discutido en un escrito anterior http://weblogs.foxite.com/vfpimaging/2012/03/16/draw-rotated-strings-with-gdiplusx en cualquier caso, he aquí una corta explicación:

"System.Drawing" brinda un método que permite dibujar cualquier objeto (forma, texto y otra imagen) utilizando rotación, de cualquier ángulo. Para esto necesitamos crear un objeto Matriz, y utilizamos el método "RotateAt", pasando el punto central del objeto que se rotará. En este caso, necesito pasar el centro de la cadena.

*  Crea una matriz para aplicar la rotación de los caracteres 
loMatrix = .Drawing2D.Matrix.New() 
*  Calcula el ángulo aleatorio
lnAngle = INT(RAND() * 80) - 40
*  Rota al centro de cada posición de carácter
loMatrix.RotateAt(lnAngle, ;
  .Point.New((n-1) * lnCharWidth + (lnCharWidth / 2), lnHeight / 2)) 
*  Asocia la matriz al objeto Graphics 
loGfx.Transform = loMatrix 
* Dibuja el carácter
loGfx.DrawString(lcChar, loFont, loBrush, (n-1) * lnCharWidth, 0)

¿QUÉ MÁS?

Podemos hacer algunas otras cosas, tales como dibujar algunas líneas aleatorias en la imagen. Una forma podría ser simplemente agregando este sencillo código inmediatamente antes de guardar la imagen:

LOCAL x1, y1, x2, y2, lnColor
FOR n = 1 TO 10
  x1 = RAND() * lnWidth
  x2 = RAND() * lnWidth
  y1 = RAND() * lnHeight
  y2 = RAND() * lnHeight
  lnColor = RAND() * 0xFFFFFF
  loGfx.DrawLine(.Pen.New(.Color.FromRGB(lnColor),1),x1, y1, x2, y2)
ENDFOR

A pesar de que se conoce que estos son únicamente algunos CAPTCHA decoradores, puede ser muy útil en determinadas situaciones. Mi objetivo en este escrito es simplemente mostrar algunas técnicas para crear buenos captchas. Hay mucho que mejorar. Usted puede utilizar fuentes irregulares aleatorias, otros fondos, aplicar algunas transformaciones a caracteres, como escalar, cortes, nublados, efectos halo. ¡ Todo esto con GDI+ !

En la próxima liberación de la biblioteca, espero enviar algunos ejemplos que creen efectos interesantes en textos, tales como, algunos que se muestran debajo pudieran también ser útiles.

Manténgase en contacto.


19 de febrero de 2016

Gauge, un nuevo proyecto en VFPx

El nuevo proyecto Gauge, disponible gratuitamente en VFPx, permite dibujar de forma rápida y fácil indicadores y medidores atractivos, o paneles de control en sus aplicaciones Visual FoxPro.

Se utiliza un componente de código abierto .NET para hacer el gráfico, junto con una pequeña clase contenedora en VFP. Un par de formularios de ejemplo muestran cómo utilizar el control Gauge.

Puede descargar la clase y los ejemplos libremente desde la página del proyecto en VFPx:

Gauge
Draws gauges in VFP applications
http://vfpx.codeplex.com/wikipage?title=Gauge

17 de febrero de 2016

Compruebe si EXE se está ejecutando y cierrelo

Traducción de una entrada del blog del ruso Sergey Berezniker que nos muestra dos métodos para terminar una aplicación que se está ejecutando

Entrada Original: CHECK IF EXE IS running AND optionally TERMINATE it

La primera opción es utilizando Scripts de WMI:

lcExeName = "notepad.exe"

* Se esta ejecutando el EXE
? IsExeRunning(lcExeName)

* Cierra el EXE si se esta ejecutando
? IsExeRunning(lcExeName, .T.)
RETURN

FUNCTION IsExeRunning(tcName, tlTerminate)
  LOCAL loLocator, loWMI, loProcesses, loProcess, llIsRunning
  loLocator = CREATEOBJECT('WBEMScripting.SWBEMLocator')
  loWMI = loLocator.ConnectServer()
  loWMI.Security_.ImpersonationLevel = 3  && Impersonate

  loProcesses = loWMI.ExecQuery([SELECT * FROM Win32_Process WHERE Name = '] + tcName + ['])
  llIsRunning = .F.
  IF loProcesses.COUNT > 0
    FOR EACH loProcess IN loProcesses
      llIsRunning = .T.
      IF tlTerminate
        loProcess.TERMINATE(0)
      ENDIF
    ENDFOR
  ENDIF
  RETURN llIsRunning
ENDFUNC

La otra opción es mediante API:

#DEFINE WM_CLOSE 0x10

DECLARE LONG FindWindow IN WIN32API STRING ClassName, STRING WindowTitle
DECLARE LONG SendMessage IN WIN32API LONG HWND, LONG Msg, LONG wParam, LONG LPARAM

lcWindowTitle = "Sin título - Bloc de notas"

HWND = FindWindow( NULL, lcWindowTitle )
IF HWND = 0
  ? "Ventana no encontrada"
  RETURN
ENDIF
=SendMessage( HWND, WM_CLOSE, 0, 0 )
RETURN

14 de febrero de 2016

Un Ping-Pong en VFP

A raiz de la consulta de que si se puede hacer rebotar un objeto dentro de un formulario, envío este código para los que estén interesados.

Funciona con un par de variables, por supuesto que solo es la base del movimiento de un objeto.

Obs: La idea principal para el movimiento del objeto es de "Edwin Macz" un compañero de trabajo.

Este es el código:
PUBLIC oForm1
oForm1 = NEWOBJECT("Form1")
oForm1.SHOW(1)
RETURN

DEFINE CLASS Form1 AS FORM
  HEIGHT = 307
  WIDTH = 447
  DOCREATE = .T.
  AUTOCENTER = .T.
  CAPTION = "Ping-Pong"
  BACKCOLOR = RGB(0,128,255)
  NAME = "Form1"
  ADD OBJECT bola AS SHAPE WITH ;
    TOP = 0, ;
    LEFT = 0, ;
    HEIGHT = 23, ;
    WIDTH = 26, ;
    CURVATURE = 75, ;
    BACKCOLOR = RGB(255,255,0), ;
    NAME = "bola"
  ADD OBJECT timer1 AS TIMER WITH ;
    TOP = 0, ;
    LEFT = 420, ;
    HEIGHT = 23, ;
    WIDTH = 23, ;
    NAME = "Timer1"
  ADD OBJECT barra AS COMMANDBUTTON WITH ;
    TOP = 200, ;
    LEFT = 171, ;
    HEIGHT = 13, ;
    WIDTH = 108, ;
    CAPTION = "", ;
    BACKCOLOR = RGB(0,0,160), ;
    NAME = "barra"
  ADD OBJECT lblb2 AS COMMANDBUTTON WITH ;
    TOP = 200, ;
    LEFT = 171, ;
    HEIGHT = 13, ;
    WIDTH = 4, ;
    CAPTION = "", ;
    BACKCOLOR = RGB(128,0,0), ;
    NAME = "LblB2"

  PROCEDURE INIT
    THISFORM.timer1.INTERVAL = 5
    PUBLIC x AS INTEGER, Y AS INTEGER, vCont AS INTEGER
    STORE 1 TO x, Y
    vCont = 0
  ENDPROC

  PROCEDURE MOUSEMOVE
    LPARAMETERS nButton, nShift, nXCoord, nYCoord
    THISFORM.barra.LEFT = nXCoord
    THISFORM.lblb2.LEFT = nXCoord
  ENDPROC

  PROCEDURE timer1.TIMER
    THISFORM.bola.LEFT = THISFORM.bola.LEFT + x
    THISFORM.bola.TOP = THISFORM.bola.TOP + Y
    vcol1 = INT(RAND()*256)
    vcol2 = INT(RAND()*256)
    vcol3 = INT(RAND()*256)
    IF THISFORM.bola.LEFT + THISFORM.bola.WIDTH => THISFORM.WIDTH
      x = -1
    ELSE
      IF THISFORM.bola.LEFT = 0
        x = 1
      ENDIF
    ENDIF
    IF THISFORM.bola.TOP + THISFORM.bola.HEIGHT => THISFORM.HEIGHT
      Y = -1
    ELSE
      IF THISFORM.bola.TOP = 0
        Y = 1
      ENDIF
    ENDIF
    IF THISFORM.bola.TOP + THISFORM.bola.HEIGHT = THISFORM.barra.TOP ;
        AND THISFORM.bola.LEFT + THISFORM.bola.WIDTH >= THISFORM.barra.LEFT ;
        AND THISFORM.bola.LEFT <= THISFORM.barra.LEFT + THISFORM.barra.WIDTH
      THISFORM.bola.BACKCOLOR = RGB(vcol1,vcol2,vcol3)
      Y = -1
      IF THISFORM.lblB2.WIDTH < THISFORM.barra.WIDTH
        THISFORM.lblB2.WIDTH = THISFORM.lblB2.WIDTH + 10
      ELSE
        MESSAGEBOX("Finalizado")
        THISFORM.RELEASE
      ENDIF
    ENDIF
    IF THISFORM.bola.TOP = THISFORM.barra.TOP + THISFORM.barra.HEIGHT ;
        AND THISFORM.bola.LEFT + THISFORM.bola.WIDTH >= THISFORM.barra.LEFT ;
        AND THISFORM.bola.LEFT <= THISFORM.barra.LEFT + THISFORM.barra.WIDTH
      THISFORM.bola.BACKCOLOR=RGB(vcol1,vcol2,vcol3)
      Y = 1
      IF THISFORM.lblB2.WIDTH >10
        THISFORM.lblB2.WIDTH = THISFORM.lblB2.WIDTH - 10
      ENDIF
    ENDIF
  ENDPROC
ENDDEFINE



Otto Pérez

9 de febrero de 2016

Desglose de billetes y monedas

Esta utilidad sirve para determinar la cantidad de billetes y monedas para el pago de salarios.

Partimos de dos tablas:

1. Una tabla con la denominación y el valor de los billetes y monedas disponibles. Esta tabla puede variar de acuerdo a los billetes y monedas disponibles en cada país o en el momento de efectuar el pago.

2. Una tabla con el listado de los nombres ó legajos y el importe a percibir.

Al principio del siguiente código se crean las dos tablas: "Cambio" con los valores disponibles, y "Sueldos" con los legajos e importes aleatorios.

Luego se recorre la tabla "Cambio" ordenada por el campo Valor en forma descendente (esto es premisa fundamental para el cálculo) y se crea una sentencia SELECT ... por cada valor, que añade una nueva columna por cada ciclo.
*-- Creo tabla de Billetes y Monedas disponibles
CREATE TABLE Cambio (Codigo C(8), Valor N(10,2))
INDEX ON Valor TAG Valor Descending
INSERT INTO Cambio (Codigo, Valor) VALUES ("B_100", 100)
INSERT INTO Cambio (Codigo, Valor) VALUES ("B_50", 50)
INSERT INTO Cambio (Codigo, Valor) VALUES ("B_20", 20)
INSERT INTO Cambio (Codigo, Valor) VALUES ("B_10", 10)
INSERT INTO Cambio (Codigo, Valor) VALUES ("B_5", 5)
INSERT INTO Cambio (Codigo, Valor) VALUES ("B_2", 2)
INSERT INTO Cambio (Codigo, Valor) VALUES ("M_1", 1)
INSERT INTO Cambio (Codigo, Valor) VALUES ("M_050", 0.50)
INSERT INTO Cambio (Codigo, Valor) VALUES ("M_025", 0.25)
INSERT INTO Cambio (Codigo, Valor) VALUES ("M_010", 0.10)
INSERT INTO Cambio (Codigo, Valor) VALUES ("M_005", 0.05)
INSERT INTO Cambio (Codigo, Valor) VALUES ("M_001", 0.01)

*-- Creo tabla de Sueldos (Salarios)
LOCAL ln
CREATE TABLE Sueldos (Legajo C(6), Importe N(12,2))
FOR ln = 1010 TO 1500 STEP 10
 INSERT INTO Sueldos (Legajo, Importe) VALUES (PADL(ln,6,"0"), RAND()*5000)
ENDFOR

*-- Begin
LOCAL lcSetPoint, llPrimeraVez, lcDeno, lcVal, lcSQL
 
lcSetPoint = SET("POINT")
llPrimeraVez = .T.
lcDeno = ""
lcVal = ""

*-- Creo cursor de resultados
SELECT Legajo, Importe FROM Sueldos INTO CURSOR Result

*-- Recorro tabla de Cambio
SET POINT TO "."
SELECT Cambio
SCAN ALL
  IF llPrimeraVez
    SELECT Legajo, Importe FROM Result INTO CURSOR Auxi
  ELSE
    lcSQL = [SELECT Result.Legajo, Auxi.Importe - ] + ;
      lcDeno + [*] + lcVal + [ AS Importe ] + ;
      [FROM Result ] + ;
      [INNER JOIN Auxi ON Result.Legajo = Auxi.Legajo ] + ;
      [INTO CURSOR Auxi]
    &lcSQL
  ENDIF
  lcSQL = [SELECT Result.*, ] + ;
    [INT(Auxi.Importe/] + ALLTRIM(STR(Cambio.Valor,10,2)) + ;
    [) AS ] + ALLTRIM(Cambio.Codigo) + ;
    [ FROM Result ] + ;
    [INNER JOIN Auxi ON Result.Legajo = Auxi.Legajo ] + ;
    [INTO CURSOR Result]
  &lcSQL
  lcDeno = ALLTRIM(Cambio.Codigo)
  lcVal = ALLTRIM(STR(Cambio.Valor,10,2))
  llPrimeraVez = .F.
ENDSCAN
SET POINT TO (lcSetPoint)

*-- Muestro cursor final de resultados
SELECT Result
BROWSE  

*-- End
Luis María Guayán

2 de febrero de 2016

Expresiones regulares en Visual FoxPro - Parte 2

Artículo original: Regular Expressions in Visual FoxPro Part 2
http://www.sweetpotatosoftware.com/SPSBlog/PermaLink,guid,01177828-56ba-4e93-8bfb-04b96c43ce7f.aspx
Autor: Craig Boyd
Traducido por: Ana María Bisbé York


Desde el comienzo

En una entrada de blog anterior (Una biblioteca de expresiones regulares para Visual FoxPro) desvelé una FLL nueva para Visual FoxPro que permitiría a los desarrolladores Visual FoxPro utilizar Expresiones regulares para encontrar correspondencias. Esta FLL, que es muy útil cuando se es competente en temas de expresiones regulares, fue sólo el comienzo de lo que es posible.

Ahora, que ya tengo la biblioteca básica que realiza parte de la funcionalidad de Boost Regex, puedo expanderla incluyendo funcionalidad adicional. Creo que encontrará útiles las mejoras que incluyen formatos y fraccionamientos.

Descarga de regexp.fll (61 KB aprox) haciendo clic aquí


Documentación de regexp.fll ...


Función REGEXP()

Sintaxis: RegExp(cString, cRegularExpression [, nFunctionType[, cFormatExpression | cArrayName]])

Parámetros:

cString - Cadena a ser buscada, formateada o cortada

cRegularExpression - Expresión regular con sintaxis Perl. Vea documentación adicional en http://perldoc.perl.org/perlre.html#Regular-Expressions

nFunctionType - Existen tres tipos de funciones disponibles. El valor de este parámetro determina la operación que realizará RegExp así como su valor de retorno.

Lista de las funciones actualmente soportadas:

  • 0 = Patrón de coincidencia - RegExp() devuelve .T. si se encuentra el patrón en cString que coincida con cRegularExpression, de lo contrario devuelve .F., lo que significa que no hay coincidencia. Este es el comportamiento predeterminado para RegExp() cuando solamente se pasan los dos primeros parámetros.
  • 1 = Formato - RegExp() devuelve una cadena de caracteres que se crea por la iteración entre cString buscando las coincidencias de cRegularExpression y aplicando cFormatExpression para hacer esas coincidencias.
  • 2 = Corte (Split) - RegExp() devuelve el número de cortes cuando se crea una matriz de los patrones que se crea en cString y coinciden con cRegularExpression. El nombre de la matriz se especifica en cArrayName.

cFormatExpression | cArrayName - Dependen del valor del parámetro nFunctionType

Valor devuelto:

Depende del valor del parámetro nFunctionType (vea arriba la documentación para el parámetro nFunctionType). RegExp() devolverá un valor lógico que indica, si existe coincidencia o no, si nFunctionType es 0. RegExp() devolverá una cadena de caracteres si nFunctionType es 1. RegExp() tras una exitosa creación de la matriz de cortes, devolverá la cantidad de elementos (números de cortes) si nFunctionType es 2.

Ejemplo de empleo: (corte y pegue en un PRG y ejecútelo desde Visual FoxPro)

*!* El ejemplo siguiente muestra cómo utilizar la función RegExp()
*!* para validar, dar formatos y dividir números de tarjetas de crédito
CLEAR 
*!* Informa a Visual FoxPro de la nueva función
*!* estableciendo la biblioteca para regexp.fll
SET LIBRARY TO LOCFILE("regexp.fll","FLL","Locate regexp.fll") 
LOCAL lcExpression
lcExpression = "\A(\d{3,4})[- ]?(\d{4})[- ]?(\d{4})[- ]?(\d{4})\z" 
? "Patrones de coincidencia:"
*!* Valida un número de tarjeta de crédito usando RegExp()
? RegExp("1234-5678-9101-1121",lcExpression) && Hay coincidencia
? RegExp("123-567-910-112", lcExpression) && No hay coincidencia
? 
? "Formatos:"
*!* Dar formato a números de tarjeta de crédito usando RegExp()
? RegExp("1234567891011121",lcExpression, 1, "\1-\2-\3-\4") && Hay coincidencia
? RegExp("12345678910111", lcExpression, 1, "\1-\2-\3-\4") && No hay coincidencia
? 
*!* Cortar los números de una CC utilizando RegExp()
? "Splits: " + TRANSFORM(RegExp("1234-5678-9101-1121",lcExpression, 2, "arySplits")) && Hay coincidencia
ShowSplits()
? "Splits: " + TRANSFORM(RegExp("1234-5678-9101-112", lcExpression, 2, "arySplits")) && No hay coincidencia
ShowSplits() 
*!* libera la referencia de la biblioteca
SET LIBRARY TO 
***************************
Procedure ShowSplits()
***************************
  LOCAL lnCounter
  FOR lnCounter = 1 TO ALEN(arySplits)
    ? CHR(9) + TRANSFORM(lnCounter) + ": " + arySplits(lnCounter)
  ENDFOR
  ?
ENDPROC