PDA

Ver la Versión Completa : Guardar en un archivo el vector de componentes de una form


Majo
14-02-2004, 05:19:45
Hola amigos,

Tengo el siguiente problema, espero puedan colaborarme

Tengo cierta cantidad de componentes que se crean en tiempo de ejecución sobre una form. Lo que deseo hacer, es guardar el vector de componentes de la form en un archivo, para luego poder cargarlo en una form diferente.

Entiendo que el vector de componentes es un array dinámico. No se si este hecho produzca un problema al leer el archivo con la siguiente linea

ComponentesPrueba:array of TComponents;
...
FileRead(FileHandle,ComponentesPrueba,SizeOf(ComponentesPrueba));

La lectura me funciona con cualquier variable que no sea del tipo String ó array dinámico.

Si alguien me puede sacar de dudas ó de el error se lo agradecería enormemente

suerte..... :cool:

roman
14-02-2004, 07:42:18
La lectura me funciona con cualquier variable que no sea del tipo String ó array dinámico.


Un String o arreglo dinámico son realmente punteros de manera que SizeOf siempre te dará 4.

De cualquier manera no entiendo cómo es que intentas guardar y leer las componentes ya que cada componente es también un puntero.

Te voy a recomendar que leas este hilo:

http://www.clubdelphi.com/foros/showthread.php?t=1334

de donde creo puedes tomar ideas para hacer lo que quieres. En breve resumen, lo que ahí propongo es un método que usa lo mismo que el IDE de Delphi para guardar las componentes de un formulario durante el diseño y que te evita mantener tú mismo un arreglo de las componentes que se van insertando durante la ejecución.

// Saludos

Majo
16-02-2004, 15:24:24
Veo que lo propusiste en este hilo es muy parecido o igual a lo que yo deseo hacer.

Intentaré hacerlo y luego te cuento como me fue,

Gracias Roman por sacarme del error, y perdona mi ignorancia.....


.... Todos los días estamos para aprender

Majo
16-02-2004, 23:20:22
Roman....

leï el hilo que dijiste y lo aplique a mi caso, aparentemente funcionaba, pero al examinar cada componente cargado me di cuenta que no cargaba ciertas propiedades y varibles que yo había definido en ese componente y que el usuario cambió en tiempo de ejecución.

Por ejemplo, uno de los componentes es TGenerador el cual tiene una variable publica llamada KVnominal, el usuario modifica este valor en tiempo de ejecución y guarda.... cuando carga el archivo este Valor de KVNominal sigue con el valor por defecto e igual sucede con la mayoría de las variables y propiedades de cada objeto.

No se que pasa, si podes ayudarme, te lo agradezco.....

roman
16-02-2004, 23:42:31
Las únicas propiedades que pueden guardarse son las propiedades publicadas. Intenta pasar las variables que quieres a la secciòn published del formulario.

Majo
17-02-2004, 00:16:29
Tengo una propiedad publicada llamada Color, y esta funciona.

....y tengo una propiedad, también publicada , llamada PermisoArrastrar que es de tipo Boolean y esta no funciona, entonces no se que pasa.

delphi.com.ar
17-02-2004, 14:21:20
¿No tendra un Default o Stored?

Majo
17-02-2004, 17:00:07
Si tenía un default, cuando se lo quite funcionó.

Ahora el problema es con una propiedad que se estructura así:

type TPointArray=Array of TPoint;
....

private
FMatImage:TPointArray;
procedure SetMatImage(const valor:TPointArray);
....

published
property MatImage:TPointArray Read FMatImage write SetMatImage;
....

Implementation

procedure TElemento.SetMatImage(Valor:TPointArray);
var I:Integer;
begin
SetLength(FMatImage,Length(Valor));
for i:=0 to Length(FMatImage) do
begin
FMatImage[i].X:= valor[i].X;
FMatImage[i].Y:= valor[i].Y;
end;
end;

... el problema que se genera es que esta propiedad se escribe bien durante el proceso de ejecución pero al guardarla en un archivo y recuperándola de nuevo, ya no queda con el valor que se guardó. Lo mismo me sucede con las otras propiedades del tipo array dinámico.....

delphi.com.ar
17-02-2004, 17:11:20
Si tenía un default, cuando se lo quite funcionó.
Pero tendrías que evaluar si quitarlo o no, posiblemente este bien que no guarde esta propiedad porque precisamente es el valor por default. ¿No?

