2017-04-05 22 views
0

我有一個程序,目前正在轉換一些大的UTF16字體文件(大小爲兆字節),其中3來一個集。這個程序使用這些字體文件,將其縮減爲僅使用的字符,並保存所需的字符。程序本身工作正常,但加載過程需要大約25秒的時間線程,大約40-45秒沒有線程化。我認爲在那裏有一個進度條來顯示加載線程的進度會很好。我發現一個指導here如何處理這個消息(還沒有能夠測試我的解決方案是否工作)。德爾福窗體顯示進度條,同時加載線程中的東西

我做了一個帶有三個進度條的新窗體,並且將它連接起來,以便在加載時報告它們的進度。問題是,當我在表單上調用ShowModal方法時,表單加載時沒有控件(只是一個空白的白色窗口),加載是否完成,控件是否顯示,我可以退出窗口和進程完成。

我現在的問題是,如何讓所有控件在開始加載前先出現?這是我不太確定如何問(因此解釋和問題),我的研究沒有給出任何東西(我猜這是「你需要知道正確的話」場景之一)。

事情我已經嘗試:

  • 將代碼放在將要-RAN在OnShow中和的OnCreate事件。沒有運氣。
  • 人們說OnActivate是我應該使用的事件,但是使用該事件給出了相同的問題。
  • 試圖幹掉事件。稱爲Show(),然後是我的Load()函數,最後是Close()。沒有運氣。
  • 只顯示()窗體,手動執行邏輯,然後關閉()。
  • 在這個網站上徹底查看
  • 檢查是否Embarcadero提供了這樣的東西,我已經不知道(如果有我還沒有意識到)。

我想這個功能並不重要,但是30秒的時間段很長一段時間沒有發現任何事情正在發生。

+1

不要做任何顯示之前的形式,全面,你不會有問題,將在主線程阻塞。 –

+1

通常的解決方案是在OnShow的末尾發佈自定義消息給自己,然後在該消息的處理程序中開始處理。這允許首先處理所有其他消息。 –

回答

-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

您應該做的是將工作線程從UI表單中分離出來。在TThread後裔中做你的工作。

兩者之間的所有通信都應通過發佈異步消息來完成。工作線程應該在啓動時向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; 
+0

只是爲了看看我是否完全理解,我打開表單,表單產生一個線程,它將阻塞,直到它從窗體收到一條消息開始。當線程接收到綠燈時,它開始執行它的操作並將更新消息發送到窗體以進行更新,然後最終讓窗體知道它已完成? – Zulukas

+0

它可以工作,但我做的稍微有點不同。我產生了一個線程,它發送一條消息,說它正在啓動。我的主窗體收到消息並彈出一個表單,其中包含進度條,任務描述和工作統計信息。線程發送帶有進度並最終結束作業的消息。進度彈出窗口消耗這些消息來更新UI並最終在任務完成時關閉。 – Frazz