Recent

Author Topic: Mis-behaving TSplitter(s)  (Read 9050 times)

MorbidFractal

  • Jr. Member
  • **
  • Posts: 97
Mis-behaving TSplitter(s)
« on: October 29, 2010, 08:13:41 pm »
Previously I have written a text file, e.g.

Code: [Select]
,02,,1,0000,000,003,000,003,000,000,Nil,1,0,0,0,
,01,,1,0000,000,000,000,200,000,000,Nil,1,0,1,0,
  ,06,,7,0000,001,029,000,175,001,087,Nil,0,0,0,0,
  ,05,,0,1010,001,029,176,024,000,000,<,0,0,1,1,
  ,03,,7,0000,035,360,000,000,001,098,Sheet Setup,2,0,0,0,

I read this in, process it into records and then write it back out again. The indents indicate parentage with the first number indicating a control type which I will create when I read the file back in again, trust me I'm stupid..

In the above,

02 is a TSplitter
01 is a TPanel
06 is a TComboBox
05 is a TButton
03 is a TGroupBox

The comma separated parts define things like alignment, width, top and so on.

I define my types and set up arrays of them as,

Code: [Select]
type
  Item = Record
    Indent:   Integer;
    ItemType: Integer;
    Index:    Integer;
    Align:    Integer;
    Anchors:  String;
    Top:      Integer;
    Height:   Integer;
    Left:     Integer;
    Width:    Integer;
    PLeft:    Integer;
    PWidth:   Integer;
    Caption:  String;
    ColourA:  Integer;
    ColourB:  Integer;
    EventA:   Integer;
    LinkA:    Integer;
  end;
  ItemPanel = class(Tpanel)      public ItemRecord: Item; end;
  ItemSplit = class(TSplitter)   public ItemRecord: Item; end;
  ItemGroup = class(TGroupBox)   public ItemRecord: Item; end;
  ItemEdits = class(TEdit)       public ItemRecord: Item; end;
  ItemButts = class(TButton)     public ItemRecord: Item; end;
  ItemCombo = class(TComboBox)   public ItemRecord: Item; end;
  TextParts = Array [0..15] of String;
  procedure ClearItems();
  procedure ConvFile(InputFile: String);
  procedure LoadItems(InputFile: String);
  procedure MakeItems(Sender: TObject; ItemRecord: Item);
  procedure SetParent(Sender: TObject; ItemArray: Array of Item; IndexA: Integer);

var
  PanelArray: Array of ItemPanel;
  SplitArray: Array of ItemSplit;
  GroupArray: Array of ItemGroup;
  EditsArray: Array of ItemEdits;
  ButtsArray: Array of ItemButts;
  ComboArray: Array of ItemCombo;

I read the file back in and start creating the objects,

Code: [Select]
procedure LoadItems(InputFile: String);
var
ItemArray: Array of Item;
FileItem:  File of Item;
IndexA:    Integer;
begin
  ClearItems();
  ConvFile(InputFile);
  SetLength(ItemArray,0);
  Assign(FileItem,GetCurrentDir + '/' + InputFile + '.rec');
  Reset (FileItem);
  While not EOF(FileItem) do
  begin
    SetLength    (ItemArray,Length(ItemArray) + 1);
    Read(FileItem,ItemArray[Length(ItemArray) - 1]);
  end;
  CloseFile(FileItem);
  SetLength(PanelArray,0);
  SetLength(SplitArray,0);
  SetLength(GroupArray,0);
  SetLength(EditsArray,0);
  SetLength(ButtsArray,0);
  SetLength(ComboArray,0);
  For IndexA := 0 to Length(ItemArray) - 1 do
  begin
    Case ItemArray[IndexA].ItemType of
    0:;
    1:begin
        SetLength(PanelArray,Length(PanelArray) + 1);
        ItemArray[IndexA].Index := Length(PanelArray) - 1;
        PanelArray[Length(PanelArray) - 1] := ItemPanel.Create(Nil);
        PanelArray[Length(PanelArray) - 1].ItemRecord := ItemArray[IndexA];
        SetParent(PanelArray[Length(PanelArray) - 1],ItemArray,IndexA);
        MakeItems(PanelArray[Length(PanelArray) - 1],ItemArray[IndexA]);
      end;
    2:begin
        SetLength(SplitArray,Length(SplitArray) + 1);
        ItemArray[IndexA].Index := Length(SplitArray) - 1;
        SplitArray[Length(SplitArray) - 1] := ItemSplit.Create(Nil);
        SplitArray[Length(SplitArray) - 1].ItemRecord := ItemArray[IndexA];
        SetParent(SplitArray[Length(SplitArray) - 1],ItemArray,IndexA);
        MakeItems(SplitArray[Length(SplitArray) - 1],ItemArray[IndexA]);
      end;
