how to calculate the Framerate and Timecode during screen capturing? - delphi

Delphi 6 project
I have searched google pretty thorough but am not finding the answers to my delima. basically i want to have the timecode and videos framerate of a current screen capture session showing in my app, in the statusbar or label. i also need this with respect to syncing the captures to the framerate of the sofware player playing the video, otherwise i get a lot of duplicate or missed frames. the videos are 29.970 and 23.976 fps. So i need to be able to configure for both, somehow.
Currently, I can screen capture from tv cards and software video players like, vlc, ffplay, mplayer, virtualdub, etc.
i'm not sure how to implement the necessary routines into mine, let alone where. i've been reading a lot about the following items below but they are all over my head though i did make many attempts at it:
timer1 control -- setting interval to 34 is not exact, it duplicates or misses frames during screen capture
gettimetick and timegettime
timeBeginPeriod and timeEndPeriod
QueryPerformanceTimer and QueryPerformanceCounter
To help simiplfy the process, i snipped a lot of code of the original project to only feature the screen capturing. Here is the complete routine (along with some remarked-out experimental code) for this:
(thanks in advanced for any help)
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, mmsystem,
ExtCtrls, clipbrd, DXClass;
type
TForm1 = class(TForm)
Timer1: TTimer;
Panel1: TPanel;
m1: TMemo;
btnCapOnOff: TButton;
txtHandle: TEdit;
Edit2: TEdit;
stDataRate: TStaticText;
btnCopy: TButton;
btnSetHDC: TButton;
dxt1: TDXTimer;
sb1: TScrollBox;
Splitter1: TSplitter;
im1: TImage;
procedure btnCapOnOffClick(Sender: TObject);
procedure FormActivate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure capturewindow;
procedure Timer1Timer(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure btnCopyClick(Sender: TObject);
procedure btnSetHDCClick(Sender: TObject);
procedure dxt1Timer(Sender: TObject; LagCount: Integer);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
start,
finish : cardinal; //int64;
i : integer;
s : string;
bm: tbitmap;
dc: hdc=0;
implementation
{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject);
begin
form1.DoubleBuffered:=true;
sb1.DoubleBuffered:=true; // this is a scrollbox control
end;
procedure TForm1.FormActivate(Sender: TObject);
begin
im1.Picture.Bitmap.PixelFormat:=pf24bit;
im1.Width:=352;
im1.Height:=240;
end;
procedure TForm1.btnSetHDCClick(Sender: TObject);
begin
if dc=0 then dc := getdc(strToint(txtHandle.text));
end;
procedure TForm1.capturewindow;
begin
//timeBeginPeriod(1);
start := timegettime;
//sleep(1);
bitblt(bm.canvas.Handle, 0,0, 352,240, dc, 0,0, srccopy);
finish := timegettime-start;
//m1.lines.Add(intTostr(finish)); // debugging: to spill out timing values, etc.
im1.Picture.Bitmap := bm;
stDataRate.Caption := 'Date Rate: '+intTostr(finish) + ' fps or ms';
end;
procedure TForm1.dxt1Timer(Sender: TObject; LagCount: Integer);
begin
capturewindow;
end;
procedure TForm1.Timer1Timer(Sender: TObject);
begin
// capturewindow; // timer1 is too slow or unpredictable
end;
// button: a cheeters way to turn On or Off capturing
procedure TForm1.btnCapOnOffClick(Sender: TObject);
begin
if btnCapOnOff.caption='Cap is Off' then begin
btnCapOnOff.caption:='Cap is On';
//timer1.Enabled:=true; // capture the window // too slow
dxt1.Enabled:=true; // capture the window // a better timer control component (delphiX)
end else begin
btnCapOnOff.Caption:='Cap is Off';
//timer1.Enabled:=false; // too slow
dxt1.Enabled:=false; // stop capturing the window // a better timer control component (delphiX)
end;
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
bm.free;
releaseDC(dc,dc);
//timeEndPeriod(1);
end;
procedure TForm1.btnCopyClick(Sender: TObject);
begin
clipboard.assign(im1.picture.bitmap); // to take quick pics
end;
initialization
bm := tbitmap.Create;
bm.PixelFormat:=pf24bit;
bm.Width:=352;
bm.Height:=240; beep;
end.

Actually hooking the software that's playing the video, and synchronizing to it, I'm not sure how to do that. But working on timing might help out. Assuming that the software playing the video is also well timed, you should be able to get a smooth capture.
This tutorial is useful: http://www.codeproject.com/Articles/1236/Timers-Tutorial
The "Multimedia Timers" offer good resolution (down to 1ms on most machines), and I've found them to be reliable.
What I would try is using the Performance Timer (queryperformancetimer, as you've already mentioned) to time your "CaptureWindow" procedure. Then, when you call "timesetevent" in a multimedia timer, subtract the amount of time the capture took from the overall time of a single frame, and use that as your "uDelay" value.
HowLongTimerShouldWait := LengthOfASingleFrame - TimeSpentCapturingPreviousFrame
The nice thing about the Multimedia Timers is that they let you use it as a 'one shot', where each interval can have a different delay period. I've generally set the timer to call a single procedure recursively, until it's flagged to stop.
This way, with a bit of fine tuning, you should be able to get capture rates are are within a +/-1ms tolerance of the actual video FPS.

As promised, here is the code i came up with based on some google searches and working them out in delphi. the following links did help me out some though (but due to c/c++/c# i could not translate as easily to delphi) so most of the final answer were based on lots of trial and error:
http://www.andrewduncan.ws/Timecodes/Timecodes.html
http://puredata.hurleur.com/sujet-990-framenumber-timecode-conversion
To my knowledge the routine works flawless. but just so you know, i like my numbers formated for spacing purposes, so i padded to 2-digits, this way there is not shriking back and forth as numbers progress past 59.
Heres how it works:
It computes the timecode based on the frame rate of your video source (ie 29.970 interlace or progressive, and 23.976 for 24p film) .. so just feed it a frame number and the function will return the timecode in string format.
Example Preporation/Useage:
put two Tedit's and one Tbutton control on your form1
in button1 onClick event, enter this: edit2.text := frameNo2timecode(strToint(edit1.text), 29.970);
now, run the program and enter your frame number in the first edit1.text
then, press the button1 control, it will compute the timecode in edit2.text
source code to calculate timecode:
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, math;
type
TForm1 = class(TForm)
Edit1: TEdit;
Edit2: TEdit;
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
function FrameNo2Timecode(fn: longint; rate: real): string;
var
hours,mins,secs,milli: extended;
hoursStr, minsStr, secsStr, milliStr: string;
function padzero(N: longint; Len: Integer): string;
begin
FmtStr(Result, '%d', [N]);
while Length(Result) < Len do
Result := '0' + Result;
end;
begin
hours := floor( (fn/rate)/3600) mod 60;
hoursStr := padzero(floor(hours),2);
mins := floor( (fn/rate)/60.0) mod 60;
minsstr := padzero(floor(mins),2);
secs := floor( (fn/rate)) mod 60;
secsstr := padzero(floor(secs),2);
milli := floor( (1000*fn/rate)) mod 6000 mod 1000;
millistr := padzero(floor(milli),3);
result := hoursStr +':'+ minsStr +':'+ secsStr +'.'+ milliStr;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
edit2.text := frameNo2timecode(strToint(edit1.text), 29.970);
end;
end.

Related

How do I move an image up and down when a button is clicked

I am trying to animate a celebratory trophy image that will 'bounce' up and down. I tried using a timer and then I used modulus to determine whether its odd or even, if its odd it goes up 10 if even it goes down 10 etc. I think the problem is looping, I need to use some form of loop right?
unit Unit11;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, pngimage, ExtCtrls,math, StdCtrls;
type
Tfrmwinner = class(TForm)
Panel1: TPanel;
Label1: TLabel;
Label2: TLabel;
Image1: TImage;
Image2: TImage;
Label3: TLabel;
Label4: TLabel;
Timer1: TTimer;
procedure Timer1Timer(Sender: TObject);
procedure Label4Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
frmwinner: Tfrmwinner;
implementation
uses Unit12,Unit4;
{$R *.dfm}
procedure Tfrmwinner.Label4Click(Sender: TObject);
var
position:integer;
begin
frmwinner.Hide;
frmboard.show;
unit12.frmboard.memlead.Lines.Add('Position'+#9+'Name'+#9+'ID Number');
unit12.frmboard.memlead.Lines.Add('___________________________________');
while not unit4.frmcontest.ADOLead.Eof do
begin
position:=position+1;
unit4.frmcontest.ADOLead.First;
unit12.frmboard.memlead.Lines.Add(inttostr(position)+#9+unit4.frmcontest.ADOLead['Name(s)']+#9+inttostr(unit4.frmcontest.ADOLead['ID Number']));
unit4.frmcontest.ADOLead.Next;
end;
end;
procedure Tfrmwinner.Timer1Timer(Sender: TObject);
var
icount,i:integer;
begin
icount:=0;
icount:=icount+1;
if (icount mod 2)=1 then
begin
image1.top:= image1.top+10;
image2.top:= image2.top+10;
end;
if (icount mod 2)=0 then
begin
image1.top:= image1.top-10;
image2.top:= image2.top-10;
end;
if icount=16 then
begin
timer1.Enabled:=false;
end;
end;
end.
This is what I've tried, with no luck
The problems related to how the image jumps (or doesn't jump) are in procedure TfrmWinner.Timer1Timer(Sender: TObject);
Note that event handlers like an OnTimer or OnKeyPress etc. are triggered by certain system events, and that the event handler you write should do its task as fast as possible and then exit. Also, anything you need to persist until the event handler is called the next time, must be saved in a "safe place" outside of the event handler.
First, the icount: integer variable cannot be declared in the OnTimer handler, because it would cease to exist every time the procedure exits.
Secondly, you can not initialize it (assign 0 to it) in the OnTimer handler, because then it would obviously never reach the final value of 16.
So, move the declaration of icount: integer to the private section of the form:
private
{ Private declarations }
icount: integer;
Then, initialize it to 0 and start the timer in the Label4Click() procedure if that is the purpose (it's unclear in your current code).
icount := 0;
Timer1.Enabled := True;

For loop continue going after reaching goal. Delphi

This issue appears only with numbers, bigger, then 12 including.
Those two pictures captured in one time. How it is even possible?
For loop must go from 0 to 12-1=11, doesn't it?
Nevertheless, when I use while loop instead, it works fine.
Is it my fault or Delphi's?
P.S. Code down bellow.
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, Grids;
type
TForm1 = class(TForm)
StringGrid1: TStringGrid;
Button1: TButton;
Edit1: TEdit;
Button2: TButton;
Label1: TLabel;
Button3: TButton;
Label2: TLabel;
Label3: TLabel;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure StringGrid1KeyPress(Sender: TObject; var Key: Char);
procedure Button3Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
n:Integer;
implementation
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject); //Button, that sets array length
var
i, index:Integer;
begin
val(Edit1.Text, n, index);
if(index<>0) then
begin
ShowMessage('Wrong number');
Edit1.Clear();
exit;
end;
StringGrid1.ColCount:=n;
for i:=0 to n-1 do
StringGrid1.Cells[i,0]:=IntToStr(i+1);
StringGrid1.SetFocus();
end;
procedure TForm1.Button2Click(Sender: TObject); //Main button
var
i, index:Integer;
a:array[0..10] of Real;
denom, sum:Real;
begin
i:=0;
sum:=0;
denom:=-1;
//that for loop from screenshot is here
for i:=0 to n-1 do
//while i<=(n-1) do
begin
Val(StringGrid1.cells[i,1], a[i], index);
if(index<>0) then
begin
ShowMessage('Wrong number with ' + IntToStr(i+1) + ' Id');
StringGrid1.Col:=i;
StringGrid1.Row:=1;
StringGrid1.SetFocus();
exit;
end;
a[i]:=a[i]/denom;
sum:=sum+a[i];
StringGrid1.Cells[i,2]:=FloatToStrF(a[i],ffFixed,5,3);
denom:=-denom*(i+2);
//Inc(i);
end;
Label2.Caption:=FloatToStrF(sum,ffFixed,5,3);
end;
//code down bellow just allow to go to another cell by pressing Enter
procedure TForm1.StringGrid1KeyPress(Sender: TObject; var Key: Char);
begin
if (Key=#13) and (StringGrid1.Col=(n-1)) then
Button2.SetFocus()
else if (Key=#13) and (StringGrid1.Col<>(n-1)) then
StringGrid1.Col:=StringGrid1.Col+1;
end;
procedure TForm1.Button3Click(Sender: TObject);
begin
Close();
end;
end.
As to answer your question of 'how is this even possible'...
In your screen, n is 12. As pointed out by Kermation, the highest index of a is 10, so when i is 11, unless you have range checking activated, when you write to a[11] (i=11) you will overwrite something else. This is in the local variable area so it might be i, for instance, or even internal variables you can't see like the limit used for the for loop, which is calculated at the start of the loop. Once you allow this to happen, pretty much anything is possible.
Of course the exact manifestation of the problem will very from one version of the compiler to another. In one version you might get away with it. in another you won't.
Array a size was smaller, then amount of cells.

PNGImage "Access violation" error at procedure end

I am using PNGImage library in my project, which entire GUI is made up of .png images, which i loaded to TImages at run-time. For some purposes i have to dynamically create plenty of components groups that are similar to each other. Every group consists of some TImages and have a button that lets user proceed to another page with more details about clicked item.
The code i am using:
procedure TMain_Frame.selection_click(Sender: TObject);
var id: string;
begin
id := StringReplace(TLabel(sender).Name, 'label_item_select_', '', [rfReplaceAll]);
hide_created_components; // It does Free all components
show_details(id);
end; // (1)
Access violation error occurs at (1). The odd thing is that it happenes completly random: error may happen at the very first click or may not happen for 10 clicks. If no error occured, F8 leads me inside PNGImage library where some stuff is done. However when error occurs, F7/8 immediately throws it without doing what it has to. This problem happenes only when i go from dynamicaly created objects to static.
CPU window shows that error occured at this ASM code:
movzx ecx, [edi]
ecx value is 755A2E09, edi is 00000000
Is it correct to .Free all dynamically created components? Or should be .Destroy used instead? And why does PNGImage goes inside itself on procedure end;?
Demo:
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, pngimage, ExtCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
Image1: TImage;
procedure selection_click(Sender: TObject);
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure create_label;
var Button: TLabel;
begin
Button := TLabel.Create(Form1);
with Button do
begin
Name := 'dynamic_label_1';
Parent := Form1;
Autosize := false;
Left := 100;
Top := 100;
Width := 150;
Height := 20;
Caption := 'Dynamic Label: Click Me';
BringToFront;
Cursor := crHandPoint;
end;
Button.OnClick := Form1.selection_click;
end;
procedure hide_dyn_label(L: TLabel; mode: boolean);
begin
if mode then
begin
L.Free;
Form1.Image1.Picture.LoadFromFile(PAnsiChar('button_close.png'));
Form1.Image1.Visible := true;
end
else
create_label;
end;
procedure TForm1.selection_click(Sender: TObject);
var id: string;
begin
id := StringReplace(TLabel(Sender).Name, 'dynamic_label_', '', [rfReplaceAll]);
Form1.Button1.Visible := true;
hide_dyn_label(Form1.FindComponent('dynamic_label_1') as TLabel, true);
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
create_label;
Form1.Image1.Visible := false;
Form1.Button1.Visible := false;
end;
end.
You are freeing the TLabel while still in its OnClick event handler, Selection_Click which calls hide_dyn_label() which calls L.Free. You can't do that. Use some kind of delayed destruction, f.ex. with a boolean variable FreeDynLabels which you can check in Application.OnIdle. Or post a custom message to the form.

Delphi7, Create many controls with same properties

I am new to Delphi.
I would like to make an application in which will create a number of Buttons. Declaring an array of Tbuttons and create the buttons 1 by 1 is not very satisfying, because it is confusing and takes a lot of time. Using the Command For is also unsatisfying, because i won't be able to change some of button's properties, if needed, for example their position.
So i decided to declare a procedure in TForm1 Class, which creates the buttons based on what properties I send to the procedure. But for some reason it is not working (There aren't any syntax Errors):
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ExtCtrls;
type
TForm1 = class(TForm) //Declaring the procedure
procedure CreateButton(Button: TButton; L: Integer; T: Integer);
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
B1, B2: TForm1; //Declaring controls
implementation
{$R *.dfm}
procedure TForm1.CreateButton(Button: TButton; L: Integer; T: Integer);
begin
Button:= TButton.Create(Self);
Button.Parent:= Self;
Button.Width:= 100; Button.Height:= 50;
Button.Left:= L; Button.Top:= T;
end;
procedure TForm1.FormCreate(Sender: TObject);
Var
Button1, Button2: TButton;
begin
B1.CreateButton(Button1, 100, 50); //Sending properties
B2.CreateButton(Button2, 200, 40); //Sending properties
end;
end.
AS: during the communication with topic starters answer grown too. The total outcome is like http://pastebin.ca/2426760
procedure TForm1.CreateButton(VAR Button: TButton; CONST L: Integer; CONST T: Integer);
That is of basics of Pascal language how to pass parameters to procedures/functions.
http://docwiki.embarcadero.com/RADStudio/XE4/en/Parameters_(Delphi)
Actually, I don't think there is any problem with the parameters
Button = nil, which means the values of "Button1" and "Button2" are not sent, however
http://pastebin.ca/2427238
Kudoes to Bill for spotting this. Using separate properties to position your controls is both inefficient and prone to copy-paste errors.
http://docwiki.embarcadero.com/Libraries/XE3/en/Vcl.Controls.TControl.SetBounds
http://docwiki.embarcadero.com/CodeExamples/XE3/en/BoundsRect_(Delphi)
Using the 2nd link:
procedure TForm1.CreateButton(out Button: TButton; const L: Integer; const T: Integer);
begin
Button:= TButton.Create(Self);
Button.Parent:= Self;
Button.SetBounds( L, T, 100, 50);
end;
Actually what do you do with pointers to newly created buttons ?
In your code you just loose them!
procedure TForm1.FormCreate(Sender: TObject);
Var
Button1, Button2: TButton;
begin
...
end;
In this your code those pointers would be just lost! If you do need those values - pass them outside of the procedure. If you do not - do not ask for them - http://en.wikipedia.org/wiki/YAGNI http://en.wikipedia.org/wiki/KISS_principle
Procedure TForm1.CreateButton(const L, T: Integer);
begin
With TButton.Create(Self) do begin
Parent := Self;
SetBounds( L, T, 100, 50);
Caption := 'Caption at ' + IntToStr(T);
Name := 'Name at ' + IntToStr(L);
End;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
B1.CreateButton( 100, 50); //Sending properties
B2.CreateButton( 200, 40); //Sending properties
end;
Now to those B1, B2...
You claim that you want 2 buttons on the form, but you code shows you try to make THREE FORMS and one button on the 2nd form and one button on the 3rd form. So what do you really want ??? And do you check that B1 and B2 forms were created befoe tryign to add buttons to them ?
Perhaps you really wanted
procedure TForm1.FormCreate(Sender: TObject);
begin
SELF.CreateButton( 100, 50); //Sending properties
SELF.CreateButton( 200, 40); //Sending properties
end;
Then to go with DRY principle and to keep all the variables in one place.
http://docwiki.embarcadero.com/Libraries/XE2/en/System.Types.TPoint
Procedure TForm1.CreateButtons(const a: array of TPoint);
Var p: TPoint;
Begin
for p in a do
CreateButton( p.x, p.y );
End;
type TPointDynArray = array of TPoint;
procedure TForm1.FormCreate(Sender: TObject);
begin
CreateButtons( TPointDynArray.Create(
Point( 100,50 ), Point(200, 40) ) );
end;
Kudos to Delphi array initialization
Later you can always add more coordinates to an array and keep it consistent.
Well, to bring down this to Delphi 7 abilities that would - somewhat redundantly - be coded like
const BtnCnt = 2;
BtnLocations : array [1..BtnCnt] of TSize = (
( cx: 100, cy: 50 ), ( cx: 200, cy: 40 )
);
Procedure TForm1.CreateButtons(const a: array of TSize);
Var i: integer;
Begin
for i := Low(a) to High(a) do
with a[i] do
CreateButton( cx, cy );
End;
procedure TForm1.FormCreate(Sender: TObject);
begin
CreateButtons( BtnLocations );
end;
But while Delphi 5 and Dephi 7 were great releases, they are very outdated. I definitely suggest you either upgradeing to Delphi XE or more recent, or side-stepping to CodeTyphon
TForm1 = class(TForm) //Declaring the procedure
procedure CreateButton(Button: TButton; L: Integer; T: Integer);
Declaring that one-purpose procedure in PUBLISHED section of the form class is also not a very good style. You'd better declare them in PRIVATE section. Adhering to "least visibility" would help you to make interdependencies controllable. Otherwise in a year your program would become a spaghetti mess, where you just cannot change anything without ruining everything else. I am workign on a project with 10+ years of history now and i see the consequences of "everything is public" very clear. It is a great pain!

Delphi local net app

Trying to learn how to make server-client apps and stuff like. I trying to draw circles(on mouse click) in all clients so this is how i trying to do that. But it's not working - no errors but form is empty. What i need to fix?
Client code
unit Client;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ExtCtrls, {Figure, Ball,} IdBaseComponent, IdComponent,
IdTCPConnection, IdTCPClient, ScktComp;
type
TForm1 = class(TForm)
Timer1: TTimer;
Button1: TButton;
ClientSocket: TClientSocket;
procedure FormCreate(Sender: TObject);
procedure Button1Click(Sender: TObject);
procedure Timer1Timer(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
procedure FormMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
private
public
{ Public declarations }
end;
var
Form1: TForm1;
f:boolean;
p:MyPoint;
s:MyPoint;
z:TCanvas;
obj: MyFigure;
pX, pY:Integer;
myBuf: array[1..32] of Integer;
dataBuf: array[1..32] of Integer;
implementation
{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject);
begin
Timer1.Enabled:=false;
Timer1.Interval:=5;
z:=Form1.Canvas;//TCanvas.Create;
Button1.Caption:='Пуск';
f:=false;
ClientSocket.Port:=1234;
ClientSocket.Active:= False;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
if not f then
begin
Timer1.Enabled:=true;
Button1.Caption:='Стоп';
f:=not f;
end
else
begin
Timer1.Enabled:=false;
Button1.Caption:='Пуск';
ClientSocket.Active:= True;
f:=not f;
end;
end;
procedure TForm1.Timer1Timer(Sender: TObject);
begin
//z.Lock;
//z.Brush.Color:=ClWhite;
//z.FillRect(Canvas.ClipRect);
//obj.Draw(z);
if ClientSocket.Active then
ClientSocket.Socket.ReceiveBuf(dataBuf, 32);
z.Brush.Color:=ClRed;
z.Ellipse(dataBuf[1] + 10, dataBuf[2] + 10,dataBuf[1] - 10, dataBuf[2] - 10);
//z.Unlock;
end;
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
ClientSocket.Active := false;
end;
procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
myBuf[1]:=X;
myBuf[2]:=Y;
if ClientSocket.Active then
ClientSocket.Socket.SendBuf(myBuf, 32);
end;
end.
Server
unit ServerProject;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ScktComp;
type
TForm1 = class(TForm)
ServerSocket1: TServerSocket;
procedure FormCreate(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
procedure ServerSocket1ClientRead(Sender: TObject;
Socket: TCustomWinSocket);
procedure ServerSocket1ClientWrite(Sender: TObject;
Socket: TCustomWinSocket);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
sBufer : array [1..32] of Integer;
implementation
{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject);
begin
ServerSocket1.Port:=1234;
ServerSocket1.Active := True;
end;
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
ServerSocket1.Active := false;
end;
procedure TForm1.ServerSocket1ClientRead(Sender: TObject;
Socket: TCustomWinSocket);
var
i:integer;
begin
for i := 0 to ServerSocket1.Socket.ActiveConnections-1 do
begin
with ServerSocket1.Socket.Connections[i] do
begin
ReceiveBuf(sBufer, 32);
end;
end;
end;
procedure TForm1.ServerSocket1ClientWrite(Sender: TObject;
Socket: TCustomWinSocket);
var
i:integer;
begin
for i := 0 to ServerSocket1.Socket.ActiveConnections-1 do
begin
with ServerSocket1.Socket.Connections[i] do
begin
SendBuf(sBufer, 32);
end;
end;
end;
end.
Your painting code is in the wrong place and is painting to the wrong thing. In Windows programs you are meant to paint in response to a WM_PAINT message. You are not doing so. What's more, you have to paint on a device context that is provided by a call to BeginPaint.
The VCL wraps all those details up for you, but you still need to follow the rules. In your case I recommend that you add a TPaintBox component to your form. Then implement an OnPaint event handler for the paint box. Finally, whenever you wish to repaint the paint box, for example on a timer, call the Invalidate method of the paint box.
I suspect that you want your each new ellipse to be drawn in addition to the earlier drawn ellipses. In which case you are probably best served by drawing them to an off-screen bitmap first and then, when you come to paint to the paint box, draw that bitmap on the paint box. The point is that a window needs to be able to re-paint itself in its entirety. When you paint to a screen device, what you painted is lost the next time that window needs to be painted. So it's the responsibility of the application to be able to paint its entire self at any point, if it is asked.
More generally I urge you to stop using global variables. They will cause you no end of trouble. Prefer local variables wherever possible. If you need state to persist between different method calls, use member variables. The guiding principle is to use the narrowest scope possible.
Your current design uses a timer to poll for new data. That's a very poor approach. The most efficient and effective approach is to use synchronous blocking communication. Indy takes that approach. Windows sockets components instead tend to be used in an asynchronous mode. Irrespective of the relative merits of these two approaches, you should not be polling on a timer. If you do use asynchronous communication, then respond to new data by handling an event rather than polling.
Your program is currently trying to mix together GDI painting, and network communication. I suggest that you attempt to get on top of these concepts one at a time. Learn how to paint without the distraction of communication. Then when you have cracked painting, try to bring in the communication aspect.

Resources