Skip to content

Commit bbd80bf

Browse files
committed
Fix for Python 3.7.
Demo 34 added to demonstrate PythonVesions.
1 parent fc43086 commit bbd80bf

File tree

9 files changed

+956
-1
lines changed

9 files changed

+956
-1
lines changed

PythonForDelphi/Components/Sources/Core/PythonEngine.pas

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -838,9 +838,20 @@ interface
838838
tp_cache : PPyObject;
839839
tp_subclasses : PPyObject;
840840
tp_weaklist : PPyObject;
841+
tp_del : PyDestructor;
842+
tp_version_tag : NativeUInt; // Type attribute cache version tag. Added in version 2.6
843+
tp_finalize : PyDestructor;
841844
//More spares
845+
tp_xxx1 : NativeInt;
846+
tp_xxx2 : NativeInt;
847+
tp_xxx3 : NativeInt;
848+
tp_xxx4 : NativeInt;
849+
tp_xxx5 : NativeInt;
850+
tp_xxx6 : NativeInt;
842851
tp_xxx7 : NativeInt;
843-
tp_xxx8 : LongInt;
852+
tp_xxx8 : NativeInt;
853+
tp_xxx9 : NativeInt;
854+
tp_xxx10 : NativeInt;
844855
end;
845856

846857
PPyMethodChain = ^PyMethodChain;
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
program Project1;
2+
3+
{$I Definition.Inc}
4+
5+
uses
6+
{$IFDEF MSWINDOWS}
7+
Forms,
8+
{$ENDIF }
9+
{$IFDEF LINUX}
10+
QForms,
11+
{$ENDIF }
12+
Unit1 in 'Unit1.pas' {Form1};
13+
14+
{$R *.res}
15+
16+
begin
17+
Application.Initialize;
18+
Application.CreateForm(TForm1, Form1);
19+
Application.Run;
20+
end.

PythonForDelphi/Demos/Demo34/Project1.dproj