-
-
-

SetParent finds the controls Parent based on the indenting and assigns it to its parent.

MakeItems takes the values from the ItemRecord and applies them to the control like this,

Code: [Select]
procedure MakeItems(Sender: TObject; ItemRecord: Item);
begin
  With Sender as TControl do
  begin
    Top     := ItemRecord.Top;
    Height  := ItemRecord.Height;
    Left    := ItemRecord.Left;
    Width   := ItemRecord.Width;
    Anchors := Anchors - Anchors;
    If ItemRecord.Anchors[1] = '1' then Anchors := Anchors + [akLeft];
    If ItemRecord.Anchors[2] = '1' then Anchors := Anchors + [akRight];
    If ItemRecord.Anchors[3] = '1' then Anchors := Anchors + [akTop];
    If ItemRecord.Anchors[4] = '1' then Anchors := Anchors + [akBottom];
    Case ItemRecord.Align of
    0:Align := alNone;
    1:Align := alLeft;
    2:Align := alRight;
    3:Align := alTop;
    4:Align := alBottom;
    5:Align := alClient;
    6:Align := alCustom;
    7:begin
        Align := alNone;
        Left  := ItemRecord.PLeft  * Parent.Width div 100;
        Width := ItemRecord.PWidth * Parent.Width div 100;
      end;
    end;
    //Caption := ItemRecord.Caption;
  end;
end;

I realise that this may trip me up if I try to set something in a control where it does not have an appropriate attribute, you can see I have commented out Caption, and that might be part of what is going wrong.

However things seem to work, up to a point.. Sorry for the rubbish screenshot,

http://img44.imageshack.us/img44/4521/laz291010.png

This is just me playing about so there is a Panel on the left on which I have placed a number of controls including nested GroupBoxes containing multiple Edits. This has a Vertical Splitter on it's right side and then then there are two more Panels with a Horizontal Splitter.

I can click on things and enter text into the Edits but.... as soon as my mouse rests over either of the Splitters the program halts with a blank Error Message box. Previously it has said I've experienced a SigServ error although that might not be what is happening this time.

I have recently un-installed and then re-installed Lazarus but I'm not sure why it is not displaying an error message. Previously on exiting the program, via X, I also got a SigServ error but that has also stopped

Anyway generally I get those sort of errors, if this is one, when trying to access a component where it does not exist, usually I've tried to read past the end of an array of the component. This time, as suggested, it is happening when I 'poke' the component/splitter as if I have triggered some event handler [?] which can't be found.

Apologies for being wordy.

Edit

If I omit the Vertical Splitter then the Horizontal Splitter behaves itself..


Any Ideas?
« Last Edit: October 29, 2010, 08:21:54 pm by MorbidFractal »

José Mejuto

  • Full Member
  • ***
  • Posts: 136
Re: Mis-behaving TSplitter(s)
« Reply #1 on: October 29, 2010, 10:04:12 pm »
Previously I have written a text file, e.g.[...]
Any Ideas?

Hello,

You are reading the "config" file:

  begin
    SetLength    (ItemArray,Length(ItemArray) + 1);
    Read(FileItem,ItemArray[Length(ItemArray) - 1]);
  end;

