Mostrando entradas con la etiqueta TSringList. Mostrar todas las entradas
Mostrando entradas con la etiqueta TSringList. Mostrar todas las entradas

viernes, 31 de enero de 2020

Crear un formulario en tiempo de ejecución.

Muchas veces cuando programamos queremos ver los datos de las variables, ya sean arrays, stringlist o lo que sea, y nos valemos de uno o más TMemo para mostrar allí los datos. También para mostrar al usuario los logs o alguna otra información. Hay muchas formas de hacer esto, una de ellas creando un TForm con un Tmemo, de esta manera tendremos en la unidad más de un formulario, claro que solo uno será el creado por Lazarus con su correspondiente archivo .lfm.

Para el caso agregar en el Form 3 TButtons, solo eso.

Veamos el código.

unit Unit1;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls;

type

  { TForm1 }

  TForm1 = class(TForm)
  Button1: TButton;
  Button2: TButton;
  Button3: TButton;
  procedure Button1Click(Sender: TObject);
  procedure Button2Click(Sender: TObject);
  procedure Button3Click(Sender: TObject);
  procedure FormClose(Sender: TObject; var CloseAction: TCloseAction);
  procedure FormCreate(Sender: TObject);
  private
  sl1:TStringList;
  sl2:TStringList;
  sl3:TStringList;
  procedure VerLista(sl:TStringList);
public

end;

var
  Form1: TForm1;

implementation

{$R *.lfm}

{ TForm1 }

procedure TForm1.FormCreate(Sender: TObject);
var
  i:Integer;
begin
  sl1:=TStringList.Create;
  sl2:=TStringList.Create;
  sl3:=TStringList.Create;
  for i:=0 to 99 do
  begin
    sl1.Add(IntToStr(i));
    sl2.Add(IntToStr(i*10));
    sl3.Add(IntToStr(i*100));
   end;
end;

procedure TForm1.VerLista(sl: TStringList);
var
  F:TForm;
  mm:TMemo;
begin
  F:=TForm.Create(nil);
  F.Height:=380;
  F.Width:=500;
  F.Position:=poMainFormCenter;
  mm:=TMemo.Create(nil);
  mm.Text:=sl.Text;
  mm.Parent:=F;
  mm.Align:=alClient;
  mm.ScrollBars:=ssAutoBoth;
  F.ShowModal;
  FreeAndNil(mm);
  FreeAndNil(F);
end;

procedure TForm1.FormClose(Sender: TObject; var CloseAction: TCloseAction);
begin
  CloseAction:=caFree;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  VerLista(sl1);
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
  VerLista(sl2);
end;

procedure TForm1.Button3Click(Sender: TObject);
begin
  VerLista(sl3);
end;

end.


Lo más importante es esto
mm.Parent:=F;
que establece el parentesco, sitúa el TMemo dentro del formulario F. El resto es simplemente asignar valores a las propiedades tanto del formulario como del memo.

viernes, 8 de noviembre de 2019

Obtener los archivos de una carpeta con GetFilesInDir

GetFilesInDir es un procedimiento de clase que la clase TShellTreeView hereda de la clase TCustomShellTreeView.
TShellTreeView es un componente que se encuentra en la paleta Misc de Lazarus y que ha quedado estancado por el tema de la compatibilidad con versiones anteriores o retrocompatibilidad. No obstante es muy útil y lo veremos más en profundidad en otra entrada.
Vale esta aclaración, porque el procedimiento en cuestión, tiene una fuga de memoria que no se corrige ni se corregirá, según lo leído en un hilo del foro oficial de Lazarus y Free Pascal, justamente para mantener la compatibilidad, es decir, para solucionarlo habría que hacer cambios que afectarían la compatibilidad con versiones anteriores, es decir, con programas viejos o no tanto, que necesitan compilarse con la versión más reciente de FPC.
Lo bueno es que con una línea de código se soluciona el problemita del memory leaks.

Otra forma de obtener los archivos de un directorio es mediante FindFirst, FindNext y FindClose.

