Cómo añadir gráficos a vuestra app



A continuación os voy a explicar cómo hacer la pantalla de la app “señales de tráfico de España” en la que se muestran en varios gráficos los resultados de los diferentes tests.
En el gráfico superior muestra el resultado de los test realizados en el día de hoy y en el gráfico inferior muestra el resultado de todos los test realizados desde que se instaló la aplicación.
Los valores de ambos gráficos se pueden resetear desde la pestaña "Ajustes"




DISEÑO

Como siempre empezamos añadiendo un toolbar (Align=Top) y varios speedbuttons

  Para volver a la pantalla anterior


Form.Close;
FormAnterior.Show;

   Para mostrar una imagen de ayuda

image2.Bitmap.loadfromfile(‘Imagen1.jpg’)

 Pone o quita las marcas numéricas asociadas a cada barra

  graficoHoy.Series[0].Marks.Visible := not graficoHoy.Series[0].Marks.Visible
  graficoTotal.Series[1].Marks.Visible := not graficoTotal.Series[1].Marks.Visible;


 Pone o quita la leyenda de los gráficos

  graficoHoy.Legend.Visible := not graficoHoy.Legend.Visible;
  graficoglobal.Legend.Visible := not graficoGlobal.Legend.Visible;


Después añadiremos un tTabControl con tres pestañas PROGRESO, GRÁFICOS y AJUSTES. Para añadir una pestaña hay que pulsar con el botón derecho sobre el tTabControl y activar el menú Add tTabitem.

Posteriormente seleccionaremos  dos componentes tLayout (Align=top y Align=client) y dos  tChart (Align=client) y los colocaremos sobre la pestaña Gráficos. Hay que comprobar que en la la vista de estructura quede de esta forma:


Los dos Layout se deben comportar como contenedores de los charts "GraficoGlobal" y "GraficoHoy", esto hay que hacerlo de esta forma para que todas las propiedades de alineamiento del chart  (excepto la propiedad contains) se apliquen sobre los Layout.


AÑADIENDO DATOS A LOS GRÁFICOS.

Haciendo doble clic sobre los tchart abrimos el diseñador de gráficos




Este componente nos permitirá modificar la forma en el que el gráfico se muestra al usuario. Podremos añadir nuevas series, cambiar el tipo de gráfico, colores, quitar y poner leyenda, márgenes, cambiar el fondo, añadir profundidad 3D, etc…

En la app he añadido 2 series, ya que quería mostrar el número de señales correctas e incorrectas de cada test:

- Series[0] llamada LineSeries1 que corresponde con el número de aciertos.
- Series[1] llamada Series1 que se corresponde con el número de errores.

Para añadir valores a la serie 0 asociada con el tchart ( Name=grafico)

grafico.Series[0].AddXY(contador, NumAciertos);

y para añadir un nuevo dato correspondiente a la serie 1

grafico.Series[1].AddXY(contador, NumErrores);

Recordar que siempre hay que inicializar los datos de las series (Yo lo hago en el evento OnShow del Form)

grafico.Series[0].Clear;

grafico.Series[1].Clear;

Es útil crear unos datos de prueba de la forma que os muestro a continuación para ver cómo queda:

for i:=1 to 10 do 
begin
grafico.Series[o].AddXY(i, random(10));
grafico.Series[1].AddXY(i, random(10));
end;


Cómo configurar los estilos en un tlistbox con rad studio


En este post os voy a explicar cómo se configuran los estilos en un listbox utilizando como ejemplo lo que he desarrollado para preparar la pantalla de la app “señales de tráfico de España”.
En esta pantalla se muestran al usuario los diferentes grupos de señales.




DISEÑO DEL FORM:

Como siempre es muy recomendable abrir el módulo Structure View de Rad Studio para ver cómo se organizan en el form los diferentes elementos que lo componen.
Seguidamente empezaremos a configurar la barra superior del form:

Para ello vamos al menú Tool Palette  y seleccionamos:

- un ttoolbar con la propiedad Align=Top
- tRectangle con las siguientes propiedades:
  Align=Client, 
  Fill-Gradient-Edit=#FFE0E0E0
  Fill-Kind=Gradient. 
- dos speedbutton (Uno con Align=Right y otro con Alig=Left)

Después tenemos que crear un objeto que indique qué grupos de señales se muestran en cada momento:


- tLayout (Align = Top) 
-tRectangle (Fill.Color=Blue, Fill.Kind=Solid, XRadius=6, YRadius=6, para poner los bordes redondeados)
- tlabel con el texto “GRUPOS DE SEÑALES DE TRÁFICO”

No olvidar que desde Structure View debemos dejar definido qué componentes dependen unos de otros, por ejemplo en el caso anterior hay que hacer que del tLayout (Contenedor principal) cuelgue un tRectangle, y que a su vez un tLabel cuelgue del tRectangle.

Después situaremos debajo del componente anterior un tListbox (Align=client)

Y por último seleccionaremos un tStylebook (StylebookCateg) para poder diseñar el estilo que queremos aplicar a cada uno de los componentes del ListBox.

Al final tiene que quedar de esta forma:


















DISEÑO DEL TLISTBOX:

Para diseñar cada uno de los ítems del Listbox, vamos a crear un estilo.
Hacemos doble clic sobre el componente StyleBookCateg, para abrir el Style Designer:
En la vista de Estructura tendremos que ir añadiendo componentes, de la misma forma que cuando añadimos componentes al form, para que nos quede del siguiente modo:





En la parte de diseño nos debería quedar así:





Ahora ya se puede ver más claro cómo se corresponde el estilo que hemos creado con cada uno de los ítems del listbox.



Tenemos:

- a la derecha un tspeedbutton:Speedbutton1Style con el símbolo de flecha (Align=Right)
- abajo un tText:Text4Style (Align=Bottom), 
- a la izquierda un tImage:ICON (Align=Left)
- en la parte central un tText:Text (Align=Client). 

Como veis al final siempre es lo mismo, hay que ir seleccionando componentes y jugando con el tipo de alineación para conseguir el efecto deseado.

Por último pulsamos sobre Style Designer con el botón derecho para cerrar la página y guardar cambios










PROGRAMACIÓN

Para diseñar cada uno de los ítems del tListbox:LBSENALES, procederemos a crear un elemento tListBoxItem

Para manejar los objetos que componen cada ítem, Delphi nos proporciona el evento OnApplyStyleLookup, en nuestro caso previamente he creado un array con las descripciones de las categorías de las señales (InfoCateg) para poderlas asociar al estilo “Text4Style”.

Var
Item: TListBoxItem;
Descripción:string;

begin
LBSenales.BeginUpdate;
Item := TListBoxItem.Create(NIL);
Item.Parent := LBSenales;
Item.StyleLookup := 'CustomItemCateg';
Item.Tag := NumSenal;
Item.StylesData['SpeedButton1Style.OnClick'] :=TValue.From(DoInfoClick2);
Item.text := descripcion;
Item.OnApplyStyleLookup := DoItemApplyStyle;
LBSenales.EndUpdate;
End;

PROCEDURE TForm.DoItemApplyStyle(Sender: TObject);
VAR
Item: TListBoxItem;
ItemKey: integer;
StyleObject: TFmxObject;
MiTexto: tText;
BEGIN

Assert(Sender IS TListBoxItem,
'Este handler solo controla ListboxItem');

IF NOT(Sender IS TListBoxItem) THEN
Exit;
Item := Sender AS TListBoxItem;
ItemKey := Item.Tag;

StyleObject := Item.FindStyleResource('Text4Style');
IF Assigned(StyleObject) AND (StyleObject IS tText) THEN
BEGIN
MiTexto := StyleObject AS tText;
BEGIN
MiTexto.text := InfoCateg[ItemKey];
END;
END;

END;



El procedimiento  DoInfoClick2 se disparará cuando el usuario pulse el botón “>”.



