I have developed a client application that use PostgreSQL 8.4 RDBMS.
My Application is written in Lazarus and ZeosLib 7.2 for database access.
I use a lot of stored procedures and in specific point I use raise notice to get info of the procedure status, Es:
RAISE NOTICE 'Step 1: Import Items from CSV file';
....
....
RAISE NOTICE 'Step 2: Check Items data';
When I execute procedures in PgAdmin3 it show notice in "Messages" Tab.
There is a way to capture raised notices in my client application?
Ok, while I interesting in this topic too, here is some working example crated after the quick investigation:
uses
Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls,
ZConnection, ZDbcPostgreSql;
type
{ TForm1 }
TForm1 = class(TForm)
Button1: TButton;
Memo1: TMemo;
pgConn: TZConnection;
procedure Button1Click(Sender: TObject);
procedure pgConnAfterConnect(Sender: TObject);
private
{ private declarations }
public
{ public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.lfm}
procedure PGNotifyProcessor(arg: Pointer; message: PAnsiChar); cdecl;
begin
Form1.Memo1.Lines.Add(message);
end;
{ TForm1 }
procedure TForm1.pgConnAfterConnect(Sender: TObject);
var
pg: IZPostgreSQLConnection;
args: Pointer;
begin
pg := pgConn.DbcConnection as IZPostgreSQLConnection;
pg.GetPlainDriver.SetNoticeProcessor(pg.GetConnectionHandle, #PGNotifyProcessor, args);
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
pgConn.ExecuteDirect('select foo(''bar'')');
end;
end.
It works for me.
I guess this example is not accurate and contains some issues. For example usage LCL calls in the procedure called from external source. But I hope it is enough to start.
Test environment is: FPC 2.6.4, Lazarus 1.5, Postgres 9.3, Linux Mint
Related
I have a problem, see if you can help me. I have a base form.
type
TForm_Base = class(TForm)
oObjectoVO : TObject;
...
procedure Search<M:class,constructor>;
...
procedure TForm_Base.Search<M>;
begin
TBussinesObj<M>.Pesquisa(FDMemTableGrid);
end;
And I have a form that inherits the base form.
procedure TForm_Client.FormCreate(Sender: TObject);
begin
// TClient is class simple with the properties(write, read) of id, name, ...
oObjectoVO := TClient.Create;
end;
procedure TForm_Client.ButtonSearchClick(Sender: TObject);
begin
inherited;
end;
procedure TForm_Client.FormDestroy(Sender: TObject);
begin
FreeAndNil(oObjectoVO);
end;
My problem is here. I cannot pass the type of the object instantiated in the client form, to the generic method (Search ) to the base form. I don't know if it's possible.
procedure TForm_Base.ButtonSearchClick(Sender: TObject);
begin
Search<oObjectoVO.ClassType>; ******* Error *******
end;
Tanks.
Generics are a compile time construct. Consider this code:
Search<oObjectoVO.ClassType>
You are attempting to instantiate the generic with a type that is not known until run time.
You need to change Search from being a generic to being non-generic and accepting a parameter that specifies the class.
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.
I'm trying to figure out the best way to distribute state changes to multiple forms which make up an application.
In my scenario, I have a number of hardware devices which are monitored by my application. As an example, one of the devices is a GPS device. Data from these devices come in and information is then stored in one big state object. As an example, GPS positions coming in are stored with both present and historic positions made available to the application.
The application has many different forms and windows available to the user. As an example, the GPS has a form which displays the visible satellites overhead, as well as a form displaying the signal-to-noise ratios and another showing the GPS track over time.
One solution I've considered is to using something like the Observer pattern on my state object. New forms subscribe to the state object and then receive push notifications when the state object changes (new GPS position forces a push to the forms, who then re-paint and re-update their own states).
IObserver = interface
procedure Update;
end;
IObservable = interface
procedure Subscribe(Observer : IObserver);
end;
TObserverForm = class(TForm, IObserver)
// ....
procedure Update;
begin
// State has changed, update
end;
end;
TApplicationState = class(IObservable);
private
FObservers : TList<IObserver>;
FPosition : TPoint;
public
procedure Subscribe(Observer : IObserver);
begin
FObservers.Add(Observer);
end;
procedure PushUpdate;
begin
foreach Observer in Observers
Observer.Update;
end;
property Position : TPoint read GetPosition write SetPosition;
procedure SetPosition(Pos : TPoint);
begin
FPosition : Pos;
// Notify all observers
Self.PushUpdate;
end;
end;
The above is a crude mock-up of what I'm considering. This relies on state calling Subscribe on each TObserverForm that is created during the application life-cycle (and un-subscribing when they're destroyed).
I can't really see any downsides to this. This solution could be tailored somewhat so that the TObserverForms only update and react to certain types of push updates.
Is there something fundamental missing here? Are there more logical/simple solutions to this common problem?
I think you should first determine what you try to get.
Should the event receiving and propagation be:
single threaded or multithreaded
synchronous or asynchronous
cross-platform or Windows/VCL-only
For simplistic - single-thread synchronous VCL - application you may just use multi-events from http://www.Spring4D.org library. All interested forms just declare the event handler and register/unregister it in the Tracker object.
type iTrackerUpdated = iEvent<TNotifyEvent>;
type TMyGPSTracker = class.....
public
property OnUpdate: iTrackerUpdated read FOnUpdate;
procedure AfterCreation; override;
property Coords: TMyGPSCoords read FCoords write SetCoords;
end;
var GPSTracker: TMyGPSTracker;
procedure TMyGPSTracker.AfterCreation;
begin
inherited;
FOnUpdate := TEvent<TNotifyEvent>.Create; // auto-freed on destruction
end;
procedure TMyGPSTracker.SetCoords(const NewValue: FCoords);
begin
if NewValue = FCoords then exit;
FCoords := NewValue;
OnUpdate.Invoke(Self);
end;
And in forms then something like that
type TMyForm = class(TForm)
.....
private
procedure CoordsUpdated(Sender: TObject);
public
procedure AfterCreation; override;
procedure BeforeDestruction; override;
end;
procedure TMyForm.CoordsUpdated(Sender: TObject);
begin
Caption := (Sender as TMyGPSTracker).Coords.ToString();
end;
procedure TMyForm.AfterCreation;
begin
inherited;
GPSTracker.OnUpdate.Add( CoordsUpdated );
end;
procedure TMyForm.BeforeDestruction;
begin
inherited;
GPSTracker.OnUpdate.RemoveAll( Self );
end;
This is all it takes for such a situation. However...
If some form might take a loooong processing of the change - then it would block both other forms updating and the new coords acquiring from GPS driver.
In such a situation you better
extract GPS object into a separate idle-priority thread, that would keep updating data even when the main VCL thread is long busy.
switch to asynchronous event propagation, for VCL the simplest thing would be using PostMessage API. Then you should know that a busy form might "collect" few alerts before it would be ready to process them - and would only have to process one of those.
http://docwiki.embarcadero.com/RADStudio/Berlin/en/Understanding_the_Message-Handling_System
const WM_GPS_UPDATE = WM_USER + 10;
type TMyGPSTracker = class.....
public
property OnUpdateAlertForms: TThreadList<TForm> read FOnUpdate;
property UpdateCounter: Cardinal read FUpdateCounter;
property Coords: TMyGPSCoords read FCoords write SetCoords;
end;
var GPSTracker: TMyGPSTracker;
procedure TMyGPSTracker.SetCoords(const NewValue: FCoords);
var Form: TForm;
begin
if NewValue = FCoords then exit;
Inc(FUpdateCounter);
FCoords := NewValue;
for Form in FOnUpdate do
if Form.HandleAllocated and Form.Showing then
PostMessage( Form.WindowHandle, WM_GPS_UPDATE,
FUpdateCounter, LPARAM(Pointer(Self)) );
// in some rare cases sometimes this might post messages to nowhere
// but the only consequence would be a single non-update
end;
And then
type TMyForm = class(TForm)
.....
private
procedure CoordsUpdated(var Message: TMessage); message WM_GPS_UPDATE;
var LastCoordUpdateProcessed: Cardinal;
end;
procedure TMyForm.CoordsUpdated(var Message: TMessage);
begin
if LastCoordUpdateProcessed >= UpdateCounter then exit;
Caption := GPSTracker.Coords.ToString();
LastCoordUpdateProcessed := GPSTracker.UpdateCounter;
end;
procedure TMyForm.FormShow(Sender: TObject);
begin
GPSTracker.OnUpdateAlertForms.Add( Self );
end;
procedure TMyForm.FormHide(Sender: TObject);
begin
GPSTracker.OnUpdateAlertForms.Remove( Self );
end;
For non-VCL cross-platform targets you would have to find similar asynchronous messaging tools and replace PostMessage and Windows GDI Handles with their corresponding means.
I have a dll file that contains several Forms.
I have a reference to main function in console application that loads this dll.
I want hide main Form of dll file, but all attempts until now was without sucess.
I tried several ways for this, but nothing that I tried worked, except for hide from taskbar.
This is my code:
Main form on dll
unit Conectar;
interface
uses
Windows, Messages, SysUtils, Classes {.......};
type
TForm1 = class(TForm)
procedure FormShow(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
procedure CreateParams(var Params: TCreateParams); override;
end;
procedure TForm1.CreateParams(var Params: TCreateParams);
begin
inherited;
params.exstyle := params.exstyle and not WS_EX_APPWINDOW or WS_EX_TOOLWINDOW;
end;
procedure TForm1.FormShow(Sender: TObject);
begin
ShowWindow(Form1.Handle,SW_HIDE);
end;
Dll
library DLL;
uses
System.SysUtils,
Conectar,
vcl.Forms,
Classes;
{$R *.res}
procedure ShowDllForm; stdcall;
begin
Form1 := TForm1.Create(nil);
try
Form1.ShowModal;
finally
FreeAndNil(Form1);
end;
end;
exports
ShowDllForm;
begin
end.
Console application
{$APPTYPE GUI}
var
Msg: TMsg;
procedure ShowDllForm;stdcall;
external 'DLL.dll' name 'ShowDllForm';
begin
CoInitialize(nil);
ShowDllForm;
CoUninitialize;
end.
procedure TForm1.FormCreate(Sender: TObject);
begin
SetWindowLong(Handle, GWL_EXSTYLE, WS_EX_TOOLWINDOW);
end;
SOLUTION:
Dll
procedure ShowDllForm; stdcall;
begin
Application.CreateForm(TForm1, Form1);
ShowWindow(Application.Handle, SW_HIDE);
end;
Console application
var
Msg: TMsg;
begin
ShowDllForm;
while GetMessage(Msg, 0, 0, 0) do begin
TranslateMessage(Msg);
DispatchMessage(Msg);
end;
end.
OR
on dll file in FormActivate event:
Form1.Hide;
Having a situation: two packages: "Base" and "Descendant" and an application "Example".
The Base package and the Example application may be in one project group, but the Descendant package has to be in other project group, without any source code of Base and Example.
The aim of such manipulation is to hide Base and Application sources from workers who will work with Descendant package.
Base package contains a form: TFormBase with some components and some code. I build it and get some binary files: bpl, dcp, etc...
type
TFormBase = class(TForm)
Panel1: TPanel;
BOk: TButton;
BCancel: TButton;
procedure BOkClick(Sender: TObject);
procedure BCancelClick(Sender: TObject);
private
protected
function GetOkButtonCaption: string; virtual;
function GetCancelButtonCaption: string; virtual;
public
end;
implementation
{$R *.dfm}
procedure TFormBase.BCancelClick(Sender: TObject);
begin
ShowMessage('"' + GetCancelButtonCaption + '" button has been pressed');
end;
procedure TFormBase.BOkClick(Sender: TObject);
begin
ShowMessage('"' + GetOkButtonCaption + '" button has been pressed');
end;
function TFormBase.GetCancelButtonCaption: string;
begin
Result := 'Cancel';
end;
function TFormBase.GetOkButtonCaption: string;
begin
Result := 'Ok';
end;
Descendant package contains TFormDescendant = class(TFormBase)
type
TFormDescendant = class(TFormBase)
private
protected
function GetOkButtonCaption: string; override;
function GetCancelButtonCaption: string; override;
public
end;
implementation
{$R *.dfm}
function TFormDescendant.GetCancelButtonCaption: string;
begin
Result := 'Descendant Cancel';
end;
function TFormDescendant.GetOkButtonCaption: string;
begin
Result := 'Descendant Ok';
end;
And code of Descendant.dfm:
inherited FormDescendant: TFormDescendant
Caption = 'FormDescendant'
end
Descendant.dpr:
requires
rtl,
vcl,
Base;
contains
Descendant in 'Descendant.pas' {FormDescendant};
When creating FormDescendant it should look like FormBase because it is simply inherited from it. And we can add some other components on this FormDescendant saving FormBase look.
But when we try to open FormDescendant in Delphi IDE, it crashes with "Error creating form: Ancestor for 'TFormBase' not found."
And that's right: Base.bpl contains only binary code and Delphi doesn't know how TBaseForm looks in design-time.
What should I do to open FormDescendant in Delphi?
I've read How do I use or resolve issues with visual form inheritance in Delphi? and Register custom form so I can inherit from it from multiple projects, without copying the form to the Object Repository folder
But those advices didn't help.
Is there a way to open FormDescendant in design-time without sources of TFormBase?
Here're the projects of the example for experiments: http://yadi.sk/d/IHT9I4pm1iSOn
You can provide a stub unit with (some of the) implementation code stripped, only the .dfm and interface has to be identical. This is what Allen Bauer did for his Opening Doors article showing how to implement dockable IDE forms.
Your developers will then need to first open the stub form unit and then they will be able to open the descendant form.