But your record have fields of variable length (string) which can not be read directly because you will simply corrupt memory.

You should read the line in an string, and parse each field one by one and assign. If you want to use this read method you must change your record from:

X=record
 Field1: integer;
 Caption: string;
end;

To something like:

X=record
 Field1: integer;
 Caption: array[0..100] of char;
end;

Now your caption is limited to 100 chars, but it can be loaded directly.

ivan17

  • Full Member
  • ***
  • Posts: 173
Re: Mis-behaving TSplitter(s)
« Reply #2 on: October 29, 2010, 11:17:29 pm »
...
man, that was one long post. luckily my answer will be short...

Any Ideas?
sure:
MyStream.WriteComponents(Button1);

theo

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 1933
Re: Mis-behaving TSplitter(s)
« Reply #3 on: October 29, 2010, 11:24:12 pm »
man, that was one long post. luckily my answer will be short...

Hehe, I always get a headache from reading such long "letters".
Remember what Blaise Pascal said:
"Je n'ai fait celle-ci plus longue que parce que je n'ai pas eu le loisir de la faire plus courte."
"I made this [letter] very long, because I did not have the leisure to make it shorter."

 :D
« Last Edit: October 29, 2010, 11:29:25 pm by theo »

MorbidFractal

  • Jr. Member
  • **
  • Posts: 97
Re: Mis-behaving TSplitter(s)
« Reply #4 on: October 30, 2010, 12:49:40 am »
Previously I have written a text file, e.g.[...]
Any Ideas?

Hello,

You are reading the "config" file:

  begin
    SetLength    (ItemArray,Length(ItemArray) + 1);
    Read(FileItem,ItemArray[Length(ItemArray) - 1]);
  end;

But your record have fields of variable length (string) which can not be read directly because you will simply corrupt memory.

You should read the line in an string, and parse each field one by one and assign. If you want to use this read method you must change your record from:

X=record
 Field1: integer;
 Caption: string;
end;

To something like:

X=record
 Field1: integer;
 Caption: array[0..100] of char;
end;

Now your caption is limited to 100 chars, but it can be loaded directly.

Yup.. Sorry. As I, almost, said. I have pre-processed the text file to convert it to records and then written it out to a 'typed' [?] file as records before bringing it back in again..

Code: [Select]
procedure ConvFile(InputFile: String);
var
TextAlone: TextParts;
TextWhole: Array of TextParts;
TextArray: Array of String;
ItemArray: Array of Item;
FileText:  TextFile;
FileItem:  File of Item;
Temps:     String;
Comma:     Integer;
Count:     Integer;
Index:     Integer;
begin
  SetLength(TextArray,0);
  SetLength(TextWhole,0);
  SetLength(ItemArray,0);
  Assign(FileText,GetCurrentDir + '/' + InputFile + '.txt');
  Reset (FileText);
  While not EOF(FileText) do
  begin
    Readln(FileText,Temps);
    If Temps[1] <> '#' then
    begin
      SetLength(TextArray,Length(TextArray) + 1);
      TextArray[Length(TextArray) - 1] := Temps;
    end;
  end;
  CloseFile(FileText);
  For Count := 0 to Length(TextArray) - 1 do
  begin
    Comma := 0;
    Index := 1;
    Temps := '';
    While Comma <> 16 do
    begin
      If TextArray[Count][Index] <> ',' then
      begin
        Temps := Temps + TextArray[Count][Index];
      end
      else
      begin
        TextAlone[Comma] := Temps;
        Temps := '';
        Inc(Comma);
      end;
      Inc(Index)
    end;
    SetLength(TextWhole,Length(TextWhole) + 1);
    TextWhole[Length(TextWhole) - 1] := TextAlone;
  end;
  For Count := 0 to Length(TextWhole) - 1 do
  begin
    SetLength(ItemArray,Count + 1);
    ItemArray[Count].Indent   := Length  (TextWhole[Count][00]);
    ItemArray[Count].ItemType := StrToInt(TextWhole[Count][01]);
    ItemArray[Count].Index    := 0;
    ItemArray[Count].Align    := StrToInt(TextWhole[Count][03]);
    ItemArray[Count].Anchors  :=         (TextWhole[Count][04]);
    ItemArray[Count].Top      := StrToInt(TextWhole[Count][05]);
    ItemArray[Count].Height   := StrToInt(TextWhole[Count][06]);
    ItemArray[Count].Left     := StrToInt(TextWhole[Count][07]);
    ItemArray[Count].Width    := StrToInt(TextWhole[Count][08]);
    ItemArray[Count].PLeft    := StrToInt(TextWhole[Count][09]);
    ItemArray[Count].PWidth   := StrToInt(TextWhole[Count][10]);
    ItemArray[Count].Caption  :=         (TextWhole[Count][11]);
    ItemArray[Count].ColourA  := StrToInt(TextWhole[Count][12]);
    ItemArray[Count].ColourB  := StrToInt(TextWhole[Count][13]);
    ItemArray[Count].EventA   := StrToInt(TextWhole[Count][14]);
    ItemArray[Count].LinkA    := StrToInt(TextWhole[Count][15]);
  end;
  Assign(FileItem,GetCurrentDir + '/' + InputFile + '.rec');
  ReWrite(FileItem);
  For Count := 0 to Length(ItemArray) - 1 do
  begin
    Write(FileItem,ItemArray[Count]);
    ItemArray[Count] := ItemArray[Count];
  end;
  Close(FileItem);