¿Esas propiedades se están guardando bien en el DFM?
¿Has definido la forma de guardar estos datos? (DefineProperties)

Saludos!

Majo
17-02-2004, 17:22:57
Haber si me hago entender bien...

Lo del Default era de una propiedad de tipo Boolean que comenté anteriormente. Ésta última es otra propiedad y es del tipo array dinamico, creo que es por eso que me crea conflicto.

La forma de guardar es con writeComponent(TForm1)... estoy guardando todos los objetos que están sobre la form1. Lo que pasa es que los objetos me los guarda a medias... entre otras las propiedades del tipo array dinámico no se almacenan correctamente.

roman
17-02-2004, 17:26:58
El problema es que con este método no puedes manejar este tipo de componentes; los arreglos no pueden ser propiedades publicadas.

// Saludos

delphi.com.ar
17-02-2004, 17:30:43
El problema es que con este método no puedes manejar este tipo de componentes; los arreglos no pueden ser propiedades publicadas.
Yo diría que no pueden ser fácilmente propiedades publicadas, pero si defines un editor de propiedades y la forma de guardar y leer esta propiedad del DFM (DefineProperties), puedes hacerlo.
Igualmente te recomiendo evaluar, si en lugar de un array te conviene tener una colección o similar.


Saludos!

roman
17-02-2004, 17:38:29
Yo diría que no pueden ser fácilmente propiedades publicadas

Corrígeme si me equivoco, y muy bien puede ser así. ¿Qué la frase correcta no sería:

"Las propiedades no publicadas no pueden ser fácilmente guardadas en el dfm."

Es decir, me parece que de cualquier forma estas propiedades no podrán aparecer en la sección published y por ende no pueden ser almacenadas con este mecanismo facilmente, a menos que se proceda como indicas.

// Saludos

Majo
17-02-2004, 17:47:13
Cuando declaré la propiedad así
property MatImage:Array of TPoint read FMatImage write SetMatImage;salió un error
pero cuando la declaro así

Type TPointArray=Array of TPoint;
property MatImage:TPointArray read FMatImage write SetMatImage;
no sale error al compilar.

Sino sale error porque no se puede hacer.

Mi gran problema es que no es la única propiedad de tipo array dinamico que necesito manejar, hay otras que incluso son del tipo "array of variant", que me permite manejar números complejos.


Entonces la pregunta es
¿Como almaceno componentes con propiedades del tipo array dinamicos en
el disco duro para luego cargarlos con los cambios que el usuario haya hecho?

delphi.com.ar
17-02-2004, 17:50:01
"Las propiedades no publicadas no pueden ser fácilmente guardadas en el dfm."

Es decir, me parece que de cualquier forma estas propiedades no podrán aparecer en la sección published y por ende no pueden ser almacenadas con este mecanismo facilmente, a menos que se proceda como indicas.
Exacto!.. Creo que la compliqué en mi mensaje ¿No?
Les recomindo ver el código del TStringGrid como ejemplo.

Saludos!

Majo
17-02-2004, 18:09:52
¿donde esta el código del TStringGrid?. ¿Está en la carpeta Fuentes de Borland o acá en el club delphi?

roman
17-02-2004, 18:10:26
Les recomindo ver el código del TStringGrid como ejemplo.

Ok. Ya lo hice. ReadData y WriteData se encargan de guardar y leer las cadenas usando al Writer y al Reader y, en efecto, estaba equivocado.

Sólo una pregunta: ¿es necesario usar DefineProperties o ésto sólo es necesario si la propiedad no está publicada?

// Saludos

delphi.com.ar
17-02-2004, 18:12:11
En la unit Grids, de los fuentes de Delphi. Si pegas TStringGrid en un form, vas a la declaración del mismo, y haces Ctrl+Click sobre el nombre de la clase, el IDE te llevará a la definición de la clase.

Saludos!

Majo
17-02-2004, 20:11:42
Hola amigos,

Ya descubrí porque no guarda ciertas propiedades del objeto,

Resulta que cuando el objeto se está cargando de un archivo del disco duro,
inmediatamente se ejecuta el metodo Create de dicho objeto, y es allí donde yo asigno ciertas propiedades por defecto, lo que quiere decir que así se hayan guardado éstas propiedades, igual se van a sobreescribir con los valores por defecto. Y no solo se ejecuta el método create, también se ejecuta los procedimientos de escritura de las propiedades.


Si alguien puede sacarme del apuro se lo agradecería enormemente, que hago para que esto no suceda?


Gracias...