PROCEDURE TForm.DoInfoClick2(Sender: TObject);
VAR
Item: TListBoxItem;
BEGIN
Item := TListBoxItem(FindItemParent(Sender AS TFmxObject, TListBoxItem));
IF Assigned(Item) THEN
BEGIN
// Aquí escribiremos lo que queramos que suceda cuando el usuario pulse el botón “>”
// en mi caso cierro el form y abro uno nuevo donde se ven las señales de la categoría
// seleccionada.
END;

END;



Configurar los form para aplicaciones móviles multidispositivo


CÓMO CONFIGURAR LOS FORM PARA APLICACIONES MÓVILES MULTIDISPOSITIVO

Seguimos tratando el tema "cómo se hizo esta app", en esta ocasión desde el punto de vista del interface gráfico (Layouts, Margins, Padding, etc...)

Empezaremos por el Form de inicio de la app:



Lo primero que tenéis que hacer es mostrar la vista de Estructura, desde el menú View-Structure 
Después desde el selector de objetos añadiremos a nuestra aplicación:

-  un tToolBar y pondremos la propiedad Align=Top,

- un tRectangle con la propiedad 
Align=Client, 
Fill-Gradient-Edit=#FFE0E0E0
Fill-Kind=Gradient. Éste debe de estar "colgando" del tToolBar.

- un tLabel colgando del tRectangle, indicando Align=Client, Text='SEÑALES DE TRÁFICO', TextSettings-HorzAlign=Center
  Haremos clic con el botón derecho del ratón y en el menú emergente seleccionaremos el menú  Control-Send to Back.

La estructura nos tiene que quedar de la siguiente forma:

y el ToolBar así:

Seguidamente añadiremos un tLayout (Align=Client) y colgando de él un tImage (que será la imagen de fondo) Align=Client
En el tImage desde la propiedad MultiresBitmap seleccionaremos la imagen que estará en nuestro PC

y pulsando el botón 
lo añadiremos a nuestro fondo del Form.

Por último nos queda añadir los botones (R1 a R7) que deberán colgar del Layout añadido anteriormente, 
La vista de estructura quedaría aproximadamente de este modo:


En mi caso he añadido un Layout intermedio (Layout11) cambiando la propiedad Margins-Bottom, Top,Left, Right a 10, con el fin de que los botones no lleguen a tocar el borde de la pantalla, lo que queda más elegante.

Dentro del código del evento OnClic de los botones llamaría a cada unit del programa; por ejemplo para activar el Juego 1 habría que hacer:

  Form1.Hide;             // es el form actual
  FormJuego1.Show;   // es el Form del Juego

Otra forma más optimizada para ahorrar memoria podría ser:

  Application.CreateForm(TFormJuego1, FormJuego1);
  FormJuego1.Show;
  Form1.Close;

TRUCO: Desde el menú Project-View Source  añadir la línea

BEGIN
....
ReportMemoryLeaksOnShutdown:=true;

....
END;

Para que  Delphi os avise cuando cerréis el programa si ha encontrado memory leaks en vuestro código, por ejemplo aquí daría un aviso:

var
fich:tstringlist;
begin
fich:=tstringlist.create
fich.loadfromfile('prueba.txt');
end;

ya que faltaría hacer "fich.free;"













Procedimientos para manejar base de datos SQLite con FIREDAC

PROCEDIMIENTOS PARA EL MANEJO DE UNA BASE DE DATOS SQLITE CON FIREDAC REALIZADOS CON RAD STUDIO BERLÍN 10.1

Continuando con la explicación sobre cómo se hizo la app, hoy me toca hablar sobre diferentes procedimientos relacionados con la base de datos SQLITE.
Tenéis que tomar nota que con el driver DBExpress sería muy parecido salvo en lo referente a la encriptación.

Componentes que he usado:

SenalesConnection : TFDConnection
SQLQuery1: TFDQuery
FDTransaction1:TFDTransaction
FDPhysSQLiteDriverLink1: TFDPhysSQLiteDriverLink
FDSqliteBackup1: TFDSqliteBackup1
FDSQLiteValidate1:TFDSQLiteValidate1
FDSQLiteSecurity1:FDSQLiteSecurity1

