Kopia wikimirror

To jest mirror wiki z pascal.chat. Edycja, historia i logowanie są tam.

Streaming components/pl

Deutsch · English · Français · Polski · Português · 日本語

Wprowadzenie

Zwykle, gdy chcesz przechowywać dane na dysku lub w strumieniu sieciowym, musisz napisać kod, który ładuje i zapisuje każdą właściwość. W tym samouczku opisano, jak pisać klasy, które można ładować i zapisywać w strumieniach przy użyciu RTTI (RunTime Type Information) bez potrzeby pisania dodatkowego kodu ładowania/zapisywania.

Źródła Lazarus zawierają przykład, który pokazuje, jak zapisać TGroupBox z dzieckiem TCheckBox w strumieniu i jak ponownie odczytać strumień, aby odtworzyć kopię obu składników. Zobacz: <lazaruspath>/examples/componentstreaming/.

Łącząc użycie odpowiednich kontrolek RTTI, możesz zminimalizować ilość kodu potrzebnego do połączenia GUI programu z odpowiednimi danymi na pamięci dyskowej/sieciowej.

TComponent / TPersistent

Klasa TPersistent jest zdefiniowana w module Classes i wykorzystuje przełącznik kompilatora {$M+}. Ten przełącznik informuje kompilator o utworzeniu informacji o typie w czasie wykonywania programu (RTTI). Oznacza to, że klasy w tym module i wszystkie klasy potomne otrzymują nową sekcję klasową published. Właściwości published są tak samo widoczne, jak właściwości public, ale dodatkowo ich struktura jest dostępna w czasie wykonywania. Oznacza to, że wszystkie właściwości published mogą być odczytywane i zapisywane w czasie wykonywania. Na przykład IDE używa RTTI do pracy z komponentami, o których inaczej nic by nie wiedział.

TComponent rozszerza TPersistent o możliwość posiadania komponentów potomnych. Jest to ważne w przypadku przesyłania strumieniowego, gdzie jednym składnikiem jest root component (zwany także lookup root), zawierający listę komponentów potomnych.

TReader / TWriter

TReader i TWriter to klasy robocze, które odczytują ze strumienia lub zapisują do strumienia dowolny składnik TComponent (patrz CreateLRSReader i CreateLRSWriter). Używają one Driver do odczytu/zapisu przy użyciu specjalnego formatu danych. W tej chwili istnieje czytnik (TLRSObjectReader) i moduł zapisujący (TLRSObjectWriter) dla formatu obiektu binarnego zdefiniowanego w jednostce LResources oraz moduł zapisujący (TXMLObjectWriter) dla TDOMDocument zdefiniowany w Laz_XMLStreaming. Moduł LResources zawiera również funkcje do konwersji zwykłych formatu danych binarnych na tekst i z powrotem (LRSObjectBinaryToText, LRSObjectTextToBinary). LCL preferuje UTF8 dla łańcuchów, podczas gdy Delphi preferuje Widestrings. Zapewnia także pewne funkcje konwersji, dzięki czemu można łatwo poradzić sobie z przesyłaniem strumieniowym danych nie tylko w Lazarus, ale także w formacie binarnym Delphi.

Kolekcje strumieniowe

Zobacz tutaj TCollection/pl#Przesyłanie strumieniowe

Jest to pełny przykład, jak utworzyć listę elementów za pomocą klas TCollectionItem i TCollection, i przesyłać strumieniowo za pomocą TComponent.

Zapisywanie własnego komponentu - Część 1

Własny komponent może być np. tak prosty, jak:

pascal
type
  TMyComponent = class(TComponent)
  private
    FID: integer;
  published
    property ID: integer read FID write FID;
  end;

Zapisywanie komponentu do strumienia

Moduł LResources zawiera funkcję taką jak:

pascal
procedure WriteComponentAsBinaryToStream(AStream: TStream; AComponent: TComponent);

Która zapisuje komponent w formacie binarnym do strumienia. Na przykład:

pascal
procedure TForm1.Button1Click(Sender: TObject);
var
  AStream: TMemoryStream;
begin
  AStream:=TMemoryStream.Create;
  try
    WriteComponentAsBinaryToStream(AStream, AGroupBox);
    ... save stream somewhere ...
  finally
    AStream.Free;
  end;
end;

Odczytywanie komponentu ze strumienia

Moduł LResources zawiera funkcję taką jak:

pascal
procedure ReadComponentFromBinaryStream(AStream: TStream;
    var RootComponent: TComponent; OnFindComponentClass: TFindComponentClassEvent; TheOwner: TComponent = nil);
pascal
procedure TCompStreamDemoForm.OnFindClass(Reader: TReader;
  const AClassName: string; var ComponentClass: TComponentClass);