delphi.com.ar
17-02-2004, 20:17:37
Cuando se crea el componente, en el cosntructor se cargan las porpiedades por defecto, pero inmediatamente despues, se tienen que asignar los valores que tenías guardados en el archivo.
¿Porqué no nos muestras el código que estas utilizando?

Majo
18-02-2004, 00:57:53
Este es el código, le quite gran cantidad de las lineas pero deje lo que me parece mas relevante.

Este no es propiamente el objeto hay unos objetos que se llaman así
TGenerador,TCarga,TTransformador.... Todos ellos heredan las propiedades de TElemento y le agregan otras como voltajes, corrientes, Potencias y otras que son vectores dinámicos y que los necesito guardar en disco para que el usuario pueda cargar un circuito que haya diseñado.

El programa que estoy haciendo se llama "Simulación de flujos de carga" es para mi trabajo de grado de "Ingenieria eléctrica" y estoy realmente necesitado de la ayuda, gracias por colaborarme

unit Elemento;

type
TKeyEvent = procedure (Sender: TObject; var Key: Word; Shift: TShiftState) of object;
TElemento = class(TGraphicControl)
private
{ Private declarations }

//*************************************************
FMatImage:TPointArray;

procedure SetMatImage(const Valor:TPointArray);

//Arrastrar Imagen
procedure ElementoClick(Sender:TObject);

protected
{ Protected declarations }
procedure Paint; override;

public
{ Public declarations }
FormDeDibujo:TComponent; //padre de todos los componentes
FImage:TImage; //imagen virtual para poder leer su matriz correspondiente
property MatImage:TPointArray read FMatImage write SetMatImage;
procedure GetMatImage(Elemento:TImage); //Guardar la matriz de la imagen
procedure Rotar;

constructor Create(AOwner: TComponent); override;
destructor Destroy;override;

published
{ Published declarations }
property PermisoMover:Boolean read FPermisoMover write SetPermisoMover;// default True;

end;

//**********************************************

implementation

{TElemento}

constructor TElemento.Create(AOwner: TComponent);
begin
inherited Create(AOwner);

{Se crea una imagen Virtual
Para poder llenar la matriz}

FImage:=TImage.Create(Self);
FImage.Picture.LoadFromFile('C:\FlujoDePotencia\BMP\Generador.bmp');
FImage.AutoSize:=True;
Width:=FImage.Width; //Para hacer un AutoSize manual
Height:=FImage.Height;
GetMatImage(FImage); // GetMatImage(FImage);
FImage.Destroy;

//Propiedades por defecto del elemento
ControlStyle := ControlStyle + [csReplicatable];
FLineColor:=clBlack;
FSeleccionado:=True;
FPermisoMover:=True;
FColorSelect:=clRed;
FDireccion:=0;
FSizeVectorId:=0;
//Eventos del elemento
OnClick:=ElementoClick;
FormDeDibujo:=AOwner;
end;

procedure TElemento.SetMatImage(const Valor:TPointArray);
var i:Integer;
begin
// if Valor<>FMatImage then
SetLength(FMatImage,Length(Valor));
for i:=0 to Length(FMatImage)-1 do
begin
FMatImage[i].X:=Valor[i].X;
FMatImage[i].Y:=Valor[i].Y;
end;
end;

procedure TElemento.SetDireccion(const valor:Integer);
begin
if FDireccion<>valor then
FDireccion:=valor;
end;

procedure TElemento.GetMatImage(Elemento:TImage);
var i,j,k:Integer;
// var FMatImageTemp:TPointArray;
begin
k:=0;
for j:=0 to Elemento.Height-1 do
begin
for i:=0 to Elemento.Width-1 do
begin
if Elemento.Canvas.Pixels[i,j]=clBlack then
begin
k:=k+1;
SetLength(FMatImage,k);
FMatImage[k-1].X:=i; //Columna
FMatImage[k-1].Y:=j; //Fila
end;
end;
end;

MatImage:=FMatImage;

end;

procedure TElemento.Rotar;
var i,temporal:Integer;
h,k:Integer;
Ancho:Integer;
// FMatImageTemp:TPointArray;
begin

if PermisoMover then

if Seleccionado=True then
begin
//Eje sobre el que va a rotar la imagen
h:=Round(Width/2);
k:=Round(Height/2);

//Cambiamos propiedades de elemento
Left:=(Left+h) - k;
Top:= (Top+k) - h;
Ancho:=Width;
Width:=Height;
Height:=Ancho;