end;

I'm sure it is my bad and it is rubbish anyway.. but there you go.


MorbidFractal

  • Jr. Member
  • **
  • Posts: 97
Re: Mis-behaving TSplitter(s)
« Reply #5 on: October 30, 2010, 01:03:32 am »
...
man, that was one long post. luckily my answer will be short...

Any Ideas?
sure:
MyStream.WriteComponents(Button1);

Sounds good to me.

I do not doubt that my method is warped but the idea is that rather than using the IDE to set up the GUI, or whatever, I can type it in as a text file and then pull things in and connect them.

I would assume Lazarus does something similar, in XML format [?], but when I have looked at configuration files I can't make sense of them. Therefore I go 'badboy' and start inventing things.

I'll assume..

Quote
MyStream.WriteComponents(Button1);

Assumes I have spent a lot of time using the IDE to set up these components and making them look 'pretty' before writing them to 'The Stream' but I could be wrong.

Of course I am just a novice.

I'm just looking to load them from the text file, look at how things look and edit the text file accordingly. Presto..... err, or rather not.

Of course I might have missed something behind the power of..

Quote
MyStream.WriteComponents(Button1);

ivan17

  • Full Member
  • ***
  • Posts: 173
Re: Mis-behaving TSplitter(s)
« Reply #6 on: October 30, 2010, 01:14:01 pm »
Of course I might have missed something behind the power of..
MyStream.WriteComponents(Button1);
aside from the fact that it works like a charm, that it saves only non-default properties (those that are bold in object inspector) that it does the job in one line and that it uses file streams / memory streams? not much.

