Модераторы: Poseidon, Snowy, bems, MetalFan

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> TPersistent. Статья. или новый взгляд на сохранение настроек 
:(
    Опции темы
Snowy
  Дата 2.5.2006, 13:07 (ссылка) |    (голосов:3) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Модератор
Сообщений: 11363
Регистрация: 13.10.2004
Где: Питер

Репутация: 192
Всего: 484



Как ни странно, но большинство дельфистов не имеют представления о том, что это за TPersistent такой и зачем он нужен.
Некоторые вообще даже не знают о его существовании. А ведь это один из базовых классов.
Данная статья написана мной в результате поиска решения о сохранении настроек и параметров.
Так я и пришел к TPersistent и использованию его возможностей.
И так статья:

TPersistent
Виртуальный класс – наследник TObject. Является базовым классом – предком большинства стандартных классов.
Основное свойство данного класса заключается в его названии – Хранимый. То есть класс, который может быть сохранен и впоследствии восстановлен. Сам TPersistent не имеет методов для сохранения и восстановления. Данную работу реализуют классы, работающие с потоками (stream).
Наиболее очевидна его работа на примере TForm. В отличии от той же JAVA мы не прописываем параметры объектов, расположенных на форме. Вместо этого создается dfm файл, в котором и сохраняются все эти данные. При компиляции программы, он помещается в ресурсы. При создании формы, он читается из ресурсов, как наследник TPersistent.

Применение
Итак, как же можно использовать TPersistent? Естественно сам по себе он не представляет особой ценности. Ценностью являются его свойства. Мы можем легко сохранять и читать свои объекты, пронаследовав их не от TObject, а от TPersistent.

Зачем это нужно?
Ответ прост. Нам постоянно приходится сохранять какие-то настройки, параметры. Придумано множество вещей для данных целей: ini файлы, реестр, xml и другие умные вещи. TPersistent позволяет забыть про это, как про страшный сон. Не нужно разбирать параметры, сравнивать версии, конвертировать типы данных. Всего лишь нужно сказать: сохрани или прочитай этот объект.

А как же версии?
Если в следующей версии Вашей программы, в Вашем объекте появятся новые поля, то прочитать файл не составит никакого труда – новые поля просто не будут прочитаны, т.к. Их нет в файле. Но все остальные поля будут корректно прочитаны, а впоследствии объект будет сохранен уже с новым полем. Сложнее, если вы убираете одно из полей. Но и тут ничего страшного – помогает блок try .. except, который в готовой программе и заметен не будет. Все остальные поля также будут корректно прочитаны, а впоследствии корректно сохранены.

Как?
Теперь непосредственно перейдем к вопросу, как это делать.
Для сохранения и чтения существуют 2 класса: TReader и TWriter. Они умеют сохранять разные типы данных, в том числе и объекты-наследники TPersistent. Есть правда одно НО. В Borland почему-то решили, что сохранять персистенты программистам совсем не нужно. Хотя это мало кого может остановить. Чтобы активировать эту возможность, нужно всего лишь пронаследовать TWriter и TReader:
Код
type
  TObjWriter = class(TWriter)
  end;
  TObjReader = class(TReader)
  end;

Это все. Никакого кода – просто наследуем. Это открывает нам protected методы для сохранения объектов-наследников TPersistent.
Далее приведу код модуля, который будет сохранять и читать объекты.
Код
unit Saver;
{* модуль для сохранения и чтения объектов }

interface

uses SysUtils, Classes;

procedure WriteObj(obj: TPersistent; st: TStream);
{* Записать объект в стрим }
procedure ReadObj(obj: TPersistent; st: TStream);
{* Прочитать объект из стрима }
procedure SaveObjToFile(obj: TPersistent; FileName: string);
{* Сохранить объект в файл }
procedure LoadObjFromFile(obj: TPersistent; FileName: string);
{* Загрузить объект из файла }

implementation

type
  TObjWriter = class(TWriter)
  end;
  TObjReader = class(TReader)
  end;

procedure WriteObj(obj: TPersistent; st: TStream);
begin
  with TObjWriter.Create(st, 64) do
  begin
    WriteProperties(obj);
    FlushBuffer; Free;
  end;
end;

procedure ReadObj(obj: TPersistent; st: TStream);
begin
  with TObjReader.Create(st, 1) do
  begin
    while st.Position < st.Size do
    try ReadProperty(obj); except Continue; end;
    Free;
  end;
end;

procedure SaveObjToFile(obj: TPersistent; FileName: string);
var fs: TFileStream;
begin
  try
    fs := TFileStream.Create(FileName, fmCreate or fmShareDenyWrite);
    try
      WriteObj(obj, fs);
    finally
      fs.Free;
    end;
  except end;
end;

procedure LoadObjFromFile(obj: TPersistent; FileName: string);
var fs: TFileStream;
begin
  if FileExists(FileName) then try
    fs := TFileStream.Create(FileName, fmShareDenyWrite);
    try
      ReadObj(obj, fs);
    finally
      fs.Free;
    end;
  except end;
end;

end.

Что же в этом коде. 4 процедуры:
procedure WriteObj(obj: TPersistent; st: Tstream);
Просто создает TWriter, сохраняет объект, уничтожает TWriter.
procedure ReadObj(obj: TPersistent; st: TStream);
Просто создает TReader, читает все параметры объекта, уничтожает TReader.
procedure SaveObjToFile(obj: TPersistent; FileName: string);
Это пример реализации сохранения объекта, на примере TFileStream.
procedure LoadObjFromFile(obj: TPersistent; FileName: string);
Это пример реализации чтения объекта, на примере TFileStream.
Принцип работы простой – создали стрим, сохранили/прочитали объект, уничтожили стрим.

Пример №1 – один объект
Теперь перейдем к конкретному примеру.
У нас есть объект, в котором мы храним какие-то настройки или просто какие-то данные нашей программы. Сохраним и прочитаем его, используя unit Saver.
Код
unit Options;

interface

uses SysUtils, Classes, Saver;

type
  TOptions = class(TPersistent)
  {* класс с какими-то настройками или данными }
  private
    FMyStr: string;
    FDateTime: TDateTime;
  published
    property DateTime: TDateTime read FDateTime write FDateTime;
    {* данные типа дата-время }
    property MyStr: string read FMyStr write FMyStr;
    {* данные типа строка }
  end;

var MainOptions: TOptions;
{* Объект с настройками, который мы будем использовать и сохранять }

implementation

const OptFile = 'options.sav';
{* имя файла с настройками }

initialization
  MainOptions := TOptions.Create;
  LoadObjFromFile(MainOptions, ExtractFilePath(ParamStr(0)) + OptFile);
  {* При запуске программы сразу создаем и читаем объект с настройками }

finalization
  SaveObjToFile(MainOptions, ExtractFilePath(ParamStr(0)) + OptFile);
  MainOptions.Free;
  {* При закрытии программы сохраняем и уничтожаем 
     объект с настройками }

end.

Для примера я взял простой объект с двумя полями – дата и строка.
Здесь все просто – создали объект, прочитали из файла. Сохранили, уничтожили.
Можно было сохранение и загрузку поместить в деструктор и конструктор соответственно. Но я не стал этого делать, чтобы не раздувать код.
В папке '1' (в прилагаемом файле) можно найти пример работы с этим кодом.

Как это работает?
Как вы наверное заметили, в нашем объекте есть private данные и published property. Зачем было делать именно так, а не просто создать данные в разделе public?
Смысл в том, что сохраняется только RTTI информация. То есть та, которая находится в разделе published. Данные, расположенные в разделе publishet имеют информацию об именах полей и их типах, в то время, как остальная информация это всего лишь поля в памяти. А для сохранения эта информация нам нужна. При этом данные могут вообще не существовать. Важны лишь property. Они могут вообще не ссылаться ни на какие данные, а только иметь функции для получения и установки значения.

Пример №2 – несколько объектов
Пока вроде все просто. Берем объект, сохраняем в стрим, читаем. Но возникает ряд вопросов. Например, как сохранить группу объектов в один файл, как сохранять только необходимые данные, а не все, для чего можно использовать property, не связанные с данными объекта.
Я решил объеденить все это в один пример.
Для примера я взял всего 2 объекта, хотя их может быть сотни.
Первый объект нам уже знаком, я не буду его описывать – беру из прошлого примера без изменения.
Второй объект интереснее.
Данный объект позволяет нам сохранять 4 основных параметра главной формы – положение и размеры. Сам объект при этом не хранит никаких данных.
Самый простой способ сохранения всех объектов вместе – это сделать сами объектры полями одного общего объекта, который мы и будем сохранять.
Код
unit Options;

interface

uses SysUtils, Classes, Saver, Forms;

type
  TOptions = class(TPersistent)
  {* Какие-то настройки или данные }
  private
    FMyStr: string;
    FDateTime: TDateTime;
  published
    property DateTime: TDateTime read FDateTime write FDateTime;
    {* данные типа дата-время }
    property MyStr: string read FMyStr write FMyStr;
    {* данные типа строка }
  end;

  TMainFormOptions = class(TPersistent)
  {* активные параметры главной формы }
  private
    FOwner:  TForm;
    {* Здесь можно было бы и не запоминать форму с которой мы работаем,
       а просто обращаться к Application.MainForm. 
       Но я привожу общее решение - это может быть и не главная форма, 
       а любая другая. }
    procedure SetHeight(const Value: integer);
    procedure SetLeft(const Value: integer);
    procedure SetTop(const Value: integer);
    procedure SetWidth(const Value: integer);
    {* Set'ы устанавливают параметры на шей формы}
    function GetHeight: integer;
    function GetLeft: integer;
    function GetTop: integer;
    function GetWidth: integer;
    {* Get'ы берут значения у формы. Сами ничего не храним. }
  public
    constructor Create(AOwner: TForm);
    {* конструктор. Запоминает какой формой управляем }
  published
    property Left: integer read GetLeft write SetLeft;
    {* Left формы }
    property Top: integer read GetTop write SetTop;
    {* Top формы }
    property Width: integer read GetWidth write SetWidth;
    {* Width формы }
    property Height: integer read GetHeight write SetHeight;
    {* Height формы }
  end;

  TAllOptions = class(TPersistent)
  {* Класс для сохранения всех настроек вместе }
  private
    FMainFormOptions: TMainFormOptions;
    FMainOptions: TOptions;
  public
    constructor Create(AOwner: TForm);
    {* Конструктор. Создает все объекты настроек и читает }
    destructor  Destroy; override;
    {* Деструктор. Записывает и уничтожает объекты с настройками }
  published
    property MainOptions: TOptions read FMainOptions write FMainOptions;
    {* Основные настройки }
    property MainFormOptions: TMainFormOptions read FMainFormOptions 
                                               write FMainFormOptions;
    {* Параметры главного окна }
  end;

var Opt: TAllOptions;
{* Объект,через который мы будем обращаться ко всем настройкам и хранить их }

implementation

const OptFile = 'options.sav';
{* Файл с настройками }

{ TMainFormOptions }

constructor TMainFormOptions.Create(AOwner: TForm);
begin
  FOwner := AOwner;
end;

function TMainFormOptions.GetHeight: integer;
begin
  result := FOwner.Height;
end;

function TMainFormOptions.GetLeft: integer;
begin
  result := FOwner.Left;
end;

function TMainFormOptions.GetTop: integer;
begin
  result := FOwner.Top;
end;

function TMainFormOptions.GetWidth: integer;
begin
  result := FOwner.Width;
end;

procedure TMainFormOptions.SetHeight(const Value: integer);
begin
  FOwner.Height := Value;
end;

procedure TMainFormOptions.SetLeft(const Value: integer);
begin
  FOwner.Left := Value;
end;

procedure TMainFormOptions.SetTop(const Value: integer);
begin
  FOwner.Top := Value;
end;

procedure TMainFormOptions.SetWidth(const Value: integer);
begin
  FOwner.Width := Value;
end;

{ TAllOptions }

constructor TAllOptions.Create(AOwner: TForm);
begin
  inherited Create;
  FMainOptions := TOptions.Create;
  FMainFormOptions := TMainFormOptions.Create(AOwner);
  LoadObjFromFile(Self, ExtractFilePath(ParamStr(0)) + OptFile);
end;

destructor TAllOptions.Destroy;
begin
  SaveObjToFile(Self, ExtractFilePath(ParamStr(0)) + OptFile);
  inherited;
end;

end.


Теперь приведу пример, как это использовать:
Код
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TForm1 = class(TForm)
  {* Главная форма }
    Button1: TButton;
    Label1: TLabel;
    Edit1: TEdit;
    procedure Button1Click(Sender: TObject);
    {* Назначить новые настройки }
    procedure FormCreate(Sender: TObject);
    {* Конструктор формы. Создает настройки отображает их }
    procedure FormDestroy(Sender: TObject);
    {* Деструктор. Уничтожает объекты с настройками }
  private
    procedure ApplyOptions;
    {* Отображает настройки }
  end;

var Form1: TForm1;

implementation

uses Options;

{$R *.dfm}

procedure TForm1.ApplyOptions;
begin
  Label1.Caption := DateTimeToStr(Opt.MainOptions.DateTime);
  Edit1.Text := Opt.MainOptions.MyStr;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  Opt.MainOptions.DateTime := now;
  Opt.MainOptions.MyStr := Edit1.Text;
  ApplyOptions;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  Opt := TAllOptions.Create(Self);
  {* Теперь настройки создаем здесь, т.к. MainFormOptions требуют
     существования нашей формы.}
  ApplyOptions;
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  Opt.Free;
end;

end.

Данный код содержится в папке '2' архива. Можете посмотреть его в действии. На мой взгляд, пример довольно полезный.
Таким образом мы можем для каждой формы нашей программы создать наследник класса, сохраняющего параметры формы, добавив всего пару строчек кода.

Возможные проблемы
Пока что на единственный момент, на который я натолкнулся – property должны иметь read и write параметры, чтобы они сохранялись. Если не будет одного из них – поле сохраняться не будет. Даже объект, указатель на который вы не собираетесь назначать, должен иметь параметр write.
Если у Вас найдутся другие проблемы, спрашивайте.

Assign
Поскольку данная статья является описанием класса TPersistent, я не могу не упомянуть еще одно отличие от TObject. Данный класс реализует открытый метод Assign, который производит присвоение полей нашему объекту от другого. Но, думаю, назначение метода Assign известно каждому. Поэтому не буду на нем останавливаться. Замечу лишь, что в самом TPersistent этот метод не производит никаких действий. Он только вызывает protected метод AssignTo, который и должны наследовать его потомки.

Заключение
Итак, TPersistent является удобным средством для сохранения и восстановления объектов без применения сторонних модулей. Данный механизм уже имеется в Вашей программе, если Вы используете модули Classes или Forms. Поэтому это не добавит размера Вашей программе. Для сохранения мы не придумываем ничего нового, а просто используем стандартный механизм.
Остались нераскрыты некоторые моменты. Например, как сохранять объекты в один файл, не объединяя их в один класс. Но это уже не входит в стандартный механизм и требует отдельной реализации.
Также не рассмотрены варианты работы с другими типами стримов. А в качестве таковых можно рассматривать любые. Отдельного внимания может заслуживать TResourceStream. Мы можем сохранить наши объекты в файл,а потом прилинковать к нашему exe в качестве ресурса.
Если вы захотите расширить данную статью своими исследованиями на эту тему – пишите, обсудим.
Жду Ваших замечаний и дополнений.
Сув. Snowy.
    

Присоединённый файл ( Кол-во скачиваний: 120 )
Присоединённый файл  TPersistent.zip 16,74 Kb
PM MAIL   Вверх
Alexeis
Дата 2.5.2006, 13:26 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Амеба
Group Icon


Профиль
Группа: Админ
Сообщений: 11743
Регистрация: 12.10.2005
Где: Зеленоград

Репутация: 109
Всего: 459



Snowy, в модуле Saver исползуется процедура
Код

procedure WriteObj(obj: TPersistent; st: TStream);    
begin    
  with TObjWriter.Create(st, 64) do    
  begin    
    WriteProperties(obj);    
    FlushBuffer; Free;    
  end;    
end;


Что значит число 64, которое передается в качестве параметра конструктору. 
------------------------------------------------------------------------------------
Странно но при компиляции первого примера у меня выскачило 
Undeclarated identifier "Self" на строку
Цитата

  LoadObjFromFile(Self, ExtractFilePath(ParamStr(0)) + OptFile);

Что он должен понимать в данном случае как Self 

Это сообщение отредактировал(а) alexeis1 - 2.5.2006, 13:41


--------------------
Vit вечная память.

Обсуждение действий администрации форума производятся только в этом форуме

гениальность идеи состоит в том, что ее невозможно придумать
PM ICQ Skype   Вверх
Rouse_
Дата 2.5.2006, 13:41 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 469
Регистрация: 23.4.2005

Репутация: 5
Всего: 29



У TComponent-а в protected есть методы WriteState и ReadState...
Но это так, к слову ;) 


--------------------
 Vae Victis
(Горе побежденным (лат.))
Демо с открытым кодом: http://rouse.drkb.ru 
PM MAIL WWW ICQ   Вверх
~FoX~
Дата 2.5.2006, 13:45 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


НЕ рыжий!!!
****


Профиль
Группа: Участник Клуба
Сообщений: 2819
Регистрация: 8.10.2003
Где: Зеленоград

Репутация: 13
Всего: 68



Цитата(alexeis1 @  2.5.2006,  14:26 Найти цитируемый пост)
Что значит число 64

TWriter наследник абстрактоного класса TFiler

Цитата

Creates a filer object.
Delphi syntax:
constructor Create(Stream: TStream; BufSize: Integer);
Description
Call Create to instantiate a filer descendant at runtime, if necessary. It is seldom necessary to directly create a filer object because the methods and routines that use them automatically create filers.
Create allocates memory for a filer object, and associates it with the stream passed in the Stream parameter, with a buffer of size BufSize.
 


--------------------
user posted image
…множественность никогда не следует полагать без необходимости…
PM MAIL WWW ICQ Jabber   Вверх
Snowy
Дата 2.5.2006, 13:50 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Модератор
Сообщений: 11363
Регистрация: 13.10.2004
Где: Питер

Репутация: 192
Всего: 484



Цитата(alexeis1 @  2.5.2006,  13:26 Найти цитируемый пост)
Что значит число 64, которое передается в качестве параметра конструктору. 
Это размер буфера. Я прописал 64, но допустимо любое положительное число.

Цитата(alexeis1 @  2.5.2006,  13:26 Найти цитируемый пост)
Странно но при компиляции первого примера у меня выскачило 
Undeclarated identifier "Self" на строку
Это не странно. Это просто я правил и забыл проверить.
Сейчас поправил - теперь все, как нужно.

Добавлено @ 13:52 
Цитата(Rouse_ @  2.5.2006,  13:41 Найти цитируемый пост)
У TComponent-а в protected есть методы WriteState и ReadState...
TComponent это уже более поздняя стадия.
Для персистента установка состояния не так актуальна. А для сохранения настроек вообще не имеет значения. 
PM MAIL   Вверх
Alexeis
Дата 2.5.2006, 13:58 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Амеба
Group Icon


Профиль
Группа: Админ
Сообщений: 11743
Регистрация: 12.10.2005
Где: Зеленоград

Репутация: 109
Всего: 459



Еще функция 
Код

procedure WriteObj(obj: TPersistent; st: TStream);    
begin    
  with TObjWriter.Create(st, 64) do    
  begin    
    WriteProperties(obj);    
    FlushBuffer; Free;    
  end;    
end;
не накладывает ограничений на число записываемых в поток объектов. Дело в том что я слабо себе представляю как работает  WriteProperties(obj); хотелось бы узнать не возникнут ли проблемы при попытке записи и чтении таким образом нескольких объектов, вчастности, не собъется ли указатель position у потока при удалении TWriter, сместится ли этот указатель на величину размера записанных данных после вызова этой функции.

  

Это сообщение отредактировал(а) alexeis1 - 2.5.2006, 13:59


--------------------
Vit вечная память.

Обсуждение действий администрации форума производятся только в этом форуме

гениальность идеи состоит в том, что ее невозможно придумать
PM ICQ Skype   Вверх
Rouse_
Дата 2.5.2006, 14:14 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 469
Регистрация: 23.4.2005

Репутация: 5
Всего: 29



Цитата(Snowy @  2.5.2006,  14:50 Найти цитируемый пост)
А для сохранения настроек вообще не имеет значения.  

Да ну? smile  


--------------------
 Vae Victis
(Горе побежденным (лат.))
Демо с открытым кодом: http://rouse.drkb.ru 
PM MAIL WWW ICQ   Вверх
Alexeis
Дата 2.5.2006, 14:25 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Амеба
Group Icon


Профиль
Группа: Админ
Сообщений: 11743
Регистрация: 12.10.2005
Где: Зеленоград

Репутация: 109
Всего: 459



Rouse_, глянул  в help - действительено, WriteState делает тоже самое, кроме того все объекты имеющие published свойства должны быть предками TComponent иначе на форме их не увидишь. 
Мне этот вариант нравится не меньше, надо бы его проработать! 


--------------------
Vit вечная память.

Обсуждение действий администрации форума производятся только в этом форуме

гениальность идеи состоит в том, что ее невозможно придумать
PM ICQ Skype   Вверх
Snowy
Дата 2.5.2006, 14:50 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Модератор
Сообщений: 11363
Регистрация: 13.10.2004
Где: Питер

Репутация: 192
Всего: 484



Rouse_, я не спорю, что можно сохранять компоненты.
Любой наследник TPersistent может сохраняться.
Для данной цели еще проще воспользоваться методом TStream.WriteComponent.
Там собрана примерно та же обертка, что и я нарисовал.
Но смысл именно в том, что TComponent нагружает нас кучей ненужных данных и занимает больше памяти.
Зачем мне делать компонент для таких вещей, как настройки? Можно конечно для этих целей и TForm приспособить...
Статья не про то, как сделать, чтобы меньше ручками набивать. Статья про TPersistent и возможности его применения.

Цитата(alexeis1 @  2.5.2006,  13:58 Найти цитируемый пост)
не накладывает ограничений на число записываемых в поток объектов.
Ограничение есть - один стрим - один объект.
Выход - создание объекта, объединяющего группу объектов в свои property.
Второй вариант - искуственная вставка разметки в стрим. Это не сложно, но накладывает ограничение на порядок сохранения и восстановления объектов.  
PM MAIL   Вверх
Alexeis
Дата 2.5.2006, 15:07 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Амеба
Group Icon


Профиль
Группа: Админ
Сообщений: 11743
Регистрация: 12.10.2005
Где: Зеленоград

Репутация: 109
Всего: 459



Цитата(Snowy @  2.5.2006,  13:50 Найти цитируемый пост)
Для данной цели еще проще воспользоваться методом TStream.WriteComponent.

В том то и дело что не очень то удобно! Перед записью каждого компонента необходимо вызвать RegisterClass с параметром имени класса! Куда удобней использовать один writer для записи любых объектов, но вот проблема как их простым путем записать в один поток, может Stream.CopyFrom(); - и передавать кажды раз свой объект потока для каждого объекта и писать перед этим размер этого потока первым полем(чтоб потом в этои же порядке извлкать).
 


--------------------
Vit вечная память.

Обсуждение действий администрации форума производятся только в этом форуме

гениальность идеи состоит в том, что ее невозможно придумать
PM ICQ Skype   Вверх
Snowy
Дата 2.5.2006, 15:14 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Модератор
Сообщений: 11363
Регистрация: 13.10.2004
Где: Питер

Репутация: 192
Всего: 484



alexeis1, это и есть второй вариант. Ограничение - порядок сохранения и восстановления должен быть одинаковым. 
PM MAIL   Вверх
McDevil
Дата 2.6.2006, 06:01 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 285
Регистрация: 8.12.2005
Где: Казахстан, Павлод ар

Репутация: 1
Всего: 3



Snowy, хорошая статья! 


--------------------
 мы знаем столько, сколько можем, а можем столько, сколь хотим... 

Тестируем программу: SPL-программа аналогов функций  
PM MAIL WWW ICQ   Вверх
Snowy
Дата 2.6.2006, 10:25 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Модератор
Сообщений: 11363
Регистрация: 13.10.2004
Где: Питер

Репутация: 192
Всего: 484



Цитата(alexeis1 @  2.5.2006,  14:25 Найти цитируемый пост)
WriteState делает тоже самое
Хочу сделать еще одно замечание по поводу механизма сохранения компонентов.
У него есть еще один недостаток.
Пример:
Код

TMyComponent = class(TComponent)
...
published
  x, y: integer;
  color: TColor;
  txt: string;
end;

к примеру, мы сохранили. Можем прочитать.
Но! Допустим мы изменили класс. Убрали color.
При загрузке возникнет исключение.
Допустим, что мы обработаем это исключение. Тогда как загрузится объект?
Загрузится x, потом y. На color возникнет исключение. Всё. txt не загрузится.
В моей схеме обрабатывается не все целиком, а каждая пропертя отдельно.
Поэтому загрузка будет такой:
Загрузится x, потом y. На color возникнет исключение - пропускаем. Дальше читаем txt. 
PM MAIL   Вверх
Teran
Дата 19.9.2006, 18:32 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 590
Регистрация: 9.9.2005
Где: Украина, Запорожь е

Репутация: нет
Всего: 3



У меня есть один вопросик поповоду этого 
Почему не получается сохранить в файл объект TBitBtn
Код

unit Options;

interface

uses SysUtils, Classes, Saver,Buttons,Windows;

type
  TOptions = class(TPersistent)
  {* êëàññ ñ êàêèìè-òî íàñòðîéêàìè èëè äàííûìè }
  private
    FMyStr: string;
    FDateTime: TDateTime;
    But:TBitBtn;
  published
    property DateTime: TDateTime read FDateTime write FDateTime;
    property MyStr: string read FMyStr write FMyStr;
    property Btn: TBitBtn read But write But;
  end;

var MainOptions: TOptions;

implementation

const OptFile = 'options.sav';

initialization
  MainOptions := TOptions.Create;
  MainOptions.Btn:=TBitBtn.Create(nil);
  LoadObjFromFile(MainOptions, ExtractFilePath(ParamStr(0)) + OptFile);
  {* Ïðè çàïóñêå ïðîãðàììû ñðàçó ñîçäàåì è ÷èòàåì îáúåêò ñ íàñòðîéêàìè }
  MessageBox(0,Pchar(MainOptions.Btn.Caption),'',MB_OK);
  
finalization
  MainOptions.Btn.Caption:='Boxa';
  SaveObjToFile(MainOptions, ExtractFilePath(ParamStr(0)) + OptFile);
  MainOptions.Free;
  {* Ïðè çàêðûòèè ïðîãðàììû ñîõðàíÿåì è óíè÷òîæàåì îáúåêò ñ íàñòðîéêàìè }
end.

выскакивающий messagebox будет постоянно пустым
caption Tbitbtn-а в файле не сохраняется

Это сообщение отредактировал(а) Teran - 19.9.2006, 18:34


--------------------
Ни цего не понимаю
PM MAIL ICQ   Вверх
Teran
Дата 20.9.2006, 11:24 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 590
Регистрация: 9.9.2005
Где: Украина, Запорожь е

Репутация: нет
Всего: 3



я так понял что можно сохранять только "Стандартные типы" (integer,string,boolean), а сохранение всего остального необходимо реализовывать вручную, основываясь на стандартные типы.
Токда ответте какая из такого сохранения выгода..? если можно тоже самое сделать например спомощью ini файла или тогоже реестра?


--------------------
Ни цего не понимаю
PM MAIL ICQ   Вверх
Snowy
Дата 20.9.2006, 11:47 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Модератор
Сообщений: 11363
Регистрация: 13.10.2004
Где: Питер

Репутация: 192
Всего: 484



Цитата(Teran @  20.9.2006,  11:24 Найти цитируемый пост)
я так понял что можно сохранять только "Стандартные типы"
Нет конечно.
Спокойно сохраняются и сложные типы и классы.
Я просто пока ещё твой пример не проверял, поэтому не дал ответа.
Но закладочку "на посмотреть" сделал.
Ошибок никаких не выдаёт?
PM MAIL   Вверх
Teran
Дата 20.9.2006, 12:42 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 590
Регистрация: 9.9.2005
Где: Украина, Запорожь е

Репутация: нет
Всего: 3



Цитата(Snowy @  20.9.2006,  11:47 Найти цитируемый пост)
Ошибок никаких не выдаёт?

нет ошибок нету но и результата тоже


--------------------
Ни цего не понимаю
PM MAIL ICQ   Вверх
Snowy
Дата 21.9.2006, 10:40 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Модератор
Сообщений: 11363
Регистрация: 13.10.2004
Где: Питер

Репутация: 192
Всего: 484



Ничего не могу поделать - не сохраняются контролы.
Если вместо батона прописать свой класс - наследник TPersistent с нужными пропертями - сохраняется без проблем.
Контролы нет.
Нужно создавать свой класс и делать ему инкапсуляцию на вызов функций, создающих нужные контролы.

Добавлено @ 10:41 
Как будет время, я ещё поковыряю на тему, чего это контролы не хотят сохраняться.
Они ведь тоже наследники TPersistent и прекрасно сохраняются, как контролы.
Вероятно разница в уровнях. Но пока точно не скажу.
PM MAIL   Вверх
EvilsInterrupt
Дата 16.9.2007, 18:38 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Executables research
***


Профиль
Группа: Завсегдатай
Сообщений: 1019
Регистрация: 14.7.2007
Где: Железнодорожный, МО, Россия

Репутация: 3
Всего: 9



Просмотрел мельком посты, вроде ссылки нету. Ссылка что я хочу предложить весьма финоменальна : Сериализация объектов стандартными средствами Delphi.
PM MAIL WWW ICQ Jabber   Вверх
Denchik
Дата 13.1.2008, 16:31 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 68
Регистрация: 16.5.2006

Репутация: нет
Всего: нет



есть еще интересная вещь FormPersist 

сохраняет/загружает настройки всех компонентов формы ( форм ) в ini файл 
баг такой же как в 1 примере данного обсуждения ( http://snowy.delphist.com/mat/TPersistent.zip)
не запоминает корректно размеры окна , если запускать его повторно без изменений ,
двигает его понемногу по диагонали вниз , никто не в курсе , как это лечить ?

хочу прикрутить подобный код к сохранению настроек приложения ....

Присоединённый файл ( Кол-во скачиваний: 28 )
Присоединённый файл  FormPersist.zip 6,25 Kb
PM MAIL   Вверх
Denchik
Дата 14.1.2008, 23:37 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 68
Регистрация: 16.5.2006

Репутация: нет
Всего: нет



Однако EhLib вроде в самом деле рулит 

Используем так :
  • Кидаем на форму TPropStorageEh
  • Нажимаем правым грызуном  - Stored Properties - Настраиваем
  • Пишем в форме в разделе  initialization

  IniPropStorageMan := TIniPropStorageManEh.Create(nil);
  IniPropStorageMan.IniFileName := ExtractFileDir(ParamStr(0)) + '\Main.Ini';
  SetDefaultPropStorageManager(IniPropStorageMan);

И радуемся жизни smile
PM MAIL   Вверх
Страницы: (2) [Все] 1 2 
Ответ в темуСоздание новой темы Создание опроса
Правила форума "Delphi: Общие вопросы"
SnowyMetalFan
bemsPoseidon
Rrader

Запрещается!

1. Публиковать ссылки на вскрытые компоненты

2. Обсуждать взлом компонентов и делиться вскрытыми компонентами

  • Литературу по Дельфи обсуждаем здесь
  • Действия модераторов можно обсудить здесь
  • С просьбами о написании курсовой, реферата и т.п. обращаться сюда
  • Вопросы по реализации алгоритмов рассматриваются здесь
  • 90% ответов на свои вопросы можно найти в DRKB (Delphi Russian Knowledge Base) - крупнейшем в рунете сборнике материалов по Дельфи


Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, Snowy, MetalFan, bems, Poseidon, Rrader.

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | Delphi: Общие вопросы | Следующая тема »


 




[ Время генерации скрипта: 0.1288 ]   [ Использовано запросов: 21 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.