Aquí tenéis las propiedades del objeto "SenalesConnection" que es donde se hace la conexión con la base de datos llamada "senales.db"


Conectando un objeto FDTransaction con el componente FDConnection os pemitirá utilizar transacciones en vuestras consultas, lo que redundará en una base de datos más estable ante bloqueos, cuelgues de la app o situaciones que puedan comprometer la integridad de la misma, además el utilizar transacciones mejora notablemente el rendimiento de la base de datos, yo he llegado a realizar consultas / inserciones, que sin utilizar transacciones tardaban 2 minutos y con transacciones eran sólo 3 segundos.


PROCEDIMIENTOS:

-Abrir una base de datos

PROCEDURE TForm1.AbrirBD;
var
 RutaBD, EstaEncriptada, PasswordBD,RutaBD: STRING;
BEGIN
  IF NOT SenalesConnection.connected THEN
  BEGIN
   PasswordBD := '123456'; // o lo que queráis
  RutaBD := 'c:\'+ 'senales.db';

  WITH SenalesConnection DO
  BEGIN
    Close;

    WITH Params DO
    BEGIN
      Clear;
      Add('DriverID=SQLite');
      Add('Database=' + RutaBD);
      Add('SharedCache=True');
      Add('LockingMode=Normal');

      WITH FDSQLiteSecurity1 DO
      BEGIN
        Database := RutaBD;
        password := PasswordBD;
      END;

      EstaEncriptada := FDSQLiteSecurity1.CheckEncryption;
      IF EstaEncriptada <> 'unencrypted' THEN
      BEGIN
        Add('Password=' + PasswordBD);
      END
      ELSE
      BEGIN
        Add('Password=');
      END;
    END;
  END;
    TRY
      // Establece la conexion
      SenalesConnection.connected := true;
    EXCEPT
      ON e: EDatabaseError DO
        showmessage('Exception raised with message: ' + e.Message);
    END;

    IF NOT SenalesConnection.connected THEN
      showmessage('no se ha activado la BD');

    WITH SQLQuery1 DO
    BEGIN
      Close;
      prepared := false;
      sql.Clear;
      Params.Clear;
    END;
  END;
END;

-EJECUTAR QUERY DE TIPO " INSERT, UPDATE, DELETE, CREATE TABLE"
 
 IF NOT SenalesConnection.connected THEN
    SenalesConnection.connected := true;
  SQLQuery1.Close;
  SQLQuery1.prepared := false;
  SQLQuery1.sql.Clear;
  SQLQuery1.Params.Clear;
  SQLQuery1.sql.Add(textoSQL);

  SenalesConnection.StartTransaction;
  TRY
    SQLQuery1.ExecSQL;
   
    SenalesConnection.commit;
  EXCEPT
    ON e: Exception DO
    BEGIN
      SenalesConnection.rollback;
      SQLQuery1.active := false;
      showmessage('error en Exec SQL: ' + e.Message);
    END;
  END;

-EJECUTAR QUERY CON UN SELECT


IF NOT SenalesConnection.connected THEN
    SenalesConnection.connected := true;

  SQLQuery1.Close;
  SQLQuery1.prepared := false;
  SQLQuery1.sql.Clear;
  SQLQuery1.Params.Clear;
  SQLQuery1.sql.Add(textoSQL);

  SenalesConnection.StartTransaction;
  TRY
    SQLQuery1.active := true; 
    SenalesConnection.commit;
  EXCEPT
    ON e: Exception DO
    BEGIN
      SenalesConnection.rollback;
      SQLQuery1.active := false;
      showmessage('Error en Query: ' + e.Message + '. Texto sql: ' +
        textoSQL);
    END;
  END;

  IF SQLQuery1.active THEN
    SQLQuery1.first;

- ENCRIPTAR UNA BASE DE DATOS SQLITE

 
VAR
  EstaEncriptada: STRING;

