Passing variable from project source to form - forms

I am putting some logic into the project source before form creation. If some conditions are met, I proceed with form creation. The logic is generating data that I need to pass to the form, let'say some variable. I declared these variables inside the public section of the form, but I can't find a way to pass these values, since the variables need the form to be created to exist.
Is there a way? I am using Delphi 2007.

I suggest to set these variables once the condition is met.
Run your logic
Check the condition
Create form
Assign variables on the form
In the project source:
var
MyVariable1 : integer;
MyVariable2 : integer;
begin
Application.Initialize;
Application.MainFormOnTaskbar := True;
//some logic to assign variables
//...
//create form and set variables only if condition is met, example:
if(MyVariable1 + MyVariable2 > 10) then
begin
Application.CreateForm(TForm1, Form1);
Form1.MyVariable1 := MyVariable1;
Form1.MyVariable2 := MyVariable2;
end;
Application.Run;
end.
In the form source:
TForm1 = class(TForm)
private
{ Private declarations }
public
MyVariable1 : integer;
MyVariable2 : integer;
{ Public declarations }
end;

How you're creating the form?
You can do something like this:
f := TMyForm.Create(Application)
f.MyProperty := 10;
f.Show;
Regards.

You can declare a global variable in form unit and use it in the form unit
you can set global var every where you want
TForm9 = class(TForm)
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
public
MyLocalVar : Int64;
end;
var
Form9: TForm9;
MyGlobalVar : Int64;
implementation
{$R *.dfm}
procedure TForm9.FormCreate(Sender: TObject);
begin
MyLocalVar := MyGlobalVar;
end;

Related

Delphi Access Violation on form show

Sorry for having to open new question but I can't find an answer anywhere.
My app is still in progress, but basically I'm trying to call another Form from my MainForm when initializing players, however I get an Access Violation error. Would you please explain to me why this could be happening?
My MainForm code:
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, Buttons, ExtCtrls, jpeg, pngimage, getPlayer_u;...
procedure TfrmMain.FormCreate(Sender: TObject);
begin
Randomize;
InitGameSetup();
end;...
procedure TfrmMain.InitGameSetup();
begin
SetWindowProperties();
InitBackGround();
InitMainMenu();
InitGameBoard();
InitScrabbleTileRack();
InitPlayers();
// GameLoop();
end; ...
procedure TfrmMain.InitPlayers();
var
I : Integer;
sName, sSurname : string;
begin
setLength(Players, NUMBER_OF_PLAYERS);
for I := 1 to High(Players) do
begin
GetPlayer(); ---------------- problem is here
with Players[I] do
begin
Name := sName;
Surname := sSurname;
end;
end;
end;...
procedure TfrmMain.GetPlayer();
begin
frmGetPlayer.Show;
end;
My frmGetPlayer:
unit getPlayer_u;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
TfrmGetPlayer = class(TForm)
btnSubmit: TButton;
edtName: TEdit;
edtSurname: TEdit;
procedure FormCreate(Sender: TObject);
procedure btnSubmitClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
sPlayerName, sPlayerSurname : string;
end;
var
frmGetPlayer: TfrmGetPlayer;
implementation
{$R *.dfm}
procedure TfrmGetPlayer.btnSubmitClick(Sender: TObject);
begin
sPlayerName := edtName.Text;
sPlayerSurname := edtSurname.Text;
if not ((Length(sPlayerName) >= 1) and (Length(sPlayerSurname) >= 1)) then
MessageDlg('Please enter a name and surname.', mtInformation, [mbOK], 0)
else
Self.Free;
end;
procedure TfrmGetPlayer.FormCreate(Sender: TObject);
begin
with Self do
begin
Position := poScreenCenter;
BorderStyle := bsDialog;
end;
end;
end.
My dpr:
program main_p;
uses
Forms,
main_u in 'main_u.pas' {frmMain},
getPlayer_u in 'getPlayer_u.pas' {frmGetPlayer};
{$R *.res}
begin
Application.Initialize;
Application.MainFormOnTaskbar := True;
Application.CreateForm(TfrmMain, frmMain);
Application.Run;
end.
The error:
Only your MainForm object is created automatically at program startup. Inside its OnCreate event, your Player Form object hasn't been created yet, so the frmGetPlayer variable is not pointing at a valid object.
frmGetPlayer is a global variable, so it is initially nil. The error message is telling you that you are accessing invalid memory near address 0, which is almost always an indication of accessing a member of a class via a nil pointer.
So, you can't call frmGetPlayer.Show() until after you have created the Player Form object and assigned frmGetPlayer to point at it. Which the code you showed is not doing.

