我有一个程序,目前正在转换一些大的UTF16字体文件(大小为兆字节),它们来自一组3。此程序获取这些字体文件,将其缩小为仅使用的字符,并保存所需的字符。程序本身工作正常,但加载过程需要大约25秒的线程和大约40-45秒无线程。我认为在那里有一个进度条来显示加载线程的进度是很好的。我找到了一个指南here,了解如何使用消息处理此问题(无法测试我的解决方案是否正常工作)。
我创建了一个带有三个进度条的新表单,并打算将其连接起来,以便他们在加载时报告进度。问题是,当我在窗体上调用ShowModal方法时,表单加载而没有控件(只是一个空白的白色窗口),加载,当它完成时,控件出现,我可以退出窗口和进程完了。
我的问题是,如何在开始加载之前首先显示所有控件?这是我不太确定如何问(因此解释和问题)和我的研究没有给出任何东西(我猜它是“你需要知道正确的单词”情景之一)。
我尝试过的事情:
我认为这个功能并不重要,但是30秒的阻止很长时间没有注意到某些事情正在发生。
答案 0 :(得分:-1)
我所做的,不需要线程,就是创建窗口DisableTaskWindows()
,用TForm.Repaint()
强制重绘,然后完成工作。像这样:
var
LWindowList: pointer;
LForm: TProgressForm;
begin
LForm := TProgressForm.Create(nil);
try
LWindowList := DisableTaskWindows(LForm.Handle);
try
LForm.Show;
LForm.Repaint;
// Do your work here.
// When updating your UI, be sure to call LForm.Repaint()
// or Application.ProcessMessages();
finally
EnableTaskWindows(LWindowList);
end;
finally
LForm.Free;
end;
end;
注意:这是直接输入答案的,可能无法编译。
答案 1 :(得分:-1)
您应该做的是将工作线程与UI表单分开。在TThread后代做你的工作。
两者之间的所有通信都应该通过发布异步消息来完成。工作线程应该在启动时向UI发布消息,报告进度以及何时结束。 UI可以使用它来可视化表单,更新进度条并最终关闭表单。如果你需要在UI上有一个用户取消按钮,这应该发送一条消息给它用来中止执行的工作线程。
通过"发布异步消息"我的意思是你要么使用RTL.Messaging之外的消息传递框架(它只处理同步消息),要么至少包含TThread.Queue中的调用。我使用我的消息类解释here。
为了处理这项工作,我自己使用这个基类:
TMEBatchOp = Class(TMELocalizableComponent)
Private
FMessageHandler: TMEMessageHandler;
FInfo: TMEBatchOpInfo;
FLastUpdateStatus: Cardinal;
FRequestedCancel: Boolean;
Procedure CheckCancelled;
Procedure Run;
Procedure SetResult(Const Value: TMEBatchOpResult);
Procedure SetStatus(Const Value: TMEBatchOpStatus);
Procedure UpdateStatus(Const DelaySecs: Integer = 0);
Protected
Function CalcWorkMax: Integer; Virtual;
Procedure DoExecute; Virtual; Abstract;
Procedure DoSetup; Virtual;
Procedure DoTeardown; Virtual;
Function GetAllowCancel: Boolean; Virtual;
Function GetDescription: String; Virtual;
Procedure ReceiveEnvelope(Const Envelope: TMEMessageEnvelope);
Property MessageHandler: TMEMessageHandler Read FMessageHandler;
Property Info: TMEBatchOpInfo Read FInfo;
Property RequestedCancel: Boolean Read FRequestedCancel;
Public
Class Procedure CaptureLocalizable(Localizer: TMELocalizer); Override;
Class Function GetModuleName: String;
Constructor Create(AOwner: TComponent); Override;
Procedure Worked(Qty: Integer = 1);
End;
特定工作线程的实现需要定义TMEBatchOp的子类并实现DoExecute方法。如果需要,还可以重新定义CalcWorkMax,DoSetup和DoTeardown。例如:
Type
TBatchOpSaveTexts = Class(TMEBatchOp)
Protected
Function CalcWorkMax: Integer; Override;
Procedure DoExecute; Override;
Function GetDescription: String; Override;
Public
Class Procedure CaptureLocalizable(Localizer: TMELocalizer); Override;
End;
执行此操作只需要这样的调用:
RunAndFree(TBatchOpSaveTexts);
RunAndFree的实现方式如下:
Function RunAndFree(BatchOpClass: TMEBatchOpClass): TMEBatchOpResult; Overload;
Begin
Result := RunAndFree(BatchOpClass.Create(Nil));
End;
Function RunAndFree(BatchOp: TMEBatchOp): TMEBatchOpResult; Overload;
Begin
Try
BatchOp.Run;
Result := BatchOp.Info.Result;
Finally
BatchOp.Free;
End;
End;
TMEBatchOpInfo包含有关工作结果和状态,时间和工作以及完成和进展的信息。它还包含有关是否可以取消操作的信息。所有消息都包含此信息的副本,因此UI可以相应地更新自身。这是它的界面:
TMEBatchOpInfo = Class(TMEPersistent)
Private
FID: String;
FParentID: String;
FDescription: String;
FTimeBegin: TDateTime;
FTimeEnd: TDateTime;
FResult: TMEBatchOpResult;
FWorkMax: Integer;
FWorkDone: Integer;
FAllowCancel: Boolean;
FStatus: TMEBatchOpStatus;
Function GetDone: Boolean;
Public
Constructor Create; Override;
Procedure Assign(Source: TPersistent); Override;
Property ID: String Read FID;
Property ParentID: String Read FID;
Property Description: String Read FDescription;
Property TimeBegin: TDateTime Read FTimeBegin;
Property TimeEnd: TDateTime Read FTimeEnd;
Property Result: TMEBatchOpResult Read FResult;
Property WorkMax: Integer Read FWorkMax;
Property WorkDone: Integer Read FWorkDone;
Property Done: Boolean Read GetDone;
Property AllowCancel: Boolean Read FAllowCancel;
Property Status: TMEBatchOpStatus Read FStatus;
End;