-
Content Count
1067 -
Joined
-
Last visited
-
Days Won
70
Everything posted by pyscripter
-
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.
-
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.
-
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.
-
Please search this forum for answers/discussion of this question.
-
Could you please attach a zipped project instead?
-
It will be called if you change your python script to: entry = manager.GetNewEntry() print(entry.Name()) entry = None
-
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.
-
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.
-
Can't print from Python script thread to Delphi main thread
pyscripter replied to Martybartfast's topic in Python4Delphi
@Martybartfast Support for output redirection from sub-interpreters has been committed. So with the latest version you do need the above workaround. -
Can't print from Python script thread to Delphi main thread
pyscripter replied to Martybartfast's topic in Python4Delphi
@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. -
Can't print from Python script thread to Delphi main thread
pyscripter replied to Martybartfast's topic in Python4Delphi
Output redirection is not supported with emNewInterpreterOwnGIL. See other threads with a discussion of the limitations of that mode in this forum. -
Help wanted: Run Python (P4D) in a Delphi thread without blocking UI
pyscripter replied to Tom F's topic in Python4Delphi
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; -
TPyDelphiWrapper and initialization/finalization issue
pyscripter replied to Jacek Laskowski's topic in Python4Delphi
Yes you can. -
TPyDelphiWrapper and initialization/finalization issue
pyscripter replied to Jacek Laskowski's topic in Python4Delphi
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. -
PythonEngine.PyUnicode_FromString() issue
pyscripter replied to Jacek Laskowski's topic in Python4Delphi
P4D provides a function: function TPythonEngine.PyUnicodeFromString(const AString : UnicodeString) : PPyObject; -
TypeError: 'PythonDelphiVar' object cannot be interpreted as an integer"
pyscripter replied to Jacek Laskowski's topic in Python4Delphi
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); -
TypeError: 'PythonDelphiVar' object cannot be interpreted as an integer"
pyscripter replied to Jacek Laskowski's topic in Python4Delphi
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; -
TypeError: 'PythonDelphiVar' object cannot be interpreted as an integer"
pyscripter replied to Jacek Laskowski's topic in Python4Delphi
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 -
TypeError: 'PythonDelphiVar' object cannot be interpreted as an integer"
pyscripter replied to Jacek Laskowski's topic in Python4Delphi
The above should also be: HEIGHT.Value = 40 WIDTH.Value = 60 You should not need to import builtins. Not sure what the problem was. -
TypeError: 'PythonDelphiVar' object cannot be interpreted as an integer"
pyscripter replied to Jacek Laskowski's topic in Python4Delphi
canvas = [[' ' for _ in range(WIDTH)] for _ in range(HEIGHT)] needs to be canvas = [[' ' for _ in range(WIDTH.Value)] for _ in range(HEIGHT.Value)] -
TypeError: 'PythonDelphiVar' object cannot be interpreted as an integer"
pyscripter replied to Jacek Laskowski's topic in Python4Delphi
Does reversing the order help? VarWidth.Value := PyWidth; VarWidth.Initialize; -
Error 'No module named' on importing python-file
pyscripter replied to Brent's topic in Python4Delphi
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. -
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.
-
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.
-
Looking for CBuilder/VS help on C open source project
pyscripter replied to Vincent Parrett's topic in Job Opportunities / Coder for Hire
Another abandonware of Microsoft! It first sounded real good.