Is there an Indy server property equivalent to TCustomWinSocket.Data?

When I work with TServerSocket I can use the property Data to store a pointer to a class for example for each client.
Now I will use Indy TIdCmdTCPServer and I'd like to know if there is an equivalent property.
Yes, there is - the TIdContext.Data property. In TIdCmdTCPServer events that give you a TIdCommand parameter instead of a TIdContext parameter, you can access the TIdContext object from the TIdCommand.Context property. For example:
type
TMyClass = class
// add whatever you need...
end;
procedure TForm1.IdCmdTCPServer1Connect(AContext: TIdContext);
var
MyCls: TMyClass;
begin
MyCls := TMyClass.Create;
// initialize MyCls as needed...
AContext.Data := MyCls;
end;
procedure TForm1.IdCmdTCPServer1Disconnect(AContext: TIdContext);
begin
AContext.Data.Free;
AContext.Data := nil;
end;
procedure TForm1.IdCmdTCPServer1CommandHandlerCommand(ACommand: TIdCommand);
var
MyCls: TMyClass;
begin
MyCls := TMyClass(ACommand.Context.Data);
// use MyCls as needed...
end;
Indy also has another useful feature. You can derive a custom class from TIdServerContext, add whatever you want to it, and then assign it to the server's ContextClass property before activating the server. That way, you can simply typecast any TIdContext pointer to your class type when you need to access your custom members. For example:
type
TMyContext = class(TIdServerContext)
public
// add whatever you need...
constructor Create(AConnection: TIdTCPConnection; AYarn: TIdYarn; AList: TIdContextThreadList = nil); override;
destructor Destroy; override;
end;
constructor TMyContext.Create(AConnection: TIdTCPConnection; AYarn: TIdYarn; AList: TIdContextThreadList = nil);
begin
inherited;
//...
end;
destructor TMyContext.Destroy;
begin
//...
inherited;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
IdCmdTCPServer1.ContextsClass := TMyContext;
end;
procedure TForm1.IdCmdTCPServer1Connect(AContext: TIdContext);
var
MyCtx: TMyContext;
begin
MyCtx := TMyClass(AContext);
// initialize MyCtx as needed...
end;
procedure TForm1.IdCmdTCPServer1CommandHandlerCommand(ACommand: TIdCommand);
var
MyCtx: TMyContext;
begin
MyCtx := TMyClass(ACommand.Context);
// use MyCtx as needed...
end;
This way, you don't need to waste time and memory allocating a separate class per client, when you can use the one that the server already creates for you.

Convert member function pointer to function pointer in FreePascal

