我有按钮点击的反应功能。当我点击按钮时,它应该开始重复并从数组中写入值并在主窗体上的标签中显示它们。问题是功能睡眠 - 有一些错误或什么的,因为当我点击按钮它等待很长时间然后它最终开始动作但很快。我们来看看我的代码。感谢建议。
procedure TForm1.ButtonMereniClick(Sender: TObject);
var
iterator: Integer;
begin
iterator := 1;
repeat
//write some values stored int arrays to labels on form
LabelTeplota.Caption:='Teplota: '+FloatToStr(poleTeplota[iterator]);
LabelVlhkost.Caption:='Vlhkost: '+FloatToStr(poleVlhkost[iterator]);
LabelTlak.Caption:='Atmosférický tlak: '+FloatToStr(poleTlak[iterator]);
LabelRychlost.Caption:='Rychlost větru: '+FloatToStr(poleRychlost[iterator]);
LabelRychlost.Caption:='Rychlost větru: '+FloatToStr(poleRychlost[iterator]);
LabelIterator.Caption:='iterator: '+IntToStr(iterator);
Sleep(500);//should be 5000
Inc(iterator);
until iterator = 20;
end;
答案 0 :(得分:19)
不要在GUI线程中使用Sleep
函数,因为您阻止了正常的消息处理,并且您的应用程序行为异常。
目前尚不清楚为什么在代码中使用Sleep
。您可能应该更新OnTimer
组件的TTimer
事件处理程序中的标签,而不是使用循环。
答案 1 :(得分:1)
我使用两个计时器“慢慢”更新一些值(以便用户可以看到进度),而不会减慢应用程序的速度,或者在最坏的情况下,系统。
// "weights" for old and new value; I was working on integers
var
Wa: integer = 1;
Wb: integer = 9;
Wt: integer = 10;
// interval of 1000 ms, of course
procedure frmMain.T1000Timer(Sender: TObject);
begin
Wa:= 1;
nValue:= calculate_new_value;
end;
// 100 ms interval
procedure frmMain.T100Timer(Sender: TObject);
begin
Wb:= Wt -Wa;
// displayed value, ie gauge.progress, or something.Value, etc.
sam.Value:= Round( (Wb *sam.Value +Wa *nValue) /Wt );
if Wa < Wt then Inc(Wa);
end;
答案 2 :(得分:0)
如果必须使用延迟或“睡眠”类型功能,则可以使用ProcessMessages的过程。使用它有一些优点和缺点,但我已经成功地在很多场合没有任何不良影响。我知道这里有其他人可以更好地评论ProcessMessages。
Procedure Delay(MSecs: Cardinal);
var
FirstTick, CurrentTick : Cardinal;
Done : Boolean;
begin
Done := FALSE;
FirstTick := GetTickCount;
While Not Done do
begin
Application.ProcessMessages;
CurrentTick := GetTickCount;
If Int64(CurrentTick) - Int64(FirstTick) < 0 Then
begin
If CurrentTick >= (Int64(FirstTick) - High(Cardinal) + MSecs) Then
Done := TRUE;
End
Else
If CurrentTick - FirstTick >= MSecs Then
Done := TRUE;
end;
end;
// Below for a service
procedure YourSvrSvc.ProcessMessages;
var
Msg: TMsg;
begin
if PeekMessage(Msg, 0, 0, 0, PM_REMOVE) then
begin
TranslateMessage(Msg);
DispatchMessage(Msg);
end;
end;
Procedure YOURSvrSvc.Delay(MSecs: Cardinal);
var
FirstTick, CurrentTick : Cardinal;
Done : Boolean;
begin
Done := FALSE;
FirstTick := GetTickCount;
While Not Done do
begin
YOURSvrSvc.ProcessMessages;
CurrentTick := GetTickCount;
If Int64(CurrentTick) - Int64(FirstTick) < 0 Then
begin
If CurrentTick >= (Int64(FirstTick) - High(Cardinal) + MSecs) Then
Done := TRUE;
End
Else
If CurrentTick - FirstTick >= MSecs Then
Done := TRUE;
end;
end;
答案 3 :(得分:0)
我很难说不使用Sleep
因为我自己一直使用它,但Application.ProcessMessages
实际上是一个危险的解决方案,尤其是在循环中使用时。我不确定你显示的是什么信息(因为我不认识这种语言),但看起来你正在做一些从Float到String的转换。虽然这些转换是在瞬间执行的,但将它们全部添加在一起,您可能会进行冗长的操作。并且假设您决定添加另一个要更新的值,这需要一些计算(例如文件传输中的每秒字节数)。这种转换会给这个操作增加一点时间,在你知道它之前,你可能会花费半秒钟的UI更新(这似乎不长,但是当涉及处理器使用时,这是相当的负载)。
因此,我建议使用Thread执行所有这些转换,计算等,并在信息发生变化时根据需要触发事件。现在,毫无疑问,线程肯定会比其他建议的解决方案复杂一点。但是使用线程也意味着很多好处。当您的应用程序仍然完美响应时,您可以在后台完成所有繁重的工作。请记住,使用线程可能非常棘手,特别是在UI更新时。
有几种方法可以制作一个帖子,但我会尝试使这个简单......
type
TMyThread = class;
TMyThreadEvent = procedure(Sender: TObject; const Val1, Val2: String) of object;
TMyThread = class(TThread)
private
FValue1: Integer;
FValue2: Integer;
FString1: String;
FString2: String;
FOnChange: TMyThreadEvent;
procedure SYNC_OnChange;
protected
procedure Execute; override;
public
constructor Create;
property Value1: Integer read FValue1 write FValue1;
property Value2: Integer read FValue2 write FValue1;
property String1: String read FString1;
property String2: String read FString2;
property OnChange: TMyThreadEvent read FOnChange write FOnChange;
end;
...
constructor TMyThread.Create;
begin
inherited Create(False);
FValue1 := '0';
FValue2 := '0';
end;
procedure TMyThread.Execute;
var
S1, S2: String;
DoChange: Bool;
begin
DoChange:= False;
FValue2:= DoSomeBigCalculation(FValue1); //Some random big calculation
S1:= FormatFloat('#,##0.#', FValue1);
S2:= FormatFloat('#,##0.#', FValue2);
if (S1 <> FString1) then begin
FString1:= S1;
DoChange:= True;
end;
if (S2 <> FString2) then begin
FString2:= S2;
DoChange:= True;
end;
if DoChange then
Synchronize(SYNC_OnChange);
end;
procedure TMyThread.SYNC_OnChange;
begin
if assigned(FOnChange) then
FOnChange(Self, FString1, FString2);
end;
现在要使用它,您可以根据需要设置Integer
属性。确保将OnChange
事件设置为包含上述TMyThreadEvent
类型参数的过程。只要任何值与其原始(或旧)值不同,它就会触发此事件。我还强烈建议您首先生成这些值的处理代码放在一个线程中。多线程的可能性是巨大的,并且在其中有很多进展的应用程序中证明是一个很大的优势。
请注意,我的上述代码只是直接输入本网站的样本,未经过测试。这只是为了让您了解如何实现一个线程来进行更新。