Top.Mail.Ru
Мастера DELPHI, Delphi programming community Титульная страница Поиск, карта сайта Написать письмо 
| Новости | Поиск | FAQ | Статьи | Книги | Новости VCL |
| Форумы ЧАТ | Орешник | KOL и MCK |
Форумы -» Основная («Начинающим»)/ Базы / WinAPI / Компоненты / Сети / Media / Игры / Corba и COM / KOL / FreePascal / .Net / Прочее / RSDN.ORG

« предыдущая ветвь | форум | следующая ветвь »

Сохранение свойств компонента в файл [D6]


Vitalik ©   (2004-07-02 10:58:00) 

Привет! Я тут бьюсь над одной задачкой. У меня есть компонент TChart на котором отображаются графики. Для TChart будут предусмотрены настройки - при помощи TChartEditor или TTeeCommander. Настройки нужно сохранять в поток. То есть пользователь сможет сам устанавливать настройки и сохранять их в файл. Вот в этом проблема - настроек у TChart великое множество. И я отказался от того, чтобы вручную каждую опцию записывать в файл, а потом считывать.

Решил я как-то автоматизировть процесс сохранения настроек. Хотя бы и не всех - это как получится.

Для этого я сначала попробовал всякие TWriter и TReader но мне показалось (долго не углублялся) что они, мягко говоря не работают. Не знаю, может я что-то не то делаю, в общем я пошёл другим путём. Для начала я попытался в дереве отобразить все

параметры компонента и написал такую процедуру:

procedure TForm1.FillProps(ANode: TtreeNode; AObject: TObject);
var
ATypeInfo: PTypeInfo;
ATypeData: PTypeData;
i, n, l: integer;
List: PPropList;
NNode: TTreeNode;
s: string;
ObjProp: TObject;

IntVal: integer;
begin
 ATypeData := GetTypeData(AObject.ClassInfo);
 n := ATypeData.PropCount;

 if N <= 0 then
   Exit;

 GetMem(List, SizeOf(PPropInfo)*N);
 try
   GetPropInfos(AObject.ClassInfo,List);
   for I:= 0 to N - 1 do
     begin
       if AObject.FieldAddress(List[I]^.name) <> nil then showmessage(List[I]^.name);

       case List[I].PropType^.Kind of

         tkEnumeration:
           begin
             ATypeInfo := List[I].PropType^;

             IntVal := GetEnumValue(ATypeInfo,List[i]^.name);
             NNode := tv.Items.AddChild(ANode, List[I]^.name + ': '+ List[I]^.PropType^.Name +' = ' + IntToStr(intVal));
           end;
         tkInteger:
           begin
             IntVal := Integer(GetPropValue(AObject, List[i]^.name));
             NNode := tv.Items.AddChild(ANode, List[I]^.name + ': Integer = ' + IntToStr(IntVal) );
           end;

         tkFloat:
           begin
             NNode := tv.Items.AddChild(ANode, List[I]^.name + ': ' + List[I]^.PropType^.Name + ' = ' +

FloatToStr(GetFloatProp(AObject, List[i]^.Name)) );
           end;
         tkString, tkLString, tkWString:
           begin
             NNode := tv.Items.AddChild(ANode, List[I]^.name + ': String = ' + GetStrProp(AObject, List[i]^.name));

           end;
         tkSet:
           begin

             s := GetSetProp(AObject, List[i]^.name, false);
             Intval := GetOrdProp(AObject, List[i]^.name);
             NNode := tv.Items.AddChild(ANode, List[I]^.name + ': Set of' + List[i]^.PropType^.name +' = ' + s + ' (' +

IntToStr(IntVal) + ')');

           end;
         tkClass:
           begin
             NNode := tv.Items.AddChild(ANode, List[I]^.name + ': ' + List[I]^.PropType^.Name);
             try
               ObjProp := nil;
               ObjProp := GetObjectProp(AObject, List[I]^.name);
               if ( ObjProp <> nil) then
                 FillProps(
                           NNode,
                           ObjProp
                           );
             except

             end;
           end;
       end;

     end;
 finally
   FreeMem(List,SizeOf(PPropInfo)*N);
 end;
end;

Она хоть и выводит ошибку при входе в один из классов, но в целом работает. Кстати, с Enumeration и Set у меня есть отдельные вопросы, но сейчас не об этом.

Потом, немного преобразовав эту процедуру я получил две другие - для чтения и записи настроек в поток/из потока:

procedure TForm1.SaveObjectToStream(AStream: TStream; AObject: TObject);
var
ATypeInfo: PTypeInfo;
ATypeData: PTypeData;
i, n, l: integer;
List: PPropList;