I want to pass to a GLUT function (glutKeyboardFunc) a pointer to a member function (TDisplayer.GlKeyboard). GLUT callback just accept function pointer. Is there a way to "pack" self pointer into the function ?
unit UDisplayer;
{$mode objfpc}
interface
type
TDisplayer = class(TObject)
public
constructor Create(x, y : Integer; caption : AnsiString);
destructor Destroy; override;
procedure GlKeyboard(key : Byte; x, y : Longint); cdecl;
private
winX : Integer;
winY : Integer;
end;
implementation
uses gl, glut, glext, UTools;
constructor TDisplayer.Create(x, y : Integer; caption : AnsiString);
var
cmd : array of PChar;
cmdCount : Integer;
keyboardCallback : pointer;
begin
winX := x;
winY := y;
cmdCount := 1;
SetLength(cmd, cmdCount);
cmd[0] := PChar(ParamStr(0));
glutInit(#cmdCount, #cmd);
glutInitDisplayMode(GLUT_DOUBLE or GLUT_RGB or GLUT_DEPTH);
glutInitWindowSize(x, y);
glutCreateWindow(PChar(caption));
glClearColor(0.0, 0.0, 0.0, 0);
//glutKeyBoardFunc(#self.glKeyBoard); <--- HERE
glutMainLoop;
end;
destructor TDisplayer.Destroy;
begin
inherited;
end;
procedure TDisplayer.GlKeyboard(key : Byte; x, y : Longint); cdecl;
begin
end;
end.
No. A method pointer is two pointers large, and a simple function pointer only one, so it simply won't fit.
If the callback system provides some "context" you can sometimes pass the instance into the context, and make a somewhat more general thunk like
function callme(context:pointer;x,y:integer);integer; cdecl;
begin
TTheClass(context).callme(x,y);
end;
Then pass "Self" as context when registering the callback. But it doesn't look like this callback setter has a context that is passed back to the callback when it is called.
In a first time, declare the callback as a global procedure.
It'll be a context-independent method, not relying on Self
type TDisplayer = class(TObject)
public
constructor Create(x, y : Integer; caption : AnsiString);
destructor Destroy; override;
private
winX : Integer;
winY : Integer;
end;
procedure GlKeyboard(key : Byte; x, y : Longint); cdecl;
Then since glutCreateWindow() returns an unique context, you can use it to associate it to your class instance. So you define an associative array wich allows to retrieve a TDisplayer instance using a GLUT window as Key:
type TCtxtArr = specialize TFPGMap<Integer,TForm>;
You add one as a global var which will be created and freed in the initialization and the finalization sections:
var
ctxtarray: TCtxtArr;
initialization
ctxtarray:= TCtxtArr.create;
finalization
ctxtarray.free;
end.
Then in TDisplayer.Create() you add an entry to the AA:
// id is a local integer.
id = glutCreateWindow(PChar(caption));
ctxtarray.Add(id, Self);
// assign the callback here or elsewhere
glutKeyBoardFunc(#glKeyBoard);
When your callback is called, you can retrieve the TDisplayer instance, so that you can access to its variables and methods:
procedure GlKeyboard(key : Byte; x, y : Longint); cdecl;
var
disp: TDisplayer;
id: integer;
begin
glutGetWindow(id);
disp := ctxtarray[id];
end;
Unfortunately, i cant test the answer as it seems to be part of a bigger program. However this sample works in an analog way:
unit Unit1;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, FileUtil, Forms, Controls, Graphics, dialogs, fgl;
type
TForm1 = class;
TProc = procedure(x,y: integer);
TCtxtArr = specialize TFPGMap<Integer,TForm1>;
TForm1 = class(TForm)
constructor Create(TheOwner: TComponent); override;
procedure hello;
end;
procedure callback(x,y: integer);
var
Form1: TForm1; Proc: TProc;
ctxtarray: TCtxtArr;
implementation
{$R *.lfm}
constructor TForm1.Create(TheOwner: TComponent);
begin
inherited;
proc := #callback;
ctxtarray.Add(0,Self);
proc(0,0);
end;
procedure TForm1.hello;
begin
showmessage('hello');
end;
procedure callback(x, y: integer);
var
frm: TForm1;
begin
frm := ctxtarray.Data[0];
frm.hello;
end;
initialization
ctxtarray:= TCtxtArr.create;
finalization
ctxtarray.free;
end.
As a foot note: theorically FPC allows to define static class methods (similar to global procedures), but for some reason it seems that they cant be assigned to a global procedure pointer, at least it fails with FPC 2.6.4
You have to assemble some bytecode and keep wrapper with 'hardcoded' self pointer, which manages invocation stack:
procOfObj = packed record
method : pointer;
this : pointer;
end;
obj = packed object
procedure ASIOBufferSwitch(
ip: pointer; {the added IP artifact }
doubleBufferIndex: longint; directProcess: longbool); cdecl;
end;
cdeclProxy = packed object
procedure build( const src: procOfObj );
private
push : byte; push_arg: pointer;
call : byte; call_arg: pointer;
add_ret : longint;
end;
procedure cdeclProxy.build( const src: procOfObj );
begin
push := $68; push_arg := src.this;
call := $e8; call_arg := pointer( src.method - #call - 5 );
add_ret := $c304c483;
result := #push;
end;
var cdp : cdeclProxy;
o : obj;
begin
cdp.build( procOfObj( #o.ASIOBufferSwitch ))
pointer(... procedure var ...) := #cdp;
end.
Notice that provided example requires additional arg in method signature, but it allows to build wrapper without knowledge of arg count. If you do not want ip arg, you have to re-push all args again before calling actual method and then clean up stack inside wrapper.

How to copy data from one class into another class

how to copy data from one class into a second class using operator overloading with DELPHI ?
my dummy app goes like this :
type
TClass_A = class
a: String;
end;
TClass_B = class(TClass_A)
b: String;
end;
implementation
procedure TForm1.Button1Click(Sender: TObject);
var
a: TClass_A;
b: TClass_B;
begin
a := TClass_A.Create;
b := TClass_B.create;
b := a; // <<-- What code should be here? Can I overload := operator?
end;
The assignment operator cannot be overloaded in Delphi.
You will need to introduce a method to perform the copying. An example of how this might be done is TPersistent.Assign. It would be perfectly reasonable for you to derive from TPersistent and override the virtual Assign method to implement the desired functionality.
Here is a small example of how to do this with new style RTTI (D2010 and higher).
Please note that this is a very basic example which only copies fields (not properties) and works best with basic classes (do not use this with TComponent), the fields in the destination object must be the same type. FYI, there are better examples out there :)
uses
Rtti,...
function CopyObject(const FromObj, ToObj: TObject): Boolean;
var
Ctx : TRTTIContext;
FromObjType : TRttiType;
ToObjType : TRttiType;
FromField : TRttiField;
ToField : TRttiField;
begin
Result := False;
FromObjType := Ctx.GetType(FromObj.ClassInfo);
ToObjType := Ctx.GetType(ToObj.ClassInfo);
for FromField in FromObjType.GetFields do
begin
ToField := ToObjType.GetField(FromField.Name);
if Assigned(ToField) then
begin
if ToField.FieldType = FromField.FieldType then
ToField.SetValue(ToObj, FromField.GetValue(FromObj));
Result := True;
end;
end;
end;

How to access TPanel child form controls?

I have a main form with TPanel. I have also a Form2 with a TButton which I show in TPanel as a child. I mean TPanel of main form is parent of Form2. I use these steps to create the form2 in MainForm OnCreate method
MainFormOnCreate()
Form2 := TForm2.create(nil)
Form2.Parent := Panel1;
Form2.show;
But the problem is that when I access the button on Form2 it does nothing. For example, when I want to disable the button on Form2 I use this method
A button2 on main form with on click event
btn2OnClick();
Form2.btn.enabled := false;
But it does nothing. Some friends says it's because of child to TPanel it will get no message.
So give me a solution.
Thanks in advance
The main problem is, that you create 2 instances of TForm2.
Your .dpr file look like this
begin
Application.Initialize;
Application.CreateForm( TForm1, Form1 );
Application.CreateForm( TForm2, Form2 );
Application.Run;
end.
After you create an instance of TForm2 in TForm1.OnCreate and save this instance into global variable Form2, another instance of TForm2 is created and stored into Form2.
In the TForm1.btn5.OnClick event you address the second created, non visible TForm2.
Solution
go to Project / Options -> Formula and remove TForm2 from AutoCreate List
store the instance of TForm2 created inside of TForm1 in a private class field of TForm1
Your code should look like this
.dpr file:
begin
Application.Initialize;
Application.CreateForm( TForm1, Form1 );
Application.Run;
end.
Unit1.pas
TForm1 = class( TForm )
...
procedure FormCreate( Sender : TObject );
procedure btn2Click( Sender : TObject );
private
FForm2 : TForm2;
...
end;
procedure TForm1.FormCreate( Sender : TObject );
begin
FForm2 := TForm2.Create( Self );
FForm2.Parent := Panel1;
FForm2.Show;
end;
procedure TForm1.btn2Click( Sender : TObject );
begin
FForm2.btn.Enabled := True;
end;
Try this one
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ExtCtrls, Unit2;
type
TForm1 = class(TForm)
Panel1: TPanel;
Button1: TButton;
procedure Button1Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
private
lForm: TForm;
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
begin
if Assigned(lForm) then
TForm2(lForm).Button1.Enabled:= False;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
lForm := TForm2.Create(self);
lForm.Parent := Panel1;
lForm.Align:= alClient;
lForm.show;
end;