pyscripter / python4delphi

Free components that wrap up Python into Delphi and Lazarus (FPC)
MIT License
906 stars 310 forks source link

Access violation: Open Tk GUI and running a python function without closing the GUI #474

Closed idanmiara closed 4 months ago

idanmiara commented 4 months ago

Hi,

Is it possible to open a Python GUI (created with tk for instance) and then run a python function without closing the tk window? I've created a project to demonstrate the issue here: https://github.com/idanmiara/pyscripter_test in order to demonstrate the issue please run the project then click the buttons in the following order:

init, py load, py1 - this works init, py load, py5, close the tk window, py1 - this works init, py load, py5, py1 - this would cause Access violation.

(there are some other buttons there which are not related to this question)

Thanks!

pyscripter commented 4 months ago

When your run the tkinter script the ExecStrings does not exit until you close the tkinter window. The tkinter mainloop somehow keeps the Delphi message loop alive, and when you try to execute py1, this is effectively a recursive call to ExecStrings and does not work.

To make the above work, you need to run all scripts in threads using for instance ThreadPythonExec. See https://github.com/pyscripter/python4delphi/wiki/PythonThreads for more details.

idanmiara commented 4 months ago

Hi! Thanks for the quick response! I tried to run the ExecStrings using ThreadPythonExec but it deadlocks on gilstate := PyGILState_Ensure(); in TPythonThread.Execute; and I'm not sure why.

My use case is to run some tk gui and at the same time run some Python code that uses a PyModule.

I see that in PyScripter you register PyModules on the Internal Interpreter, and I was able to register and run my module on the Internal Interpreter in PyScripter. I see that PyScripter doesn't run the Internal Interpreter code it a thread. Can you register and run MyModules also on the Remote Interpreter ?

I see that if I use remote interpreter then it runs the Python code in a thread (and doesn't go into a deadlock) but I couldn't figure out how to run another function at the same time from the PyScripter IDE (as the run button is grayed out until I close the tk window I ran) How can I run Python code in threads in PyScipter?

Thanks!

pyscripter commented 4 months ago

Did you follow the instructions?

Preparation When PythonEngine loads the python DLL, the main thread holds the GIL. Before you can run python code in threads you must release the GIL. TPythonThread has two class methods that allow you to release the GIL and then acquire back.

class procedure Py_Begin_Allow_Threads;
class procedure Py_End_Allow_Threads;

Py_Begin_Allow_Threads saves the thread state and releases the GIL, whilst Py_End_Allow_Threads tries to get hold of GIL and restore the previous thread state. You should only call Py_Begin_Allow_Threads if you alread hold the GIL. Otherwise a deadlock will occur.

You should call Py_Begin_Allow_Threads in your main thread, after the python DLL was loaded, for example in your FormCreate event handler. Also you can call Py_End_Allow_Threads when you are done executing python in threads.

idanmiara commented 4 months ago

Did you follow the instructions?

Oh, Thanks, I misread that section before. Now it works and I can open the code that opens a tk window multiple times and also run other functions. but for some reason if I open a tk window, close it and reopen it (run the code again) then it crashes. any pointers?