s: string;
StrLen: String;
ObjProp: TObject;

IntVal: integer;
FloatVal: real;
begin
 ATypeData := GetTypeData(AObject.ClassInfo);
 n := ATypeData.PropCount;

 if N <= 0 then
   Exit;

 GetMem(List, SizeOf(PPropInfo)*N);
 try
   GetPropInfos(AObject.ClassInfo,List);
   for I:= 0 to N - 1 do
     begin
       case List[I].PropType^.Kind of

         tkEnumeration:
           begin
             ATypeInfo := List[I].PropType^;

             IntVal := GetEnumValue(ATypeInfo,List[i]^.name);
             AStream.Write(IntVal, SizeOF(IntVal) );
           end;
         tkInteger:
           begin
             IntVal := Integer(GetPropValue(AObject, List[i]^.name));
             AStream.Write(IntVal, SizeOF(IntVal) );
           end;

         tkFloat:
           begin
             FloatVal := GetFloatProp(AObject, List[i]^.Name);
             AStream.Write(FloatVal, SizeOF(FloatVal) );
           end;
         tkString, tkLString, tkWString:
           begin
             s := GetStrProp(AObject, List[i]^.name);
             IntVal := Length(S);
             AStream.Write(IntVal, SizeOf(IntVal) );
             AStream.Write(PChar(s)^, IntVal);
           end;
         tkSet:
           begin
             Intval := GetOrdProp(AObject, List[i]^.name);
             AStream.Write(IntVal, SizeOf(IntVal) );
           end;
         tkClass:
           begin
             try
               ObjProp := nil;
               ObjProp := GetObjectProp(AObject, List[I]^.name);
               if ( ObjProp <> nil) then
                 SaveObjectToStream(AStream, ObjProp);
             except

             end;
           end;
       end;

     end;
 finally
   FreeMem(List,SizeOf(PPropInfo)*N);
 end;
end;

Продолжение - в следующем сообщении, потому что в одно всё не помещается :)


Amoeba   (2004-07-02 18:39:00) [1]


> TJvFormStorage

В FreeWare библиотеке JVCL http://jvcl.sourceforge.net/


Vitalik   (2004-07-02 15:02:00) [2]

А кто-нибудь значет, почему вообще не работает

 SetInt64Prop(button1, 'left', 100);

?

И как сделать чтоб работало?


Vitalik   (2004-07-02 13:37:00) [3]

4, 7 приводит примерно к следующему результату:

object Chart1: TChart
 Left = 528
 Top = 96
 Width = 136
 Height = 250
 Title.Text.Strings = (
   'TChart')
 TabOrder = 1
end

2NeyroSpace
Ну вот видите, только эти свойства. А ведь на форме наверняка были и кнопки! Где кнопки-то в полученном файле?

Я не проверял, но думаю, что если добавить в мои процедуры обработку также и массивов tkArray - то тогда будет в дереве отображаться и свойства Controls и Components.

Так что это как бы не то, что мне нужно... Попробуйте запустить процедуру FillProps и станет ясно, что мне нужно пробегаться по большему количеству свойств!

2TUser
TJvFormStorage - а что это и где можно взять?


Анонимщик   (2004-07-02 12:53:00) [4]

Да еще есть и TParser в Classes.pas, с dfm-ами этими поможет работать. А список свойств вытаскивается с помощью функций types.pas. Но со всем вышесказанным его можно и не использовать.


Семен Сорокин   (2004-07-02 12:49:00) [5]

и в RX тоже есть


Romkin   (2004-07-02 12:49:00) [6]

А нельзя просто использовать TStream.WriteComponent? все published запишется (только не забыть для чтения вернуть позицию в начало ;) )
Есть еще TWriter, TReader, TFiler - они с dfm работают.


TUser   (2004-07-02 12:45:00) [7]

Можно еще использовать TJvFormStorage.


NeyroSpace   (2004-07-02 12:37:00) [8]

unit Unit1;

interface

uses
 Windows, Messages, Classes, Forms;

type
 TForm1 = class(TForm)
   procedure FormCreate(Sender: TObject);
 private
   { Private declarations }
 public
   { Public declarations }
 end;

var
 Form1: TForm1;
 Form2: TForm;
 srtlist: TStringList;

implementation
{$R *.DFM}

function ComponentToString(Component: TComponent): string;
var
 ms: TMemoryStream;
 ss: TStringStream;
begin
 ss := TStringStream.Create(' ');
 ms := TMemoryStream.Create;
 try
   ms.WriteComponent(Component);
   ms.position := 0;
   ObjectBinaryToText(ms, ss);
   ss.position := 0;
   Result := ss.DataString;
 finally
   ms.Free;
   ss.free;
 end;