you can even clone components that way (if they are similar) - read one, change its name (because two can not have the same name), change the top property (so they don't overlap), read same data into second component...

MorbidFractal

  • Jr. Member
  • **
  • Posts: 97
Re: Mis-behaving TSplitter(s)
« Reply #7 on: October 30, 2010, 03:46:07 pm »
OK.. Thanks, understood. One I should bear in mind to tidy up later however I think we are still at cross purposes.

When I say I 'write a text file' I am not doing that within the program I am using a text editor and typing the information in by hand. The program reads in that text file and creates the objects.

I was writing the data in the text file out as records after parsing it in the hope that things would load up more quickly subsequently. This WriteComponents definitely looks like the proper way to do it however I need to have the components in place before I can write them out.

Either I can do it the 'proper' way and use the IDE to design the form or I can do it my, stupid, way by writing a text file by hand loading the components in from that file and then write them back out again using this WriteComponents method.

For my sins, apart from the grief of getting the thing working, I believe using a text file to initially define the components will, dare I say, ultimately be more productive. As you suggest there are many 'defaults' and other things I may not generally be interested in but using the IDE I have to find the others to set them up.. and then move things about with the mouse or whatever.

Otherwise I can take a 'base' set and for example

Code: [Select]
#Prototype Panel;
#01 [TYPE 01]Integer
#02 [INDEX]Integer*
#03 [ALIGN](0{None},1{Left},2{Right},3{Top},4{Bottom},5{Client},6{Custom},7{Relative})
#04 [ANCHORS]{Top,Left,Right,Bottom; 0000}
#05 [TOP]Integer
#06 [HEIGHT]Integer
#07 [LEFT]Integer
#08 [WIDTH]Integer
#09 [PercentLeft]Integer
#10 [PercentRight]Integer
#11 [CAPTION]String
#12 [Color](0{clButnFace},1{clMoneyGreen}3{clEtc},#{colorstring}
#13 [Color](0{clButnFace},1{clMoneyGreen}3{clEtc},#{colorstring}
#14 [EVENT]Integer
#15 [Linkage]Integer

and

Code: [Select]
#Prototype Splitter;
#01 [TYPE 02]Integer
#02 [INDEX]Integer*
#03 [ALIGN](0{None},1{Left},2{Right},3{Top},4{Bottom},5{Client},6{Custom},7{Relative})
#04 [ANCHORS]{Top,Left,Right,Bottom; 0000}
#05 [TOP]Integer
#06 [HEIGHT]Integer
#07 [LEFT]Integer
#08 [WIDTH]Integer
#09 [PercentLeft]Integer
#10 [PercentRight]Integer
#11 [CAPTION]String,
#12 [Color](0{clButnFace},1{clMoneyGreen}3{clEtc},#{colorstring}
#13 [Color](0{clButnFace},1{clMoneyGreen}3{clEtc},#{colorstring}
#14 [EVENT]Integer
#15 [Linkage]Integer

Then if I want to design a form based on Panels and Splitters my text file might look like this

Code: [Select]
,02,,1,0000,000,003,000,003,000,000,Nil,1,0,0,0,     //place a splitter align left.
,01,,1,0000,000,000,000,200,000,000,Nil,1,0,1,0,     //place a panel align left
,01,,5,0000,000,000,000,200,000,000,Nil,1,0,1,0,     //place a panel align client
  ,02,,3,0000,000,003,000,003,000,000,Nil,1,0,0,0,   //place a splitter align top
  ,01,,3,0000,000,000,000,200,000,000,Nil,1,0,1,0,   //place a panel align top
  ,01,,5,0000,000,000,000,200,000,000,Nil,1,0,1,0,   //place a panel align client
    ,02,,1,0000,000,003,000,003,000,000,Nil,1,0,0,0, //place a splitter align left
    ,01,,1,0000,000,000,000,200,000,000,Nil,1,0,1,0, //place a panel align left
    ,01,,5,0000,000,000,000,200,000,000,Nil,1,0,1,0, //place a panel align client

With the indents sorting out the parentage, zero indent places the component on the parent form and then it works back from there. If, on loading things, they do not look right then rather than finding the appropriate control and then looking for the attribute in the object inspector or dragging it about I can just go back to the text file and, hopefully, quickly modify the 'offending' entry then reload it to see how things are going.

Once I am happy with it then I can write things out properly for future use using this WriteComponents method.

As things stand I am stuck with TSplitters that do not wish to play nicely. Sorry for providing too much confusing background information.

MorbidFractal

  • Jr. Member
  • **
  • Posts: 97
Re: Mis-behaving TSplitter(s)
« Reply #8 on: October 31, 2010, 04:25:58 pm »
Might this be a clue as to why things, Splitters and in fact others, are not working..?

http://img828.imageshack.us/img828/7523/notfound.png
http://img835.imageshack.us/img835/7/err201.png
« Last Edit: October 31, 2010, 05:07:25 pm by MorbidFractal »

 

TinyPortal © 2005-2018