我正在使用Delphi 7,我正在编写的程序需要不断在屏幕上绘图。虽然它目前没有任何重要的东西,但这在以后的程序中是必需的。但是,当我将屏幕绘制的过程放在while循环中时,只能通过按任何按钮来停止,程序会完全停止响应。我不明白为什么会这样。当然,由于while循环可以退出,程序应该继续正常运行。 这是源代码:
clojure.core.async/alts!
答案 0 :(得分:2)
procedure TForm1.OnCreate(Sender: TObject);
begin
IsDone := False;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
Image1OnCreate();
Button1.Visible := False;
While(IsDone = False) do
begin
ScreenRender();
end;
end;
假设IsDone
始终为False
(因为否则我们不会进入循环),此循环无法终止。这是无限的。
procedure TForm1.FormKeyDown(Sender: TObject; var Key: Word;
Shift: TShiftState);
begin
IsDone := True;
end;
您不从TForm1.Button1Click
循环内部调用此过程,因此在您进入该循环后永远不会调用它。由于您永远不会退出TForm1.Button1Click
过程,因此您不允许任何外部代理(如VCL中的消息调度循环)执行并调用该过程。要在进入循环后对其进行总结,就不会有任何可以更改IsDone
值的可执行代码。所以,它没有改变。
事件处理程序应该是非常短的程序,几乎立即执行,并放弃执行流程控制"回到VCL内部。每一次漫长(更无限)的处理都会导致程序变得无法响应。无论Windows可能想要告诉程序多少新闻 - 程序都不会要求它们。
曾经有人告诉过,Windows窗口(GDI对象)生活在"消息风暴的中心。他们必须及时解决问题。每秒都有数百条消息传入,并且一个Window Procedure(在Delphi 7表单的VCL类中构建)应该在它们为时已晚之前接收,发送和处理它们。
一旦你通过使一个事件处理程序长期甚至无休止地阻止了这个过程 - 你就破坏了操作系统和应用程序之间的基本合同。
你必须做"反转控制",将你的连续工作分成小的短块,并在适当的时候让Windows调用那些块。
例如,尝试使用TTimer
。
PS。你可以看一个非常遥远的问题:
在那里略过所有多线程的东西,对于你的情况,只有其他线程创建那些"大块的工作"当Windows要求我们以合理的帧速率(不太快且不太慢)时,我们必须在我们的表单上绘制 。你的工作块根本不同,所以所有的线程都与你无关。
渲染是在TTimer
个事件中进行的。所以"框架"设置计时器,打开和关闭它可能会引起你的兴趣。但是,您在.OnTimer
事件中要做的工作会有很大的不同(只是绘制一些内容,甚至只是invalidating
表单的某些部分并等待Windows触发OnPaint
事件)。
答案 1 :(得分:2)
你已经得到了一个很好的答案,为什么你当前的代码不起作用,在你的评论中你提到你想从玩家的角度进行射线投射和绘图,所以我假设某种游戏背景。
我不确定VCL是游戏的最佳基础。不同的哲学和需求。作为Arioch'解释说Delphi的VCL是事件驱动的。事情发生在窗户消息的响应中,甚至是绘画。如果没有任何东西需要重新绘制,则不会重新绘制任何内容。
这与我理解游戏引擎的方式非常不同(我绝不是专家)。即使没有任何反复发生,它们也会不断地逐帧绘制,以尽可能呈现流畅。每个帧可能包括基于游戏规则,物理,玩家输入,动画的基础结构的更新,但即使它们保持不变,也将绘制新的帧。基本上在简化的“游戏循环”中会发生三个步骤
所有这一切都发生在每一帧。可能没有输入,没有游戏结构的更新,甚至不需要演示。但是所有三个步骤都属于一起,导致稍后呈现的更新的输入发生在与结果图形完全相同的帧中。
这是我觉得很难融入VCL的东西。作为解决方案必须基于现有的VCL循环和Windows消息。你基本上试图在VCL中创建这样的游戏循环。
解决您的直接问题的方法 - 您希望基于计算呈现某些内容 - 可能只是使用VCL的原则。你想要绘制一些东西。 VCL控件通常表达他们希望由Invalidate
绘制的愿望,导致他们的BoundsRect
无效。完成计算后,您可以这样做。在下面的例子中,我将使用一个计时器来模拟你的计算完成。请注意,Invalidate
将导致为控件生成WM_PAINT
消息,但不会立即重新绘制。在处理WM_PAINT
之前,可能会有消息排队。
我正在使用TPaintBox
的OnPaint
来实际进行绘画工作,您可能希望将来在项目进展时拥有自己的控制权。
unit Unit2;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ExtCtrls;
type
TFormMain = class(TForm)
procedure FormCreate(Sender: TObject);
private
Timer1: TTimer;
PaintBox1: TPaintBox;
{ Private declarations }
procedure PaintBox1Paint(Sender: TObject);
procedure Timer1Timer(Sender: TObject);
public
{ Public declarations }
end;
implementation
{$R *.dfm}
procedure TFormMain.FormCreate(Sender: TObject);
begin
PaintBox1 := TPaintBox.Create(Self);
PaintBox1.Parent := Self;
PaintBox1.Align := alClient;
PaintBox1.OnPaint := PaintBox1Paint;
Timer1 := TTimer.Create(Self);
Timer1.Interval := 100;
Timer1.OnTimer := Timer1Timer;
Randomize;
end;
procedure TFormMain.PaintBox1Paint(Sender: TObject);
var
AColor: TColor;
I: Integer;
begin
for I := 0 to PaintBox1.ClientWidth - 1 do
begin
AColor := RGB(Random(256), Random(256), Random(256));
PaintBox1.Canvas.Pen.Color := AColor;
PaintBox1.Canvas.MoveTo(I, 0);
PaintBox1.Canvas.LineTo(I, PaintBox1.ClientHeight);
end;
end;
procedure TFormMain.Timer1Timer(Sender: TObject);
begin
PaintBox1.Invalidate;
end;
end.