1 {
2 When writing a program where the processing takes a long time to
3 complete, it is useful to provide the user with an indication of
4 progress and allow him to interrupt the processing. Examples of
5 this are programs which perform long calculations or search
6 a large database. This tip shows one way of achieving this using
7 Windows messages and the application idle event. To use this
8 approach, it must be possible to break down the processing into
9 smaller chunks (for example, searching 500 records at a time).
10
11 The code below shows how this was done for an application which did
12 a very large number of calculations (about a billion). The code has
13 been simplified to make the principle clearer.
14
15 - A user message identifier is required.
16 }
17 const
18 kTestRH = WM_USER + 9;
19 {
20 - The form included the following controls and event handlers.
21 }
22 type TzfTest = class (TForm)
23 ...
24 zbTRH : TButton; // Start button.
25 zbC : TButton; // Cancel button.
26 zntSR : TEdit; // Progress text box.
27 zaeE : TApplicationEvents;
28 ...
29 // Application idle event.
30 procedure zaeE_I ( Sender : TObject;
31 var Done : boolean);
32 // Start button click event.
33 procedure zbTRH_K ( Sender : TObject);
34 // Cancel button click event.
35 procedure zbC_K ( Sender : TObject);
36 ...
37 {
38 - The form also included the following methods and variables.
39 }
40 private
41 iSR : integer; // Starting number.
42 iER : integer; // Ending number.
43 iLim : integer; // Number of calculations in a chunk.
44 iAct : boolean; // Processing active flag.
45 ...
46 procedure mhProcRH (var pMsg : TMessage); message kTestRH;
47 {
48 - The method which starts the processing sets up the variables
49 and posts a user-defined Windows message. This is done in
50 the Start button's click event.
51 }
52 procedure TzfTest.zbTRH_K (Sender : TObject);
53 begin
54 iSR := -500000000; // Starting value.
55 iER := 500000000; // Ending value.
56 iLim := 10000; // Number of calculations in a "chunk".
57 iAct := true; // Set the "active" flag.
58 PostMessage (Handle,
59 kTestRH, // User-defined message.
60 0,
61 0)
62 end;
63 {
64 - The message handling method performs a chunk of processing, then
65 updates the progress indicator and tests whether to end.
66 }
67 procedure TzfTest.mhProcRH (var pMsg : TMessage);
68 var
69 wCount : integer;
70 begin
71 if iAct then
72 begin
73 wCount := 0;
74 while (wCount < iLim) and (iSR <= iER) do
75 begin
76 ... // Perform one calculation.
77 Inc (wCount);
78 Inc (iSR)
79 end;
80 zntSR.Text := IntToStr (iSR); // Update the progress indicator.
81 if iSR >= iER then // If finished, clear the flag.
82 iAct := false
83 end
84 end;
85 {
86 - Once all the Windows and VCL activity to update the progress indicator
87 has completed, the application idle event is called. This checks
88 whether processing is complete, and posts another message if not.
89 }
90 procedure TzfTest.zaeE_I ( Sender : TObject;
91 var Done : boolean);
92 begin
93 if iAct then // If more processing required ...
94 PostMessage (Handle, // ... request another chunk.
95 kTestRH,
96 0,
97 0)
98 end;
99 {
100 - The Cancel button allows the user to interrupt the processing. This
101 has the following click event method. If the user clicks the button
102 then the "active" flag is reset, so the idle event will no longer post
103 messages to the message processing event.
104 }
105 procedure TzfTest.zbC_K (Sender : TObject);
106 begin
107 iAct := false
108 end;
109 {
110 This approach could be enhanced as follows:
111 - Change the cursor (to crAppStart, perhaps) while processing is active.
112 - Disable the Start button (and other controls on the form) while processing
113 is active.
114 - Instead of using a Cancel button, have a key press event on the form
115 so that the user can "Press Esc to cancel".
116 - Make the number of calculations in a chunk variable by the user.
117 - Have the program determine the number of calculations dynamically by
118 setting an initial value, measuring how long the first chunk takes, and
119 then adjusting the value so that subsequent chunks takes a fixed time.
120 - If the long-running process is searching a file or a database, then the
121 start and end values (iSR and iER) are not needed. You would need some
122 other way of indicating progress (perhaps the key of the last record
123 examined), and also code at the end to clean up (close the file or the
124 query cursor).
125 }
|