Jump to content
iqrf

TPythonThread

Recommended Posts

Hi,

According to Demo33, we edited Demo01 to run the script in a thread using the Start and Stop buttons.. PythonGUIInputOutput1 replaced PythonInputOutput1.

unit Unit1;

interface

uses
  Classes, SysUtils,
  Windows, Messages, Graphics, Controls, Forms, Dialogs,
  StdCtrls, ComCtrls, ExtCtrls, System.Math,System.SyncObjs,
  PythonEngine, Vcl.PythonGUIInputOutput;

type
  TPyThread = class(TPythonThread)
  protected
    procedure ExecuteWithPython; override;
  end;

  TForm1 = class(TForm)
    PythonEngine1: TPythonEngine;
    Memo1: TMemo;
    Panel1: TPanel;
    Button1: TButton;
    Splitter1: TSplitter;
    Button2: TButton;
    Button3: TButton;
    OpenDialog1: TOpenDialog;
    SaveDialog1: TSaveDialog;
    PythonGUIInputOutput1: TPythonGUIInputOutput;
    Memo2: TMemo;
    PythonInputOutput1: TPythonInputOutput;
    Button4: TButton;
    Button5: TButton;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure Button3Click(Sender: TObject);
    procedure PythonInputOutput1SendUniData(Sender: TObject;
      const Data: string);
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure Button4Click(Sender: TObject);
    procedure Button5Click(Sender: TObject);
  private
    FCriticalSection : TCriticalSection;
    PyThread: TPyThread;
    procedure ThreadDone(Sender: TObject);
    { Déclarations privées }
  public
    { Déclarations publiques }
  end;


var
  Form1: TForm1;

implementation

{$R *.DFM}

procedure TPyThread.ExecuteWithPython;
begin
  inherited;

  Form1.PythonEngine1.ExecString(UTF8Encode(Form1.Memo1.Text));
end;

procedure TForm1.ThreadDone(Sender: TObject);
begin
  TPythonThread.Py_End_Allow_Threads;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  s: String;
begin
  MaskFPUExceptions(True);
  PythonEngine1.ExecStrings(Memo1.Lines);
 end;

procedure TForm1.Button2Click(Sender: TObject);
begin
  with OpenDialog1 do
    begin
      if Execute then
        Memo1.Lines.LoadFromFile( FileName );
    end;
end;

procedure TForm1.Button3Click(Sender: TObject);
begin
  with SaveDialog1 do
    begin
      if Execute then
        Memo1.Lines.SaveToFile( FileName );
    end;
end;

procedure TForm1.Button4Click(Sender: TObject);
begin
  TPythonThread.Py_Begin_Allow_Threads;
  PyThread := TPyThread.Create(True);
  PyThread.FreeOnTerminate := True;
  PyThread.OnTerminate := ThreadDone;
  PyThread.Start;
end;

procedure TForm1.Button5Click(Sender: TObject);
begin
  if Assigned(PyThread) and (not PyThread.Finished) then begin
    GetPythonEngine.PyEval_AcquireThread(PyThread.ThreadState);
    GetPythonEngine.PyErr_SetString(GetPythonEngine.PyExc_KeyboardInterrupt^, 'Terminated');
    GetPythonEngine.PyEval_ReleaseThread(PyThread.ThreadState);

    while not PyThread.Finished do application.ProcessMessages;
  end;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  FCriticalSection := TCriticalSection.Create;
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  if Assigned(PyThread) and (not PyThread.Finished) then Button5Click(Sender);

  FCriticalSection.Free;
end;

procedure TForm1.PythonInputOutput1SendUniData(Sender: TObject;
  const Data: string);
begin
  fCriticalSection.Enter;
  try
  TThread.ForceQueue(nil, procedure
    begin
      memo2.Lines.Add(data);
    end);
  finally
    fCriticalSection.Leave;
  end;

  Sleep(1);
end;

end.

If I run this script, everything is OK

from datetime import datetime
import time

while True:
   time.sleep(0.1)
   print(datetime.now())
2023-04-24 12:24:21.726342
2023-04-24 12:24:21.834341
2023-04-24 12:24:21.943236
2023-04-24 12:24:22.051522
Traceback (most recent call last):
  File "<string>", line 8, in <module>
KeyboardInterrupt: Terminated

If this one

from datetime import datetime
import time

while True:
   print(datetime.now())

so i get an error

2023-04-24 12:44:57.749385
2023-04-24 12:44:57.764722
2023-04-24 12:44:57.780558
2023-04-24 12:44:57.796288
2023-04-24 12:44:57.811762
KeyboardInterrupt: Terminated

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "<string>", line 5, in <module>
SystemError: <function DebugOutput.write at 0x03FF6758> returned a result with an exception set