end;

procedure StringToComponent(Component: TComponent; Value: string);
var
 StrStream:TStringStream;
 ms: TMemoryStream;
begin
 StrStream := TStringStream.Create(Value);
 try
   ms := TMemoryStream.Create;
   try
     ObjectTextToBinary(StrStream, ms);
     ms.position := 0;
     ms.ReadComponent(Component);
   finally
     ms.Free;
   end;
 finally
   StrStream.Free;
 end;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
 Form2 := TForm.Create(nil);
 srtlist := TStringList.Create;
 srtlist.LoadFromFile('Form2.txt');

 StringToComponent(Form2, srtlist.Text);

 Form2.parent := nil;
 Form2.Visible := true;
 Form2.Caption := 'Создана из Form2.DFM';

 srtlist.Free;
end;

end.

==============
файл:
object Form2: TForm
 Left = 195
 Top = 107
 Width = 443
 Height = 218
 Caption = 'Form1'
 Color = clBtnFace
 Font.Charset = DEFAULT_CHARSET
 Font.Color = clWindowText
 Font.Height = -11
 Font.Name = 'MS Sans Serif'
 Font.Style = []
 OldCreateOrder = False
 PixelsPerInch = 96
 TextHeight = 13
end
==============

Также можно и сохранить


Vitalik   (2004-07-02 12:08:00) [9]

Ну вообще-то мне нужно именно сохранять в файл, а не отображать свойства на экране. Но я попробую разобраться в коде ExtLib Где-то там наверняка должен же быть поиск свойств!


Анонимщик   (2004-07-02 11:37:00) [10]

Кажется, лучше стандартным все же. Есть, например, ExtLib


Vitalik   (2004-07-02 10:58:00) [11]

Продолжение.

procedure TForm1.LoadObjectFromStream(AStream: TStream; AObject: TObject);
var
ATypeInfo: PTypeInfo;
ATypeData: PTypeData;
i, n, l: integer;
List: PPropList;

s: string;
StrLen: String;
ObjProp: TObject;

IntVal: integer;
FloatVal: real;
begin
 ATypeData := GetTypeData(AObject.ClassInfo);
 n := ATypeData.PropCount;

 if N <= 0 then
   Exit;

 GetMem(List, SizeOf(PPropInfo)*N);
 try
   GetPropInfos(AObject.ClassInfo,List);
   for I:= 0 to N - 1 do
     begin
       if AObject.FieldAddress(List[I]^.name) <> nil then showmessage(List[I]^.name);

       case List[I].PropType^.Kind of

         tkEnumeration:
           begin
{              AStream.Read(IntVal, SizeOf(IntVal) );
             SetEnumProp(AObject, List[i]^.Name, IntVal);}
           end;
         tkInteger:
           begin
             AStream.Read(IntVal, SizeOf(IntVal) );
             SetInt64Prop(AObject, List[i]^.Name, IntVal);

           end;

         tkFloat:
           begin
             AStream.Read(FloatVal, SizeOf(FloatVal) );
             SetFloatProp(AObject, List[i]^.Name, FloatVal);
           end;
         tkString, tkLString, tkWString:
           begin
             AStream.Read(IntVal, SizeOf(IntVal) );
             SetLength(s, IntVal);
             AStream.Read(PChar(s)^, IntVal);
             SetStrProp(AObject, List[I]^.name, s);
           end;
         tkSet:
           begin
{              AStream.Read(IntVal, SizeOf(IntVal) );
             SetSetProp(AObject, List[I]^.name, );}
           end;
         tkClass:
           begin
             try
               ObjProp := nil;
               ObjProp := GetObjectProp(AObject, List[I]^.name);
               if ( ObjProp <> nil) then
                 LoadObjectFromStream(AStream, ObjProp);
             except

             end;
           end;
       end;

     end;
 finally
   FreeMem(List,SizeOf(PPropInfo)*N);
 end;
end;

Сохраняется в общем-то без проблем (хотя выдаёт ошибку при заходе в тот же класс). При загрузке происходит такая последовательность:
1. Загружается свойство Tag и нормально устанавливается.
2. Загружается свойство name - тоже всё нормально.
3. Загружается свойство Left а вот при установке свойства происходит ошибка. И проблема не в считывании данных из потока, а именно при установке занчения, т.е. в SetInt64Prop(AObject, List[i]^.Name, IntVal);

Так вот что у меня не правильно?

И вообще, что вы думаете о моём огороде? :)
Может действительно можно чем-то стандартным обойтись? Или мой подправить? Лучше - править мой, так пользы больше :)

Спасибо!


Наверх

Титульная страница Поиск, карта сайта Написать письмо