Si bien TDBGrid tiene la propiedad Anchors (Anclajes), cuando la definimos y agrandamos y achicamos el formulario, la grilla también lo hace, mas no sus columnas, la cuales carecen de la propiedad Anchors, entonces el ancho de las columnas permanece estático, no cambia.
¿Cómo hacer que las columnas cambien el ancho cuando se agranda el formulario que contiene la DBGrid?
Creando el evento FormResize y asignarlo a los eventos del Form OnResize y OnChangeBounds. Desde ya el grid debe estar anclado al formulario y tener definido al menos las constraints de valores mínimos. Luego, un poco de matemáticas y eso es todo.
Así tengo definido el anclaje del DBGrid, mucho no entiendo de este tema, mucho prueba y error hasta dar con el resultado que busco.
Las Constraints del DBGrid, aunque esto es relativo si están definidas las del Form.
En los eventos del DBGrid, desde el inspector de objetos, creamos el evento para OnResize y también lo asignamos a OnChangeBounds.
procedure TfrmBancos.FormResize(Sender: TObject);
var
ndiv, nmod, ndist:Integer;
begin
ndist:=(Width-Constraints.MinWidth);
if ndist < 4 then exit;
ndiv:=ndist div 4;
nmod:=ndist mod 4;
DBGrid1.Columns.Items[1].Width:=160+ndiv;
DBGrid1.Columns.Items[2].Width:=120+ndiv;
DBGrid1.Columns.Items[3].Width:=150+ndiv;
DBGrid1.Columns.Items[4].Width:=160+ndiv;
if nmod>0 then
DBGrid1.Columns.Items[4].Width:=DBGrid1.Columns.Items[4].Width+nmod;
end;
En este caso, la columna 0 (cero) no la muestro, solo las 1,2,3 y 4 con un ancho definido en la propiedad width de cada columna de 160, 120, 150 y 160 respectivamente.
Lo que hago es calcular en cuántos píxeles se agranda el formulario y en base a ello, lo distribuyo en las columnas, como son 4, hago la división entera sobre 4 y el resto (mod) lógicamente también sobre 4. Luego elijo a que columna se asigno el sobrante si es que lo hay (mod 4).
En este ejemplo vamos a enviar un archivo "listado.txt" que debe estar en la misma carpeta que el programa a un servidor remoto, es decir, necesitamos sí o sí un servidor al cual conectarnos vía FTP, y un usuario con privilegio de lectura y escritura. La conexión la hacemos por el puerto 21. Desde ya el archivo y el puerto se pueden cambiar desde el código fuente.
Necesitamos también el paquete synapse.
En la clase TForm vamos a incluir estos procedimientos:
procedure btnEnviarClick (Sender: TObject);
procedure btnSalirClick (Sender: TObject);
procedure FormCreate (Sender: TObject);
procedure SockCallBack (Sender: TObject; Reason: THookSocketReason; const Value: string);
FormCreate solamente hace los TEdit tipo password por lo tanto no es necesario para "practicar", o puede dejarse como password solo el correspondiente al TEdit de la contraseña.
edContrasena.EchoMode:=emPassword;
edServidor.EchoMode:=emPassword;
edUsuario.EchoMode:=emPassword;
El método SockCallBack solo es necesario para mostrar una barra de progreso.
case Reason of
HR_WriteCount:
El enumerado HR_WriteCount se utiliza porque se envían archivos, si en cambio se recibieran archivos, el enumerado es otro, lo veremos en otra entrada donde explicare como recibir archivos vía FTP que desde ya, es parecido a esto.
Variables del evento btnEnviarClick:
procedure TForm1.btnEnviarClick(Sender: TObject);
var
ftp: TFTPSend;
remotefile: string;
localfile: String;
i: Integer;
Lo primero es definir una variable (en este caso llamada ftp) del tipo TFTPSend que se encuentra en la unidad ftpsend.pas. Si bien el nombre de esta unidad puede llevar a pensar que exista otra llamada ftpreciebe.pas, pues no, en ftpsend está todo, ya sea para enviar como para recibir archivos.
Luego de instanciar la variable "ftp" ftp := TFTPSend.Create; debemos "envolver" todo el código de conexión y envío del archivo en un try ... finally, no es obligatorio pero si recomendable.
ftp.DSock.OnStatus := @SockCallBack;
Es para incrementar la progressbar.
ftp.Timeout:=4000;
Esto es muy importante y lamentablemente no se menciona en la mayoría de ejemplos de FTPSend que hay en Internet. Si no definimos un tiempo de espera, el programa puede colgarse o largar un error, de esta forma establecemos en 4 segundo el tiempo de espera de respuesta del servidor.
if ftp.StoreFile(remotefile,False)
La función StoreFile es la que manda el archivo al servidor, el primer parámetros es un string que contiene la carpeta del servidor y el nombre del archivo /prueba/listado.txt y el segundo parámetro, booleano, solo debe ser True si el servidor soporta reanudar subidas, si es así solo se sube la parte que resta y si el tamaño del archivo es el mismo que el del archivo que está en el servidor, entonces no se envía nada. Como tercera opción, en caso de que el archivo del servidor sea más grande que el archivo local, entonces se envía todo el archivo desde el comienzo.
Hacer el "benchmark" de una función, proceso o programa es algo relativamente simple en Lazarus/FreePascal utilizando la unidad dateutils.
En este ejemplo vamos a calcular cuanto se tarda en imprimir 10.000 líneas en un TMemo. También obtendremos la velocidad promedio de líneas por segundo.
unit Unit1;
procedure TForm1.Button1Click(Sender: TObject);
var
i:Integer;
begin
Empezar;
if CheckBox1.Checked then
begin
for i:=1 to 10000 do
begin
Application.ProcessMessages;
Memo1.Lines.Add(IntToStr(i));
end;
end
else
begin
for i:=1 to 10000 do
Memo1.Lines.Add(IntToStr(i));
end;
Finalizar;
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
Memo1.Clear;
end;
procedure TForm1.Empezar;
begin
dtpEmpieza.Time:=Now;
end;
procedure TForm1.Finalizar;
begin
dtpFinaliza.Time:=Now;
dtpTranscurrido.Time:=dtpFinaliza.Time-dtpEmpieza.Time;
edImpps.Text:=FloatToStr((10000/((SecondOf(dtpTranscurrido.Time)+((MilliSecondOf(dtpTranscurrido.Time)/1000))))));
end;
end.
Desde ya se pueden quitar los dos TDateTimePicker y reemplazarlos por dos variables del tipo TTime, o ocultar dichos componentes. También reemplazar el valor 10000 por una constante o variable. Se puede jugar un buen rato.
TListBox es un componente que muestra una lista de cadenas (strings) y resalta la seleccionada por el usuario. Se encuentra en la pestaña Standard de la paleta de componentes. Permite la multiselección pero en este ejemplo se usará la selección única.
Esta lista se compone de Items que son del tipo TString que es una clase abstracta.
Es como la implementación gráfica de un vector de cadenas, con propiedades y métodos que permiten su manipulación.
En el ejemplo se crea un programa que carga al comienzo la lista con algunos elementos y permite agregar, eliminar, borrar la lista, ordenarla, copiarla a un TMemo y recorrerla mostrando el recorrido en el Memo. También se muestra en un TEdit el elemento seleccionado.
procedure TForm1.btnAgregarClick(Sender: TObject);
begin
if Length(Trim(edAgregar.Text))<1 then exit;
//Trim elimina espacios en blanco, Length el tamaño.
//Si ingresa un dato en blanco no hace nada.
if not (cbDuplicados.Checked) then if YaExiste(edAgregar.Text) then exit;
//Si no se marcó Permitir duplicados entonces se llama a la función YaExiste, si devuelve True no se agrega.
ListBox1.AddItem(edAgregar.Text,ListBox1);
//Además del string a agregar hay que especificar el objeto.
edAgregar.Clear;
//Limpia el TEdit. edAgregar.Text:='' también es válido.
end;
procedure TForm1.btnRecorrerClick(Sender: TObject);
var
i:Integer;
begin
Memo1.Clear;
Memo1.Lines.Add('');
Memo1.Lines.Add('for i:=0 to ListBox1.Count-1 do'+#13#10+' Memo1.Lines.Add(ListBox1.Items.Strings[i]);'+#13#10);
for i:=0 to ListBox1.Count-1 do
//TListBox es base 0 (cero) por eso desde 0 hasta cantidad-1
Memo1.Lines.Add('ListBox1.Items.Strings['+IntToStr(i)+']: '+ListBox1.Items.Strings[i]);
//ListBox1.Items.String[i] así se accede al texto de cada ítem.
end;
procedure TForm1.btnBorrarTodoClick(Sender: TObject);
begin
ListBox1.Clear;
//Limpia la ListBox
end;
procedure TForm1.btnEliminarClick(Sender: TObject);
begin
if ListBox1.ItemIndex>=0 then ListBox1.Items.Delete(ListBox1.ItemIndex);
//Si hay algún ítem seleccionado, entonces ItemIndex tendrá un valor mayor o igual a cero, caso contrario será -1.
end;
procedure TForm1.btnListAMemoClick(Sender: TObject);
begin
Memo1.Lines.Add('Memo1.Lines:=ListBox1.Items;');
Memo1.Lines:=ListBox1.Items;
//Ya la propiedades Lines e Items son del mismo tipo TStrings basta con asignar una a la otra.
end;
procedure TForm1.btnOrdenarClick(Sender: TObject);
begin
ListBox1.Sorted:=True;
ListBox1.Sorted:=False;
//El False se agrega para que si se agrega un nuevo elemento lo agregue al final y se pueda volver a ordenar.
end;
procedure TForm1.ListBox1Click(Sender: TObject);
begin
edSeleccionado.Text:=ListBox1.Items.Strings[ListBox1.ItemIndex];
//Se accede al texto Items.Strings y el índice lo tomamos de la propia lista ItemIndex que es el ítem seleccionado.
end;
function TForm1.YaExiste(elemento: String): Boolean;
//elemento es el texto de edAgregar
var
i:Integer;
ret:Boolean;
begin
ret:=False;
for i:=0 to listbox1.Count-1 do
if elemento=listbox1.Items.Strings[i] then ret:=True;
//Recorremos toda la lista buscando si ya existe.
YaExiste:=ret;
end;
end.
Como vemos, cuando ordenamos la lista, los elementos cambian el índice, no es solo un ordenamiento visual.
Para que la lista esté siempre ordenada basta con establecer la propiedad Sorted en True, de esta forma cuando se agregue un elemento, lo hará en la posición que le corresponda y no al final. Haciendo esto, el botón ordenar ya no tiene sentido y debe quitarse.
Antes esto era una tarea un poco complicada según pude observar después de varias búsquedas que me mostraban hilos de foros de hace unos cuantos años. Por suerte esto ya no es así e incluir una imagen de cualquier formato (dentro de los más populares) almacenada en un campo o columna del tipo BLOB es tan sencillo que no requiere ni una línea de código.
Desde el diseñador LazReport debemos incluir un objeto del tipo imagen y nos aparecerá el siguiente diálogo:
La opción Cargar es para cargar una imagen contenida en un archivo, no es el caso. Debemos hacer click en Texto.
Y aquí tanto solo indicamos el campo que contiene la imagen. Si el dataset está conectado, podemos agregarlo desde el botón Campo de DB.
Eso es todo.
Esto que explicaré a continuación no cambia el icono del ejecutable, solo del programa en ejecución y los formularios (ventanas).
Para cambiar el icono principal, del programa, tenemos que ir a opciones del proyecto:
Es la primera opción así que encontrarla es bastante simple. Es importante respetar el tamaño y los bits por pulgada (bpp). No importa desde donde cargamos la imagen ya que pasará a ser parte del ejecutable. El formato debe ser .ico. Cualquier imagen se puede convertir a ICO usando GIMP se logra esto desde archivos--> Exportar como y escribir el nombrearchivo.ico y guardar.
Cuando se ejecute el programa en el panel se verá así:
Para cambiar o agregar un icono a un formulario (Form) desde el inspector de objetos ir a la propiedad icon y pulsar sobre los tres puntos (...):
El comando VACUUM copia todo el contenido de la base de datos a una base de datos temporal y luego sobre escribe la original, quedando de este modo, "limpia" u optimizada. Como cualquier comando SQLite lo podemos ejecutar ya sea desde consola, cómodamente utilizando la IDE SQLite Studio o desde código Free Pascal mediante una conexión Zeos con ExecuteDirect, por ejemplo:
ZConnection1.ExecuteDirect('VACUUM;');
Desde ya este comando necesita acceso exclusivo, no debe haber ninguna consulta activa ni transacción.
Desde la versión 3.15.0 (14 de octubre de 2016) se puede utilizar este comando en bases de datos adjuntas. Es importante saber que si se intentase ejecutar VACUUM a una base de datos adjunta desde una versión anterior, la adjunta (attached) será ignorada y VACUUM se ejecutará sobre la base de datos principal.
Tablas sin una clave primaria entera: VACUUM puede alterar los ROWIDs. Las tablas que tienen definida una INTEGER PRIMARY KEY no se modifican, solo aquellas que no lo posean. Es decir, si se utilizan los ROWIDs, algo poco recomendable, no se debe utilizar VACUUM ya que muy probablemente modifique sus valores.
Existe el pragma auto_vacuun que se puede habilitar, aunque según indican en el sitio oficial de SQLite "puede generar una fragmentación adicional de archivos de base de datos. Y auto_vacuum no compacta las páginas parcialmente rellenas de la base de datos como sí lo hace VACUUM.".
Este comando resulta extremadamente útil cuando se están realizando pruebas en la base de datos, generalmente en la etapa de diseño, una vez finalizas las mismas, usar VACUUM para una limpieza que además, reducirá el tamaño del archivo.
Como experiencia propia, estuve realizando pruebas con tipos de datos BLOB para almacenar imágenes, finalizada esta etapa, la base de datos estaba cerca de los 7 MB, luego de VACUUM su tamaño se redujo a 700 KB.
Si deseamos que un formulario pueda ser redimensionado por el usuario pero sobre un rango de valores mínimos y máximos, podemos valernos de las constraints (limitaciones) ya sea desde el inspector de objetos, previamente seleccionando el formulario, o por código.
Aquí vemos que MaxHeight y MaxWidth tiene valor cero, eso significa que puede agrandarse el formulario sin límites. En cambio en MinHeight y MinWidth le especificamos los valores mínimos del formulario, de esta forma si el usuario quiere achicarlo fuera de ese rango, le será imposible. Podrá achicarlo a por ejemplo 50x50 pero ni bien suelte el click del mouse, el formulario se redimensionará automáticamente a sus valores mínimos.
Lo mismo es válido para el tamaño máximo, solo hay que especificar los valores.
Es un libro libre distribuido bajo la licencia Creative Commons, del año 2012 y traducido al español. Son 150 páginas en formato PDF. Parte desde cero, motivo por el cual la mayor parte del libro son ejercicios o ejemplos por consola y recién los últimos capítulos con Lazarus. Como bien indicar el autor, el libro está dirigido a programadores y también a quienes no los son y quieren aprender a programar. Por mi parte también lo recomiendo para quienes dejaron de programar hace muchos años y en DOS, les ayudará mucho para dar ese salto a la programación con objetos, programación orientada a objetos y programación con interfaz gráfica. Y por si fuera poco, multiplataforma.
Y reitero que el año en que se hizo el libro no es un problema, ya que lo que se trata en el mismo, poco y nada a cambiado, es totalmente válido aún en 2018.
Dejo la versión original en inglés y la traducida al español.