BEGIN
 SenalesConnection.connected := False;
  EstaEncriptada := FDSQLiteSecurity1.CheckEncryption;
  IF EstaEncriptada = 'unencrypted' THEN
  BEGIN
   FDSQLiteSecurity1.password := 'aes-512':passwordBD;
    FDSQLiteSecurity1.SetPassword;
  END;
END;

- DESENCRIPTAR UNA BASE DE DATOS SQLITE

  SenalesConnection.connected := False;
  EstaEncriptada := FDSQLiteSecurity1.CheckEncryption;
  IF EstaEncriptada <> 'unencrypted' THEN
  BEGIN

    FDSQLiteSecurity1.Database := 'c:\senales.db';
   FDSQLiteSecurity1.password :='123456';
   FDSQLiteSecurity1.RemovePassword;
   SenalesConnection.Params.password := '';
  END;

- CAMBIAR CLAVE DE UNA BASE DE DATOS SQLITE


  SenalesConnection.connected := False;
  FDSQLiteSecurity1.Database :='c:\senales.db';
  FDSQLiteSecurity1.password := tipoEncrypt + ':' + passwordBD;;
  FDSQLiteSecurity1.ToPassword :='aes-512:625252';
  FDSQLiteSecurity1.ChangePassword;

- VER ESTADO DE UNA BD SQLITE


  SenalesConnection.connected := False;
  FDSQLiteSecurity1.Database :='c:\senales.db';
  FDSQLiteSecurity1.password := '123456';
  LbEstado.Text := FDSQLiteSecurity1.CheckEncryption;

- OPTIMIZAR UNA BD SQLITE


  FDSQLiteValidate1.Database  :='c:\senales.db';
  FDSQLiteValidate1.Sweep;




Nueva app " Señales de tráfico de España " (I)



Os comunico que tenéis a vuestra disposición la app "señales de tráfico de España" realizada con Rad Studio Berlín 10.1. 
En la columna de la derecha tenéis el link por si la queréis descargar de la Play Store y probarla gratuitamente en vuestro móvil Android.

He tenido que solucionar muchos problemas de programación, visitando páginas web, acudiendo a la ayuda de Rad Studio, docwiki de Embarcadero, probando una y otra solución hasta que por fin lo he conseguido.
Me gustaría aportar algo a la comunidad publicando cómo lo he hecho, por si alguien se quiere animar a hacer algo parecido, así que en sucesivos posts lo iré haciendo por lo que os animo a visitar regularmente este blog.
Realmente una vez que se sabe cómo se hacen las cosas no es complicado y con Delphi por lo que he visto es INFINITAMENTE más simple e intuitivo que con otros lenguajes.

BASE DE DATOS QUE UTILIZA:
He utilizado las "señales de tráfico" porque quería  utilizar imágenes de dominio público y como base de datos SQLite, intenté utilizar Firebird (que funciona en Windows perfectamente), pero al llevarla al móvil no funcionaba (si alguien sabe cómo se hace, por favor que nos lo diga).
He utilizado el driver FireDAC en vez de DBExpress ya que quería encriptar la Base de datos y con DBExpress no me lo permitía.
FireDAC me daba la sensación que era más rápido y más estable, además por lo que he visto en internet, DBExpress será descatalogado en próximas versiones de Delphi.

PERMISOS
Sólo utiliza el "permiso de lectura y escritura en el almacenamiento externo" ya que necesito almacenar los parámetros de la interfaz que el usuario podría cambiar en cualquier momento.
Si se quiere poner publicidad habría que activar también el permiso "internet", que se hace desde el menú Project-Options-User Permissions.


ORIENTACIÓN
He seleccionado la opción "Portrait" desde el menú Project-Options-Application-Orientation






Aquí tenéis una página en la que estamos colaborando los MVP de Embarcadero con 36 video-tutoriales sobre temas relacionados con RAD-Studio que tratan sobre la configuración de Rad Studio para el desarrollo de aplicaciones web, utilización de Firemonkey, FireUI, LiveBindings, etc.


https://www.youtube.com/playlist?list=PLwUPJvR9mZHg6qx6v6uWn5duWEuK-2KNX



Seguiremos comentando...