forked from Alexey-T/Python-for-Lazarus
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Tutorial.txt
840 lines (722 loc) · 34.4 KB
/
Tutorial.txt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
Using Python components.
Here's a simple tutorial, each step is linked to a demo project (demoXXX)
located in the Demos folder.
--------------------------------------------------------------
1) A simple Python evaluator: (Kylix ready)
--------------------------------------------------------------
Create a new Form
Drop a TMemo (or a TRichEdit)
Drop a TPythonGUIInputOutput for displaying Python's messages
Drop a TMemo for the source code
Drop a TPythonEngine
Connect the attribute IO of the TPythonEngine to the TPythonGUIInputOutput.
Connect the attribute Output of TPythonGUIInputOutput to the TRichEdit.
Drop a TButton and call it "Execute script"
Double-click on the button and add:
PythonEngine1.ExecStrings( Memo1.Lines );
That's all !
Compile and execute.
Write in the Memo1: print 2+2
Click on the Execute button
You should see in the Output window: 4
--------------------------------------------------------------
2) Evaluate a Python expression (Kylix ready)
--------------------------------------------------------------
WARNING !!! The evaluation of an expression works only for arithmetic expressions
and not for instructions ! The use of variables and functions is of course allowed
but constructs like for, def, class, print, import... are not allowed.
Use Exec... instead (like ExecStrings).
Create a new Form
Drop a TMemo (or a TRichEdit)
Drop a TPythonGUIInputOutput for displaying Python's messages
Drop a TMemo for the source code
Drop a TPythonEngine
Connect the attribute IO of the TPythonEngine to the TPythonGUIInputOutput.
Connect the attribute Output of TPythonGUIInputOutput to the TRichEdit.
Drop a TButton and call it "Evaluate script"
Double-click on the button and add:
var
Result : PPyObject;
begin
with PythonEngine1 do
begin
Result := EvalStrings( Memo1.Lines );
if Assigned(Result) then
begin
ShowMessage(Format('Eval: %s',[PyObjectAsString(Result)]));
Py_DECREF(Result);
end
else
ShowMessage('Could not evaluate the script');
// Or you could simply use:
// ShowMessage('Eval: ' + EvalStringsAsStr( Memo1.Lines ) );
end;
That's all !
Compile and execute.
Write in the Memo1: print 2+2
Click on the Execute button
You should see in the Output window: 4
--------------------------------------------------------------
3) Defining Python/Delphi vars (simple case): (Kylix ready)
--------------------------------------------------------------
Warning ! If you access the Python/Delphi vars with
the variant interface, you'll have to use simple types
like integers, floats or strings.
If you want to use any Python object type, don't use
variants, but use the Extended interface OnExtGetData, OnExtSetData
and the ValueObject property. But in this case, be careful with the
reference counting ! Study carefully the Demo4 example.
Drop a TPythonDelphiVar
Connect its Engine attribute to PythonEngine1
Change the name of the variable in the VarName attribute and set it to "test".
Drop a Button and call it "Show var content"
Double-click on it, and add:
ShowMessage( 'Value = ' + PythonDelphiVar1.ValueAsString );
Run the application, and type in the source window:
varname.Value = 10
print varname, varname.Value
Click on the Execute button
Click on the "Show var content button"
So, you can read/write the variable's content in both Python and Delphi !
--------------------------------------------------------------
4) Defining Python/Delphi vars (advanced case): (Kylix ready)
--------------------------------------------------------------
Warning ! If you access to the Python/Delphi vars with
the variant interface, you'll have to use simple types
like integers, floats or strings.
If you want to use any Python object type, don't use
variants, but use the Extended interface OnExtGetData, OnExtSetData
and the ValueObject property. But in this case, be careful with the
reference counting ! Study carefully the Demo4 example.
Use what you did at 3)
Drop a TEdit component
Click on the PythonDelphiVar1 object, show its events
Double-click on the OnGetData attribute, and add:
Data := Edit1.Text;
Double-click on the OnSetData attribute, and add:
Edit1.Text := Data;
Double-click on the OnChange attribute, and add:
with Sender as TPythonDelphiVar do
ShowMessage( 'Var test changed: ' + ValueAsString );
Add the following line to the private section:
FMyPythonObject : PPyObject;
Drop a TPythonDelphiVar
Connect its Engine attribute to PythonEngine1
Change the name of the variable in the VarName attribute and set it to "object".
Double-click on the OnExtGetData attribute, and add:
with GetPythonEngine do
begin
Data := FMyPythonObject;
Py_XIncRef(Data); // This is very important
end;
Double-click on the OnExtSetData attribute, and add:
with GetPythonEngine do
begin
Py_XDecRef(FMyPythonObject); // This is very important
FMyPythonObject := Data;
Py_XIncRef(FMyPythonObject); // This is very important
end;
Run the application, and type in the source window:
print "Current value of var test is: ", varname
varname.Value = "New value set by Python"
print "-----------------------------------------------------"
class C:
def __init__(Self, Arg):
Self.Arg = Arg
def __str__(Self):
return "<C instance contains: " + str(Self.Arg) + ">"
print "Current value of var object is: ", object
object.Value = C("Hello !")
print "New value is:", object
Click on the Execute button, and look at the Edit1 component.
--------------------------------------------------------------
5) Defining a new Module: (Kylix ready)
--------------------------------------------------------------
Drop a TPythonModule
Connect its Engine attribute to PythonEngine1
Change the ModuleName attribute to "spam"
Select its events, and Double-click on "OnInitialization" and add:
with Sender as TPythonModule do
AddMethod( 'foo', spam_foo, 1, 'foo' );
Before the PythonModule1Initialization procedure, add:
function spam_foo( self, args : PPyObject ) : PPyObject; cdecl;
begin
with GetPythonEngine do
begin
ShowMessage( 'args of foo: '+PyObjectAsString(args) );
Result := Py_None;
Py_IncRef(Result);
end;
end;
Run the application, and type in the source window:
import spam
print spam.foo( "Hello", 1 )
Click on the Execute button, and look at the Message dialog.
--------------------------------------------------------------
6) Defining a new Type: (Kylix ready)
--------------------------------------------------------------
A new type is more complex, because you must define a record
that contains the objects attributes. It must always contain
the two attributes at first:
ob_refcnt : Integer;
ob_type : PPyTypeObject;
Drop a TPythonType
Connect its Engine attribute to PythonEngine1
Change the TypeName attribute to "MyObject"
You must define procedures that will handle all kind of access
to the object: Destructor, Get attribute, Set attribute, print...
You must define its own methods
You'll need a function in a module that will create an
instance of that object.
You must set the attributes of the Type record.
Add the Objects's methods to the PythonType.
Look at the demo6 for more information about this.
There's a complete implementation of a new type
object "Point", which contains two attributes (x and y),
and one method (OffsetBy).
The object is created by the function CreatePoint of
the spam module.
--------------------------------------------------------------
7) Using Delphi methods as Python functions (Kylix ready)
--------------------------------------------------------------
This demo is the same as the previous one (6) but it introduces
the use of Delphi methods instead of global functions for the
python functions of Modules or Types.
Note: Look at (21) for the new Events feature, which will ease the
declaration of new Python functions.
--------------------------------------------------------------
8) Using Delphi classes for new Python types (Kylix ready)
--------------------------------------------------------------
Warning for Python version below 2.0:
Python must be correctly installed otherwise this demo
will fail. It needs indeed the file exceptions.py
located in the lib folder of the Python distribution
in order to initialize correctly the Exceptions as
Python objects instead of strings.
If you want to distribute your application with the
Python Dll, don't forget to copy this file (and maybe the
whole lib folder) and use the event OnPathInitialization
of TPythonEngine to initialize properly your Python
environment.
Note: Look at (21) for the new Events feature, which will ease the
declaration of new Python methods.
Note: Loot at (26) if you're interested in building a new type that
may be subclassed inside Python.
Note: Look at (32) if you're using Delphi7 or later as it can make
your life much easier when trying to expose a regular Delphi
class to Python. See also the WrapDelphi.pas unit.
Look at demo8, it's a default template that yous should follow
if you want to build new Python types.
Create a new project.
Drop a TRichEdit
Drop a TPythonGUIInputOutput for displaying Python's messages
Drop a TPythonEngine on the form.
Connect the attribute IO of the TPythonEngine to the TPythonGUIInputOutput.
Connect the attribute Output of TPythonGUIInputOutput to the TRichEdit.
Drop a TMemo
Drop a TButton
Double click on it and add:
PythonEngine1.ExecStrings( Memo1.Lines );
Drop a TPythonModule
Change its property "ModuleName" to "spam"
Connect it to the PythonEngine1
Double-click on the property "Errors"
Add an error
name it "PointError"
set it to etClass
Add an error
name it "EBadPoint"
set it to etClass
Double-click on "ParentClass"
Set Name to "PointError"
Drop a TPythonType
Connect it to the PythonEngine1
Change the property "TypeName" to "Point"
Connect the property "Module" to the PythonModule1
Define the services you'll use in the property "Services"
Double-click on its event "OnInitialization" and add:
PythonType1.PyObjectClass := TPyPoint;
Add this code to the interface section of the unit:
// This is a Delphi class implementing a new Python type
// it must derive from TPyObject or one of its descendants.
// Then it must override some methods, like the constructors,
// the RegisterMethods and the type services' virtual methods.
TPyPoint = class(TPyObject)
x, y : Integer;
// Constructors & Destructors
constructor Create( APythonType : TPythonType ); override;
constructor CreateWith( PythonType : TPythonType; args : PPyObject ); override;
// Type services
////////////////
// Basic services
function GetAttr(key : PChar) : PPyObject; override;
function SetAttr(key : PChar; value : PPyObject) : Integer; override;
function Repr : PPyObject; override;
// Class methods
class procedure RegisterMethods( PythonType : TPythonType ); override;
// Methods of TPyPoint
procedure OffsetBy( dx, dy : Integer );
// Interface methods
function DoOffsetBy( args : PPyObject ) : PPyObject; cdecl;
function DoRaiseError( args : PPyObject ) : PPyObject; cdecl;
end;
Add this code to the implementation section of the unit:
// We override the constructors
constructor TPyPoint.Create( APythonType : TPythonType );
begin
inherited;
x := 0;
y := 0;
end;
// Don't call the Create constructor of TPyPoint, because
// we call the inherited constructor CreateWith that calls
// the Create constructor first, and because the constructors
// are virtual, TPyPoint.Create will be automatically be called.
constructor TPyPoint.CreateWith( PythonType : TPythonType; args : PPyObject );
begin
inherited;
with GetPythonEngine do
begin
if PyArg_ParseTuple( args, 'ii:CreatePoint', [@x, @y] ) = 0 then
exit;
end;
end;
// Then we override the needed services
function TPyPoint.GetAttr(key : PChar) : PPyObject;
begin
with GetPythonEngine do
begin
if key = 'x' then
Result := PyInt_FromLong( x )
// Or Result := PyInt_FromLong( x )
else if key = 'y' then
Result := PyInt_FromLong( y )
// Or Result := PyInt_FromLong( y )
else
Result := inherited GetAttr(key);
end;
end;
function TPyPoint.SetAttr(key : PChar; value : PPyObject) : Integer;
begin
Result := 0;
with GetPythonEngine do
begin
if key = 'x' then
begin
if PyArg_Parse( value, 'i:Point.SetAttr', [@x] ) = 0 then
Result := -1;
end
else if key = 'y' then
begin
if PyArg_Parse( value, 'i:Point.SetAttr', [@y] ) = 0 then
Result := -1;
end
else
Result := inherited SetAttr(key, value);
end;
end;
function TPyPoint.Repr : PPyObject;
begin
with GetPythonEngine do
Result := PyString_FromString( PChar(Format('(%d, %d)',[x, y])) );
// or Result := PyString_FromString( PChar(Format('(%d, %d)',[x, y])) );
end;
// Class methods
// We register the methods of our type
class procedure TPyPoint.RegisterMethods( PythonType : TPythonType );
begin
inherited;
with PythonType do
begin
AddMethod( 'OffsetBy', @TPyPoint.DoOffsetBy, 'Point.OffsetBy( dx, dy )' );
AddMethod( 'RaiseError', @TPyPoint.DoRaiseError, 'Point.RaiseError()' );
end;
end;
// Methods of TPyPoint
// They do the real actions on the object
// It's better to split the functions that interface
// Delphi to Python and the functions that do the
// real implementation.
procedure TPyPoint.OffsetBy( dx, dy : Integer );
begin
Inc( x, dx );
Inc( y, dy );
end;
// Interface methods
// They will be called directly by Python, so we extract the
// python arguments and we call the method that will really do
// the action.
function TPyPoint.DoOffsetBy( args : PPyObject ) : PPyObject;
var
dx, dy : Integer;
begin
with GetPythonEngine do
begin
// We adjust the transmitted self argument
Adjust(@Self);
// first we extract the arguments
if PyArg_ParseTuple( args, 'ii:Point.Offset', [@dx, @dy] ) <> 0 then
begin
// if it's ok, then we call the method that does the job
// with the correct arguments
OffsetBy( dx, dy );
// Finally, we return nothing
Result := ReturnNone;
end
else // the arguments were not right
Result := nil;
end;
end;
// Here's an example of how you can raise errors defined
// in the module linked to our type.
function TPyPoint.DoRaiseError( args : PPyObject ) : PPyObject;
begin
with GetPythonEngine do
begin
// We adjust the transmitted self argument
Adjust(@Self);
// This is a simple call:
//GetModule.RaiseError( 'PointError', 'this is an example of raising an error !' );
// This is an advanced call:
// We provide the instance vars as a dictionary, so that we can intercept the
// error with "except" and extract informations from the error object.
// ArrayToPyDict needs a list of pairs: varName (string), varValue (anything)
GetModule.RaiseErrorObj( 'EBadPoint', 'this is an example of raising an error !',
ArrayToPyDict( ['a', 1, 'b', 2, 'c', 3] ) );
Result := nil;
end;
end;
Compile and execute this application, then type in the memo
the following text and execute it:
import spam
p = spam.CreatePoint(2, 5)
print p
p.OffsetBy( 3, 3 )
print p.x, p.y
print dir(spam)
print spam.Point
print "p = ", p, " --> ",
if type(p) is spam.Point:
print "p is a Point"
else:
print "p is not a point"
p = 2
print "p = ", p, " --> ",
if type(p) is spam.Point:
print "p is a Point"
else:
print "p is not a point"
p = spam.CreatePoint(2, 5)
try:
print "raising an error of class EBadPoint"
p.RaiseError() # it will raise spam.EBadPoint
except spam.PointError, what: #it shows you that you can intercept a parent class
print "Catched an error derived from PointError"
print "Error class = ", what.__class__, " a =", what.a, " b =", what.b, " c =", what.c
# You can raise error from a Python script to !
print "------------------------------------------------------------------"
print "Errors in a Python script"
try:
raise spam.EBadPoint, "this is a test !"
except:
pass
try:
err = spam.EBadPoint()
err.a = 1
err.b = 2
err.c = 3
raise err
except spam.PointError, what: #it shows you that you can intercept a parent class
print "Catched an error dirived from PointError"
print "Error class = ", what.__class__, " a =", what.a, " b =", what.b, " c =", what.c
--------------------------------------------------------------
9) Making a Python module as a Dll (Kylix ready)
--------------------------------------------------------------
The demo9 contains 2 delphi projects: one for the application
and one for the dll. The dll extension is renamed as ".pyd".
The application script does an "import demodll" that will try
to find first an internal module, then a "demodll.dll" or a
"demodll.pyd".
Python executes a procedure named initXXX in the dll, where
XXX must be the name of the module (in this case it will be
"demodll"). So, your dll must export only one procedure like this:
procedure initXXX; cdecl;
In the function initXXX of our DLL, we create an instance of
TPythonEngine and we define its property "AutoFinalize" to False,
because TPythonEngine must absolutely not call the function
Py_Finalize. After you call its method LoadDll.
Then we create an instance of TPythonModule, we setup its name,
the engine used, add the methods and finally call Initialize.
At last, you add a finalize section in order to free the two instances.
WARNING !!! Python cares about letter case, so check if the
resulting Dll is "demodll.pyd", all in lowercase, otherwise
the "import demodll" statement won't find the module.
--------------------------------------------------------------
10) Mapping Delphi VCL components inside Python.
--------------------------------------------------------------
Full mapping of TTable and TQuery that lets you access
Database from inside Python as you would do it with Delphi.
Look at the demo, I tried to put some comments and I think it is
self-explicit.
It shows you how you can use Delphi class inheritence for
making new Python types and avoid duplication of code.
It shows you how to use some special object services like
sequences.
It shows you how you can subclass any Python object, using
a Proxy class (see example 6).
--------------------------------------------------------------
11) Using Threads inside Python. (Kylix ready)
--------------------------------------------------------------
Demonstrates how you can execute python code that interacts with
Delphi in threads.
--------------------------------------------------------------
12) Using PythonAtom. (Deprecated since Delphi 6, See VarPyth instead)
--------------------------------------------------------------
Look at Demo12
Simply add the PythonAtom in the uses clause, declare a new var of type OleVariant
and call the function getAtom( any Python object ). It will return a new OleVariant
that will let you access properties or methods very simply, as you would do with Word !
var
pObject : PPyObject;
myAtom : OleVariant;
myString : String;
begin
pObject := ... a python object ...(using a TPythonDelphiVar.ValueObject for example);
myAtom := getAtom(pObject); // This is the magic object
myAtom.myMethod();
myString := myAtom.myProperty.myProperty2;
myAtom.myProperty.myProperty3:='Olivier';
ShowMessage(myAtom.myMethod2('Olivier'));
ShowMessage(myAtom.myListProperty[3]);
// Or anything ! Note that type conversion are automatic !
end;
Note that if you use TPythonDelphiVar.ValueObject, you must decrement the reference on
the returned object !
--------------------------------------------------------------
13) Using TPythonDatabase.
--------------------------------------------------------------
Look at Demo13
This demo is based on the Demo10, but uses the TPythonDatabase
component that was built from the Demo10, in order to let
you easily access a Database from Python.
--------------------------------------------------------------
14) Making a Dll with TPythonDatabase
--------------------------------------------------------------
Here's how you can make a Dll that will contain the Database components
in order to let you access a database from any Python application, even
from the Python console !
Please, compile the database.dpr project prior to Project1.dpr !
If you want to use this module, from anywhere, you must copy the file
database.pyd in a folder that's in Python's path, like ...\Python\Lib
WARNING !!! Python cares about letter case, so check if the
resulting Dll is "database.pyd", all in lowercase, otherwise
the "import database" statement won't find the module.
--------------------------------------------------------------
15) Using a TDataset descendant with Python, except TTable and TQuery.
--------------------------------------------------------------
If you're using a special Dataset object, like TClientDataset or
a FlashFiler table, this demo is for you !
Note, that this will only allow you to use the TDataset interface and
not the methods introduced in the TDataset subclass ! If you need to,
then study the unit pyDBTables.pas and do the same as we do for a
TTable or a TQuery !
Add the pyDB unit to your uses clause.
In the module event OnAfterInitialization, of your dedicated module,
or simply just before you execute your instructions, do the following things:
- Instanciate your Delphi dataset subclass: ds := TClientDataset.Create(nil);
- Initialize it properly: ds.DatabaseName := 'DBDemos';
ds.TableName := 'animals.dbf';
ds.Active := True;
- Create the Python interface: obj := NewDataset(ds, True)
where 'True' means that the Python wrapper will own the delphi dataset object and
free it. It avoids memory crashes if the TDataset subclass is destroyed
before the Python engine is finalized. If you encounter such a problem,
you can use the PythonEngine1.Finalize explicit method before any object is
destroyed in your form.
- Define a var containing our object : PythonModule1.SetVar( 'Dataset', obj );
where PythonModule1 is the TPythonModule object you want to use
'Dataset' is the var name in the module that will contain our dataset object
obj contains the Python object interface
- Don't forget to decrement 'obj': GetPythonEngine.Py_XDecRef(obj);
This demo shows you how to add a function to your module, that will allow your
Python script to create its own instances of the Dataset subclass (look at the
DoCreateTable function).
--------------------------------------------------------------
16) Using a TDelphiVar or Module methods ? (Kylix ready)
--------------------------------------------------------------
Here are examples of 2 differents solutions that let you read/write properties stored
in your Delphi application (this examples were posted on the Python for Delphi
mailing list) :
- The first example uses a DelphiVar that contains a dictionary.
- The second example uses a new modules (props) that publishes 3 functions
GetProperty, SetProperty and GetPropertyList
In both examples, I show you how you can simplify any implementation with a
Proxy class that will use either the DelphiVar or the module functions.
--------------------------------------------------------------
17) Using variant arrays of 2 dimensions (Kylix ready)
--------------------------------------------------------------
This demo creates a variant array of 2 dimensions, then
converts it to a Python object (a list of sub lists) with
the method VariantAsPyObject.
The Python list is then converted back to a variant array with
PyObjectAsVariant, but notice that it is not an array of 2 dimensions,
but an array of variant arrays.
Because when PyObjectAsVariant converts a Python sequence it returns
a variant array and this method is recursive. So, if we have another
sequence as an item, we'll get an array that will be stored in the
array.
This way lets you have any depth of sub-sequences, but you can only
convert 1, 2 or 3 dimensions variant arrays to Python sequences.
--------------------------------------------------------------
18) C++ Builder: using the Python Dll in a console application
--------------------------------------------------------------
Create a new project application using the console
Edit the project options and adds the folder <INSTALLDIR>\Components\Include to
the Include directory.
Adds #include <python.h> at the beginning of your source
followed by: using namespace Py;
Drop the py15.lib to your project folder and add it to the project
Write Python code and compile !
--------------------------------------------------------------
19) C++ Builder: this is a replicate of the Delphi Demo05
--------------------------------------------------------------
--------------------------------------------------------------
20) C++ Builder: this is a replicate of the Delphi Demo08
--------------------------------------------------------------
--------------------------------------------------------------
21) Using Events in TPythonModule or TPythonType (Kylix ready)
--------------------------------------------------------------
This demo is inspired of the demos 7 and 8, but uses the new feature of the TPythonModule
and TPythonType: now there's a collection property called Events, which lets you add
easily your own Module functions, or Type methods.
Simply double click on the Events property, then add a new CollectionItem, name your event, add
a DocString if you want to document your function, and double click on the OnExecute event,
then write the code to implement your function. That's all !
Note the difference with a TPythonType: you don't use the Adjust procedure, put simply use the
PythonToDelphi function to convert the PSelf argument to a Delphi instance pointer.
--------------------------------------------------------------
22) Using Threading, Windows Console and Command line arguments (Kylix ready)
--------------------------------------------------------------
When doing multithreading, be sure that the calling thread finishes after all created threads,
otherwise your application will hang ! This is easily done by using the threading module of the
standard Python distribution, and calling the "join" method of the thread objects.
If you want to display messages in your threads, you can't use the PythonForDelphi redirection to
a memo, as the VCL is not thread safe !
So, a simple way is to use the new TPythonEngine.UseWindowsConsole property which will use a
Windows threadsafe console.
In this demo, we use the sys.argv list which contains the command line arguments.
Note for Python 1.5.2:
if you're using the threading module, you should do a TPythonEngine.Finalize in the
OnClose event of your form, otherwise you'll get an access violation ! I don't know why ?
Because it works fine with new 1.6 version !
--------------------------------------------------------------
23) Using Threading and Delphi log window. (Kylix ready)
--------------------------------------------------------------
This is the same demo as Demo22, but we use the Delphi log window for displaying Python output,
instead of the Windows console.
The main advantage is that you can scroll the log window, and save its content to a file,
but the drawback is that you must run Delphi !
To do it, you must drop a TPythonInputOutput component (instead of the TPythonGUIInputOutput),
connect it to the TPythonEngine through the IO property, double-click on the TPythonInputOutput.OnSendData
event and write:
procedure TForm1.PythonInputOutput1SendData(Sender: TObject;
const Data: String);
begin
OutputDebugString( PChar(Data) );
end;
--------------------------------------------------------------
24) Using TAtomPythonEngine (Deprecated since Delphi 6, See VarPyth instead)
--------------------------------------------------------------
This demo describes how you can make Delphi methods that are going to be
used from python without even thinking about conversion and reference
counting by the use of PythonAtoms and AtomPythonEngine. The Demo is a
modified version of Demo 5, but the techniques described here can be used
in aspects of python/delphi programming as well.
The demo describes the use of the PyObjectAsVariant method defined in
AtomPythonEngine. It returns PythonAtoms when the Python object isn't of a
standard variant type (string, integer, array etc.). AtomPythonEngine is a
drop in for PythonEngine with all functionality inherited except for the
PyObjectAsVariant that is extended as stated above.
The VariantAsPyObject of PythonEngine is also shown to work on
PythonAtoms.
--------------------------------------------------------------
25) Using VarPyth.pas (Kylix ready)
--------------------------------------------------------------
Here's a unit test for demonstrating most of what you can do with the
new custom variant that wraps up the Python objects.
--------------------------------------------------------------
26) Demo8 revisited to allow the new Python type to be subclassed
--------------------------------------------------------------
Warning! Type subclassing is allowed only in Python 2.2 and later.
Note: Look at (32) if you're using Delphi7 or later as it can make
your life much easier when trying to expose a regular Delphi
class to Python. See also the WrapDelphi.pas unit.
You should not override GetAttr and SetAttr anymore and you should not
select the Basic services flags [bsGetAttr, bsSetAttr].
Instead, simply select the flags [bsGetAttrO, bsSetAttrO] and the class
TPyObject does simply call PyObject_GenericGetAttr in GetAttrO
and PyObject_GenericSetAttr in SetAttrO.
The Python Generic Get/Set functions will use new slots introduced in
the type object for specifying a list of members (class fields),
a list of get/set functions (like properties in Delphi) and a list
of methods. That way a subclass can introduce its own members/get,set/methods
and call the others coming from the base classes.
So, now You can override the class methods RegisterMembers and RegisterGetSets
for allowing your type to be subclassed and offer attributes and
methods to its subclasses.
This demo declares two members for x and y fields, and one get/set for
accessing the name fields which is a Delphi string and thus needs to
be converted from/to Python object.
The old way of registering methods has not changed at all.
--------------------------------------------------------------
27) Container indexing
--------------------------------------------------------------
#index:
s[0]
#slice
s[0:2]
#ellipsis
s[...]
#extended slice
s[0:2:2]
#multidimensional slice:
s[10, 10:20, 20:100:5, ...]
--------------------------------------------------------------
28) Iterator (Kylix ready)
--------------------------------------------------------------
This demo implements a new container type wrapping a TStringList
and shows how to return an iterator that will browse the container's content.
--------------------------------------------------------------
29) Using Python Imaging Library (PAL)
--------------------------------------------------------------
This demo shows how to exchange images between Delphi and the
Python Imaging Library.
This demo relies on VarPyth.pas and thus on Delphi 6 or above.
You need also the library that you can get at:
http://www.pythonware.com/products/pil/
--------------------------------------------------------------
30) Using Named Parameters (Kylix ready)
--------------------------------------------------------------
This demo shows how you can use named parameters when invoking Python functions or methods.
This requires VarPyth.
Look at the code in TForm1.Button1Click
--------------------------------------------------------------
31) Using WrapDelphi to access Delphi Form attributes (Requires Delphi5 or later)
--------------------------------------------------------------
This Demo shows how you expose a Delphi Form to Python, or your
own custom Delphi classes.
Note that it will use its full potential with Delphi7 or later.
--------------------------------------------------------------
32) Demo08 revisited using WrapDelphi (Requires Delphi7 or later)
--------------------------------------------------------------
This Demo shows how you can expose a Delphi class much simpler
using WrapDelphi.
--------------------------------------------------------------
33) Using Threads inside Python II.
--------------------------------------------------------------
Enhanced Demo 11 showing how to properly interrupt running
threads.
--------------------------------------------------------------
That's all folks !!!
Hope this helps to build cool Python/Delphi applications.
Morgan