begin
  if CompareText(AClassName, 'TGroupBox') = 0 then
    ComponentClass := TGroupBox
  else if CompareText(AClassName, 'TCheckBox') = 0 then
    ComponentClass := TCheckBox;
end;

Właściwości strumieniowalne

TReader i TWriter mają kilka ograniczeń co do typów, które mogą przesyłać strumieniowo:

Strumieniowanie danych niestandardowych - DefineProperties

Możesz przesyłać strumieniowo dodatkowe dowolne dane, zastępując metodę DefineProperties. Umożliwia to przesyłanie strumieniowe danych, które nie są podstawowym typem Pascala, oraz klas, które nie są potomkami TPersistent. Na przykład, aby przesłać strumieniowo zmienną rekordu FMyRect: TRect, która jest polem w twoim komponencie, dodaj do komponentu następujące trzy metody:

pascal
procedure DefineProperties(Filer: TFiler); override;
procedure ReadMyRect(Reader: TReader);
procedure WriteMyRect(Writer: TWriter);

Z następującym kodem:

pascal
procedure TMyComponent.DefineProperties(Filer: TFiler);
var
  MyRectMustBeSaved: Boolean;
begin
  inherited DefineProperties(Filer);
  MyRectMustBeSaved := (MyRect.Left <> 0)
                     or (MyRect.Top <> 0)
                     or (MyRect.Right <> 0)
                     or (MyRect.Bottom <> 0);
  Filer.DefineProperty('MyRect', @ReadMyRect, @WriteMyRect, MyRectMustBeSaved);
end;

procedure TMyComponent.ReadMyRect(Reader: TReader);
begin
  with Reader do begin
    ReadListBegin;
    FMyRect.Left := ReadInteger;
    FMyRect.Top := ReadInteger;
    FMyRect.Right := ReadInteger;
    FMyRect.Bottom := ReadInteger;
    ReadListEnd;
  end;
end;

procedure TMyComponent.WriteMyRect(Writer: TWriter);
begin
  with Writer do begin
    WriteListBegin;
    WriteInteger(FMyRect.Left);
    WriteInteger(FMyRect.Top);
    WriteInteger(FMyRect.Right);
    WriteInteger(FMyRect.Bottom);
    WriteListEnd;
  end;
end;

Spowoduje to zapisanie MyRect jako właściwości 'MyRect'.

Jeśli przesyłasz strumieniowo wiele pól TRect, prawdopodobnie nie chcesz powtarzać tego kodu za każdym razem. Moduł LResources zawiera przykład, w jaki sposób można napisać procedurę definiującą właściwość rect:

pascal
procedure DefineRectProperty(Filer: TFiler; const Name: string; ARect, DefaultRect: PRect);

Po napisaniu procedury definiowania właściwości rect powyższy kod można sprowadzić do:

pascal
procedure TMyComponent.DefineProperties(Filer: TFiler);
begin
  inherited DefineProperties(Filer);
  DefineRectProperty(Filer, 'MyRect', @FMyRect, nil);
end;

Zapisywanie własnego komponentu - Część 2

Teraz przykład można rozszerzyć i możemy użyć dowolnych właściwości za pomocą tylko kilku wierszy kodu:

pascal
type
  TMyComponent = class(TComponent)
  private
    FID: integer;
    FRect1: TRect;
    FRect2: TRect;
  protected
    procedure DefineProperties(Filer: TFiler); override;
  public
    property Rect1: TRect read FRect1 write FRect1;
    property Rect2: TRect read FRect2 write FRect2;
  published
    property ID: integer read FID write FID;
  end;

procedure TMyComponent.DefineProperties(Filer: TFiler);
begin
  inherited DefineProperties(Filer);
  DefineRectProperty(Filer, 'Rect1', @FRect1,nil);
  DefineRectProperty(Filer, 'Rect2', @FRect2,nil);
end;

Ten komponent może teraz zostać zapisany, załadowany lub użyty przez kontrolki RTTI. Nie musisz pisać żadnego kodu.

Zapisywanie i odczytywanie komponentów z/do LFM

Zobacz przykładowe funkcje z modułu lresources: ReadComponentFromTextStream i WriteComponentAsTextToStream.

Zapisywanie i odczytywanie komponentów z/do XML

Przesyłanie strumieniem komponentów jest proste: Zobacz przykład w lazarus/examples/xmlstreaming/.

Nazwy

Wniosek

RTTI to potężny mechanizm umożliwiający łatwe przesyłanie strumieniowe całych klas. RTTI pomaga również uniknąć wielokrotnego pisania nudnego kodu ładowania/zapisywania.

Zobacz także


From the Free Pascal wiki, page "Streaming components/pl", as it stood on 2023-01-31 after a change by Slawek, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's Licensing page. If you have concerns about this material, message the admin.

Wersja 1, ostatnia zmiana: WikiArchive, .