Jump to content

pyscripter

Members
  • Content Count

    1067
  • Joined

  • Last visited

  • Days Won

    70

Everything posted by pyscripter

  1. pyscripter

    Correctly let python free a result

    For the benefit of anyone reading this thread, the most powerful way to allow your python script to create instances of a Delphi class (e.g. TEntry) is to create a wrapper: type TEntryClassWrapper = class(TPyClassWrapper<TEntry>) constructor CreateWith(APythonType: TPythonType; args, kwds: PPyObject); overload; override; end; { TEntryClassWrapper } constructor TEntryClassWrapper.CreateWith(APythonType: TPythonType; args, kwds: PPyObject); var _obj : PPyObject; begin if GetPythonEngine.PyArg_ParseTuple( args, 'O:CreteWith',@_obj ) <> 0 then begin Create(APythonType); DelphiObject := TEntry.Create(GetPythonEngine.PyObjectAsString(_obj)); end; end; You need to register and initialize the wrapper in your Form.Create: procedure TForm1.FormCreate(Sender: TObject); begin PyDelphiWrapper1.RegisterDelphiWrapper(TEntryClassWrapper).Initialize; end; and then in python: from spam import * entry = Entry('Test') print(entry.Name()) entry = None The Delphi TEntry class is exposed as the python type Entry in your python code.
  2. pyscripter

    Correctly let python free a result

    Not really. You can use the following ExecStrings overload procedure ExecStrings(strings: TStrings; locals, globals: PPyObject; const FileName: string = '<string>'); overload; providing your own globals and locals so that you do not mess up the python namespace. Please search in this forum. There was a discussion about this.
  3. pyscripter

    Correctly let python free a result

    TEntry = class(TObject) strict private FstrName: string; FclsStringList: TStringList; public constructor Create(strName: string); destructor Destory; function Name: string; end; Destroy misspelled and not declared with override. If you fix this TEntry gets destroyed. But the better way is: function TManager.GetNewEntry: TEntry; begin Result := TEntry.Create('Test'); end; and in python from spam import * entry = manager.GetNewEntry() print(entry.Name()) entry.__owned__ = True entry = None Explanation: When P4D wraps the result of GetNewEntry it does not know who owns the return value. The default is that Delphi does. But the ownership can be changed in python using the __owned__ property.
  4. pyscripter

    Correctly let python free a result

    Please search this forum for answers/discussion of this question.
  5. pyscripter

    Correctly let python free a result

    Could you please attach a zipped project instead?
  6. pyscripter

    Correctly let python free a result

    It will be called if you change your python script to: entry = manager.GetNewEntry() print(entry.Name()) entry = None
  7. pyscripter

    Correctly let python free a result

    function TManager.GetNewEntry: PPyObject; begin // I want TEntry to be destroyed by python once it is no longer used Result := Form1.GetDelphiWrapper.Wrap(TEntry.Create('Test'), soOwned); Form1.GetPythonEngine.Py_DECREF(Result); end; In the code above Py_DECREF will destroy the TEntry instance immediately, You can confirm that by debugging and putting a brekpoint at TEntry.Destroy. Just remove the Py_DECREF statement. Explanation.: Wrap returns a python object with reference count of 1. When you decrease the reference count, it goes down to 0, and because the wrapped Delphi object is owned it gets destroyed The difference with clsManager := TManager.Create; pyManager := PyDelphiWrapper1.Wrap(clsManager, soReference); PythonModule1.SetVar( 'manager', pyManager ); PythonEngine1.ExecStrings( memo1.Lines ); PythonEngine1.Py_DecRef(pyManager); is that you call SetVar which increases the reference count. However the above code leaks clsManager. It does not get destroyed because you set the Ownership to soReference. Either change that or call clsManager.Free at the end. Conclusion: Python reference counting can be tricky. You should try to understand the rules behind reference counting. (e.g. Reference Counting in Python (tripod.com)) if you use the Python API directly. High level P4D modules (WrapDelphi and VarPython) to a large extent insulate you from that and ensure correct use of reference counting.
  8. pyscripter

    Stopping a P4D Python thread

    TPythonThread is a TThread descendent. But TThread.Terminate, does not stop threads. Your code needs to check the value of Terminated. Did you check Demo 33?https://github.com/pyscripter/python4delphi/blob/b6b63c7a2e94e027a52eac340d6fb38f084d5269/Demos/Demo33/SortThds.pas#L147C1-L158C5. This works in that case because the python code releases the GIL regularly (when you call swap). Alternatively you can expose to your python scripts a Delphi variable that the python scripts check at short intervals. In other words you have to engineer a way to stop the threads gracefully. The same applies to standard Delphi threads.
  9. pyscripter

    Can't print from Python script thread to Delphi main thread

    @Martybartfast Support for output redirection from sub-interpreters has been committed. So with the latest version you do need the above workaround.
  10. pyscripter

    Can't print from Python script thread to Delphi main thread

    @Martybartfast Actually there is an easy solution: Replace TPyThread.ExecuteWithPython with procedure TPyThread.ExecuteWithPython; // Run the script begin TPythonModule(PyEngine.IOPythonModule).MultInterpretersSupport := mmiPerInterpreterGIL; TPythonModule(PyEngine.IOPythonModule).InitializeForNewInterpreter; PyEngine.DoRedirectIO; PyEngine.ExecString(FScript); // Run Python script end; and printing should work.
  11. pyscripter

    Can't print from Python script thread to Delphi main thread

    Output redirection is not supported with emNewInterpreterOwnGIL. See other threads with a discussion of the limitations of that mode in this forum.
  12. From https://github.com/pyscripter/python4delphi/wiki/PythonThreads Why do the following inside the thread? GetPythonEngine.IO := PythonInputOutput1; // do these assignments go here, or before the ThreadPythonExec GetPythonEngine.FatalMsgDlg := FALSE; GetPythonEngine.FatalAbort := TRUE; GetPythonEngine.Py_FatalError := @MyFatalMessage;
  13. pyscripter

    TPyDelphiWrapper and initialization/finalization issue

    Yes you can.
  14. pyscripter

    TPyDelphiWrapper and initialization/finalization issue

    There is no need to create and destroy TPyDelphiWrapper. Just create it once with the engine. It will be automatically initialized when the python dll is loaded.
  15. pyscripter

    PythonEngine.PyUnicode_FromString() issue

    P4D provides a function: function TPythonEngine.PyUnicodeFromString(const AString : UnicodeString) : PPyObject;
  16. Yes but note that PyObject_SetAttrString increments the reference count of pObj. So if you want to pass the responsibility of destroying the object to python, you need to decrement it: fPyEngine.Py_DecRef(pObj);
  17. Actually for this use case the TPythonModule is not needed. You can directly import the variables into the '__main__' module. e,g, procedure TForm1.Button1Click(Sender: TObject); var PyEngine: TPythonEngine; PyHeight: Integer; PyIO: TPythonGUIInputOutput; PyWidth: Integer; VarHeight: TPythonDelphiVar; VarWidth: TPythonDelphiVar; begin PyWidth := 80; PyHeight := 40; PyEngine := TPythonEngine.Create(nil); PyIO := TPythonGUIInputOutput.Create(nil); try PyIO.Output := Memo1; PyEngine.IO := PyIO; PyEngine.UseLastKnownVersion := True; PyEngine.AutoLoad := False; PyEngine.AutoUnload := False; try PyEngine.LoadDll; except on E: Exception do begin ShowMessage('Error at load Python: ' + E.Message); Exit; end; end; VarWidth := TPythonDelphiVar.Create(nil); VarWidth.Engine := PyEngine; VarWidth.Module := '__main__'; VarWidth.VarName := 'WIDTH'; VarWidth.Initialize; VarWidth.Value := PyWidth; VarHeight := TPythonDelphiVar.Create(nil); VarHeight.Engine := PyEngine; VarHeight.Module := '__main__'; VarHeight.VarName := 'HEIGHT'; VarHeight.Initialize; VarHeight.Value := PyHeight; var Source := ''' import math result = sum(i * i for i in range(1, 11)) print("Suma kwadratów od 1 do 10 to:", result) canvas = [[' ' for _ in range(WIDTH.Value)] for _ in range(HEIGHT.Value)] print(canvas) '''; PyEngine.ExecString(Utf8Encode(Source), '__main__'); finally VarWidth.Free; VarHeight.Free; PyIO.Free; PyEngine.Free; end; end;
  18. Here the following runs fine (without importing builtins): procedure TForm1.Button1Click(Sender: TObject); var PyEngine: TPythonEngine; PyHeight: Integer; PyIO: TPythonGUIInputOutput; PyModule: TPythonModule; PyWidth: Integer; VarHeight: TPythonDelphiVar; VarWidth: TPythonDelphiVar; begin PyWidth := 80; PyHeight := 40; PyEngine := TPythonEngine.Create(nil); PyModule := TPythonModule.Create(nil); PyIO := TPythonGUIInputOutput.Create(nil); try PyIO.Output := Memo1; PyEngine.IO := PyIO; PyEngine.UseLastKnownVersion := True; PyEngine.AutoLoad := False; PyEngine.AutoUnload := False; PyModule.Engine := PyEngine; PyModule.ModuleName := '__main__'; try PyEngine.LoadDll; except on E: Exception do begin ShowMessage('Error at load Python: ' + E.Message); Exit; end; end; PyModule.Initialize; //PyEngine.PyRun_SimpleString('import builtins; __builtins__ = builtins'); VarWidth := TPythonDelphiVar.Create(nil); VarWidth.Engine := PyEngine; VarWidth.Module := '__main__'; VarWidth.VarName := 'WIDTH'; VarWidth.Initialize; VarWidth.Value := PyWidth; VarHeight := TPythonDelphiVar.Create(nil); VarHeight.Engine := PyEngine; VarHeight.Module := '__main__'; VarHeight.VarName := 'HEIGHT'; VarHeight.Initialize; VarHeight.Value := PyHeight; var Source := ''' import math result = sum(i * i for i in range(1, 11)) print("Suma kwadratów od 1 do 10 to:", result) canvas = [[' ' for _ in range(WIDTH.Value)] for _ in range(HEIGHT.Value)] print(canvas) '''; PyEngine.ExecString(Utf8Encode(Source), '__main__'); finally VarWidth.Free; VarHeight.Free; PyIO.Free; PyModule.Free; PyEngine.Free; end; end; However you should not call the module '__main__', since by doing that you will be overriding the default python module with the same name. Call the module something else say my_module and then add to your python script: from my_module import WIDTH, HEIGHT
  19. The above should also be: HEIGHT.Value = 40 WIDTH.Value = 60 You should not need to import builtins. Not sure what the problem was.
  20. canvas = [[' ' for _ in range(WIDTH)] for _ in range(HEIGHT)] needs to be canvas = [[' ' for _ in range(WIDTH.Value)] for _ in range(HEIGHT.Value)]
  21. Does reversing the order help? VarWidth.Value := PyWidth; VarWidth.Initialize;
  22. I am confused. You are talking about your own python file (CustomClasses), but also something you installed via pip. For python to find your file it needs to be either on the python path or on the current directory. Run the following using P4D: import sys import os print(sys.path) print(os.getcwd()) print(sys.executable) and check the values of the path and the current directory. Also check which python version you are using.
  23. The code of TReader.ReadCollection is as follows: procedure TReader.ReadCollection(const Collection: TCollection); var Item: TPersistent; begin Collection.BeginUpdate; try if not EndOfList then Collection.Clear; while not EndOfList do begin if NextValue in [vaInt8, vaInt16, vaInt32] then ReadInteger; Item := Collection.Add; ReadListBegin; while not EndOfList do ReadProperty(Item); ReadListEnd; end; ReadListEnd; finally Collection.EndUpdate; end; end; It has been like this forever. My question is about the statement: if not EndOfList then Collection.Clear; It means that if you read an empty collection, the collection you are reading is not cleared or otherwise changed. The practical implication of this is that if you have a component that initializes a collection property with some items, there is no way to really clear it from the collection property inspector. Next time the form is loaded, either at run time or at design time, the collection will revert to the initialized state. Can you think of any reason TReader.ReadCollection does this? I think that if you read an empty collection, the collection property you are loading should be cleared. Should I report this as an issue? Also is there any workaround? I want a collection property to have a default state with some items created by the component constructor, but the users to be able to clear it at design time.
  24. pyscripter

    Python Output

    Multi-phase extension module initialization has been implemented. See Multi-phase module initialization implemented · pyscripter/python4delphi · Discussion #505 for details. What this means is that you can now create python extension modules with P4D that can be used with sub-interpreters including those created with the new concurrent.interpreters module of python 3.14.
  25. Another abandonware of Microsoft! It first sounded real good.
×