解决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中:
- 替换原
TPythonGUIInputOutput为自定义的TCustomPythonGUIInputOutput组件。 - 将自定义组件的
Output关联到你的Memo控件。 - 绑定
TPythonEngine.IO到自定义输入输出组件。 - 通过按钮触发线程执行脚本:
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
相关产品推荐
相关产品推荐

