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:
--------------------------------------------------------------
  Create a new Form
  Drop 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
--------------------------------------------------------------
  Create a new Form
  Drop 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):
--------------------------------------------------------------
  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):
--------------------------------------------------------------
  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 );

  Run the application, and type in the source window:
    print "Current value of var test is: ", varname
    varname.Value = "New value set by Python"
  Click on the Execute button, and look at the Edit1 component.

--------------------------------------------------------------
5) Defining a new Module:
--------------------------------------------------------------
  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:
--------------------------------------------------------------
  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
--------------------------------------------------------------
  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.

--------------------------------------------------------------
8) Using Delphi classes for new Python types
--------------------------------------------------------------
  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 dirived 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
--------------------------------------------------------------
  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.


--------------------------------------------------------------
That's all folks !!!
Hope this helps to build cool Pythons/Delphi applications.

Morgan

