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 ""
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 \Components\Include to
the Include directory.
Adds #include 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.
--------------------------------------------------------------
--------------------------------------------------------------
34) Dynamically creating, destroying and recreating PythonEngine
--------------------------------------------------------------
Demo 26 revisited to demonstrate the use of the new
PythonVersions.pas unit. It also shows how to create
destroy and recreate PythonEngine etc. using code without
the Visual components.
--------------------------------------------------------------
That's all folks !!!
Hope this helps to build cool Python/Delphi applications.
Morgan