Reputation: 45
I am using Python 3.8 with Delphi 10.4.2.
I am trying to use the components of Python4Delphi to access through a Python script some interfaces defined in Delphi.
At design-time I added the TPythonEngine, TPythonModule and TPyDelphiWrapper components to my project's VCL form.
So I defined 3 interfaces, implemented by 3 classes respectively, as below
type
IPerson = interface (IUnknown)
['{1D21B5B6-25DE-4884-8BDB-8E2D9A239D64}']
function GetName : string;
procedure SetName ( value : string );
property Name: string read GetName write SetName;
function GetSurname: string;
procedure SetSurname(value : string);
property Surname : string read GetSurname write SetSurname;
function GetInfo : string;
end;
ICustomer = interface (IPerson)
['{8742364C-33E8-4FF4-86FB-C19AF67A735B}']
function GetCustomerNumber : string;
procedure SetCustomerNumber ( value : string );
property CustomerNumber : string read GetCustomerNumber write SetCustomerNumber;
end;
ISupplier = interface ( IPerson )
['{420FFF78-92DE-4D7E-9958-FDA95748EEB7}']
function GetSupplierNumber : string;
procedure SetSupplierNumber ( value : string );
property SupplierNumber : string read GetSupplierNumber write SetSupplierNumber;
end;
TPerson = class ( TInterfacedObject , IPerson)
private
FName : string;
FSurname : string;
function GetName : string;
procedure SetName ( value : string );
function GetSurname: string;
procedure SetSurname(value : string);
public
property Surname : string read GetSurname write SetSurname;
property Name: string read GetName write SetName;
function GetInfo : string; virtual;
end;
TCustomer = class ( TPerson , ICustomer)
private
FCustomerNumber : string;
function GetCustomerNumber : string;
procedure SetCustomerNumber ( value : string);
public
property CustomerNumber : string read GetCustomerNumber write SetCustomerNumber;
function GetInfo: string; override;
end;
TSupplier = class ( TPerson , ISupplier)
private
FSupplierNumber : string;
function GetSupplierNumber : string;
procedure SetSupplierNumber ( value : string );
public
property SupplierNumber : string read GetSupplierNumber write SetSupplierNumber;
function GetInfo : string; override;
end;
In the Create method of the form, I defined 3 variables, one for each of the 3 interfaces, and through the PyDelphiWrapper I passed them to the Python module in 3 different Python variable.
procedure TFrmTestInterface.FormCreate(Sender: TObject);
var
LPerson : IPerson;
LCustomer : ICustomer;
LSupplier : ISupplier;
Py: PPyObject;
begin
LPerson := TPerson.Create;
LCustomer := TCustomer.Create;
LSupplier := TSupplier.Create;
LPerson.Name := 'Pippo';
LPerson.Surname := 'Rossi';
LCustomer.Name := 'Pluto';
LCustomer.Surname := 'Verdi';
LSupplier.Name := 'Paperino';
LSupplier.Surname := 'Bianchi';
Py := PyDelphiWrapper1.WrapInterface(TValue.From(LPerson));
PythonModule1.SetVar('delphi_person', Py);
GetPythonEngine.Py_DecRef(Py);
Py := PyDelphiWrapper1.WrapInterface(TValue.From(LCustomer));
PythonModule1.SetVar('delphi_customer', Py);
GetPythonEngine.Py_DecRef(Py);
Py := PyDelphiWrapper1.WrapInterface(TValue.From(LSupplier));
PythonModule1.SetVar('delphi_supplier', Py);
GetPythonEngine.Py_DecRef(Py);
end;
At runtime the variables are correctly interpreted, but every time I try to access one of the properties defined in the interface I always get the same error.
This is the Python script I try to run:
from delphi_module import delphi_person, delphi_customer, delphi_supplier
print('type(delphi_person) = ', type(delphi_person))
print('type(delphi_customer) = ', type(delphi_customer))
print('type(delphi_supplier) = ', type(delphi_supplier))
print(delphi_person.Name)
And the error I get
Traceback (most recent call last): File "", line 7, in AttributeError: Error in getting property "Name". Error: Unknown attribute
The type(...) command runs correctly for the three variables.
If instead of using 3 variables of the interface type, I declare each variable as a class type, using the PyDelphiWrapper.Wrap method, everything works correctly!
procedure TFrmTestInterface.FormCreate(Sender: TObject);
var
LPerson : IPerson;
LCustomer : ICustomer;
LSupplier : ISupplier;
Py: PPyObject;
begin
LPerson := TPerson.Create;
LCustomer := TCustomer.Create;
LSupplier := TSupplier.Create;
LPerson.Name := 'Pippo';
LPerson.Surname := 'Rossi';
LCustomer.Name := 'Pluto';
LCustomer.Surname := 'Verdi';
LSupplier.Name := 'Paperino';
LSupplier.Surname := 'Grandi';
Py := PyDelphiWrapper1.Wrap(LPerson, TObjectOwnership.soReference);
PythonModule1.SetVar('delphi_person', py);
GetPythonEngine.Py_DECREF(py);
Py := PyDelphiWrapper1.Wrap(LCustomer, TObjectOwnership.soReference);
PythonModule1.SetVar('delphi_customer', py);
GetPythonEngine.Py_DECREF(py);
Py := PyDelphiWrapper1.Wrap(LSupplier, TObjectOwnership.soReference);
PythonModule1.SetVar('delphi_supplier', py);
GetPythonEngine.Py_DECREF(py);
end;
With the same Python script I get the correct output without errors
Anyone have any idea what I'm doing wrong with using the TPyDelphiWrapper to wrap interface type variables for Python scripts?
Upvotes: 1
Views: 622
Reputation: 46
Delphi does not add RTTI on properties defined in interfaces. Therefore property 'Name' cannot be found by the Python engine. RTTI is available for the methods when you add {$M+} before the interface declaration. Calling delphi_person.GetName() should work then.
There is another issue with using interfaces and the Python engine, the interface is not locked when you call WrapInterface. Therefore the object will be released when it goes out of scope.
Upvotes: 1