Buenos días,
Como todos los años, ya tenemos preparadas nuestras ofertas de Black Friday y Cyber Monday. Ofrecemos un 20% de descuento no acumulable con otras ofertas en todos nuestros productos. No dejéis de pasar esta oportunidad.
Un saludo
Buenos días,
Como todos los años, ya tenemos preparadas nuestras ofertas de Black Friday y Cyber Monday. Ofrecemos un 20% de descuento no acumulable con otras ofertas en todos nuestros productos. No dejéis de pasar esta oportunidad.
Un saludo
En los próximos artículos vamos a repasar todos los nuevos controles que incluye el futuro Xailer 7. Existen nuevos controles que son simplemente versiones más vistosas de controles ya existentes, pero adaptados al estilo de Windows. Motivo por el cual todos estos nuevos controles se califican como ‘modernos’ y tiene una pestaña propia en el propio IDE. Sería el caso de los controles:
Además de estos controles se han incorporado algunos nuevos que o son completamente nuevos o por su gran mejora, merece la pena dedicarles un artículo a cada uno de ellos, como son:
Todos estos nuevos controles que son susceptibles de tener su equivalente como DataControl, también han sido creados para el nuevo Xailer 7. Por lo tanto y como pueden imaginar, Xailer 7 es sin duda el mayor esfuerzo que se ha realizado en una actualización de Xailer desde Xailer 2.
Otra característica fundamental de los nuevos controles es que admiten el uso de etiquetas básicas tipo HTML en las propiedades de tipo cadena, como por ejemplo la propiedad cText del TButtonMod.
Comenzamos con el control TBtnPanelMod. Este nuevo control de Xailer 7 ofrece una gran funcionalidad que consiste en poder establecer que un contenedor de controles se comporte como un botón en su conjunto.
Esto nos permite crear botones con una mayor funcionalidad que la que teníamos hasta ahora. Seguro que ya habrá descubierto que son ampliamente utilizados en Windows 10.
Espero que les guste. Pronto más controles. Estén atentos. Gracias.
Xailer 7 incorpora controles para visualizar e imprimir cualquier archivo PDF de una forma muy sencilla, sin limitaciones, con una increíble velocidad y sin que nuestra aplicación vaya a crecer demasiado por ello. Para conseguirlo Xailer 7 se apoya en la utilidad Sumatra PDF.
Sumatra PDF reader es una estupenda herramienta para visualizar e imprimir archivos PDF. Es muy rápida, liviana y su integración con Xailer es total, ya que se comporta como un control más dentro de su aplicación, que puede fácilmente seleccionar desde la propia paleta de controles del IDE.
Para poder utilizar el control es necesario que su aplicación pueda acceder a un único fichero de nombre SUMATRAPDF.EXE. Con Xailer 7 ofrecemos dos versiones de dicho ejecutable. La versión 3.1 por tener un tamaño ridículo de 2.721 Kb que es posible que sea suficiente para los archivos que usted desea mostrar. Y la la versión 3.2 que tiene un tamaño de 14.017 Kb que tampoco es muy grande teniendo en cuenta todo lo que hace.
Sumatra PDF viewer soporta los siguientes tipos de archivos: PDF, EPUB, MOBI, CHM, XPS, DjVu, CBZ y CBR. Y las siguientes extensiones:
Puede obtener más información sobre Sumatra en esta dirección: https://www.sumatrapdfreader.org
La licencia con la que se ofrece este producto es: GNU General Public License v3, que en principio es compatible con el uso comercial del producto siempre que sigan todas las restricciones que impone la licencia. No obstante, el equipo de Xailer se puso en contacto con el desarrollador Krzysztof Kowalczyk para confirmarlo. Puede seguir la pregunta y respuesta en el siguiente enlace en su foro.
Os sigo comentando las mejoras importantes que incluirá el futuro Xailer 7. Como muchos sabéis, cuando se utilizan DataControls, Xailer es capaz de acceder a los distintas campos de una tabla o cursor como si se trataran de miembros de la propia clase TDataset. Un ejemplo:
WITH OBJECT oDataset
:Edit()
:Codigo := 1 // campo
:Nombre := "Ignacio" // campo
:Update()
END WITH
Cuando el nombre del campo coincide con un miembro real del propio dataset, el acceso al campo no es posible, ya que tiene preferencia el miembro de la clase TDataset. Para resolver este problema, hasta ahora se podía utilizar el método TDataset:FieldPut() directamente o bien recuperar el objeto TDataField con el método oFieldByName() y luego asignarle el valor.
A partir de Xailer 7 tenemos una solución más elegante para acceder a cualquier campo del TDataset aunque coincida su nombre con algún miembro de la clase y consiste en utilizar la siguiente instrucción: TDataset:!Campo
Observe como la única diferencia existente es la admiración después de ‘:’. Además, de esta forma, usted mismo como programador, sabrá si está accediendo a un campo o a un miembro de la clase, lo que hará que su código sea mucho más legible.
Otra mejora importante: En Xailer 7 hemos hecho un pequeño guiño a las bases de datos NoSQL. En las bases de datos NOSQL los esquemas de datos son dinámicos, es decir, no existe una estructura fija de tabla y la consistencia de los datos es menos importante. En Xailer 7 hemos querido incluir alguna de sus cualidades y ahora cualquier dataset, incluso una tabla DBF puede tener la habilidad de guardar en su tabla cualquier campo, aunque éste no exista ni siquiera en la tabla. Sólo es necesario la existencia de un campo en la base de datos de nombre MoreData, que lógicamente deberá de ser tipo BLOB o MEMO.
Para acceder o establecer el valor de éste campo virtual tan sólo que hay que usar la sintaxis: oDataset:!Campo. Es decir, hay que incluir el carácter ‘!’ del que hemos hablado anteriormente. ¡Eso es todo! Internamente toda la información se guarda con formato JSON dentro de ese campo y por lo tanto se pueden realizar búsquedas en el campo ‘MoreData’ e incluso editarlo desde su editor de bases de datos preferido. Nuestro editor de tablas SQLite ya soporte el tipo de campo JSON, por lo que en dicho caso, lo preferible es definir el campo con ese tipo.
Las posibilidades de este nuevo sistema son múltiples. A modo de ejemplo:
Una de las grandes ventajas de este nuevo sistema, es que puede guardar en la tabla cualquier tipo de dato, incluso matrices y objetos Hash.
No obstante, existen limitaciones, que son insalvables, como son:
Las animaciones son clases que permiten establecer distintos tipos de animaciones en cualquier control visual. Su funcionamiento se basa en utilizar Futuros para de esta forma no provocar la congelación de la aplicación durante todo el tiempo que dure la animación. Para más información acerca de los futuros, consulte el artículo donde se trata.
Demo de animaciones en el canal de Xailer en YouTube
La clase base de todas las animaciones es TAnimation la cual ofrece toda funcionalidad necesaria para realizar cualquier tipo de animación. Existen dos clases que heredan de TAnimation que están especializadas en realizar la animación de una forma muy sencilla:
TAniNumProperty: Esta clase permite establecer la animación en base a una de las propiedades del control.
En este ejemplo puede entender perfectamente su comportamiento. El objeto TAniNumProperty controla la propiedad ‘nWidth‘ del control ‘oEdit1‘ y va modificar dicha propiedad desde su valor actual (lStartFromCurrent a verdadero) hasta un valor de 300 (nStopValue) y lo hará en un periodo de 1 segundo (nDuration). Todo el proceso comenzará en cuanto la propiedad lEnabled se encuentre a verdadero o ejecute su método Start(). Es así de fácil establecer una animación en cualquier control.
TAniControlSize: Esta clase permite establecer la animación en base a las dimensiones del control. Es decir, de su tamaño marcado por sus propiedades: nLeft, nTop, nWidth y nHeight. Su uso se restringe básicamente a modificar el tamaño de cualquier control desde una posición y tamaño inicial a una posición y tamaño final.
En este ejemplo puede entender perfectamente su comportamiento. El objeto TAniControlSize controla las dimensiones del control ‘oMemo1‘ y va modificar dichas dimensiones desde su valor actual (lStartFromCurrent a verdadero) hasta un valor de {96,48,300,200} (aStopValues) y lo hará en un periodo de 1 segundo (nDuration). Todo el proceso comenzará en cuanto la propiedad lEnabled se encuentre a verdadero o ejecute su método Start().
Ambas clases tienen además una serie de propiedades muy interesantes, que son:
Espero que estas animaciones sean de su agrado. Estarán disponibles en Xailer 7.
La cláusula FOR EACH que Harbour incorporó al lenguaje CA-Clipper fue una gran mejora, pero hay mucha gente que sólo lo utiliza de su forma más básica:
FOR EACH value IN aValues
? value
NEXT
Sin embargo ofrece multitud de posibilidades. Vamos a enumerar unas cuantas:
FOR EACH a, b, c IN aVal, cVal, hVal
? a, b, c
NEXT
FOR EACH a IN aVal DESCEND
? a
NEXT
s := "abcdefghijk"
FOR EACH c IN @s
IF c $ "aei"
c := Upper( c )
ENDIF
NEXT
? s // AbcdEfghIjk
FOR EACH a IN aVal
? a:__enumIndex()
NEX
Harbour incluso permite modificar como se debe de recorrer la iteración utilizando los métodos :__enumStart(), :__enumSkip() y :__EnumStop(). Tenéis un ejemplo de uso en el fichero \harbour\tests\foreach2.prg.
Espero que os haya sido de utilidad.
Un saludo
Sin duda, los futuros o promesas son la funcionalidad más importante que incorporamos en el futuro Xailer 7 e incluso al propio lenguaje de programación Harbour. Los futuros o promesas son ampliamente conocidos por programadores de otros lenguajes más modernos y para aquellos que aún no los conozcáis, creo que la mejor introducción para ellos es decir que son la simplificación completa de la programación multi-hilo.
Según la Wikipedia, un valor futuro (también llamado un futuro o una promesa) es «Un reemplazo para un resultado que todavía no está disponible, generalmente debido a que su cómputo todavía no ha terminado, o su transferencia por la red no se ha completado.» Puede parecer una definición un poco críptica, pero indica claramente su funcionalidad aunque no hace mención a cómo funcionan ni en que se basan. La funcionalidad de los futuros o promesas está totalmente ligada a la programación multi-hilo. La programación multi-hilo puede llegar a ser tremendamente compleja debido a la facilidad existente de cometer errores de programación que colapsen nuestras aplicaciones o las hagan actuar de forma inesperada.
Un futuro o promesa está estrechamente ligado con el concepto de asíncrono. Es decir, un código que sabemos cuándo lo ejecutamos, pero que no sabemos cuando retornará con un resultado. Pensemos un una función estándar; ésta siempre devuelve un resultado cuando se termina de ejecutar el código que tiene en su interior. Si dicha función tiene que acceder a Internet o realizar un trabajo pesado, nuestra aplicación se quedará completamente paralizada hasta que dicho código termine. Para evitar este problema, en clásica programación multi-hilo crearíamos un proceso asíncrono a través de un hilo:
TThread():Run( bCode )
Donde bCode, es por ejemplo, un bloque de código que se ejecutará cuando se inicie el hilo. Independientemente del tiempo de proceso que necesite dicho código, el retorno desde la ejecución de esa línea será inmediato. Por lo tanto, será responsabilidad suya el crear mecanismos para saber cuando ese código ha terminado y si lo ha hecho correctamente o no. El asunto se complica cuando desde dicho código que se ejecuta en un segundo hilo queremos acceder a zonas de memoria que no han sido creadas en ese mismo hilo o incluso se pretende acceder a la pantalla en el más amplio sentido. La primera regla de oro a tener en cuenta es que desde un hilo secundario nunca se debe acceder a la pantalla y esto es así para cualquier entorno de desarrollo. No es una limitación de Xailer, Harbour o Windows. Aunque le parezca que funciona, olvídese, es un auténtico espejismo, su aplicación terminará con un GPF esporádico más pronto que tarde. La segunda regla de oro es evitar acceder a áreas de memoria creadas por el hilo principal u otros hilos y si se hace, establecer los mecanismos necesarios para que todos los hilos involucrados puedan acceder a esas mismas zonas de memoria de forma ordenada y sin bloquearse. En definitiva, demasiadas complicaciones que impiden que la programación multi-hilo sea ampliamente utilizada.
Los futuros o promesas nos simplifican tremendamente estos inconvenientes ya que tienen mecanismos para solucionar esos dos problemas, que son:
Xailer 7 incorpora varias formas de implementar los futuros:
A través de cláusula ASYNC:
Sólo tiene que añadir la cláusula ASYNC en la definición de método o función. Algo así:
METHOD Btn1Click( oSender ) CLASS TForm1 ASYNC
El código que se ejecutará en el futuro debe de ir incluido en comandos AWAIT:
AWAIT INLINE {||
FOR nFor := 1 TO 100
Sleep(10)
RETURN "Exit from first task"NEXT
}
Puede haber más de una sentencia AWAIT en una misma función o método. Cuando termine la primera sentencia AWAIT de forma asíncrona, se procederá con la ejecución del código del siguiente AWAIT. Esto le permite encadenar procesos asíncronos de una forma tremendamente sencilla.
FUNCTION MyTest(...) ASYNC
AWAIT INLINE {||....}
AWAIT INLINE {||....}
AWAIT INLINE {||....}
Observe que es posible que cada sentencia AWAIT devuelva un valor y además también es posible saber si el AWAIT terminado ha sido con éxito o no. La variable privada de nombre LastAwait recoge la tarea (TFutureTask) que se acaba de terminar de procesar. Y por lo tanto desde la siguiente cláusula AWAIT puede comprobar si la tarea terminó con éxito y el valor retornado:
LastAwait:nState (uncompleted, completed with value, completed with error)
LastAwait:ReturnValue
AWAIT admite la definición del código como un bloque de código extendido utilizando la expresión AWAIT INLINE. Pero también puede llamar directamente a una función con la siguiente sintaxis:
AWAIT FUNCTION MiFuncion( … )
Para controlar como y cuando ha terminado el proceso asíncrono puede crear un AWAIT INLINE adicional sólo para controlarlo o simplemente utilizar el evento TFuture:OnComplete. Si es un poco observador se habrá dado cuenta de que aparentemente no existe ninguna variable que haga referencia a ese objeto TFuture que se menciona. Xailer crea esa variable por usted con ámbito local cuando utiliza la cláusula ASYNC con el nombre ThisFuture. De hecho Xailer crea un objeto TFuture de forma automática en cada función o método con la cláusula ASYNC y lo asigna a dicha variable. Por lo tanto, controlar el fin del proceso asíncrono sería tan sencillo como hacer algo así:
ThisFuture:OnComplete := {|| Msginfo( LastAwait:ReturnValue ) }
Ya hemos visto como crear y ejecutar procesos asíncronos, que incluso se ejecuten con varias tareas en cascada, pero aún no hemos visto nada de como ejecutar código en el hilo principal desde la tarea asíncrona, como por ejemplo cualquier operación de pantalla, que ya hemos indicado que no se pueden hacer desde tareas asíncronas. Para ello Xailer ofrece otro comando de uso muy sencillo y de parecida sintaxis al comando AWAIT, que es el comando SYNCHRO. Este comando se debe de utilizar únicamente desde bloques AWAIT INLINE o desde funciones AWAIT FUNCTION. Por ejemplo:
AWAIT INLINE {||
FOR nFor := 1 TO 100
Sleep(10)
SYNCHRO INLINE {|| ::oProgressBar:nValue := nFor }
RETURN "Exit from first task"NEXT
}
Observe como actualizamos una barra de progreso desde la propia tarea asíncrona. Más fácil imposible. Al igual que el comando AWAIT que puede utilizar una función con AWAIT FUNCTION, el comando SYNCHRO también admite la sintaxis SYNCHRO FUNCTION en la cual puedo desarrollar toda la complejidad que pudiera tener el código.
A través de programación orientada a objetos:
Este es el método recomendado si tiene experiencia con Xailer o la programación orientada a objetos que ofrece Harbour. Observará que básicamente es lo mismo que hemos explicado hasta ahora, pero sin tener que usar un sólo comando. El primer paso es crear un objeto de la clase TFuture (operación que realiza internamente la cláusula ASYNC):
LOCAL oFuture AS CLASS TFuture
oFuture := TFuture():New()
A continuación hay que añadir las tareas para dicho futuro. Y lo haremos con el método AddThreadTask( bCode ) que recibe como parámetro un bloque de código a ejecutar en dicha tarea:
LOCAL oTask AS CLASS TFutureTask
LOCAL bWorkbWork := { ||
FOR nFor := 1 TO 100
Sleep(30)
NEXT
RETURN "Exit from first task"
}oTask := oFuture:AddThreadTask( bWork )
Para poder ejecutar código en el hilo principal desde la tarea asíncrona, utilizaremos el método RunSynchroTask( bCode ) y lógicamente las llamadas a este método deben de realizarse desde la propia tarea:
LOCAL oFuture AS CLASS TFuture
LOCAL oTask AS CLASS TFutureTask
LOCAL bWorkbWork := { ||
FOR nFor := 1 TO 100
Sleep(30)
oFuture:RunSynchroTask( {||::oProgressBar:nValue := nFor } )
NEXT
RETURN "Exit from first task"
}oTask := oFuture:AddThreadTask( bWork )
Para controlar el resultado final del proceso:
oFuture:OnComplete := {|| .... }
Y por último ejecutar el proceso asíncrono:
oTask := oFuture:AddThreadTask( bWork )
Una puntualización importante a realizar es el hecho de que los procesos asíncronos se disparan inmediatamente cuando se crea la tarea. Es decir, AWAIT INLINE y su equivalente en POO TFuture:AddThreadTask( bCode ) no esperan. Ellos son los responsables de lanzar el hilo de ejecución secundario. Si existen varios AWAIT INLINE, cuando se ejecutan, se añaden a la lista de tareas a procesar por el Futuro. Si no hay ninguna tarea pendiente, el primer AWAIT INLINE se procesará de inmediato. Cuando éste termine, se ejecutará el siguiente.
Todo se puede complicar un poquito más, pero no mucho 😉 Es probable que en una tarea se produzca un error de ejecución. ¿Es posible controlarlo? ¡Lo es! y de una forma muy sencilla. El objeto ThisFuture tiene un evento de nombre OnError que recibe como parámetros el objeto TError y la TFutureTask. Si desde dicho evento retornamos un valor lógico verdadero, la siguiente tarea del futuro se ejecutará como si nada hubiese pasado, en caso contrario, se producirá un error de ejecución.
Cuando se publique Xailer 7 le recomendamos que eche un vistazo a las clases TFuture y TFutureTask. Observará que tienen muy pocas propiedades y métodos y que su uso es tremendamente sencillo. Espero que así se lo parezca. Por último comentar, que los futuros estarán disponible en todas las versiones de Xailer, incluso la personal.
Estimados usuarios de Xailer y Xbase en general,
Aprovechando estas fechas navideñas, hemos decidido ofrecer nuestra versión personal de Xailer completamente gratis y por tiempo ilimitado. Os recordamos que esta versión es completamente funcional e incluye todo lo necesario para realizar aplicaciones de gestión robustas utilizando cualquier RDD existente. Toda la potencia del IDE de Xailer, como son su rápido depurador, el soporte de intellisense y su facilidad en la creación de ejecutables, están disponibles para el programador.
Esperamos que esta oferta tenga una gran acogida y anime a muchos usuarios de [x]Harbour, Clipper y Visual FoxPro a probar nuestro entorno de desarrollo integrado para xBase. Estamos seguros que no defraudará. Os agradecemos de antemano la difusión que podáis hacer de esta noticia.
Un cordial saludo y ¡Felices Navidades!
Estimados usuarios de Xailer,
Hoy publicamos una nueva versión de Xailer, la versión 6.1.0.
En esta nueva versión hemos actualizado la herramienta a las últimas versiones de todas las herramientas externas que usamos como pueden ser compiladores, editores de código o bases de datos. Se ha adaptado Xailer a las las últimas versiones de:
Y se han añadido grandes mejoras, como son:
Para una lista completa de todas las mejoras puede consultar el siguiente enlace:
https://www.xailer.com/?lonuevo
Esta nueva versión de Xailer arranca directamente desde la versión 6.1 debido a que la versión 6.0 sólo ha estado disponible para beta-testers y durante un tiempo considerable. Posiblemente, esta nueva versión de Xailer es la que más haya sido probada antes de su publicación definitiva debido a la actualización realizada en todos las herramientas en las que se apoya nuestro producto. Esperamos que los posibles errores que se puedan encontrar sean mínimos. Y en cualquier caso serán corregidos rápidamente.
La nueva versión de Xailer y Harbour las puede encontrar en los siguientes enlaces:
http://www2.xailer.com/download/?es&file=1
http://www2.xailer.com/download/?es&file=2
Es necesario que reconstruya completamente todos sus proyectos, incluido cualquier librería estática o dinámica que esté utilizando.
Un cordial saludo,
[El equipo de Xailer]
Estimados usuarios de Xailer,
Hoy publicamos una nueva actualización de Xailer, la versión 5.1.0 que básicamente corrige todos los errores encontrados hasta la fecha y se prepara internamente para el salto inminente a Xailer 6 que incorporará la última versión de Harbour, MinGW, SQLite, MySQL, MariaDB y Scintilla. Más información en los siguientes enlaces:
https://www.xailer.com/?lonuevo
http://www2.xailer.com/download/?es&file=1
Un cordial saludo
[El equipo de Xailer]