Streaming components/pt: what changed
Revision 1 to 2. Removed lines have a minus sign; added lines have a plus sign.
| Old | New | Line | |
|---|---|---|---|
| 1 | 1 | ## Introdução | |
| 2 | 2 | ||
| 3 | 3 | Normalmente, quando você necessita armazenar dados em disco ou rede, você deve escrever código para carregamento e o salvamento de cada propriedade. Este tutorial descreve como escrever classes, estas podem ter carregado de e conservado para os streams(fluxos) sem código extra da escrita ler/salvar usando o RTTI. | |
| 4 | 4 | ||
| 5 | 5 | Aqui tem um exemplo nos fontes do lazarus, demonstrando como salvar um TGroupBox com um TCheckBox filho para uma stream(fluxo) e ler uma stream de volta para criar uma cópia de ambos os componentes. | |
| 6 | 6 | ||
| 7 | - | ||
| 7 | + | ```text | |
| 8 | + | Veja <lazaruspath>/examples/componentstreaming/ | |
| 9 | + | ``` | |
| 8 | 10 | ||
| 9 | 11 | Na combinação com [[RTTI controls]] você pode reduzir o tamanho de código necessário para conectar os dados do programa com a GUI e o Disco/Rede para um minímo. | |
| 10 | 12 | ||
| 11 | 13 | ## TComponent / TPersistent | |
| 12 | 14 | ||
| 13 | 15 | A classe **TPersistent** é definida na unidade Classes e estão utilizando a diretiva de compilação **{\$M+}**. Esta diretiva diz ao compilado para criar o Tipo de Informação em Tempo de Execução (**RTTI**). Isto significa que e todos seus descendentes começam uma nova seção **published** da classe . Propriedades da 'Published'(difundida) são visíveis como a 'public'(pública), mas adicionalmente sua estrutura é acessível em tempo de execução. O que significa que toda propriedade difundida pode ser lida e escrita em tempo de execução. A IDE por exemplo usa este para trabalhar com componentes que nunca se ouviu. | |
| 14 | 16 | ||
| 15 | 17 | **TComponent** extende a habilidade de TPersistent para ter componentes filhos. Isto é importante para streaming, onde um componente é o **root component**(raiz) também chamado de **lookup root** com uma lista de componentes filho. | |
| 16 | 18 | ||
| 17 | 19 | ## TReader / TWriter | |
| 18 | 20 | ||
| 19 | 21 | Aqui estão as classes que iremos trabalhar, que le/escreve um componente para/de um stream (Veja CreateLRSReader e CreateLRSWriter). Eles usam um **Driver** para ler/escrever um especial formato. No momento aqui temos um leitor (TLRSObjectReader) e um escritor (TLRSObjectWriter) de formato binário do objeto definido na unidade LResources e um escritor (TXMLObjectWriter) para TDOMDocument definido no Laz_XMLStreaming. A unidade LResources também contém funções para converter do formato binário par textual e vice-versa (LRSObjectBinaryToText, LRSObjectTextToBinary). A LCL prefere UTF8 para strings, enquanto o Delphi prefere Widestrings. Assim há algumas funções da conversão também. | |
| 20 | 22 | ||
| 21 | 23 | ## Escrevendo seu próprio componente - Parte 1 | |
| 22 | 24 | ||
| 23 | 25 | Um componente próprio pode ser um simples como: type | |
| 24 | 26 | ||
| 25 | - | ||
| 26 | - | ||
| 27 | - | ||
| 28 | - | ||
| 29 | - | ||
| 30 | - | ||
| 27 | + | ``` pascal | |
| 28 | + | TMyComponent = class(TComponent) | |
| 29 | + | private | |
| 30 | + | FID: integer; | |
| 31 | + | published | |
| 32 | + | property ID: integer read FID write FID; | |
| 33 | + | end; | |
| 34 | + | ``` | |
| 31 | 35 | ||
| 32 | 36 | ## Escrevendo um componente para uma stream | |
| 33 | 37 | ||
| 34 | 38 | A unidade [[doc:lcl/lresources|LResources]] tem a seguinte função: | |
| 35 | 39 | ||
| 36 | - | ||
| 40 | + | ``` pascal | |
| 41 | + | procedure WriteComponentAsBinaryToStream(AStream: TStream; AComponent: TComponent); | |
| 42 | + | ``` | |
| 37 | 43 | ||
| 38 | 44 | Ela escreve um componente no formato binário num stream. Por exemplo: | |
| 39 | 45 | ||
| 40 | 46 | procedure TForm1.Button1Click(Sender: TObject); | |
| 41 | 47 | var | |
| 42 | 48 | AStream: TMemoryStream; | |
| 43 | 49 | begin | |
| 44 | 50 | AStream:=TMemoryStream.Create; | |
| 45 | 51 | try | |
| 46 | 52 | WriteComponentAsBinaryToStream(AStream,AGroupBox); | |
| 47 | 53 | ... salvar a stream em algum lugar ... | |
| 48 | 54 | finally | |
| 49 | 55 | AStream.Free; | |
| 50 | 56 | end; | |
| 51 | 57 | end; | |
| 52 | 58 | ||
| 53 | 59 | ## Lendo um componente de um stream | |
| 54 | 60 | ||
| 55 | 61 | A unidade LResources tem a seguinte função: | |
| 56 | 62 | ||
| 57 | - | ||
| 58 | - | ||
| 63 | + | ``` pascal | |
| 64 | + | procedure ReadComponentFromBinaryStream(AStream: TStream; | |
| 65 | + | var RootComponent: TComponent; OnFindComponentClass: TFindComponentClassEvent; TheOwner: TComponent = nil); | |
| 66 | + | ``` | |
| 59 | 67 | ||
| 60 | 68 | - AStream é a stream contendo um componente no formato binário. | |
| 61 | 69 | - RootComponent é qualquer componente existente, cujo os dados serão sobre-escritos, ou esse é nil(valor vazio) um componente novo será criado. | |
| 62 | 70 | - OnFindComponentClass é uma função, que é usada pelo TReader para pegar a classe a partir de classnames na stream. Por exemplo: | |
| 63 | 71 | ||
| 64 | 72 | procedure TCompStreamDemoForm.OnFindClass(Reader: TReader; | |
| 65 | 73 | const AClassName: string; var ComponentClass: TComponentClass); | |
| 66 | 74 | begin | |
| 67 | 75 | if CompareText(AClassName,'TGroupBox')=0 then | |
| 68 | 76 | ComponentClass:=TGroupBox | |
| 69 | 77 | else if CompareText(AClassName,'TCheckBox')=0 then | |
| 70 | 78 | ComponentClass:=TCheckBox; | |
| 71 | 79 | end; | |
| 72 | 80 | ||
| 73 | 81 | - TheOwner é o componente raiz ou dono, quando criando um novo componente. | |
| 74 | 82 | ||
| 75 | 83 | ## Propriedades que podem ser guardadas num Stream | |
| 76 | 84 | ||
| 77 | 85 | Há algumas limitações, que tipos TReader/TWriter podem colocar no stream: | |
| 78 | 86 | ||
| 79 | 87 | - Tipos básicos podem ser armazenados na stream: string, integer, char, single, double, extended, byte, word, cardinal, shortint, method pointers, etc. . | |
| 80 | 88 | - TPersistent e seus descendentes podem ser armazenados na stream | |
| 81 | 89 | - records, objects e classes não descendendo de TPersistent não podem ser guardadas. Para armazená-los você precisa dizer como no TReader/TWriter. Veja a seguir em armazenando dados customizados - DefineProperties. | |
| 82 | 90 | ||
| 83 | 91 | ## Armazenadno Dados Customizados - DefineProperties | |
| 84 | 92 | ||
| 85 | 93 | Você pode guardar arbitrariamente dados sobrescrevendo(overriding) DefineProperties. Isto habilita a guardar qualquer dado na stream, que não tem tipos básico. Por exemplo para armazenar uma variável *FMyRect: TRect*' de seu componente, adicione os seguintes três métodos: | |
| 86 | 94 | ||
| 87 | 95 | procedure DefineProperties(Filer: TFiler); override; | |
| 88 | 96 | procedure ReadMyRect(Reader: TReader); | |
| 89 | 97 | procedure WriteMyRect(Writer: TWriter); | |
| 90 | 98 | ||
| 91 | 99 | Com o seguinte código: | |
| 92 | 100 | ||
| 93 | 101 | procedure TMyComponent.DefineProperties(Filer: TFiler); | |
| 94 | 102 | var | |
| 95 | 103 | MyRectMustBeSaved: Boolean; | |
| 96 | 104 | begin | |
| 97 | 105 | inherited DefineProperties(Filer); | |
| 98 | 106 | MyRectMustBeSaved:=(MyRect.Left<>0) | |
| 99 | 107 | or (MyRect.Top<>0) | |
| 100 | 108 | or (MyRect.Right<>0) | |
| 101 | 109 | or (MyRect.Bottom<>0); | |
| 102 | 110 | Filer.DefineProperty('MyRect',@ReadMyRect,@WriteMyRect,MyRectMustBeSaved); | |
| 103 | 111 | end; | |
| 104 | 112 | ||
| 105 | 113 | procedure TMyComponent.ReadMyRect(Reader: TReader); | |
| 106 | 114 | begin | |
| 107 | 115 | with Reader do begin | |
| 108 | 116 | ReadListBegin; | |
| 109 | 117 | FMyRect.Left:=ReadInteger; | |
| 110 | 118 | FMyRect.Top:=ReadInteger; | |
| 111 | 119 | FMyRect.Right:=ReadInteger; | |
| 112 | 120 | FMyRect.Bottom:=ReadInteger; | |
| 113 | 121 | ReadListEnd; | |
| 114 | 122 | end; | |
| 115 | 123 | end; | |
| 116 | 124 | ||
| 117 | 125 | procedure TMyComponent.WriteMyRect(Writer: TWriter); | |
| 118 | 126 | begin | |
| 119 | 127 | with Writer do begin | |
| 120 | 128 | WriteListBegin; | |
| 121 | 129 | WriteInteger(FMyRect.Left); | |
| 122 | 130 | WriteInteger(FMyRect.Top); | |
| 123 | 131 | WriteInteger(FMyRect.Right); | |
| 124 | 132 | WriteInteger(FMyRect.Bottom); | |
| 125 | 133 | WriteListEnd; | |
| 126 | 134 | end; | |
| 127 | 135 | end; | |
| 128 | 136 | ||
| 129 | 137 | Este irá salvar MyRect como uma propriedade 'MyRec'. | |
| 130 | 138 | ||
| 131 | 139 | Se você tem muitos objetos TRect, então você provavelmente não precisa escrever este código toda vez. A unidade LResources contém um exemplo de como escrever um procedimento para definir uma propriedade rect: | |
| 132 | 140 | ||
| 133 | - | ||
| 141 | + | ``` pascal | |
| 142 | + | procedure DefineRectProperty(Filer: TFiler; const Name: string; ARect, DefaultRect: PRect); | |
| 143 | + | ``` | |
| 134 | 144 | ||
| 135 | 145 | Desta maneira o código acima pode ser escrito de forma curta: | |
| 136 | 146 | ||
| 137 | 147 | procedure TMyComponent.DefineProperties(Filer: TFiler); | |
| 138 | 148 | begin | |
| 139 | 149 | inherited DefineProperties(Filer); | |
| 140 | 150 | DefineRectProperty(Filer,'MyRect',@FMyRect,nil); | |
| 141 | 151 | end; | |
| 142 | 152 | ||
| 143 | 153 | ## Escrevendo seu próprio componente - Parte 2 | |
| 144 | 154 | ||
| 145 | 155 | Agora o exemplo pode ser extendido e nós podemos usar propriedades arbitrárias com somente algumas linhas de código: | |
| 146 | 156 | ||
| 147 | 157 | type | |
| 148 | 158 | TMyComponent = class(TComponent) | |
| 149 | 159 | private | |
| 150 | 160 | FID: integer; | |
| 151 | 161 | FRect1: TRect; | |
| 152 | 162 | FRect2: TRect; | |
| 153 | 163 | protected | |
| 154 | 164 | procedure DefineProperties(Filer: TFiler); override; | |
| 155 | 165 | public | |
| 156 | 166 | property Rect1: TRect read FRect1 write FRect1; | |
| 157 | 167 | property Rect2: TRect read FRect2 write FRect2; | |
| 158 | 168 | published | |
| 159 | 169 | property ID: integer read FID write FID; | |
| 160 | 170 | end; | |
| 161 | 171 | ||
| 162 | 172 | procedure TMyComponent.DefineProperties(Filer: TFiler); | |
| 163 | 173 | begin | |
| 164 | 174 | inherited DefineProperties(Filer); | |
| 165 | 175 | DefineRectProperty(Filer,'Rect1',@FRect1,nil); | |
| 166 | 176 | DefineRectProperty(Filer,'Rect2',@FRect2,nil); | |
| 167 | 177 | end; | |
| 168 | 178 | ||
| 169 | 179 | Este componente pode agora ser salvo, lido ou usado pelos [[RTTI controls]]. Você não precisa escrever algum código a mais. | |
| 170 | 180 | ||
| 171 | 181 | --- | |
| 172 | 182 | *From the Free Pascal wiki, page "Streaming components/pt", as it stood on 2007-09-19 after a change by Aliotti, 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](/wiki-contact).* |
