Jacek Laskowski 57 Posted August 6 My code in Delphi: procedure TForm1.btnRunClick(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 := mmoLog; PyEngine.IO := PyIO; PyEngine.DllName := 'python38.dll'; PyEngine.DllPath := ExtractFilePath(Application.ExeName) + 'python\'; PyEngine.UseLastKnownVersion := False; 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; PyEngine.ExecStrings(mmoSource.Lines, '__main__'); finally VarWidth.Free; VarHeight.Free; PyIO.Free; PyModule.Free; PyEngine.Free; end; end; And python script: canvas = [[' ' for _ in range(WIDTH)] for _ in range(HEIGHT)] After run this I get an error: Traceback (most recent call last): File "__main__", line 1, in <module> TypeError: 'PythonDelphiVar' object cannot be interpreted as an integer I want to pass two variables WIDTH and HEIGHT to the script. What am I doing incorrectly? Share this post Link to post
pyscripter 828 Posted August 6 31 minutes ago, Jacek Laskowski said: VarWidth.Initialize; VarWidth.Value := PyWidth; Does reversing the order help? VarWidth.Value := PyWidth; VarWidth.Initialize; Share this post Link to post
Jacek Laskowski 57 Posted August 6 (edited) 14 minutes ago, pyscripter said: Does reversing the order help? VarWidth.Value := PyWidth; VarWidth.Initialize; No, then I get an error on assignment line: VarWidth.Value := PyWidth; <-- here I get an error VarWidth.Initialize; Error message: ThreadId=4800 ProcessId=38 ThreadName="" ExceptionMessage="No variable was created" ExceptionName="Exception" ExceptionDisplayName="Exception" ExceptionAddress=751DC5AF FileName=<not available> LineNumber=<not available> ExceptionObject=033BE960 Classes=[Exception,TObject] Error is raised here: procedure TPythonDelphiVar.SetValue( const val : Variant ); begin if Assigned( FVarObject ) then with TPyVar(PythonToDelphi(FVarObject)) do SetValueFromVariant(val) else raise Exception.Create(SVarNotCreated); <-- here end; --- edit When I change the python script to: print(HEIGHT, WIDTH) then all run ok, and in the log I get: <PythonDelphiVar: 40> <PythonDelphiVar: 60> Edited August 6 by Jacek Laskowski adding Share this post Link to post
pyscripter 828 Posted August 6 canvas = [[' ' for _ in range(WIDTH)] for _ in range(HEIGHT)] needs to be canvas = [[' ' for _ in range(WIDTH.Value)] for _ in range(HEIGHT.Value)] 1 Share this post Link to post
Jacek Laskowski 57 Posted August 7 (edited) 10 hours ago, pyscripter said: canvas = [[' ' for _ in range(WIDTH)] for _ in range(HEIGHT)] needs to be canvas = [[' ' for _ in range(WIDTH.Value)] for _ in range(HEIGHT.Value)] Thanks! This was the cause of the problem. By the way, I have a question. This script in Python is quite a bit bigger, here I have given a minimal causing the error, the script uses WIDTH and HEIGHT variables. Before I passed these variables from Delphi they were defined at the beginning of the script by a simple assignment: HEIGHT = 40 WIDTH = 60 However, when I added these two variables (TPythonDelphiVar) from the Delphi side, and the module (TPythonModule) object it stopped working many keywords including “import” and only adding a line in the Delphi code: PyEngine.PyRun_SimpleString(‘import builtins; __builtins__ = builtins’); ...resulted in correct working. What is the result of this? Why is it that without variable and module objects, the contents of “builtins” are automatically available, but disappear after adding them? Edited August 7 by Jacek Laskowski Share this post Link to post
pyscripter 828 Posted August 7 4 hours ago, Jacek Laskowski said: HEIGHT = 40 WIDTH = 60 The above should also be: HEIGHT.Value = 40 WIDTH.Value = 60 You should not need to import builtins. Not sure what the problem was. Share this post Link to post
Jacek Laskowski 57 Posted August 7 (edited) Probably my English is too poor, that's why you misunderstood me. Again from start. In the beginning I had a self-contained script, without external variables. I had a simple script to just run P4D, I'm using it for the first time and learning. In that simple script there were only python variables set up as you do in python: HEIGHT = 40 WIDTH = 60 And then, once everything started working, I decided to learn how to pass variables from Delphi. Then I removed the setting of these two variables from the beginning of the script and added to the delphi code the use of TPythonDelphiVar with these two variables. Well, this is where the problems started, because I didn't know that after this change I had to add .Value to these variables. At the same time, it turned out that I also need to add a line of code with: PyEngine.PyRun_SimpleString(‘import builtins; __builtins__ = builtins’); Because without this line I got errors: Traceback (most recent call last): File "__main__", line 1, in <module> ImportError: __import__ not found The above error comes from a script that starts with a line: import math When I use another script, such as: result = sum(i * i for i in range(1, 11)) print("Suma kwadratów od 1 do 10 to:", result) I get a different error: Traceback (most recent call last): File "__main__", line 1, in <module> NameError: name 'sum' is not defined In both cases, the very first word of the script is problematic. When I add a delphi line code with "builtins" it all works. What is the problem? Edited August 7 by Jacek Laskowski Share this post Link to post
pyscripter 828 Posted August 7 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 Share this post Link to post
Jacek Laskowski 57 Posted August 8 @pyscripter Thanks, it works. Still a question about that module name. Should I change the module name in the TPythonDelphiVar and TPythonModule components? That is, should it be like this: PyModule.Engine := PyEngine; PyModule.ModuleName := 'my_name'; <-- here [..] VarWidth := TPythonDelphiVar.Create(nil); VarWidth.Engine := PyEngine; VarWidth.Module := 'my_name'; <-- here [...] Or only in variable objects? Share this post Link to post
pyscripter 828 Posted August 8 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; Share this post Link to post
Jacek Laskowski 57 Posted August 8 @pyscripter And if I just want to pass a string to a Python script is this way: var pObj: PPyObject; data: string; [...] data := 'some long string'; pObj := fPyEngine.PyUnicode_FromString(PAnsiChar(data)); fPyModule.SetVar('data', pObj); ...sufficient and correct? Share this post Link to post
pyscripter 828 Posted August 9 17 hours ago, Jacek Laskowski said: ...sufficient and correct? 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); 1 Share this post Link to post