Jump to content

PierreC

Members
  • Content Count

    9
  • Joined

  • Last visited

Posts posted by PierreC


  1. Hello everyone,

     

    I have another question: is there a way to get the prompt argument of the python input() function?

    For example if the python code is 

    input("Please, give me a number")

    I would like to get this string when dealing with the OnReceiveData or OnReceiveUniData events.


  2. Hello,

     

    In my quest to integrate python scripting into my application, I encounter another problem. I have to use P4D in a kind of plugin of my Windows application, which forces me to instantiate and release the python library several times in the same process (but never simultaneously). This is not a problem except when some modules are imported. For example, if I import numpy or win32com, the second call to import triggers an exception: EPyTypeError for win32com, Access violation for numpy.
    Here is an ultra-simplified example of what I do:

    program LoadTwice;
    
    {$APPTYPE CONSOLE}
    
    {$R *.res}
    
    uses
      System.SysUtils,
      PythonEngine;
    
    var
      pythonEngine: TPythonEngine;
    
    begin
      pythonEngine:= TPyThonEngine.Create(nil);
      pythonEngine.LoadDll;
      pythonEngine.ExecString('import numpy');
      pythonEngine.Free;
    
      pythonEngine:= TPyThonEngine.Create(nil);
      pythonEngine.LoadDll;
      pythonEngine.ExecString('import numpy');
      pythonEngine.Free;
    end.

    Do you have any idea what I'm doing wrong?


  3. Hello,

    I have been having a lot of trouble getting this mechanism to work in my application. RTTI doesn't seem to be able to enumerate the methods of my instances, this is certainly due to the fact that there is no "correctly" registered type library.
    So I looked at another possibility which is to :
    On the Delphi side:
    1 - define a variable of type integer in a module,
    2 - store the IDispatch interface address of the instance in this variable,
    On the Python side
    3 - retrieve this value,
    4 - Using pythoncom.ObjectFromAddress(), retrieve a python object instance from this address.
    5 - Wrap the python object in IDispatch class using the win32com.client.Dispatch() method
    This may not be perfect (I'm a total beginner in python) but it seems to works well and allows me to use the IDispatch mechanism, which is fine for me because I have objects implementing dynamic methods and properties through GetIDOfNames and Invoke.

     

    Here is a simple demo that may help someone else who is looking for this kind of information:

    program MiniIDispatchDemo;
    
    {$APPTYPE CONSOLE}
    
    {$R *.res}
    
    uses
      System.SysUtils,
      PythonEngine,
      ComObj,
      ActiveX;
    
    var
      pythonEngine: TPythonEngine;
      delphiModule: TPythonModule;
      regExp: IDispatch;
      addressAsInt: NativeInt;
      pyAddress: PPyObject;
    begin
      // We are using COM, so we need to initialize it first
      CoInitialize(nil);
      try
        // Let's create a PythonEngine
        pythonEngine:= TPyThonEngine.Create(nil);
    
        // And a module to store our variable
        delphiModule := TPythonModule.Create(nil);
        delphiModule.Name := 'DelphiModule';
        delphiModule.Engine := pythonEngine;
        delphiModule.ModuleName := 'delphi';
    
        // Initialize the Python library
        pythonEngine.LoadDll;
    
        // Here we instanciate a RegExp object as a simple IDispatch interface
        // This can be replaced by any other IDispatch interface
        regExp:= CreateOleObject('VBScript.RegExp');
        // Then we get the address as an int
        addressAsInt:= NativeInt(regExp);
        // Get an integer Python object with the same value
        pyAddress:= pythonEngine.PyLong_FromLong(addressAsInt);
        // and store the value in a variable of our module
        delphiModule.SetVar('regexpAddress', pyAddress);
        // Don't forget the decrease the ref count
        pythonEngine.Py_DecRef(pyAddress);
    
        try
          pythonEngine.ExecString(
            // Imports all needed modules incuing our own module named "delphi"
            'from delphi import regexpAddress' + sLineBreak +
            'import pythoncom' + sLineBreak +
            'from win32com.client import Dispatch' + sLineBreak +
            // Get a python object containing from the address of the interface
            're = pythoncom.ObjectFromAddress(regexpAddress, pythoncom.IID_IDispatch)' + sLineBreak +
            // Cast this object to an IDispatch object
            'regexp = Dispatch(re)' + sLineBreak +
            // And use it's properties and methods
            'regexp.Pattern = "delphi"' + sLineBreak +
            'print(regexp.Replace("Using regexp in delphi", "python"))');
            // This prints: Using regexp in python
        finally
          delphiModule.Free;
          pythonEngine.Free;
        end;
      finally
        CoUninitialize();
      end;
    end.

    Pierre


  4. Hy again,

    I was able to reproduce the example given in WrapDelphiTest.pas without difficulty but I think that it does not suit my situation.
    If I'm not mistaken, it seems to me that this method can only work if you have the Delphi class that implements the interface because it is RTTI that enumerates the members of the class and makes them available to the wrapper. In my case, I don't have the Delphi classes at my disposal because they are defined in other dlls.
    Does my reasoning seem correct to you?
    Here is an example of what I would like to do, I made it as short as possible and it uses an interface and a co-class normally available on any Windows machine.

    uses
      Rtti, VarPyth, ComObj;
    
    {$R *.dfm}
    
    const
      IID_IRegExp: TGUID = '{3F4DACA0-160D-11D2-A8E9-00104B365C9F}';
      CLASS_RegExp: TGUID = '{3F4DACA4-160D-11D2-A8E9-00104B365C9F}';
    type
      IRegExp = interface(IDispatch)
        ['{3F4DACA0-160D-11D2-A8E9-00104B365C9F}']
        function Get_Pattern: WideString; safecall;
        procedure Set_Pattern(const pPattern: WideString); safecall;
        function Get_IgnoreCase: WordBool; safecall;
        procedure Set_IgnoreCase(pIgnoreCase: WordBool); safecall;
        function Get_Global: WordBool; safecall;
        procedure Set_Global(pGlobal: WordBool); safecall;
        function Execute(const sourceString: WideString): IDispatch; safecall;
        function Test(const sourceString: WideString): WordBool; safecall;
        function Replace(const sourceString: WideString; const replaceString: WideString): WideString; safecall;
        property Pattern: WideString read Get_Pattern write Set_Pattern;
        property IgnoreCase: WordBool read Get_IgnoreCase write Set_IgnoreCase;
        property Global: WordBool read Get_Global write Set_Global;
      end;
    
      CoVBScriptRegExp = class
        class function Create: IRegExp;
      end;
    
    class function CoVBScriptRegExp.Create: IRegExp;
    begin
      Result := CreateComObject(CLASS_RegExp) as IRegExp;
    end;
    
    procedure TForm1.Button2Click(Sender: TObject);
    var
      pythonEngine: TPythonEngine;
      delphiModule: TPythonModule;
      pyDelphiWrapper: TPyDelphiWrapper;
      Py : PPyObject;
      regExp: IRegExp;
    begin
      pythonEngine:= TPyThonEngine.Create(nil);
    
      delphiModule := TPythonModule.Create(nil);
      delphiModule.Name := 'DelphiModule';
      delphiModule.Engine := pythonEngine;
      delphiModule.ModuleName := 'delphi';
    
      pyDelphiWrapper := TPyDelphiWrapper.Create(nil);
      PyDelphiWrapper.Name := 'PyDelphiWrapper';
      PyDelphiWrapper.Engine := pythonEngine;
      PyDelphiWrapper.Module := delphiModule;
    
      pythonEngine.LoadDll;
    
      regExp:= CoVBScriptRegExp.Create;
      regExp.Pattern:= 'i';
      ShowMessage(regExp.Replace('This is a beautiful day', '##'));
    
      Py := pyDelphiWrapper.WrapInterface(TValue.From(regExp));
      delphiModule.SetVar('regexp', Py);
      pythonEngine.Py_DecRef(Py);
    
      try
        pythonEngine.ExecString(
          'from delphi import regexp' + sLineBreak +
          'print(regexp.Replace("This is a beautiful day", "##"))');
        { This raises the following error:
          AttributeError: Error in getting property "Replace".
          Error: Unknown attribute. }
      finally
        pyDelphiWrapper.Free;
        delphiModule.Free;
        pythonEngine.Free;
      end;
    end;

    Do you think it is possible to map the pointer on the interface to a corresponding object in python whose class would have been generated by importing a type library with ComTypes or to a Dispatch class of Win32Com?

    I'm sorry if I'm not using the right terms or if my question is a bit obscure but I know very little about python and even less about its API.

     

    Best regards,

    Pierre

     


  5. Hello,

    First of all, I want to congratulate the creator of P4D for the excellent work done.
    I am currently trying to use P4D to add scripting functions to an existing application. This one already has interfaces and co-class to manipulate it. I can't manage to make these COM objects available to the python code, would you have a clue ? I don't know if it is possible to do late-binding in python (calls by the Invoke method of the IUnknown interface) or if it is mandatory to use ComTypes or Win32Types to generate the corresponding python classes. But in this case, how to transtype the objects from Delphi ?
    If it is necessary, I can build an example project.

     

    Pierre

×