for i:=0 to Length(FMatImage)-1 do
begin
temporal:=FMatImage[i].X; //Columna
FMatImage[i].X:=FMatImage[i].Y; //Columna lleve fila
FMatImage[i].Y:=(Height-1)-Temporal;
end;

MatImage:=FMatImage;

FDireccion:=FDireccion+90;
if FDireccion=360 then FDireccion:=0;

Invalidate;

end;
end;

procedure TElemento.Paint;
var i,j,k:Integer;
begin
//Colocar Imagen
for i:=0 to Length(MatImage)-1 do
begin
if FSeleccionado=False then
Canvas.Pixels[MatImage[i].X,MatImage[i].Y]:=FLineColor
else
Canvas.Pixels[MatImage[i].X,MatImage[i].Y]:=FColorSelect;
end;
end;

//**********************************************************//
// Esta propiedad bloquea la propiedad de arrastrar y rotar*//
// de un Elemento *//
//**********************************************************//
procedure TElemento.SetPermisoMover(const valor:Boolean);
begin
if FPermisoMover<>valor then
FPermisoMover:=valor;
end;
//**********************************************************//

procedure TElemento.ElementoClick(Sender:TObject);
var Edit:TEdit;
begin
//if not ConstruyendoLine then
DesSeleccionar(Form2);//lo selecciona a él y le quita la seleccion
//al resto
end;
end.

delphi.com.ar
18-02-2004, 14:20:10
A simple vista te faltaría el definir la forma para guardar la propiedad MatImage, si es que quieres que se guarde.
¿Por otro ládo, cuál es el código que usas para generar el archivo?

Majo
18-02-2004, 15:33:38
Este es el código para guardar y cargar los componentes de una form.

De cualquier manera de tanto ensayar y ensayar, hice el intento de guardar en un archivo un arreglo declarándolo así:

a:File of Array of integer;
y sale el siguiente error
[Error] WindowsDesigner.pas(107): Type 'dynamic array' needs finalization - not allowed in file type

pero cuando lo declaro así
a:File of Array[1..100] of integer;
al compilar no sale error

¿Tiene esto algo que ver con que Los componentes de la form no se guarden completamente?.

Mi gran problema es que realmente necesito en la propiedades arreglos.
!y vaya la contradicción ¡.... cuando declaro la propiedad como un arreglo dinámico no sale error, y cuando declaro la propiedad como un arreglo estático sale el error de que una propiedad no se puede declarar como arreglo


procedure TForm2.Load;
var
Stream: TFileStream;
i:Integer;
begin
Form3.OpenDialog1.Execute;
Stream := TFileStream.Create(Form3.OpenDialog1.FileName, fmOpenRead or fmShareDenyWrite);
//Borre todo lo que hay en la form de diseño
LimpiarForm;
//Lee los componentes del archivo y lo inserta en la form de diseño
try
Stream.ReadComponent(Self);
finally
Stream.Free;
end;
end;

procedure TForm2.Save;
var
Stream: TFileStream;
begin
Form3.SaveDialog1.Execute;
Stream := TFileStream.Create(Form3.SaveDialog1.FileName, fmCreate);
try
Stream.WriteComponent(Self);
finally
Stream.Free;
end;
end;

procedure TForm2.LimpiarForm;
var i,a:Integer;
begin
a:=ComponentCount;
for i:=0 to a-1 do
Components[ComponentCount-1].Free;
end;

roman
18-02-2004, 15:43:28
Majo

Por favor utiliza la etiqueta [ code ] (Puedes ver instrucciones para usarla en el enlace de mi firma) y te agradecería que edites por lo menos este último mensaje para darle formato al código. Es muy pesado leer código sin indentar.

// Gracias

