Search code examples
pythonclassdelphipython4delphi

Get Python Object as Variant


I`m using Python4Delphi

I have a python file that a class declared on it like this :

class Student:
  SName = "MyName"
  SAge = 26

  def GetName(self):
    return SName

  def GetAge(self):
    return SAge

I want to get a refrence of this class and access it`s fields or methods with my Delphi code

I have found an example here : http://www.atug.com/andypatterns/pythonDelphiTalk.htm

But when I try to do it like that example an error shown : "No such Interface supported"

This is my Delphi Code :

var
 Err : Boolean;
 S : TStringList;
 MyClass : OLEVariant;
 PObj : PPyObject;
begin
 ...

 S := TStringList.Create;
 try
  S.LoadFromFile(ClassFileEdit.Text);
  Err := False;
  try
   PyEngine.ExecStrings(S);
  except
   on E:Exception do
    begin
     Err := True;

     MessageBox(Handle, PChar('Load Error : ' + #13 + E.Message), '', MB_OK+MB_ICONEXCLAMATION);
    end;
  end;
 finally
  S.Free;
 end;

 if Err then
  Exit;

 Err := False;
 try
  try
   PyEngine.ExecString('ClassVar.Value = Student()');
  except
   on E:Exception do
    begin
     Err := True;

     MessageBox(Handle, PChar('Class Name Error : ' + #13 + E.Message), '', MB_OK+MB_ICONEXCLAMATION);
    end;
  end;
 finally
  if not Err then
   begin
    PObj := ClassDelphiVar.ValueObject;
    MyClass := GetAtom(PObj);
    GetPythonEngine.Py_XDECREF(PObj);

    NameEdit.Text := MyClass.GetName();
    AgeEdit.Text := IntToStr(MyClass.GetAge());
   end;
 end;

Error occurs on this line :

NameEdit.Text := MyClass.GetName();

It seems that MyClass not filled with Student Object

I searched a lot and I found that GetAtom is deprecated in new versions, but how I can do this in another way ?

  • ClassDelphiVar is a TPythonDelphiVar component with "ClassVar" as VarName

Solution

  • I Found the answer, I will post here may be helpful for someone

    Put a PythonDelphiVar Component on the form and set it's OnExtGetData and OnExtSetData Events like the this code :

    procedure TMainFrm.ClassDelphiVarExtGetData(Sender: TObject;
      var Data: PPyObject);
    begin
     with GetPythonEngine do
       begin
         Data := FMyPythonObject;
         Py_XIncRef(Data); // This is very important
       end;
    end;
    
    procedure TMainFrm.ClassDelphiVarExtSetData(Sender: TObject; Data: PPyObject);
    begin
     with GetPythonEngine do
      begin
       Py_XDecRef(FMyPythonObject); // This is very important
       FMyPythonObject := Data;
       Py_XIncRef(FMyPythonObject); // This is very important
      end;
    end;
    

    We should be careful about Reference-Counting of Python Objects

    FMyPythonObject declared as a PPyObject Variable in the Public section of Class of the Form

    Now if we run this script in out Python module :

    ClassVar.Value = MyClass()
    

    (ClassVar is VarName of PythonDelphiVar Component)

    Then we can get the Attributes of our Python Object like this :

    var
     PObj : PPyObject;
    begin
     ...
     PObj := GetPythonEngine.PyObject_GetAttrString(FMyPythonObject, PAnsiChar(WideStringToString('AttrName', 0)));
     AttrValueEdit.Text := GetPythonEngine.PyObjectAsString(PObj);
     ...
    end
    

    ...

    function WideStringToString(const Source: UnicodeString; CodePage: UINT): RawByteString;
    var
     strLen: Integer;
    begin
     strLen := LocaleCharsFromUnicode(CodePage, 0, PWideChar(Source), Length(Source), nil, 0, nil, nil);
     if strLen > 0 then
      begin
       SetLength(Result, strLen);
       LocaleCharsFromUnicode(CodePage, 0, PWideChar(Source), Length(Source), PAnsiChar(Result), strLen, nil, nil);
       SetCodePage(Result, CodePage, False);
      end;
    end;