py4d

 

Wrapping Delphi Objects

Page history last edited by Oliver Bock 3 yrs ago

Wrapping Delphi Objects

 

If you have an existing object in Delphi that you'd like to use in Python but you don't want to modify that object to make it Python-aware, then you can wrap that object just for the purposes of supplying it to python.

 

As an example, if you have an existing Delphi class called Color:

 

TRGBColor = class

private

fRed, fGreen, fBlue: Integer;

public

property Red: read fRed write fRed;

property Green: read fGreen write fGreen;

property Blue: read fBlue write fBlue;

end;

 

You want to use Color within some Python code but you don't want to change anything about the class. So you make a wrapper that provides some very basic services, such as getting and setting attributes of a Color, and getting a string representation:

 

TPyColor = class(TPyObject)

private

fColor: TRGBColor;

public

constructor Create( APythonType : TPythonType ); override;

// Basic services

function GetAttr(key : PChar) : PPyObject; override;

function SetAttr(key : PChar; value : PPyObject) : Integer; override;

function Repr : PPyObject; override;

end;

 

implementation

 

constructor TPyColor.Create( APythonType : TPythonType );

begin

inherited;

fColor := nil;

end;

 

function TPyColor.GetAttr(key : PChar) : PPyObject;

begin

if key = "Red" then

result := GetPythonEngine.VariantAsPyObject(fColor.Red)

else if key = "Green" then

result := GetPythonEngine.VariantAsPyObject(fColor.Green)

else if key = "Blue" then

result := GetPythonEngine.VariantAsPyObject(fColor.Blue)

else

result := inherited GetAttr(key);

end;

 

function TPyColor.SetAttr(key : PChar; value : PPyObject) : Integer;

begin

result := 0;

if key = "Red" then

fColor.Red := Integer(GetPythonEngine.PyObjectAsVariant(value))

else if key = "Green" then

fColor.Green := Integer(GetPythonEngine.PyObjectAsVariant(value))

else if key = "Blue" then

fColor.Blue := Integer(GetPythonEngine.PyObjectAsVariant(value))

else

result := inherited SetAttr(key, value);

end;

 

function TPyColor.Repr : PPyObject;

begin

result := GetPythonEngine.VariantAsPyObject(IntToStr(fColor.Red)+','+

IntToStr(fColor.Green)+','+

IntToStr(fColor.Blue));

end;

 

As you can see, the wrapper object accepts Python code getting and setting certain attributes and it simply passes these on to its internal fColor instance.

 

Now we must have a way to pass our wrapper object to Python. We create a Python dictionary for the local variables that our Python code can access, and add our TPyColor to it.

 

// Given a Python dictionary, add an instance of TRGBColor. This code

// could be separated into different functions for your own purposes. Use the

// dictionary by passing it into PythonEngine's PyRun_String as the 'locals'

// parameter.

function AddColor(py_dict: PPyObject; var_name: string; c: TRGBColor);

var

t: TPythonType;

pyc: TPyColor;

pyc_value: PPyObject;

begin

// Set up TPyColor's type within the Python engine.

t := TPythonType.Create(GetSandboxEngine);

t.Engine := GetPythonEngine;

t.Name := 'PythonTRGBColor';

t.TypeName := typename;

t.Prefix := 'Create';

t.Services.Basic := bsGetAttr, bsSetAttr, bsRepr;

t.TypeFlags := result.TypeFlags + tpfBaseType;

t.PyObjectClass := TPyColor;

t.Initialize;

// Create a new instance of the type in the Python engine.

pyc_value := t.CreateInstance;

// Add the instance we created under the given variable name.

GetPythonEngine.PyDict_SetItemString(py_dict, PChar(var_name), pyc_value);

GetPythonEngine.Py_DECREF(pyc_value); // Important. Give the instance solely to Python.

// Set the Python instance of TPyColor to our TRGBColor

pyc := PythonToDelphi(pyc_value) as TPyColor;

pyc.fColor := c;

end;

 

This is how you would set up the local dictionary that needs to be passed to AddMyColor :

 

procedure RunPyCode;

var

locals: PPyObject;

c: TRGBColor;

begin

// Create the local variable dictionary.

locals := GetPythonEngine.PyDict_New;

try

// Make the color blue.

c := TRGBColor.Create;

c.Red := 0; c.Green := 0; c.Blue := 255;

// Add our color to the local variables.

AddColor(locals, 'my_color', c);

// Run some code that uses TRGBColor.

GetPythonEngine.PyRun_String(PChar('all = my_color.Red + my_color.Green + my_color.Blue'),

file_input,

GetPythonEngine.PyModule_GetDict(GetPythonEngine.GetMainModule),

locals);

finally

Py_XDECREF(fLocals_py);

end;

end;

Comments (0)

You don't have permission to comment on this page.