Please how to get rid of this error.

Thank you

 

 

Edited by iqrf

Share this post


Link to post

I modified as per the SimpleDemo but I still get the same error when exiting the thread 

Quote
procedure TForm1.PythonInputOutput1SendUniData(Sender: TObject;
  const Data: string);
var
  ScheduleWrite: Boolean;
begin
  if Data = '' then Exit;

  fCriticalSection.Enter;
  try
    ScheduleWrite := FOutputStream.Size = 0;
    FOutputStream.Write(Data[1], Length (Data) * 2);

    if ScheduleWrite then

    TThread.ForceQueue(nil, procedure
      var
        WS: string;
      begin
        fCriticalSection.Enter;
        try
          if fOutputStream.Size > 0 then begin
            SetLength(WS, fOutputStream.Size div 2);
            fOutputStream.Position := 0;
            fOutputStream.Read(WS[1], Length(WS) * 2);
            fOutputStream.Size := 0;
            if (Memo2.Lines.Count > 0) and (Memo2.Lines[Memo2.Lines.Count -1] <> '') then
            Memo2.Lines.BeginUpdate;
            try
              Memo2.Lines.Add('');
              Memo2.Text := Memo2.Text + WS;
            //  Memo2.Lines.GoToTextEnd;
            finally
              Memo2.Lines.EndUpdate;
            end;
          end;
        finally
          fCriticalSection.Leave;
        end;
      end);
  finally
    fCriticalSection.Leave;
  end;

  Sleep(1);
end;
2023-04-24 18:00:05.560557

2023-04-24 18:00:05.575824

KeyboardInterrupt: Terminated

The above exception was the direct cause of the following exception:

Traceback (most recent call last):

  File "<string>", line 8, in <module>

SystemError: <function DebugOutput.write at 0x02C56758> returned a result with an exception set

 

Share this post


Link to post
2 hours ago, iqrf said:

when exiting the thread

How are you exiting the thread?  Are you raising a KeyboardInterrupt as in demo 33?   Then why are you surprised that an exception is raised, when the print statement executes.  You can modify your script as follows (not tested):

 

while True:
    try:
        time.sleep(0.1)
        print(datetime.now())
    except KeyboardInterrupt:
        break

or handle the exception in your Delphi code

Edited by pyscripter

Share this post


Link to post

Yes, I end with

    GetPythonEngine.PyEval_AcquireThread(PyThread.ThreadState);
    GetPythonEngine.PyErr_SetString(GetPythonEngine.PyExc_KeyboardInterrupt^, 'Terminated');
    GetPythonEngine.PyEval_ReleaseThread(PyThread.ThreadState);

I am having trouble terminating a thread using PyThread.Terminate. It doesn't work in the Demo33 example either. Why?

I don't mean the KeyboardInterrupt: Terminated exception, that's ok.

My point is that in the case of this script, where there is no wait time, another exception will be thrown

while True:
    print(datetime.now())
SystemError: <function DebugOutput.write at 0x02C56758> returned a result with an exception set
  
---------------------------
Debugger Exception Notification
---------------------------
Project Demo01.exe raised exception class EPySystemError with message 'SystemError: <function DebugOutput.write at 0x02C56758> returned a result with an exception set'.
---------------------------
Break   Continue   Help   Copy   
---------------------------

It looks like the python engine is trying to write to DebugOutput at the moment when the thread is already canceled. If there is a wait in the script, the error will not occur.

Thanks a lot for your help.

Edited by iqrf

Share this post


Link to post

The KeyboardInterrupt is the correct way to terminate a thread from the main thread.

 

47 minutes ago, iqrf said:

<function DebugOutput.write at 0x02C56758> returned a result with an exception set'

This is because print statement executes without clearing the KeyboardInterrupt exception.  You need to trap the KeyboardInterrupt exception either in Python or Delphi code.

 

You can also add to your PythonInputOutput1SendUniData

with GetPythonEngine do
  if PyErr_Occurred <> nil then begin
    PyErr_Clear;
    Exit;
  end;

 

Edited by pyscripter

Share this post


Link to post

Unfortunately SystemError: <DebugOutput.write function
happens even if I catch the KeyboardInterrupt in a python script or in Delphi. I am attaching a test application.
If I insert a sleep mode (0,1) before printing, the error does not appear.
If I put sleep(0.08) sometimes it throws SystemError: <function DebugOutput.write sometimes
SystemError: <builtin sleep function> returned a result with an exception set
The value of sleep when the error does not occur will depend on the speed of the computer.
Thanks

Demo01.zip

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×