pr2501
asked on
Reading from ini
Am using code from proposition of Geert_Gruwez from the post:
https://www.experts-exchange.com/questions/26588395/Ini-file-complication.html Now i need also code to modificate my code below to read data about TLabel.
Sorry i have tray but i can't do it by myself.
https://www.experts-exchange.com/questions/26588395/Ini-file-complication.html Now i need also code to modificate my code below to read data about TLabel.
Sorry i have tray but i can't do it by myself.
procedure TForm1.FormCreate(Sender: TObject);
var
cmp: TShape;
i: integer;
Fname:string;
begin //1
FShapeNumber := 0;
with TINIFile.Create(ExtractFilePath(Application.Exename) + 'yourini.ini')do begin //2
try //3
caption:= ReadString(Self.Name, 'Caption', Caption);
LoadBackground( ReadString('_global_', 'bitmapfile', '' ) );
pathstrBitmapFile:= ReadString('_global_', 'bitmapfile', '' );
n := ReadInteger(Self.Name, 'Shapes', 0);
for i := 1 to n do
begin //4
// if you use TShape as class, you only need to cast once
cmp := TShape(FindComponent('Shape' + IntToStr(i)));
if cmp = nil then
begin //5
cmp := TShape.Create(Self);
cmp.Name := 'Shape' + IntToStr(I);
cmp.Parent := Self;
cmp.onMouseDown := ShapeMouseDown;
cmp.onMouseMove := ShapeMouseMove;
cmp.onMouseUp := ShapeMouseUp;
cmp.Tag := FShapeNumber;
inc(FShapeNumber);
cmp.Brush.Color := TColor(ReadInteger(Self.Name, cmp.Name + ' Brush Color', integer(clGreen)));
cmp.Width := ReadInteger(Self.Name, cmp.Name + ' Width', cmp.Width);
cmp.Height := ReadInteger(Self.Name, cmp.Name + ' Height', cmp.Height);
cmp.Top := ReadInteger(Self.Name, cmp.Name + ' Top', cmp.Top);
cmp.Left := ReadInteger(Self.Name, cmp.Name + ' Left', cmp.Left);
cmp.hint :=readString(Self.Name, cmp.Name + ' Hint',TShape(cmp).Hint);
cmp.showhint :=readBool(Self.Name, cmp.Name + ' ShowHint',TShape(cmp).Showhint);
end; //5
end; // 4
finally
free;
end; //3
Application.Showhint := true;
end; //2
end; //1
Just added the TLabel default properties that you might be using, and also just worked on the shape code to get this:
procedure TForm1.FormCreate(Sender: TObject);
var
cmp: TShape;
i: integer;
Fname:string;
begin //1
FShapeNumber := 0;
with TINIFile.Create(ExtractFilePath(Application.Exename) + 'yourini.ini')do begin //2
try //3
caption:= ReadString(Self.Name, 'Caption', Caption);
LoadBackground( ReadString('_global_', 'bitmapfile', '' ) );
pathstrBitmapFile:= ReadString('_global_', 'bitmapfile', '' );
LabelNumber := 0;
n := ReadInteger(Self.Name, 'Labels', 0);
for i := 1 to n do
begin
cmp := TLabel(FindComponent('Label' + IntToStr(i)));
if cmp = nil then
begin
cmp := TLabel.Create(Self);
cmp.Name := 'Label' + IntToStr(I);
cmp.Parent := Self;
cmp.onClick := LabelClick;
inc(LabelNumber);
cmp.Caption := ReadString(Self.Name, cmp.Name + ' Caption', TLabel(cmp).Caption));
cmp.Font.Color := TColor(ReadInteger(Self.Name, cmp.Name + ' Font Color', integer(clWindowText)));
cmp.Font.name := ReadString(Self.Name, cmp.Name + ' Font Name', TLabel(cmp).Font.Name));
cmp.Font.Size := ReadInteger(Self.Name, cmp.Name + ' Font Size', TLabel(cmp).Font.Size);
cmp.Top := ReadInteger(Self.Name, cmp.Name + ' Top', cmp.Top);
cmp.Left := ReadInteger(Self.Name, cmp.Name + ' Left', cmp.Left);
cmp.AutSize := ReadBool(Self.Name, cmp.Name + ' AutoSize', TLabel(cmp).AutoSize);
cmp.Transparent := ReadBool(Self.Name, cmp.Name + ' Transparent', TLabel(cmp).Transparent);
cmp.WordWrap := ReadBool(Self.Name, cmp.Name + ' WordWrap', TLabel(cmp).WordWrap);
end;
end;
n := ReadInteger(Self.Name, 'Shapes', 0);
for i := 1 to n do
begin //4
// if you use TShape as class, you only need to cast once
cmp := TShape(FindComponent('Shape' + IntToStr(i)));
if cmp = nil then
begin //5
cmp := TShape.Create(Self);
cmp.Name := 'Shape' + IntToStr(I);
cmp.Parent := Self;
cmp.onMouseDown := ShapeMouseDown;
cmp.onMouseMove := ShapeMouseMove;
cmp.onMouseUp := ShapeMouseUp;
cmp.Tag := FShapeNumber;
inc(FShapeNumber);
cmp.Brush.Color := TColor(ReadInteger(Self.Name, cmp.Name + ' Brush Color', integer(clGreen)));
cmp.Width := ReadInteger(Self.Name, cmp.Name + ' Width', cmp.Width);
cmp.Height := ReadInteger(Self.Name, cmp.Name + ' Height', cmp.Height);
cmp.Top := ReadInteger(Self.Name, cmp.Name + ' Top', cmp.Top);
cmp.Left := ReadInteger(Self.Name, cmp.Name + ' Left', cmp.Left);
cmp.hint :=readString(Self.Name, cmp.Name + ' Hint',TShape(cmp).Hint);
cmp.showhint :=readBool(Self.Name, cmp.Name + ' ShowHint',TShape(cmp).Showhint);
end; //5
end; // 4
finally
free;
end; //3
Application.Showhint := true;
end; //2
end; //1
Sorry, forgot to change from the "cmp" taht was for the TShape, it should be different, I changed to "Lbel":
procedure TForm1.FormCreate(Sender: TObject);
var
cmp: TShape;
Lbel : TLabel;
i: integer;
Fname:string;
begin //1
FShapeNumber := 0;
with TINIFile.Create(ExtractFilePath(Application.Exename) + 'yourini.ini')do begin //2
try //3
caption:= ReadString(Self.Name, 'Caption', Caption);
LoadBackground( ReadString('_global_', 'bitmapfile', '' ) );
pathstrBitmapFile:= ReadString('_global_', 'bitmapfile', '' );
LabelNumber := 0;
n := ReadInteger(Self.Name, 'Labels', 0);
for i := 1 to n do
begin
Lbel := TLabel(FindComponent('Label' + IntToStr(i)));
if Lbel = nil then
begin
Lbel := TLabel.Create(Self);
Lbel.Name := 'Label' + IntToStr(I);
Lbel.Parent := Self;
Lbel.onClick := LabelClick;
inc(LabelNumber);
Lbel.Caption := ReadString(Self.Name, Lbel.Name + ' Caption', TLabel(Lbel).Caption));
Lbel.Font.Color := TColor(ReadInteger(Self.Name, Lbel.Name + ' Font Color', integer(clWindowText)));
Lbel.Font.name := ReadString(Self.Name, Lbel.Name + ' Font Name', TLabel(Lbel).Font.Name));
Lbel.Font.Size := ReadInteger(Self.Name, Lbel.Name + ' Font Size', TLabel(Lbel).Font.Size);
Lbel.Top := ReadInteger(Self.Name, Lbel.Name + ' Top', Lbel.Top);
Lbel.Left := ReadInteger(Self.Name, Lbel.Name + ' Left', Lbel.Left);
Lbel.AutSize := ReadBool(Self.Name, Lbel.Name + ' AutoSize', TLabel(Lbel).AutoSize);
Lbel.Transparent := ReadBool(Self.Name, Lbel.Name + ' Transparent', TLabel(Lbel).Transparent);
Lbel.WordWrap := ReadBool(Self.Name, Lbel.Name + ' WordWrap', TLabel(Lbel).WordWrap);
end;
end;
n := ReadInteger(Self.Name, 'Shapes', 0);
for i := 1 to n do
begin //4
// if you use TShape as class, you only need to cast once
cmp := TShape(FindComponent('Shape' + IntToStr(i)));
if cmp = nil then
begin //5
cmp := TShape.Create(Self);
cmp.Name := 'Shape' + IntToStr(I);
cmp.Parent := Self;
cmp.onMouseDown := ShapeMouseDown;
cmp.onMouseMove := ShapeMouseMove;
cmp.onMouseUp := ShapeMouseUp;
cmp.Tag := FShapeNumber;
inc(FShapeNumber);
cmp.Brush.Color := TColor(ReadInteger(Self.Name, cmp.Name + ' Brush Color', integer(clGreen)));
cmp.Width := ReadInteger(Self.Name, cmp.Name + ' Width', cmp.Width);
cmp.Height := ReadInteger(Self.Name, cmp.Name + ' Height', cmp.Height);
cmp.Top := ReadInteger(Self.Name, cmp.Name + ' Top', cmp.Top);
cmp.Left := ReadInteger(Self.Name, cmp.Name + ' Left', cmp.Left);
cmp.hint :=readString(Self.Name, cmp.Name + ' Hint',TShape(cmp).Hint);
cmp.showhint :=readBool(Self.Name, cmp.Name + ' ShowHint',TShape(cmp).Showhint);
end; //5
end; // 4
finally
free;
end; //3
Application.Showhint := true;
end; //2
end; //1
here is your code 'reformated' and adding some TLabel support
procedure TForm1.FormCreate(Sender: TObject);
var
shp: TShape;
lbl: TLabel
i: integer;
Fname:string;
begin
FShapeNumber := 0;
FLabelNumber := 0; // declare that
with TINIFile.Create(ExtractFilePath(Application.Exename) + 'yourini.ini') do
begin
try
caption:= ReadString(Self.Name, 'Caption', Caption);
LoadBackground( ReadString('_global_', 'bitmapfile', '' ) );
pathstrBitmapFile:= ReadString('_global_', 'bitmapfile', '' );
n := ReadInteger(Self.Name, 'Shapes', 0);
for i := 1 to n do
begin
// if you use TShape as class, you only need to cast once
shp := TShape(FindComponent('Shape' + IntToStr(i)));
if shp = nil then
begin
shp := TShape.Create(Self);
shp.Name := 'Shape' + IntToStr(I);
shp.Parent := Self;
shp.onMouseDown := ShapeMouseDown;
shp.onMouseMove := ShapeMouseMove;
shp.onMouseUp := ShapeMouseUp;
shp.Tag := FShapeNumber;
inc(FShapeNumber);
shp.Brush.Color := TColor(ReadInteger(Self.Name, shp.Name + ' Brush Color', integer(clGreen)));
shp.Width := ReadInteger(Self.Name, shp.Name + ' Width', shp.Width);
shp.Height := ReadInteger(Self.Name, shp.Name + ' Height', shp.Height);
shp.Top := ReadInteger(Self.Name, shp.Name + ' Top', shp.Top);
shp.Left := ReadInteger(Self.Name, shp.Name + ' Left', shp.Left);
shp.hint :=readString(Self.Name, shp.Name + ' Hint', shp.Hint);
shp.showhint :=readBool(Self.Name, shp.Name + ' ShowHint', shp.Showhint);
end;
end;
n := ReadInteger(Self.Name, 'Labels', 0);
for i := 1 to n do
begin
// if you use TLabel as class, you only need to cast once
lbl := TLabel(FindComponent('Label' + IntToStr(i)));
if lbl = nil then
begin
lbl := TLabel.Create(Self);
lbl.Name := 'Label' + IntToStr(I);
lbl.Parent := Self;
lbl.Tag := FLabelNumber;
inc(FLabelNumber);
lbl.Font.Color := TColor(ReadInteger(Self.Name, lbl.Name + ' Font Color', integer(clBlack)));
lbl.Top := ReadInteger(Self.Name, lbl.Name + ' Top', lbl.Top);
lbl.Left := ReadInteger(Self.Name, lbl.Name + ' Left', lbl.Left);
lbl.Caption :=readString(Self.Name, lbl.Name + ' Caption',lbl.Caption);
end;
end;
finally
free;
end;
Application.Showhint := true;
end;
end;
Is the some special reason why it has to be in an INI file?
Filestreams would be much easier, since they already know how to save and load TComponents (and decendants) already.
like; SaveComponent('c:\label1.d at', Label1);
and LoadComponent('c:\label1.d at', Label1);
Filestreams would be much easier, since they already know how to save and load TComponents (and decendants) already.
like; SaveComponent('c:\label1.d
and LoadComponent('c:\label1.d
procedure SaveComponent(AFilename:String; Comp:TComponent);
var fs:TFilestream;
begin
fs:=TFilestream.create(AFilename,fmCreate);
fs.WriteComponent(Comp);
FreeAndNil(fs);
end;
procedure LoadComponent(AFilename:String; var Comp:TComponent);
var fs:TFilestream;
begin
fs:=TFilestream.create(AFilename,fmOpenRead);
comp := fs.ReadComponent;
FreeAndNil(fs);
end;
ASKER
comp := fs.ReadComponent;
[Pascal Error] openmap.pas(663): E2035 Not enough actual parameters
[Pascal Error] openmap.pas(663): E2035 Not enough actual parameters
ASKER
Sorry am still sliping.
change ; comp := fs.ReadComponent;
to; fs.ReadComponent(comp);
to; fs.ReadComponent(comp);
Use loadcomponent below;
var tc:TButton;
...
tc := TButton.Create(Form1); /// Form1 is current form
LoadComponent('c:\temp\but ton.bin', tcomponent(tc), tform(Form1));
....
initialization
RegisterClasses([TButton, TEdit, TListBox, TMemo, TForm,TLabel]);
var tc:TButton;
...
tc := TButton.Create(Form1); /// Form1 is current form
LoadComponent('c:\temp\but
....
initialization
RegisterClasses([TButton, TEdit, TListBox, TMemo, TForm,TLabel]);
procedure LoadComponent(AFilename:String; var Comp:TComponent;var AForm:TForm);
var fs:TFilestream; nc : TComponent;
begin
fs:=TFilestream.create(AFilename,fmOpenRead);
try
fs.ReadComponent(comp);
if Assigned(AForm) then
begin
if comp is TControl then
Aform.InsertControl(tcontrol(comp))
else AForm.InsertComponent(comp);
end;
finally
FreeAndNil(fs);
end;
end;
Maybe a string version of the save as documented in TStream.ReadComponent;
This ComponentToStringProc creates a DFM like text that is easy to manipulate and read. The demo program attached does a good job of educating what is possible.
Adding a simply a
Memo1.Lines.SavetoFile('c: \myfile.tx t') and Memo1.Lines.LoadFromFile(' c:\myfile. txt');
could be all you need.?
NOTE : You could use a TStringList instead of a Memo1
This ComponentToStringProc creates a DFM like text that is easy to manipulate and read. The demo program attached does a good job of educating what is possible.
Adding a simply a
Memo1.Lines.SavetoFile('c:
could be all you need.?
NOTE : You could use a TStringList instead of a Memo1
function ComponentToStringProc(Component: TComponent): string;
var
BinStream:TMemoryStream;
StrStream: TStringStream;
s: string;
begin
BinStream := TMemoryStream.Create;
try
StrStream := TStringStream.Create(s);
try
BinStream.WriteComponent(Component);
BinStream.Seek(0, soFromBeginning);
ObjectBinaryToText(BinStream, StrStream);
StrStream.Seek(0, soFromBeginning);
Result:= StrStream.DataString;
finally
StrStream.Free;
end;
finally
BinStream.Free
end;
end;
function StringToComponentProc(Value: string): TComponent;
var
StrStream:TStringStream;
BinStream: TMemoryStream;
begin
StrStream := TStringStream.Create(Value);
try
BinStream := TMemoryStream.Create;
try
ObjectTextToBinary(StrStream, BinStream);
BinStream.Seek(0, soFromBeginning);
Result:= BinStream.ReadComponent(nil);
finally
BinStream.Free;
end;
finally
StrStream.Free;
end;
end;
Project1.zip
So what you would end up is something ; (Add the last two code bits together)
That would save and load DFM like files (not completely tested)
That would save and load DFM like files (not completely tested)
procedure SaveComponent(AFilename:String: AComp:TComponent);
var sl : TStringlist;
begin
sl := TStringlist.create;
try
sl.Text := ComponentToStringProc(AComp) ;
sl.SaveToFile(aFilename);
finally
freeandnil(sl);
end;
end;
procedure SaveComponent(AFilename:String: var AComp:TComponent; AForm:TForm);
var sl : TStringlist;
begin
sl := TStringlist.create;
try
sl.LoadFromFile(aFilename);
AComp := StringToComponentProc(sl.text);
if Assigned(AForm) then
begin
if Acomp is TControl then
Aform.InsertControl(tcontrol(Acomp))
else AForm.InsertComponent(Acomp);
end;
finally
freeandnil(sl);
end;
end;
Here's a unit DFMStorage;
only two calls
procedure LoadComponent(AFilename:St ring; var AComp:TComponent; AForm:TForm);
procedure SaveComponent(AFilename:St ring; AComp:TComponent);
DFMStorage.pas
only two calls
procedure LoadComponent(AFilename:St
procedure SaveComponent(AFilename:St
DFMStorage.pas
Further simplified I found that the AComp:Tcomponent parameter is not really needed;
So I overloaded it; it is not required unless you want to pass it in and get a reference to it and change something;
procedure LoadComponent(AFilename:St ring; AForm:TForm); overload;
procedure LoadComponent(AFilename:St ring; AForm:TForm;var AComp:TComponent );overload;
procedure SaveComponent(AFilename:St ring; AComp:TComponent);
Update attached unit;
DFMStorage.pas
So I overloaded it; it is not required unless you want to pass it in and get a reference to it and change something;
procedure LoadComponent(AFilename:St
procedure LoadComponent(AFilename:St
procedure SaveComponent(AFilename:St
Update attached unit;
DFMStorage.pas
One of the reasons you have a problem reading back the information is because you dont store it properly
Your ini file does not really distinguish b/n labels and shapes
As I suggested in your previous question you should have different sections for sections for shapes and labels
eg
[SHAPES]
chape1.width= xxx
[LABELS]
label1.top = yyy
Then you don't need to keep searching for the components when loading from the INI.
Load a whole section into a stringlist and process the string list
And for me, an even better solution would be to use XML. Set properties of a node to correspond to the properties of your control and everything suddenly becomes so simple
Your ini file does not really distinguish b/n labels and shapes
As I suggested in your previous question you should have different sections for sections for shapes and labels
eg
[SHAPES]
chape1.width= xxx
[LABELS]
label1.top = yyy
Then you don't need to keep searching for the components when loading from the INI.
Load a whole section into a stringlist and process the string list
And for me, an even better solution would be to use XML. Set properties of a node to correspond to the properties of your control and everything suddenly becomes so simple
ASKER
Thank you for you accurate assistance. It is that i had no time yesterday to study it. I'm not able to work on level like this, because programing with Delphi is not my job. I'm doing this for innovation in our factory. So pleas understand if am not giving expected fed back. Am still using code from Geert_Gruwez, but am stucked because i have problem which i have not recognized before with writing data into ini.
So i have spent many time on the problem and i believe that am near to resolution that am in one way refusing to start from the beginning even if the resolution would be much better.
Thank you
So i have spent many time on the problem and i believe that am near to resolution that am in one way refusing to start from the beginning even if the resolution would be much better.
Thank you
using the attached pascal file DFMStorage; (add to Uses) you can reduce the code to one line using;
SaveComponents andLoadComponents
Replacing the current FormClose and FormCreate with below code;
SaveComponents andLoadComponents
Replacing the current FormClose and FormCreate with below code;
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
DFMStorage.SaveComponents(
ExtractFilePath(Application.Exename) + 'yourini.ini',
Form1,[TLabel, TShape]);
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
DFMStorage.LoadComponents(
ExtractFilePath(Application.Exename) + 'yourini.ini',
Form1,[TLabel, TShape]);
end;
DFMStorage.pas
ASKER
briangochnauer
your code is OK but if you lock at my code there are many other propreties which a have to store and i need also option of creating of ini file.
your code is OK but if you lock at my code there are many other propreties which a have to store and i need also option of creating of ini file.
Then I guess it would be more like code below;
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
with TINIFile.Create(ExtractFilePath(Application.Exename) +'yourini.ini')do
try
WritedString(Self.Name, 'Caption', Caption);
WriteString('_global_', 'bitmapfile', pathstrBitmapFile );
finally
free;
end;
DFMStorage.SaveComponents(
ExtractFilePath(Application.Exename) + self.name+'.fdm',
Form1,[TLabel, TShape]);
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
with TINIFile.Create(ExtractFilePath(Application.Exename) + 'yourini.ini')do
try
caption:= ReadString(Self.Name, 'Caption', Caption);
LoadBackground( ReadString('_global_', 'bitmapfile', '' ) );
pathstrBitmapFile:= ReadString('_global_', 'bitmapfile', '' );
finally
free;
end;
DFMStorage.LoadComponents(
ExtractFilePath(Application.Exename) +self.name+'.fdm', Form1,[TLabel, TShape]);
end;
ASKER
thank you
This should be the resolution.
Can you help me resolve next:
First chance exception at $7C812AFB. Exception class EFOpenError with message 'Cannot open file "C:\IG\delphi\final7\final 7\Form1.fd m". The system cannot find the file specified'. Process Forma.exe (5668)
(yesterday when i have try Your code i had no error, i have no odea what happened)
This should be the resolution.
Can you help me resolve next:
First chance exception at $7C812AFB. Exception class EFOpenError with message 'Cannot open file "C:\IG\delphi\final7\final
(yesterday when i have try Your code i had no error, i have no odea what happened)
////////////////////////77
procedure LoadComponents(AFilename:String; AForm:TForm; AClasses: array of TPersistentClass);
var sl,tsl : TStringlist;
line :string;
j : integer;
tc : TComponent;
begin
sl := TStringlist.create;
tsl := TStringlist.create;
sl.LoadFromFile(Afilename);
for j := 0 to sl.Count-1 do
This (attached file) will fix that error.
DFMStorage.pas
DFMStorage.pas
ASKER
Yes, now there are no error now.
But restarting of app i lose labels and shapes.
What can be problem now?
But restarting of app i lose labels and shapes.
What can be problem now?
ASKER
I try to resolve it by changening " 'self.name+'.fdm' " to " 'yourinishapelabel.ini' " and now i may see data into ini file, so what must i change to read it also, . Or what is the reason why there is no shape and label in my app aftrer restarting of it.
ini file:
object shape1: TShape
Left = 10
Top = 30
Width = 10
Height = 10
Hint = 'xv'
Brush.Color = clGreen
end
object label1: TLabel
Left = 10
Top = 10
Width = 12
Height = 13
Caption = 'xv'
end
With seeing you complete app; I can only guess why it is not working. My test app works great.
But it may be; the name of the object can only exist once (1 time) so if it loading an object and finds there is an object with that name already; ('shape1') then that object will be ignored; the same for 'label1'
Labels (any object) don't have to be named label1 or labelx (where x is a number) they can be named with any amount of characters.
But it may be; the name of the object can only exist once (1 time) so if it loading an object and finds there is an object with that name already; ('shape1') then that object will be ignored; the same for 'label1'
Labels (any object) don't have to be named label1 or labelx (where x is a number) they can be named with any amount of characters.
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
thank you
Come on, that is not much more work to write visually nice code... And that is the first step to produce nice code in all aspects
* never put begin at the end of lines : always return to next line and ident
* put every end in the same column of the begin that corresponds
* same goes with try / finally / end , case / end
that is the absolute minimum. after, there are some habits that differ from one developer to another. I prefer indenting one space at a time only (but before and after a begin, so all my end are indented 2 spaces from each other. I found that a good balance between space used and readability
now, about your problem, what are the TLabel properties you want to save/read in your ini file ?