Lines changed: 558 additions & 0 deletions
Large diffs are not rendered by default.
3.14 KB
Binary file not shown.
766 Bytes
Binary file not shown.
3.32 KB
Binary file not shown.
Lines changed: 356 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,356 @@
1+
unit Unit1;
2+
3+
{$I Definition.Inc}
4+
5+
interface
6+
7+
uses
8+
SysUtils, Classes,
9+
{$IFDEF MSWINDOWS}
10+
Windows, Messages, Graphics, Controls, Forms, Dialogs,
11+
StdCtrls, ExtCtrls, ComCtrls, PythonVersions,
12+
{$ENDIF}
13+
{$IFDEF LINUX}
14+
QForms, QStdCtrls, QControls, QExtCtrls,
15+
{$ENDIF}
16+
PythonEngine, PythonGUIInputOutput;
17+
18+
type
19+
TForm1 = class(TForm)
20+
Splitter1: TSplitter;
21+
Memo1: TMemo;
22+
Panel1: TPanel;
23+
Button1: TButton;
24+
PythonGUIInputOutput1: TPythonGUIInputOutput;
25+
Memo2: TMemo;
26+
cbPyVersions: TComboBox;
27+
Label1: TLabel;
28+
procedure Button1Click(Sender: TObject);
29+
procedure PythonType1Initialization(Sender: TObject);
30+
procedure FormCreate(Sender: TObject);
31+
procedure cbPyVersionsSelect(Sender: TObject);
32+
private
33+
{ Déclarations privées }
34+
PythonEngine1: TPythonEngine;
35+
PythonModule1: TPythonModule;
36+
PythonType1: TPythonType;
37+
PyVersions: TPythonVersions;
38+
public
39+
{ Déclarations publiques }
40+
procedure CreatePythonComponents;
41+
end;
42+
43+
// This is a Delphi class implementing a new Python type
44+
// it must derive from TPyObject or one of its descendants.
45+
// Then it must override some methods, like the constructors,
46+
// the RegisterMethods and the type services' virtual methods.
47+
TPyPoint = class(TPyObject)
48+
x, y : Integer;
49+
Name : String;
50+
51+
// Constructors & Destructors
52+
constructor Create( APythonType : TPythonType ); override;
53+
constructor CreateWith( PythonType : TPythonType; args : PPyObject ); override;
54+
55+
// Type services
56+
////////////////
57+
58+
// Basic services
59+
function Repr : PPyObject; override;
60+
61+
// Class methods
62+
class procedure RegisterMethods( PythonType : TPythonType ); override;
63+
class procedure RegisterMembers( PythonType : TPythonType ); override;
64+
class procedure RegisterGetSets( PythonType : TPythonType ); override;
65+
66+
// Methods of TPyPoint
67+
procedure OffsetBy( dx, dy : Integer );
68+
69+
// Interface methods
70+
function DoOffsetBy( args : PPyObject ) : PPyObject; cdecl;
71+
function DoRaiseError( args : PPyObject ) : PPyObject; cdecl;
72+
end;
73+
74+
var
75+
Form1: TForm1;
76+
77+
implementation
78+
79+
{$R *.dfm}
80+
81+
procedure TForm1.cbPyVersionsSelect(Sender: TObject);
82+
begin
83+
CreatePythonComponents
84+
end;
85+
86+
procedure TForm1.CreatePythonComponents;
87+
begin
88+
if cbPyVersions.ItemIndex <0 then begin
89+
ShowMessage('No Python version is selected');
90+
Exit;
91+
end;
92+
93+
// Destroy P4D components
94+
FreeAndNil(PythonEngine1);
95+
FreeAndNil(PythonType1);
96+
FreeAndNil(PythonModule1);
97+
98+
{ TPythonEngine }
99+
PythonEngine1 := TPythonEngine.Create(Self);
100+
PyVersions[cbPyVersions.ItemIndex].AssignTo(PythonEngine1);
101+
102+
PythonEngine1.IO := PythonGUIInputOutput1;
103+
104+
105+
{ TPythonModule }
106+
PythonModule1 := TPythonModule.Create(Self);
107+
108+
PythonModule1.Name := 'PythonModule1';
109+
PythonModule1.Engine := PythonEngine1;
110+
PythonModule1.ModuleName := 'spam';
111+
with PythonModule1.Errors.Add do begin
112+
Name := 'PointError';
113+
ErrorType := etClass;
114+
end;
115+
with PythonModule1.Errors.Add do begin
116+
Name := 'EBadPoint';
117+
ErrorType := etClass;
118+
ParentClass.Name := 'PointError';
119+
end;
120+
121+
{ TPythonType }
122+
PythonType1 := TPythonType.Create(Self);
123+
124+
PythonType1.Name := 'PythonType1';
125+
PythonType1.Engine := PythonEngine1;
126+
PythonType1.OnInitialization := PythonType1Initialization;
127+
PythonType1.TypeName := 'Point';
128+
PythonType1.Prefix := 'Create';
129+
PythonType1.Services.Basic := [bsRepr,bsStr,bsGetAttrO,bsSetAttrO];
130+
PythonType1.TypeFlags :=
131+
[tpfHaveGetCharBuffer,tpfHaveSequenceIn,tpfHaveInplaceOps,
132+
tpfHaveRichCompare,tpfHaveWeakRefs,tpfHaveIter,tpfHaveClass,tpfBaseType];
133+
PythonType1.Module := PythonModule1;
134+
135+
PythonEngine1.LoadDll;
136+
end;
137+
138+
procedure TForm1.FormCreate(Sender: TObject);
139+
Var
140+
PyVersion : TPythonVersion;
141+
begin
142+
PyVersions := GetRegisteredPythonVersions;
143+
for PyVersion in PyVersions do
144+
cbPyVersions.Items.Add(PyVersion.DisplayName);
145+
if cbPyVersions.Items.Count > 0 then begin
146+
cbPyVersions.ItemIndex := 0;
147+
CreatePythonComponents;
148+
end;
149+
end;
150+
151+
152+
// First, we need to initialize the property PyObjectClass with
153+
// the class of our Type object
154+
procedure TForm1.PythonType1Initialization(Sender: TObject);
155+
begin
156+
PythonType1.PyObjectClass := TPyPoint;
157+
end;
158+
159+
// We override the constructors
160+
161+
constructor TPyPoint.Create( APythonType : TPythonType );
162+
begin
163+
inherited;
164+
x := 0;
165+
y := 0;
166+
end;
167+
168+
// Don't call the Create constructor of TPyPoint, because
169+
// we call the inherited constructor CreateWith that calls
170+
// the Create constructor first, and because the constructors
171+
// are virtual, TPyPoint.Create will be automatically be called.
172+
173+
constructor TPyPoint.CreateWith( PythonType : TPythonType; args : PPyObject );
174+
begin
175+
inherited;
176+
with GetPythonEngine do
177+
begin
178+
if PyArg_ParseTuple( args, 'ii:CreatePoint',@x, @y ) = 0 then
179+
exit;
180+
end;
181+
end;
182+
183+
// Then we override the needed services
184+
185+
function TPyPoint.Repr : PPyObject;
186+
begin
187+
with GetPythonEngine do
188+
Result := VariantAsPyObject(Format('(%d, %d)',[x, y]));
189+
// or Result := PyString_FromString( PAnsiChar(Format('(%d, %d)',[x, y])) );
190+
end;
191+
192+
// get/set functions
193+
function TPyPoint_GetName( obj : PPyObject; context : Pointer) : PPyObject; cdecl;
194+
begin
195+
with GetPythonEngine do
196+
Result := PyString_FromString(PAnsiChar(AnsiString(TPyPoint(PythonToDelphi(obj)).Name)));
197+
end;
198+
199+
function TPyPoint_SetName( obj, value : PPyObject; context : Pointer) : integer; cdecl;
200+
begin
201+
with GetPythonEngine do
202+
begin
203+
if PyString_Check(value) then
204+
begin
205+
TPyPoint(PythonToDelphi(obj)).Name := PyObjectAsVariant(value);
206+
Result := 0;
207+
end
208+
else
209+
begin
210+
Result := -1;
211+
PyErr_SetString(PyExc_AttributeError^, 'attribute Name expected a string value');
212+
end;
213+
end;
214+
end;
215+
216+
// Class methods
217+
// We register the methods of our type
218+
219+
class procedure TPyPoint.RegisterMethods( PythonType : TPythonType );
220+
begin
221+
inherited;
222+
with PythonType do
223+
begin
224+
AddMethod( 'OffsetBy', @TPyPoint.DoOffsetBy, 'Point.OffsetBy( dx, dy )' );
225+
AddMethod( 'RaiseError', @TPyPoint.DoRaiseError, 'Point.RaiseError()' );
226+
end;
227+
end;
228+
229+
class procedure TPyPoint.RegisterMembers( PythonType : TPythonType );
230+
begin
231+
with PythonType do
232+
begin
233+
AddMember( 'x', mtInt, NativeInt(@TPyPoint(nil).x), mfDefault, 'x coordinate');
234+
AddMember( 'y', mtInt, NativeInt(@TPyPoint(nil).y), mfDefault, 'y coordinate');
235+
end;
236+
end;
237+
238+
class procedure TPyPoint.RegisterGetSets( PythonType : TPythonType );
239+
begin
240+
with PythonType do
241+
begin
242+
AddGetSet('Name', TPyPoint_GetName, TPyPoint_SetName, 'Name of a point', nil);
243+
end;
244+
end;
245+
246+
// Methods of TPyPoint
247+
// They do the real actions on the object
248+
// It's better to split the functions that interface
249+
// Delphi to Python and the functions that do the
250+
// real implementation.
251+
252+
procedure TPyPoint.OffsetBy( dx, dy : Integer );
253+
begin
254+
Inc( x, dx );
255+
Inc( y, dy );
256+
end;
257+
258+
// Interface methods
259+
// They will be called directly by Python, so we extract the
260+
// python arguments and we call the method that will really do
261+
// the action.
262+
263+
function TPyPoint.DoOffsetBy( args : PPyObject ) : PPyObject;
264+
var
265+
dx, dy : Integer;
266+
begin
267+
with GetPythonEngine do
268+
begin
269+
// We adjust the transmitted self argument
270+
Adjust(@Self);
271+
// first we extract the arguments
272+
if PyArg_ParseTuple( args, 'ii:Point.Offset',@dx, @dy ) <> 0 then
273+
begin
274+
// if it's ok, then we call the method that does the job
275+
// with the correct arguments
276+
OffsetBy( dx, dy );
277+
// Finally, we return nothing
278+
Result := ReturnNone;
279+
end
280+
else // the arguments were not right
281+
Result := nil;
282+
end;
283+
end;
284+
285+
// Here's an example of how you can raise errors defined
286+
// in the module linked to our type.
287+
function TPyPoint.DoRaiseError( args : PPyObject ) : PPyObject;
288+
begin
289+
with GetPythonEngine do
290+
begin
291+
// We adjust the transmitted self argument
292+
Adjust(@Self);
293+
// This is a simple call:
294+
//GetModule.RaiseError( 'PointError', 'this is an example of raising an error !' );
295+
// This is an advanced call:
296+
// We provide the instance vars as a dictionary, so that we can intercept the
297+
// error with "except" and extract informations from the error object.
298+
// ArrayToPyDict needs a list of pairs: varName (string), varValue (anything)
299+
GetModule.RaiseErrorObj( 'EBadPoint', 'this is an example of raising an error !',
300+
ArrayToPyDict( ['a', 1, 'b', 2, 'c', 3] ) );
301+
Result := nil;
302+
end;
303+
end;
304+
305+
/////////////////////////////////////////////////
306+
307+
308+
procedure TForm1.Button1Click(Sender: TObject);
309+
var
310+
DelphiPoint : TPyPoint;
311+
p : PPyObject;
312+
begin
313+
// Here's how you can create/read Python vars from Delphi with
314+
// Delphi/Python objects.
315+
316+
// You should ask to the TPythonType to create an instance of its type
317+
// because it will do some initialization. You can use CreateInstanceWith
318+
// if you want to transmit some Python arguments.
319+
// We receive a Python object pointer
320+
p := PythonType1.CreateInstance;
321+
PythonEngine1.CheckError;
322+
// Then we cast the python object to the right delphi type
323+
DelphiPoint := TPyPoint( PythonToDelphi(p) );
324+
// We do some changes on the delphi object
325+
DelphiPoint.X:=10;
326+
DelphiPoint.Y:=20;
327+
// Add variable "myPoint" in the module "spam".
328+
// So you'll access to the var in the module called spam with:
329+
// import spam
330+
// print spam.myPoint
331+
PythonModule1.SetVar( 'myPoint', p );
332+
PythonEngine1.Py_DecRef(p);
333+
{
334+
Of course, if you want to retrieve a Python var from a module,
335+
just use the PythonModule1.GetVar or PythonModule1.GetVarAsVariant
336+
Example:
337+
p := PythonModule1.GetVar('myPoint');
338+
if Assigned(p) then
339+
begin
340+
DelphiPoint := PythonToDelphi(p) as TPyPoint;
341+
...
342+
Py_XDecRef(p);
343+
end;
344+
345+
end; }
346+
// Excecute the script
347+
PythonEngine1.ExecStrings( memo1.Lines );
348+
// Add the following line at the end of the script:
349+
// print spam.myPoint
350+
351+
// Note, that you must not free the delphi point yourself.
352+
// Instead use the GetPythonEngine.Py_XDECREF(obj) method,
353+
// because the object may be used by another Python object.
354+
end;
355+
356+
end.

0 commit comments

Comments
 (0)