I am trying to dynamiclly create a custom component with images and display them in a Grid , but the Images don't show up. Below is the code with omitted part of declarations , could someone help me and tell me what am I doint wrong ?
Custom component Class
unit Tile;
interface
uses FMX.Controls, FMX.StdCtrls, System.Classes, FMX.Types, System.StrUtils ,
System.SysUtils, System.Types, System.UITypes,
System.Variants,
FMX.Forms, FMX.Graphics, FMX.Dialogs, FMX.Ani,
FMX.Objects, FMX.Layouts;
type
TTileType = (Slider, Memory, Tile3D);
TTile = class
private
FOnChangedText: TNotifyEvent;
FType: TTileType;
FControl: TComponent;
FText: String;
FName: String;
FBitmap : TBitmap;
FAlign : TAlignLayout;
procedure TextChangedDefault(Sender: TObject);
protected
procedure SetText(aText: String);
procedure TextChanged; virtual;
procedure SetControlOnClick(AProc: TNotifyEvent);
function GetControlOnClick: TNotifyEvent;
procedure SetControlName(aName: String);
procedure SetBitmap(bitmap:TBitmap);
procedure SetAlign(align :TAlignLayout);
public
constructor Create(AParent: TFmxObject; AType: TTileType);
destructor Destroy; override;
published
property Text: String read FText write SetText;
property Name: String read FName write SetControlName;
property Bitmap:TBitmap read FBitmap write SetBitmap;
property Align:TAlignLayout read FAlign write SetAlign;
property OnChangedText: TNotifyEvent read FOnChangedText
write FOnChangedText;
property OnClick: TNotifyEvent read GetControlOnClick
write SetControlOnClick;
end;
implementation
constructor TTile.Create(AParent: TFmxObject; AType: TTileType);
begin
FType := AType;
case FType of
Slider:
begin
FControl := TButton.Create(AParent as TComponent);
FOnChangedText := TextChangedDefault;
(FControl as TFmxObject).Parent := AParent;
end;
Memory:
begin
FControl := TImage.Create(AParent as TComponent);
FOnChangedText := TextChangedDefault;
(FControl as TFmxObject).Parent := AParent;
end;
Tile3D:
FControl := nil;
else
FControl := nil;
end;
FName := FControl.Name;
end;
destructor TTile.Destroy;
begin
FControl.DisposeOf;
inherited;
end;
function TTile.GetControlOnClick: TNotifyEvent;
begin
case FType of
Slider:
begin
Result := (FControl as TButton).OnClick;
end;
Memory:
begin
Result := (FControl as TImage).OnClick;
end;
Tile3D:
begin
// TODO
end;
else
Result := nil;
end;
end;
procedure TTile.SetControlName(aName: String);
begin
FName := aName;
FControl.Name := aName;
end;
procedure TTile.SetBitmap(bitmap :TBitmap);
begin
FBitmap:=bitmap;
end;
procedure TTile.SetAlign(align :TAlignLayout);
begin
FAlign:=align;
end;
procedure TTile.SetControlOnClick(AProc: TNotifyEvent);
begin
case FType of
Slider:
begin
(FControl as TButton).OnClick := AProc;
end;
Memory:
begin
(FControl as TImage).OnClick := AProc;
end;
Tile3D:
begin
// TODO
end;
end;
end;
procedure TTile.SetText(aText: String);
begin
FText := aText;
TextChanged;
end;
procedure TTile.TextChanged;
begin
if Assigned(FOnChangedText) then
FOnChangedText(Self);
end;
procedure TTile.TextChangedDefault(Sender: TObject);
begin
(FControl as TButton).Text := FText;
end;
end.
Memory Game Class:
unit MemoryGame;
interface
uses Tile, Consts, FMX.Controls, FMX.StdCtrls, FMX.Layouts, System.Classes,
FMX.Types, System.Types, FMX.Graphics, System.SysUtils, FMX.Dialogs,Helper,FMX.ExtCtrls ,
System.UITypes,
System.Variants,
FMX.Forms,
FMX.TabControl, SliderPuzzle, System.Actions,
FMX.ActnList, FMX.StdActns, FMX.MultiView, FMX.Controls.Presentation, FMX.Edit,
DateUtils ,FMX.Objects ;
type
TMemoryGame = class(TGridLayout)
private
FTiles: TArray<TTile>;
procedure FillGrid(aTileNo: Integer);
protected
public
constructor Create(AParent: TFmxObject; aTileNo: Integer); reintroduce;
end;
var
moveCounter : Integer = 0 ;
implementation
{ MemoryGame }
constructor TMemoryGame.Create(AParent: TFmxObject; aTileNo: Integer);
begin
inherited Create(nil);
Parent := AParent;
FillGrid(aTileNo);
end;
procedure TMemoryGame.FillGrid(aTileNo: Integer);
var
I: Integer;
LTile: TTile;
begin
SetLength(FTiles, aTileNo);
for I := 0 to aTileNo - 1 do
begin
LTile := TTile.Create(Self, TTileType.Memory);
FTiles[I] := LTile;
if I = 0 then
begin
LTile.Bitmap:= TBitmap.CreateFromFile('../../img/img1.bmp');
LTile.Align := TAlignLayout.Client;
LTile.Align := TAlignLayout.Center;
end
else
begin
LTile.Bitmap:= TBitmap.CreateFromFile('../../img/img1.bmp');
LTile.Align := TAlignLayout.Client;
LTile.Align := TAlignLayout.Center;
end;
end;
end;
end.
Main Form:
unit MainForm;
interface
uses
System.SysUtils, System.Types, System.UITypes, System.Classes,
System.Variants, Consts,
FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, FMX.StdCtrls, FMX.ExtCtrls,
FMX.Layouts, FMX.TabControl, SliderPuzzle, System.Actions,
FMX.ActnList, FMX.StdActns, FMX.MultiView, FMX.Controls.Presentation, FMX.Edit,
DateUtils,MemoryGame, FMX.Objects;
type
TFormMain = class(TForm)
tcMain: TTabControl;
ti1Slider: TTabItem;
ti2Runtime: TTabItem;
ti4Game3D: TTabItem;
ti3Memory: TTabItem;
GridLayout: TGridLayout;
bTile1: TButton;
bTile2: TButton;
bTile3: TButton;
bTile4: TButton;
bTile5: TButton;
bTile6: TButton;
bTile7: TButton;
bTile8: TButton;
bTile9: TButton;
bTile10: TButton;
bTile11: TButton;
bTile12: TButton;
bTile13: TButton;
bTile14: TButton;
bTile15: TButton;
bTileEmpty: TButton;
bNew: TButton;
MultiView: TMultiView;
bExitApp: TButton;
ActionList: TActionList;
FileExitActn: TFileExit;
NewGameActn: TAction;
StyleBook: TStyleBook;
hitCountLabel: TLabel;
movesCounter: TLabel;
TimeCountLabel: TLabel;
timer: TLabel;
Timer1: TTimer;
procedure bTileClick(Sender: TObject);
procedure FormShow(Sender: TObject);
procedure NewGameActnExecute(Sender: TObject);
procedure GridLayoutResize(Sender: TObject);
procedure Timer1Timer(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
FormMain: TFormMain;
Slider: TSliderPuzzle;
Memory : TMemoryGame;
firstMove : Boolean = true;
stop, elapsed : TDateTime ;
start : TDateTime = 0 ;
implementation
{$R *.fmx}
procedure TFormMain.NewGameActnExecute(Sender: TObject);
begin
if ti1Slider.IsSelected then
repeat
begin
firstMove:=true;
Slider.ShuffleTiles(GridLayout);
Slider.resetMoveCounter;
Timer1.Enabled := true;
Timer1.Interval :=1000;
Slider.resetTimer(start);
movesCounter.Text := IntToStr(Slider.GetMoveCount);
timer.Text := '--/--/--';
end;
until not Slider.IsGameOver(GridLayout)
else if ti2Runtime.IsSelected then
repeat
Slider.ShuffleTiles
until not Slider.IsGameOver;
end;
procedure TFormMain.Timer1Timer(Sender: TObject);
var myVar:Integer;
begin
if start<>0 then
begin
myVar := SecondsBetween(start,Now);
timer.Text :=Format('%.2d:%.2d', [myVar div 60, myVar mod 60]); ;
end;
end;
procedure TFormMain.bTileClick(Sender: TObject);
begin
if firstMove then
begin
Slider.startCount(start);
firstMove:=false;
end;
Slider.incrementCounter;
movesCounter.Text := IntToStr(Slider.GetMoveCount);
Slider.SwapTiles(GridLayout, Sender as TButton, bTileEmpty);
if Slider.IsGameOver(GridLayout) then
begin
Slider.resetMoveCounter;
Slider.resetTimer(start);
// movesCounter.Text := IntToStr(Slider.GetMoveCount);
// timer.Text := '--/--/--';
Timer1.Enabled := false;
ShowMessage('GAME OVER');
firstMove:=true;
ti3Memory.Enabled := true;
ti3Memory.TabControl.SetActiveTabWithTransition(ti3Memory,TTabTransition.Slide);
end;
end;
procedure TFormMain.GridLayoutResize(Sender: TObject);
begin
GridLayout.ItemHeight := GridLayout.Height / COLS-25;
GridLayout.ItemWidth := GridLayout.Width / ROWS;
end;
procedure TFormMain.FormShow(Sender: TObject);
begin
ReportMemoryLeaksOnShutdown := true;
Slider := TSliderPuzzle.Create(Self.ti2Runtime, TILES);
Slider.Height := GridLayout.Height;
Slider.Width := GridLayout.Width;
Slider.Align := TAlignLayout.Client;
//PuzzleGame
ReportMemoryLeaksOnShutdown := true;
Memory := TMemoryGame.Create(Self.ti3Memory, TILES);
Memory.Height := GridLayout.Height;
Memory.Width := GridLayout.Width;
Memory.Align := TAlignLayout.Client;
end;
end.
Call the assign() method of the FBitmap variable inside youe Set procedure:
procedure TTile.SetBitmap(bitmap :TBitmap);
begin
FBitmap.Assign(bitmap);
end;
Adding the following code to Tile class , fixed the issues.
type
private
FOnChangedBitmap : TNotifyEvent;
protected
procedure BitmapChanged;virtual;
procedure TTile.BitmapChanged;
begin
if Assigned(FOnChangedBitmap) then
FOnChangedBitmap(Self);
end;
procedure TTile.BitmapChangedDefault(Sender: TObject);
begin
(FControl as TImage).Bitmap := FBitmap;
end;
procedure TTile.SetBitmap(bitmap :TBitmap);
begin
FBitmap:=bitmap;
BitmapChanged;
end;
This all looks very complicated and perhaps it is.
But I solved a similar problem by simply setting the parent of the image:
Fheart := TImage.Create(self);
Fheart.Parent := self;
Fheart.SetSubComponent(true);
It seems unneccessary setting the parent when that is passed as the owner in the constructor - but it did solve my problem
Related
Using Delphi 10.4.1 I tried Custom Managed record management to initialize a record but still get memory leaks.
unit Unit3;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
System.Classes, System.IOUtils, System.DateUtils, System.Character,
Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ExtCtrls;
type
TForm3 = class(TForm)
Button1: TButton;
RadioGroup1: TRadioGroup;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form3: TForm3;
type
TMyRec = record
DateTime: TDateTime;
v, size: integer;
str: string;
class operator Initialize (out Dest: TMyRec);
end;
TMyREcHolder = class
data: TMyRec;
constructor Create(const e: TMyRec);
end;
TMyList = class(TList)
procedure Clear; override;
end;
implementation
{$R *.dfm}
class operator TmyRec.Initialize (out Dest: TMyRec);
begin
Dest.str := '';
end;
{ TMyREcHolder }
constructor TMyREcHolder.Create(const e: TMyRec);
begin
inherited Create;
data := e;
end;
{ TMyList }
procedure TMyList.Clear;
var
i: integer;
begin
for i := 0 to Count - 1 do
TMyREcHolder(Items[i]).Free;
inherited Clear;
end;
procedure TForm3.Button1Click(Sender: TObject);
var
lst: TMyList;
i: integer;
rec: TMyRec;
FI: TSearchrec;
begin
Initialize(rec);
lst := TMyList.Create;
try
if FindFirst(TPath.Combine('C:\temp', '*.txt'), faAnyFile, FI) = 0 then
begin
repeat
if (FI.FindData.dwFileAttributes and faDirectory = 0) and
(FI.FindData.dwFileAttributes and faArchive = faArchive) then
begin
Application.ProcessMessages;
case RadioGroup1.ItemIndex of
0: Initialize(rec);
1: rec.str := '';
2: fillchar(rec, sizeof(rec), 0);
end;
try
rec.DateTime := FI.TimeStamp;
except
rec.DateTime := EncodeDateDay(1970, 1);
end;
rec.size := FI.size;
rec.str := FI.Name;
lst.Add(TMyREcHolder.Create(rec));
end;
until (FindNext(FI) <> 0);
FindClose(FI);
end;
finally
lst.Free;
end;
end;
end.
The radiogroup offers three items, both 1 and 3 leak memory. Can anyone explain why the Initialize one does? I want a reliable way of clearing a record to help me wean myself off a 20 year fillchar habit.
I would like to create array of all listboxes and access them. I tried to do it using pointers, however my program crashes during runtime with error access violation at address...
type ControlsCount = 4;
type PLB = ^TListBox;
var listBoxes: array of PLB;
procedure TExport.FormCreate(Sender: TObject);
var i: word; n: integer;
begin
with FormExport do
begin
ListRowHeight := List_sex.height;
List_sex.items.add('---');
List_sex.items.add('Man');
List_sex.items.add('Woman');
List_sex.onmousemove:=ListMouseMove;
setLength(listBoxes, ControlsCount);
n := -1;
for i := 0 to ControlsCount - 1 do
if Components[i] is TWinControl then
if TWinControl(Components[i]).CanFocus then
begin
inc(n);
// mistake here: should be listBoxes[n] not listBoxes[i]
listBoxes[i] := PLB(Components[i]);
end;
realControlsCount := n;
end;
end;
procedure TExport.resetListBoxes;
var i: word;
begin
for i := 0 to realControlsCount-1 do
begin
TListBox(listBoxes[i]^).height := ListRowHeight;
end;
end;
So here I try to set the pointer of the control to listBoxes[i].
listBoxes[i] := PLB(Components[i]);
and here I try to access it:
TListBox(listBoxes[i]^).height := ListRowHeight;
this is the line where it generates error.
What am I doing wrong?
Just remove all pointer stuff and check whether control is really TListBox. Also you misused ControlsCount while accessed another list Components[i]
var listBoxes: array of TListBox;
...
for i := 0 to ControlsCount - 1 do
if Controls[i] is TListBox then //note strict constraint
listBoxes[n] := Controls[i] as TListBox;
...
listBoxes[i].height := ListRowHeight;
Aslso consider using TList<TListBox> instead of array
Regarding the answere MBo gave this is how I would use it with a TList
unit Unit1;
interface
uses
System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, Generics.Collections,
FMX.Layouts, FMX.ListBox, FMX.Controls.Presentation, FMX.StdCtrls;
type
TForm1 = class(TForm)
lst1: TListBox;
lst2: TListBox;
lst3: TListBox;
btn1: TButton;
pnl1: TPanel;
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure btn1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
listboxes: TList<TListBox>; // Define list that will contain listboxes
implementation
{$R *.fmx}
procedure TForm1.btn1Click(Sender: TObject);
var
lstbx: TListBox;
begin
for lstbx in listboxes do
ShowMessage(lstbx.Height.ToString); Loop through all listboxes and show their height
end;
procedure TForm1.FormCreate(Sender: TObject);
var
control: TControl;
begin
listboxes := TList<TListBox>.Create; // Create the TList
for control in pnl1.Controls do
begin
if control is TListBox then
listboxes.Add(control as TListBox); // Loop through all listboxes on a panel and add then to the list if they are a listbox
end;
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
listboxes.Free; // Free the list
end;
end.
Good afternoon.
The client sends a message to the server, and the server responds by sending two messages to the client.
The client sees these messages, but the memo records the very first value sent by the server.
Prompt in what the reason
Server ----------------------------------------------------
unit Unit1;
interface
uses
System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, IDGlobal,
IdBaseComponent, IdComponent, IdCustomTCPServer, IdTCPServer, IdContext,
FMX.Controls.Presentation, FMX.StdCtrls;
type
TRec_Data = record
Flag: array[0..20] of char;
end;
TForm1 = class(TForm)
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
public
MainPort: TIdTCPServer;
procedure MainPortConnect(AContext: TIdContext);
procedure MainPortExecute(AContext: TIdContext);
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.fmx}
{ TForm1 }
procedure TForm1.FormCreate(Sender: TObject);
begin
MainPort := TIdTCPServer.Create;
MainPort.OnConnect := MainPortConnect;
MainPort.OnExecute := MainPortExecute;
MainPort.Bindings.Add.IP := '127.0.0.1';
MainPort.Bindings.Add.Port := 6000;
MainPort.Active := True;
end;
procedure TForm1.MainPortConnect(AContext: TIdContext);
begin
//
end;
procedure TForm1.MainPortExecute(AContext: TIdContext);
var
Rec: TRec_Data;
Buffer: TIdBytes;
begin
Rec.Flag := '1';
Buffer := RawToBytes(Rec, SizeOf(Rec));
AContext.Connection.IOHandler.Write(Buffer);
Rec.Flag := '2';
Buffer := RawToBytes(Rec, SizeOf(Rec));
AContext.Connection.IOHandler.Write(Buffer);
end;
end.
Client ----------------------------------------------------
unit Unit1;
interface
uses
System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs,
System.Generics.Collections,
IdTCPClient, IdGlobal, FMX.Controls.Presentation, FMX.ScrollBox, FMX.Memo;
type
TRec_Data = record
Flag: array[0..20] of char;
end;
TMyThread = class(TThread)
private
Progress: string;
Client : TIdTCPClient;
FQueue : TThreadedQueue<TRec_Data>;
protected
procedure Execute; override;
public
constructor Create(const AQueue : TThreadedQueue<TRec_Data>);
destructor Destroy; override;
end;
TForm1 = class(TForm)
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
private
{ Private declarations }
FQueue : TThreadedQueue<TRec_Data>;
FMyThread : TMyThread;
Timer : TTimer;
procedure OnTimer(Sender: TObject);
public
Memo1: TMemo;
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.fmx}
procedure TForm1.FormCreate(Sender: TObject);
begin
FQueue:=TThreadedQueue<TRec_Data>.Create(100, 1000, 10);
Timer:=TTimer.Create(Self);
Timer.Interval:=100;
Timer.OnTimer:=OnTimer;
Timer.Enabled:=True;
FMyThread:=TMyThread.Create(FQueue);
FMyThread.Start;
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
if Assigned(FMyThread) then
begin
FMyThread.Terminate;
FMyThread.WaitFor;
FMyThread.Free
end;
if Assigned(Timer) then
Timer.Free;
if Assigned(FQueue) then
FQueue.Free;
end;
procedure TForm1.OnTimer(Sender: TObject);
Var ARec : TRec_Data;
begin
// while FQueue.PopItem(ARec) = TWaitResult.wrSignaled do или
if FQueue.PopItem(ARec) = TWaitResult.wrSignaled then
Form1.Memo1.Lines.Insert(0, ARec.Flag);
end;
constructor TMyThread.Create(const AQueue : TThreadedQueue<TRec_Data>);
var
Rec: TRec_Data;
Buffer: TIdBytes;
begin
inherited Create(true);
FQueue:=AQueue;
Client := TIdTCPClient.Create(nil);
Client.Host := '127.0.0.1';
Client.Port := 6000;
Client.Connect;
// Передаем данные
if Client.Connected = True then
begin
Rec.Flag := 'addUser';
Buffer := RawToBytes(Rec, SizeOf(Rec));
Client.IOHandler.Write(Buffer);
end;
end;
destructor TMyThread.Destroy;
begin
if Assigned(Client) then
Client.Free;
inherited;
end;
procedure TMyThread.Execute;
var
Rec: TRec_Data;
Buffer: TIdBytes;
begin
while Not Terminated do
begin
if Client.Connected then
begin
Client.IOHandler.ReadBytes(Buffer, SizeOf(Rec));
BytesToRaw(Buffer, Rec, SizeOf(Rec));
Progress := Rec.Flag;
// Synchronize(SetProgress);
FQueue.PushItem(Rec);
end
else
Client.Connect;
TThread.Sleep(10);
end;
end;
end.
On the server side, your are ignoring the client's request, and flooding the connection with endless responses. The TIdTCPServer.OnExecute event is called in a continuous loop for the lifetime of the connection, not when the client sends a request.
On the client side, you are running a continuous reading loop in a thread, trying to take in all of those responses. But your use of TThread.Sleep() ensures that loop reads messages much slower than the server can produce them, congesting network traffic.
But worse, you are hindering your client's ability to process server messages. Your UI timer runs at 100ms intervals, while the reading thread runs at 10ms intervals. So at most, 10 messages may be pushed into the queue per timer interval. Your OnTimer event handler pops only 1 message per interval, leaving up to 9 messages in the queue. So very quickly (~1s), the queue will fill up to its max capacity of 100 messages, and PushItem() will start ignoring messages. You are not checking for push errors/timeouts at all.
In addition, I see other issues with your code.
On the server side, you are leaking your TIdTCPServer object, as you don't assign an Owner to it, and you don't Free it. But also, your Form's OnCreate event handler is adding 2 separate bindings to TIdTCPServer - one on 127.0.0.1:0 and the other on 0.0.0.0:6000. It should be adding only one binding - on 127.0.0.1:6000.
On the client side, when creating your thread, you should not be calling TIdTCPClient.Connect() or TIdIOHandler.Write() in the thread's constructor, they belong in the thread's Execute() method only.
And lastly, I would suggest using TQueue<TRec_Data> instead of TThreadedQueue<TRec_Data>. The latter uses its own internal threads to manage push/pop timeouts, which is wasted overhead in this situation. You can use TMonitor or TEvent to accomplish the same thing without the extra threads.
With that said, try something more like this instead:
Server:
unit Unit1;
interface
uses
System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, IdGlobal,
IdBaseComponent, IdComponent, IdCustomTCPServer, IdTCPServer, IdContext,
FMX.Controls.Presentation, FMX.StdCtrls;
type
TRec_Data = packed record
Flag: array[0..20] of char;
end;
TForm1 = class(TForm)
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
public
MainPort: TIdTCPServer;
procedure MainPortConnect(AContext: TIdContext);
procedure MainPortExecute(AContext: TIdContext);
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.fmx}
{ TForm1 }
procedure TForm1.FormCreate(Sender: TObject);
var
Binding: TIdSocketHandle;
begin
MainPort := TIdTCPServer.Create(Self);
MainPort.OnConnect := MainPortConnect;
MainPort.OnExecute := MainPortExecute;
// and a single listening socket for 127.0.0.1:6000
Binding := MainPort.Bindings.Add;
Binding.IP := '127.0.0.1';
Binding.Port := 6000;
MainPort.Active := True;
end;
procedure TForm1.MainPortConnect(AContext: TIdContext);
begin
//...
end;
procedure TForm1.MainPortExecute(AContext: TIdContext);
var
Rec: TRec_Data;
Buffer: TIdBytes;
begin
// check if the client has sent any messages waiting to be read...
if AContext.Connection.IOHandler.InputBufferIsEmpty then
begin
AContext.Connection.IOHandler.CheckForDataOnSource(0);
AContext.Connection.IOHandler.CheckForDisconnect;
end;
if not AContext.Connection.IOHandler.InputBufferIsEmpty then
begin
// read a pending client message and process it as needed...
AContext.Connection.IOHandler.ReadBytes(Buffer, SizeOf(Rec));
BytesToRaw(Buffer, Rec, SizeOf(Rec));
//...
end;
// send messages to the client...
Rec.Flag := '1';
Buffer := RawToBytes(Rec, SizeOf(Rec));
AContext.Connection.IOHandler.Write(Buffer);
Rec.Flag := '2';
Buffer := RawToBytes(Rec, SizeOf(Rec));
AContext.Connection.IOHandler.Write(Buffer);
end;
end.
Client:
unit Unit1;
interface
uses
System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs,
System.Generics.Collections,
IdTCPClient, IdGlobal, FMX.Controls.Presentation, FMX.ScrollBox, FMX.Memo,
System.SyncObjs;
type
TRec_Data = packet record
Flag: array[0..20] of char;
end;
TMyThread = class(TThread)
private
FQueue : TQueue<TRec_Data>;
FTermEvent : TEvent;
protected
procedure Execute; override;
procedure TerminatedSet; override;
public
constructor Create(const AQueue : TQueue<TRec_Data>); reintroduce;
destructor Destroy; override;
end;
TForm1 = class(TForm)
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
private
{ Private declarations }
FQueue : TQueue<TRec_Data>;
FMyThread : TMyThread;
Timer : TTimer;
procedure OnTimer(Sender: TObject);
public
Memo1: TMemo;
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.fmx}
procedure TForm1.FormCreate(Sender: TObject);
begin
FQueue := TQueue<TRec_Data>.Create;
Timer := TTimer.Create(Self);
Timer.Interval := 100;
Timer.OnTimer := OnTimer;
Timer.Enabled := True;
FMyThread := TMyThread.Create(FQueue);
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
if Assigned(FMyThread) then
begin
FMyThread.Terminate;
FMyThread.WaitFor;
FMyThread.Free;
end;
if Assigned(Timer) then
Timer.Free;
if Assigned(FQueue) then
FQueue.Free;
end;
procedure TForm1.OnTimer(Sender: TObject);
var
ARec : TRec_Data;
begin
// wait up to 10ms for the queue to be accessible...
if not TMonitor.Enter(FQueue, 10) then Exit;
try
// process all pending messages and remove them from the queue...
while FQueue.Count > 0 do
begin
ARec := FQueue.Dequeue;
Memo1.Lines.Insert(0, ARec.Flag);
end;
finally
TMonitor.Exit(FQueue);
end;
end;
constructor TMyThread.Create(const AQueue : TQueue<TRec_Data>);
begin
inherited Create(false);
FQueue := AQueue;
// used to signal Execute() to exit immediately while waiting
// to call Connect() after a failed connection...
FTermEvent := TEvent.Create(nil, true, false, '');
end;
procedure TMyThread.Destroy;
begin
FTermEvent.Free;
inherited;
end;
procedure TMyThread.TerminatedSet;
begin
// Terminate() was called, signal Execute() now...
FTermEvent.SetEvent;
end;
procedure TMyThread.Execute;
var
Client: TIdTCPClient;
Rec: TRec_Data;
Buffer: TIdBytes;
begin
Client := TIdTCPClient.Create(nil);
try
Client.Host := '127.0.0.1';
Client.Port := 6000;
Client.ConnectTimeout := 5000;
Client.ReadTimeout := 5000;
while not Terminated do
begin
// try to connect to the server...
try
Client.Connect;
except
// wait 5 secs to try again...
FTermEvent.WaitFor(5000);
Continue;
end;
// connected...
try
try
Rec.Flag := 'addUser';
Buffer := RawToBytes(Rec, SizeOf(Rec));
Client.IOHandler.Write(Buffer);
// communicate with the server until disconnected or terminating...
while not Terminated do
begin
// send other messages to the server as needed...
// check if the server has sent any messages waiting to be read.
// don't block the thread unless there is a message to read...
if Client.IOHandler.InputBufferIsEmpty then
begin
Client.IOHandler.CheckForDataOnSource(100);
Client.IOHandler.CheckForDisconnect;
if Client.IOHandler.InputBufferIsEmpty then Continue;
end;
// read a message...
Client.IOHandler.ReadBytes(Buffer, SizeOf(Rec));
BytesToRaw(Buffer, Rec, SizeOf(Rec));
// wait up to 1 sec for the queue to be accessible...
if not TMonitor.Enter(FQueue, 1000) then
begin
// can't add message to queue yet, do something ...
end else
begin
// add message to queue...
try
FQueue.Enqueue(Rec);
finally
TMonitor.Exit(FQueue);
end;
end;
end;
finally
Client.Disconnect;
end;
except
// something unexpected happened, will reconnect and
// try again if not terminated...
end;
end;
finally
Client.Free;
end;
end;
end.
i need help to speedup my project,i have 2 ListBoxs, the first is full with URLs, the second i store in it the URLs that causes 404 error from Listbox1, its just checking process. the idhttp takes about 2s to check 1 url, i dont need the html, cause the decryption process takes time, So i decided to add threads in my project, my code so far
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs,
IdSSLOpenSSL, Vcl.StdCtrls, IdBaseComponent, IdComponent,
IdTCPConnection, IdTCPClient, IdHTTP;
type
TForm1 = class(TForm)
IdHTTP1: TIdHTTP;
Button1: TButton;
ListBox1: TListBox;
ListBox2: TListBox;
Button3: TButton;
Memo1: TMemo;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure Button3Click(Sender: TObject);
private
public
end;
Type
TMyThread = class(TThread)
IdHTTP1: TIdHTTP;
Button1: TButton;
ListBox1: TListBox;
ListBox2: TListBox;
Button3: TButton;
Memo1: TMemo;
private
fStatusText : string;
lHTTP: TIdHTTP;
protected
procedure Execute; override;
public
Constructor Create(CreateSuspended : boolean);
end;
var
Form1: TForm1;
procedure TForm1.Button3Click(Sender: TObject);
var
MyThread : TMyThread;
begin
MyThread := TMyThread.Create(True);
MyThread.Start;
end;
constructor TMyThread.Create(CreateSuspended : boolean);
var
s: string;
IdSSL : TIdSSLIOHandlerSocketOpenSSL;
begin
FreeOnTerminate := True;
inherited Create(CreateSuspended);
lHTTP := TIdHTTP.Create(nil);
IdSSL := TIdSSLIOHandlerSocketOpenSSL.Create(nil);
try
lHTTP.ReadTimeout := 30000;
lHTTP.IOHandler := IdSSL;
IdSSL.SSLOptions.Method := sslvTLSv1;
IdSSL.SSLOptions.Method := sslvTLSv1;
IdSSL.SSLOptions.Mode := sslmUnassigned;
lHTTP.IOHandler := TIdSSLIOHandlerSocketOpenSSL.Create(lHTTP);
lHTTP.HandleRedirects := True;
finally
end;
end;
destructor TMyThread.Destroy;
begin
inherited;
end;
procedure TMyThread.Execute;
var
s: string;
i: Integer;
satir: Integer;
str: TStringList;
newStatus : string;
begin
fStatusText := 'TMyThread Starting...';
Synchronize(Showstatus);
fStatusText := 'TMyThread Running...';
while (not Terminated) do
begin
for i:= 0 to satir-1 do
begin
try
lHTTP.Get('http://website.com/'+ListBox1.Items.Strings[i]);
Memo1.Lines.Add(ListBox1.Items[i])
except
on E: EIdHTTPProtocolException do
begin
if E.ErrorCode <> 404 then
raise;
ListBox2.Items.Add(ListBox1.Items[i]);
end;
end;
end;
end;
if NewStatus <> fStatusText then
begin
fStatusText := newStatus;
Synchronize(Showstatus);
end;
end;
procedure TMyThread.ShowStatus;
begin
Form1.Caption := fStatusText;
end;
end.
now when i hit button3 the Form caption goes TMyThread is Starting... and nothing happens after!, please have a look at the codes, Many thanks.
You should be using a separate thread for each URL, not using a single thread
that loops through all of the URLs.
Try something more like this instead:
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs,
Vcl.StdCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
ListBox1: TListBox;
ListBox2: TListBox;
Button3: TButton;
Memo1: TMemo;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure Button3Click(Sender: TObject);
private
procedure MyThreadPathResult(const APath: string; AResult: Boolean);
procedure MyThreadStatus(const AStr: string);
end;
var
Form1: TForm1;
implementation
uses
IdBaseComponent, IdComponent, IdTCPConnection, IdTCPClient, IdHTTP, IdSSLOpenSSL;
type
TMyThreadPathResultEvent = procedure(const APath: string; AResult: Boolean) of object;
TMyThreadStatusEvent = procedure(const APath, AStr: string) of object;
TMyThread = class(TThread)
private
fPath: string;
fOnPathResult: TMyThreadPathResultEvent;
fOnStatus: TMyThreadStatusEvent;
procedure PathResult(AResult: Boolean);
procedure ShowStatus(const Str: string);
protected
procedure Execute; override;
public
constructor Create(const APath: string); reintroduce;
property OnPathResult: TMyThreadPathResultEvent read fOnPathResult write fOnPathResult;
property OnStatus: TMyThreadStatusEvent read fOnStatus write fOnStatus;
end;
procedure TForm1.Button3Click(Sender: TObject);
var
i: Integer;
Thread: TMyThread;
begin
for i := 0 to ListBox1.Items.Count-1 do
begin
Thread := TMyThread.Create(ListBox1.Items.Strings[i]);
Thread.OnPathResult := MyThreadPathResult;
Thread.OnStatus := MyThreadStatus;
Thread.Start;
end;
end;
procedure TForm1.MyThreadPathResult(const APath: string; AResult: Boolean);
begin
if AResult then
Memo1.Lines.Add(APath)
else
ListBox2.Items.Add(APath);
end;
procedure TForm1.MyThreadStatus(const AStr: string);
begin
Caption := AStr;
end;
constructor TMyThread.Create(const APath: string);
begin
inherited Create(True);
FreeOnTerminate := True;
fPath := APath;
end;
procedure TMyThread.Execute;
var
lHTTP: TIdHTTP;
IdSSL: TIdSSLIOHandlerSocketOpenSSL;
begin
ShowStatus('TMyThread Starting...');
lHTTP := TIdHTTP.Create(nil);
try
lHTTP.ReadTimeout := 30000;
lHTTP.HandleRedirects := True;
IdSSL := TIdSSLIOHandlerSocketOpenSSL.Create(lHTTP);
IdSSL.SSLOptions.Method := sslvTLSv1;
IdSSL.SSLOptions.Mode := sslmClient;
lHTTP.IOHandler := IdSSL;
ShowStatus('TMyThread Running...');
try
lHTTP.Get('http://website.com/'+fPath, TStream(nil));
except
on E: EIdHTTPProtocolException do
begin
if E.ErrorCode = 404 then
PathResult(False)
else
raise;
end;
end;
finally
lHttp.Free;
end;
PathResult(True);
end;
procedure TMyThread.PathResult(AResult: Boolean);
begin
if Assigned(fOnPathResult) then
begin
TThread.Synchronize(
procedure
begin
if Assigned(fOnPathResult) then
fOnPathResult(fPath, AResult);
end
);
end;
end;
procedure TMyThread.ShowStatus(const Str: string);
begin
if Assigned(fOnStatus) then
begin
TThread.Synchronize(
procedure
begin
if Assigned(fOnStatus) then
fOnStatus(fPath, Str);
end
);
end;
end;
end.
With that said, you could consider using Delphi's Parallel Programming Library instead:
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs,
Vcl.StdCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
ListBox1: TListBox;
ListBox2: TListBox;
Button3: TButton;
Memo1: TMemo;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure Button3Click(Sender: TObject);
end;
var
Form1: TForm1;
implementation
uses
System.Threading, IdBaseComponent, IdComponent, IdTCPConnection, IdTCPClient, IdHTTP, IdSSLOpenSSL;
procedure TForm1.Button3Click(Sender: TObject);
begin
TParallel.&For(0, ListBox1.Items.Count-1,
procedure(AIndex: Integer)
var
lPath: string;
lHTTP: TIdHTTP;
IdSSL: TIdSSLIOHandlerSocketOpenSSL;
begin
TThread.Synchronize(nil,
procedure
begin
Form1.Caption := 'Task Starting...';
lPath := ListBox1.Items.Strings[AIndex];
end;
end;
lHTTP := TIdHTTP.Create(nil);
try
lHTTP.ReadTimeout := 30000;
lHTTP.HandleRedirects := True;
IdSSL := TIdSSLIOHandlerSocketOpenSSL.Create(lHTTP);
IdSSL.SSLOptions.Method := sslvTLSv1;
IdSSL.SSLOptions.Mode := sslmClient;
lHTTP.IOHandler := IdSSL;
TThread.Synchronize(nil,
procedure
begin
Form1.Caption := 'Task Running...';
end;
end;
try
lHTTP.Get('http://website.com/'+lPath, TStream(nil));
except
on E: EIdHTTPProtocolException do
begin
if E.ErrorCode = 404 then
begin
TThread.Synchronize(nil,
procedure
begin
Form1.ListBox2.Items.Add(lPath);
end
);
end;
Exit;
end;
end;
finally
lHttp.Free;
end;
TThread.Synchronize(nil,
procedure
begin
Form1.Memo1.Lines.Add(lPath);
end
);
end
);
end;
end.
Or:
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs,
Vcl.StdCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
ListBox1: TListBox;
ListBox2: TListBox;
Button3: TButton;
Memo1: TMemo;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure Button3Click(Sender: TObject);
end;
var
Form1: TForm1;
implementation
uses
System.Threading, IdBaseComponent, IdComponent, IdTCPConnection, IdTCPClient, IdHTTP, IdSSLOpenSSL;
procedure TForm1.Button3Click(Sender: TObject);
var
i: Integer;
lPath: string;
begin
for i := 0 to ListBox1.Items.Count-1 do
begin
lPath := ListBox1.Items.Strings[i];
TTask.Create(
procedure
var
lHTTP: TIdHTTP;
IdSSL: TIdSSLIOHandlerSocketOpenSSL;
begin
TThread.Synchronize(nil,
procedure
begin
Form1.Caption := 'Task Starting...';
end;
end;
lHTTP := TIdHTTP.Create(nil);
try
lHTTP.ReadTimeout := 30000;
lHTTP.HandleRedirects := True;
IdSSL := TIdSSLIOHandlerSocketOpenSSL.Create(lHTTP);
IdSSL.SSLOptions.Method := sslvTLSv1;
IdSSL.SSLOptions.Mode := sslmClient;
lHTTP.IOHandler := IdSSL;
TThread.Synchronize(nil,
procedure
begin
Form1.Caption := 'Task Running...';
end;
end;
try
lHTTP.Get('http://website.com/'+lPath, TStream(nil));
except
on E: EIdHTTPProtocolException do
begin
if E.ErrorCode = 404 then
begin
TThread.Synchronize(nil,
procedure
begin
Form1.ListBox2.Items.Add(lPath);
end
);
end;
Exit;
end;
end;
finally
lHttp.Free;
end;
TThread.Synchronize(nil,
procedure
begin
Form1.Memo1.Lines.Add(lPath);
end
);
end
).Start;
end;
end;
end.
My Code for video recording is given, the recording is not in a smooth way i.e. the place where I turn my camera appears on the preview view late. How I can resolve this issue
unit VideoAttachmentUnit;
interface
uses
System.SysUtils,
System.Types,
System.UITypes,
System.Classes,
System.Variants,
FMX.Types,
FMX.Controls,
FMX.Forms,
FMX.Dialogs,
FMX.StdCtrls,
FMX.Media,
FMX.Platform,
FMX.Objects,
FMX.Layouts,
FMX.Memo,
FMX.Controls.Presentation;
type
TVideoAttachmentForm = class(TForm)
NavBar: TToolBar;
CameraChangeBtn: TButton;
PlayBtn: TButton;
CloseScreenBtn: TButton;
ToolBar1: TToolBar;
StartRecordingBtn: TButton;
StopRecordingBtn: TButton;
ImageCameraView: TImage;
CameraComponent: TCameraComponent;
procedure FormCreate(Sender: TObject);
procedure CloseScreenBtnClick(Sender: TObject);
procedure FormShow(Sender: TObject);
procedure CameraChangeBtnClick(Sender: TObject);
procedure StartRecordingBtnClick(Sender: TObject);
procedure StopRecordingBtnClick(Sender: TObject);
procedure CameraComponentSampleBufferReady(Sender: TObject;
const ATime: TMediaTime);
private
{ Private declarations }
procedure GetImage;
procedure InitialSettingsForTheRecording;
public
function AppEvent(AAppEvent: TApplicationEvent; AContext: TObject): Boolean;
end;
var
VideoAttachmentForm: TVideoAttachmentForm;
WhichCamera:String;
procedure DisplayTheVideoAttachmentScreen;
implementation
{$R *.fmx}
procedure DisplayTheVideoAttachmentScreen;
begin
try
Application.CreateForm(TVideoAttachmentForm , VideoAttachmentForm);
VideoAttachmentForm.Show;
finally
end;
end;
procedure TVideoAttachmentForm.CameraChangeBtnClick(Sender: TObject);
var
LActive: Boolean;
begin
{ Select Back Camera }
LActive := CameraComponent.Active;
try
CameraComponent.Active := False;
if WhichCamera = 'BackCamera' then
begin
CameraComponent.Kind := TCameraKind.FrontCamera;
WhichCamera := 'FrontCamera';
end
else if WhichCamera = 'FrontCamera' then
begin
CameraComponent.Kind := TCameraKind.BackCamera;
WhichCamera := 'BackCamera';
end;
finally
CameraComponent.Active := LActive;
end;
end;
procedure TVideoAttachmentForm.CameraComponentSampleBufferReady(Sender: TObject;
const ATime: TMediaTime);
begin
TThread.Synchronize(TThread.CurrentThread, GetImage);
ImageCameraView.Width := ImageCameraView.Bitmap.Width;
ImageCameraView.Height := ImageCameraView.Bitmap.Height;
end;
procedure TVideoAttachmentForm.CloseScreenBtnClick(Sender: TObject);
begin
VideoAttachmentForm.Close;
end;
procedure TVideoAttachmentForm.FormCreate(Sender: TObject);
var
AppEventSvc: IFMXApplicationEventService;
begin
if TPlatformServices.Current.SupportsPlatformService(IFMXApplicationEventService, IInterface(AppEventSvc)) then
AppEventSvc.SetApplicationEventHandler(AppEvent);
end;
procedure TVideoAttachmentForm.FormShow(Sender: TObject);
begin
InitialSettingsForTheRecording;
end;
function TVideoAttachmentForm.AppEvent(AAppEvent: TApplicationEvent; AContext: TObject): Boolean;
begin
case AAppEvent of
TApplicationEvent.WillBecomeInactive:
CameraComponent.Active := False;
TApplicationEvent.EnteredBackground:
CameraComponent.Active := False;
TApplicationEvent.WillTerminate:
CameraComponent.Active := False;
end;
end;
procedure TVideoAttachmentForm.InitialSettingsForTheRecording;
var
LSettings: TVideoCaptureSetting;
begin
CameraComponent.Kind := TCameraKind.BackCamera;
WhichCamera := 'BackCamera';
if CameraComponent.HasTorch then
begin
CameraComponent.TorchMode := TTorchMode.ModeAuto;
end;
CameraComponent.Quality := TVideoCaptureQuality.CaptureSettings;
CameraComponent.CaptureSettingPriority := TVideoCaptureSettingPriority.FrameRate;
end;
procedure TVideoAttachmentForm.StartRecordingBtnClick(Sender: TObject);
begin
CameraComponent.Active := True;
end;
procedure TVideoAttachmentForm.StopRecordingBtnClick(Sender: TObject);
begin
CameraComponent.Active := False;
end;
procedure TVideoAttachmentForm.GetImage;
begin
CameraComponent.SampleBufferToBitmap(ImageCameraView.Bitmap, True);
end;
end.