Este sencillo ejemplo consta de tres componentes, un TShellTreeView, un TMemo y un TBitBtn.
unit Unit1;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, ShellCtrls,
  StdCtrls, Buttons;

type

{ TForm1 }

TForm1 = class(TForm)
  BitBtn1: TBitBtn;
  Memo1: TMemo;
  ShellTreeView1: TShellTreeView;
  procedure BitBtn1Click(Sender: TObject);
private

public

end;

var
Form1: TForm1;

implementation

{$R *.lfm}

{ TForm1 }

procedure TForm1.BitBtn1Click(Sender: TObject);
var
  sl:TStringList;
begin
  Memo1.Clear;
  sl:=TStringList.Create;
  sl.OwnsObjects:=True; //evita memory leaks
  ShellTreeView1.GetFilesInDir(ShellTreeView1.Path,'*.*',[otNonFolders],sl,fstAlphabet);
  Memo1.Text:=sl.Text;
  FreeAndNil(sl);
end;

end.


Veamos el código fuente del procedimiento:

class procedure TCustomShellTreeView.GetFilesInDir(const ABaseDir: string;
AMask: string; AObjectTypes: TObjectTypes; AResult: TStrings; AFileSortType: TFileSortType);


Como vemos le pasamos varios parámetros, el primero el directorio en el cual debe buscar archivos, luego qué archivos (en este ejemplo simplemente *.* para todos los archivos), AObjectTypes es un conjunto de enumerados:

TObjectType = (otFolders, otNonFolders, otHidden);

TObjectTypes = set of TObjectType;


en este caso pedimos solo archivos que no sean carpetas, por lo tanto, archivos ocultos y carpetas no formarán parte del resultado. Nota: en Linux todo son archivos, también las carpetas.
El cuarto parámetro AResult: ahí le pasamos el TStringList, al cual antes de mandarlo, le establecimos la propiedad OwnObjects en True para el tema de evitar la fuga de memoria.
El último parámetro es un enumerado:

TFileSortType = (fstNone, fstAlphabet, fstFoldersFirst);

y se utiliza para indicar el orden de los archivos.
Para finalizar, se libera el TStringList.

martes, 18 de junio de 2019

Número a texto con formato.

Pasar de número a string utilizando la función Format y el registro TFormatSettings. También se puede usar Format para cadenas de texto (strings), básicamente para alinearlas y/o recortarlas, no obstante su potencia radica en lo numérico, no solo enteros y decimales, también hexadecimales, exponenciales y hasta punteros. Ejemplos de todos ellos en: https://www.freepascal.org/docs-html/rtl/sysutils/format.html la cual recomiendo leer antes de continuar. Como también las referidas a TFormatSettings y DefaultFormatSettings.

Como lo indica su nombre, DefaultFormatSettings, es una variable global del tipo TFormatSettings que contiene los valores actuales de formato, si se altera, afectará a todo el proyecto, por ende debe usarse con algunos recaudos, como ser, por ejemplo, si necesito que en una función se utilice el separador decimal punto pero luego continuar utilizando la coma, entonces una opción es, antes de llamar a la función usar DefaultFormatSettings.DecimalSeparator:='.' e inmediatamente en la siguiente línea de código que sucede a la función DefaultFormatSettings.DecimalSeparator:=',' o, lo mismo pero dentro de la función.

Otra opción, y según las necesidades, es crear una variable del tipo TFormatSettings (que es del tipo record) y especificar en ella únicamente los formatos que vamos a utilizar.

Recordemos que Format devuelve un string así que puede usarse casi para cualquier cosa, pero, siempre hay un pero, a nivel IDE, más precisamente en el componente TMemo, no produce siempre los resultados deseados, el problema es con los espacios y el ancho del caracter, a no ser que exista alguna Font de tamaño fijo que desconozco.

En mi caso, necesitaba imprimir unas tablas (no tablas de bases de datos) y no tenía ganas de recurrir a LazReport, porque era algo muy simple, por eso mi primera opción fue TMemo. Pero por qué no aprovechar lo que ya está hecho, por ejemplo un editor de texto plano que cualquier sistema operativo posee.
Fue entonces que opté por TStringList que dispone de dos procedimientos elementales: LoadFromFile y SaveToFile. Finalmente utilizo OpenDocument y listo, de ahí en más se encarga el sistema operativo.

