Avatar of pr2501
pr2501 asked on

Reading from ini

Am using code from proposition of Geert_Gruwez from the post:
https://www.experts-exchange.com/Programming/Languages/Pascal/Delphi/Q_26588395.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

Open in new window

Delphi

Avatar of undefined
Last Comment
pr2501

8/22/2022 - Mon
Emmanuel PASQUIER

You know, if you indented properly you would not need those //1 , //2 etc comments...

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 ?
jimyX

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

Open in new window

jimyX

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

Open in new window

Experts Exchange is like having an extremely knowledgeable team sitting and waiting for your call. Couldn't do my job half as well as I do without it!
James Murphy
Emmanuel PASQUIER

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;

Open in new window

briangochnauer

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.dat', Label1);
and              LoadComponent('c:\label1.dat', Label1);
 

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;

Open in new window

ASKER
pr2501

comp := fs.ReadComponent;

[Pascal Error] openmap.pas(663): E2035 Not enough actual parameters
Get an unlimited membership to EE for less than $4 a week.
Unlimited question asking, solutions, articles and more.
ASKER
pr2501

Sorry am still sliping.
briangochnauer

change ;  comp := fs.ReadComponent;
to;                fs.ReadComponent(comp);
briangochnauer

Use loadcomponent below;
var tc:TButton;
...
  tc := TButton.Create(Form1);   /// Form1 is current form
  LoadComponent('c:\temp\button.bin', tcomponent(tc), tform(Form1));
....
 
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;

Open in new window

Experts Exchange has (a) saved my job multiple times, (b) saved me hours, days, and even weeks of work, and often (c) makes me look like a superhero! This place is MAGIC!
Walt Forbes
briangochnauer

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.txt')    and     Memo1.Lines.LoadFromFile('c:\myfile.txt');
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;

Open in new window

Project1.zip
briangochnauer

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)

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; 

Open in new window

briangochnauer

Here's a unit DFMStorage;
only two calls

procedure LoadComponent(AFilename:String; var AComp:TComponent; AForm:TForm);
procedure SaveComponent(AFilename:String; AComp:TComponent);

DFMStorage.pas
Get an unlimited membership to EE for less than $4 a week.
Unlimited question asking, solutions, articles and more.
briangochnauer

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:String; AForm:TForm); overload;
procedure LoadComponent(AFilename:String; AForm:TForm;var AComp:TComponent );overload;
procedure SaveComponent(AFilename:String; AComp:TComponent);

Update attached unit;

DFMStorage.pas
Ephraim Wangoya

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
ASKER
pr2501

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
This is the best money I have ever spent. I cannot not tell you how many times these folks have saved my bacon. I learn so much from the contributors.
rwheeler23
briangochnauer

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;
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;

Open in new window

DFMStorage.pas
ASKER
pr2501

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.
briangochnauer

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;

Open in new window

Get an unlimited membership to EE for less than $4 a week.
Unlimited question asking, solutions, articles and more.
ASKER
pr2501

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\final7\Form1.fdm". 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)

////////////////////////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

Open in new window

briangochnauer

This (attached file) will fix that error.
DFMStorage.pas
ASKER
pr2501

Yes, now there are no error now.

But  restarting of app i lose labels and shapes.

What can be problem now?
I started with Experts Exchange in 2004 and it's been a mainstay of my professional computing life since. It helped me launch a career as a programmer / Oracle data analyst
William Peck
ASKER
pr2501



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

 
briangochnauer

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.
ASKER CERTIFIED SOLUTION
briangochnauer

Log in or sign up to see answer
Become an EE member today7-DAY FREE TRIAL
Members can start a 7-Day Free trial then enjoy unlimited access to the platform
Sign up - Free for 7 days
or
Learn why we charge membership fees
We get it - no one likes a content blocker. Take one extra minute and find out why we block content.
See how we're fighting big data
Not exactly the question you had in mind?
Sign up for an EE membership and get your own personalized solution. With an EE membership, you can ask unlimited troubleshooting, research, or opinion questions.
ask a question
ASKER
pr2501

thank you
Get an unlimited membership to EE for less than $4 a week.
Unlimited question asking, solutions, articles and more.