You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

解决Lazarus中Python脚本用户输入时应用冻结问题

解决Lazarus中PythonForLazarus input阻塞GUI的问题

核心原因

默认的TPythonGUIInputOutput.GetInput方法使用模态InputQuery对话框,会阻塞主线程(GUI线程),导致整个应用冻结,直到用户完成输入。

解决方案:自定义非模态输入处理 + 子线程执行Python脚本

步骤1:创建自定义非模态输入窗口

新建一个Form(命名为TInputForm),添加TEdit(输入内容)和TButton(确认输入)控件,再定义回调事件传递输入结果:

type
  TInputCompletedEvent = procedure(Sender: TObject; const InputText: string) of object;

  TInputForm = class(TForm)
    Edit1: TEdit;
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    FOnInputCompleted: TInputCompletedEvent;
  public
    property OnInputCompleted: TInputCompletedEvent read FOnInputCompleted write FOnInputCompleted;
  end;

实现按钮点击逻辑:

procedure TInputForm.Button1Click(Sender: TObject);
begin
  if Assigned(FOnInputCompleted) then
    FOnInputCompleted(Self, Edit1.Text);
end;

步骤2:继承扩展TPythonGUIInputOutput

自定义输入输出类,重写GetInput方法,替换模态对话框为非模态输入窗口:

type
  TCustomPythonGUIInputOutput = class(TPythonGUIInputOutput)
  protected
    function GetInput(const Prompt: string): string; override;
  end;

function TCustomPythonGUIInputOutput.GetInput(const Prompt: string): string;
var
  InputForm: TInputForm;
  InputEvent: TEvent;
begin
  Result := '';
  InputEvent := TEvent.Create(nil, True, False, '');
  try
    // 主线程创建并显示非模态窗口
    TThread.Synchronize(nil, procedure
      begin
        InputForm := TInputForm.Create(nil);
        InputForm.Caption := Prompt;
        InputForm.OnInputCompleted := procedure(Sender: TObject; const InputText: string)
          begin
            Result := InputText;
            InputEvent.SetEvent; // 触发事件通知输入完成
          end;
        InputForm.Show;
      end);

    // 子线程等待输入,主线程不受阻塞
    InputEvent.WaitFor(INFINITE);
  finally
    InputEvent.Free;
    // 主线程释放窗口资源
    TThread.Synchronize(nil, procedure
      begin
        InputForm.Free;
      end);
  end;
end;

步骤3:子线程执行Python脚本

创建线程类运行Python代码,避免阻塞主线程:

type
  TPythonRunnerThread = class(TThread)
  private
    FPythonEngine: TPythonEngine;
    FScript: string;
    procedure UpdateMemo(const Text: string); // 同步更新GUI
  protected
    procedure Execute; override;
  public
    constructor Create(APythonEngine: TPythonEngine; const AScript: string);
  end;

constructor TPythonRunnerThread.Create(APythonEngine: TPythonEngine; const AScript: string);
begin
  inherited Create(True);
  FPythonEngine := APythonEngine;
  FScript := AScript;
  FreeOnTerminate := True;
end;

procedure TPythonRunnerThread.UpdateMemo(const Text: string);
begin
  // 根据实际Memo控件修改(示例为Form1.Memo1)
  Form1.Memo1.Lines.Add(Text);
end;

procedure TPythonRunnerThread.Execute;
begin
  try
    FPythonEngine.Acquire; // 保证线程安全
    try
      FPythonEngine.ExecString(FScript); // 执行脚本
    finally
      FPythonEngine.Release;
    end;
  except
    on E: Exception do
      TThread.Synchronize(nil, procedure
        begin
          UpdateMemo('执行错误:' + E.Message);
        end);
  end;
end;

步骤4:配置组件并启动线程

在主Form中:

  1. 替换原TPythonGUIInputOutput为自定义的TCustomPythonGUIInputOutput组件。
  2. 将自定义组件的Output关联到你的Memo控件。
  3. 绑定TPythonEngine.IO到自定义输入输出组件。
  4. 通过按钮触发线程执行脚本:
procedure TForm1.ButtonRunScriptClick(Sender: TObject);
var
  RunnerThread: TPythonRunnerThread;
  Script: string;
begin
  Script := 'name = input("请输入你的名字:")'#10'print("你好," + name)';
  RunnerThread := TPythonRunnerThread.Create(PythonEngine1, Script);
  RunnerThread.Start;
end;

关键注意事项

  • 线程安全:操作TPythonEngine必须调用Acquire和Release,避免多线程冲突。
  • GUI更新:所有对GUI控件的操作必须通过TThread.Synchronize或TThread.Queue在主线程执行,防止跨线程访问错误。
  • 资源释放:确保输入窗口和线程资源被正确释放,避免内存泄漏。

内容的提问来源于stack exchange,提问作者Francisco Assis

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 12:26:16