procedure TForm1.BTablaRetGcias2019Click(Sender: TObject);
var
  mes:Integer=0;
  ind:Integer=0;
  FS:TFormatSettings; //Creo la variable del tipo TFormatSettings
  sl:TStringList; //Creo la variable del tipo TStringList
begin
  sl:=TStringList.Create;  //Creo la instancia porque TStringList es una clase
  FS.CurrencyFormat:=1;  //FS es un type record y no necesita instanciasión
  FS.CurrencyDecimals:=2;
  FS.DecimalSeparator:=',';
  FS.ThousandSeparator:='.';
  FS.CurrencyString:='';
  sl.Add('Año 2019');
  sl.Add('');
  sl.Add('Mes Fijo Alíc.% Exedente');
  for mes:=1 to 12 do
  begin
    sl.Add('--------------------------------------');
    for ind:=1 to 9 do
      sl.Add(
      Format('%2.0d',[mes])+
      Format('%14.2m',[TablaGcias19[mes,ind].fijo],FS)+
      Format('%6.0m',[TablaGcias19[mes,ind].alicuota],FS)+
      Format('%16.2m',[TablaGcias19[mes,ind].exedente],FS));
      //%16 cantidad de caracteres .2 es 2 decimales y m es que el argumento es currency
  end;
  sl.SaveToFile('SL_Tabla_2019.txt');
  OpenDocument('SL_Tabla_2019.txt');
  sl.Free;
end;


Algunas aclaraciones:

FS.CurrencyFormat: en este caso no importa el valor, porque lo anulo con FS.CurrencyString:=''. Pero ya que estamos, lo valores para este campo (CurrencyFormat) pueden ser:
0 : $1
1 : 1$
2 : $ 1
3 : 1 $

En este caso TablaGcias19 es una matriz del registro TTablaGcias

TTablaGcias = record
  fijo:Currency;
  exedente:Currency;
  alicuota:Currency;
end;


Y con muy poco código se obtiene esto:



Al final se libera el TStringList y de la variable FS que usamos para FormatSettings nos olvidamos ya que es local a la función y del tipo record.

miércoles, 6 de septiembre de 2017

Obtener la lista de tablas de una base de datos SQL

Es algo muy simple desde consola o en tiempo de diseño, pero en tiempo de ejecución? También es sencillo, lo difícil fue encontrar cómo hacerlo. Resulta que Zeos (ZeosLib), más precisamente su principal componente, ZConnection, posee un procedimiento llamado GetTableNames que nos devuelve un parámetro del tipo TStrings pasado como referencia (lógico, si no fuese pasado como referencia no devolvería nada) con la lista de todas las tablas de la base de datos conectada. Se le puede pasar un TStringList o un ListBox.items por ejemplo.

 Primer ejemplo con TStringList y un TMemo:

 // Definir procedimiento o función o agregar el código donde sea necesario
var
  listatablas:TStringList;
  i:Integer;        
begin
  listatablas:=TStringList.Create; 
  ZConnection1.GetTableNames('',listatablas);   // La base de datos debe estar conectada...
  for i:=0 to listatablas.Count-1 do
    Memo1.Lines.Add(listatablas[i]);     // Memo1 debe estar en el Form
  ...
end;


El primer parámetro pasado podría haber sido por ejemplo ‘c*’ en cuyo caso hubiésemos obtenido la lista de tablas cuyos nombres comiencen con c. Al pasarlo vacío le indicamos que llene el StringList con todas las tablas.

Este ejemplo, con muy poco código, alcanza y sobra para mostrar los nombres de las tablas, pero si queremos que además el usuario pueda elegir una (o varias) tablas podemos valernos de un ListBox. Para ello, no le pasaremos como parámetro el ListBox sino ListBox.Items que es del tipo TStringList.

ZConnection1.GetTableNames('',ListBox1.Items) // Requiere un ListBox en el Form