delphi.com.ar
18-02-2004, 17:10:37
En la sección ejemplos de mi página (http://www.delphi.com.ar), te he dejado un ejemplo de un componente con una propiedad que es un array dinámico de TPoint.
Si este componente lo registráramos, necesitaríamos crear un editor de propiedades para que el usuario pueda interactuar con el mismo.

Saludos!

roman
18-02-2004, 17:22:48
¡Caray! Hasta pena me da :D ¡Con negritas y todo!

Vamos a ver, ¿revisaste el código de TStrings como te propuso delphi.com.ar?

Propiedades de tipo arreglo dinámico, aunque sí pueden publicarse como ya nos dijo delphi.com.ar no se guardan en automático porque Delphi no sabe cómo hacerlo y hay que darle su ayudadita usando DefineProperties para decirle a Delphi qué propiedad queremos almacenar y con qué métodos (uno para leer los datos y otro para guardarlos).

Sin embargo pienso que quizá nos estemos complicando demasiado ya que me parece que la mayor parte de los datos que deseas guardar son de tus propias componentes de manera que no es mucha ayuda utilizar el mecanismo integrado de Delphi.

Creo que en tu lugar yo intentaría más por trabajar en la parte de guardarlas tú mismo. Quizá usando Collections como dice delphi.com.ar ya que además piensa que el mecanismo de Delphi no está pensado para muchos datos y quizá no sea muy eficiente. Otra posibilidad sería que usaras una base de datos para almacenar los datos.

// Saludos

delphi.com.ar
18-02-2004, 17:30:10
Vamos a ver, ¿revisaste el código de TStrings como te propuso delphi.com.ar?TStringGrid!!

PD: Vean mi mensaje anterior, pues me tomé el trabajo de hacer un ejemplo por favor no lo desperdicien!! :D

roman
18-02-2004, 17:36:11
¡Uy! Disculpe usted.

Me fuí por el lado del reader y writer.

Y sí, ya ví tu ejemplo (lo hice inmediatamente de que pusiste el mensaje anterior pero después de que escribí el último, no me había dado cuenta, sorry!)

// Saludos

Majo
18-02-2004, 17:51:44
Entre a Tu pagina (http://www.delphi.com.ar) pero no encontré la sección de ejemplos. Observé ciertos componentes que habían alli, pero no se cual es.

Si puedes indicarme te lo agradecería....

roman
18-02-2004, 17:57:51
delphi.com.ar

Hoy no es mi día. Lo que pusiste en tu página es un ejemplo de cómo usar la componente que, supongo, viene en la unidad ArrayDemo. ¿Esa la escribimos nosotros o te olvidaste de ponerla? :rolleyes:

// Saludos

delphi.com.ar
18-02-2004, 18:09:10
Pero... Ustedes quieren todo resuelto!!!
:D

Ya lo actualicé! :p

roman
18-02-2004, 18:21:50
Vamos a ver:

Ayer hice algo así:


unit Element;

interface

uses
Windows, Classes;

type
TPoints = array of TPoint;
TElement = class(TComponent)
private
FPoints: TPoints;
procedure SetPoints(const Value: TPoints);

procedure ReadPoints(Reader: TReader);
procedure WritePoints(Writer: TWriter);

protected
procedure DefineProperties(Filer: TFiler); override;

public
property Points: TPoints read FPoints write SetPoints;
end;

procedure Register;

implementation

{ TElement }

procedure TElement.DefineProperties(Filer: TFiler);
begin
inherited;

Filer.DefineProperty('Points', ReadPoints, WritePoints, Length(FPoints) > 0);
end;

procedure TElement.ReadPoints(Reader: TReader);
var
I: Integer;

begin
Reader.ReadListBegin;
I := 0;
while not Reader.EndOfList do
begin
SetLength(FPoints, I + 1);
FPoints[I].X := Reader.ReadInteger;
FPoints[I].Y := Reader.ReadInteger;
Inc(I);
end;
Reader.ReadListEnd;
end;

procedure TElement.SetPoints(const Value: TPoints);
begin
FPoints := Copy(Value);
end;

procedure TElement.WritePoints(Writer: TWriter);
var
I: Integer;

begin
for I := 0 to Length(FPoints) - 1 do
begin
Writer.WriteInteger(FPoints[I].X);
Writer.WriteInteger(FPoints[I].Y);
end;
end;

procedure Register;
begin
RegisterComponents('Samples', [TElement]);
end;

end.


Entonce no estaba tan errado. Claro que es mejor usar como tú un contador de los puntos.

// Saludos

Majo
18-02-2004, 18:27:15
Pero... Ustedes quieren todo resuelto!!!
:D

Ya lo actualicé! :p

Perdoname .... pero entré al enlace de componetes.... Supuse que alli estaba el ejemplo... ya lo encontré y lo voy a mirar....

Gracias......

Majo
19-02-2004, 19:28:51
Entendí perfectamente el ejemplo, lo DefineProperties no lo conocía, y no sabía como utilizarlo. ahora ya arreglé el problema que tenía. Quero darle las gracias A Roman y a Delphi.com.ar por sacarme de este apuro y por su colaboración. !Ni hablar de ClubDelphi ¡ que me ha servido tanto


Suerte y gracias......