当我运行此线程时,未检测到VK_ESCAPE键,但如果在主线程中执行类似的代码,则检测到密钥。如何检测线程中的按键?
type
{ A TThread descendent for Saving Pictures }
TFileSavingThread = class(TThread)
private
{ Private declarations }
procedure ImageEnProcFinishWork(Sender: TObject);
procedure ImageEnProcProgress(Sender: TObject; per: integer);
protected
{ Protected declarations }
procedure Execute; override;
public
{ Public declarations }
constructor Create(CreateSuspended: Boolean);
end;
Form1.KeyPreview = true;
procedure TForm1.FormKeyDown(Sender: TObject; var Key: Word;
Shift: TShiftState);
begin
if Key = VK_ESCAPE then
ACancel := True;
end;
procedure TFileSavingThread.Execute;
var
iImageEnIO: TImageEnIO;
iFolder: string;
iFilename: string;
iNode: TTreeNode;
begin
inherited;
{ Get the Folder }
if not Terminated then
begin
Synchronize(
procedure
begin
iFolder := Form1.Folder1.Text;
end);
end;
iImageEnIO := TImageEnIO.Create(nil);
try
iImageEnIO.OnProgress := ImageEnProcProgress;
iImageEnIO.OnFinishWork := ImageEnProcFinishWork;
{ Save the Pictures }
if not Terminated then
begin
Synchronize(
procedure
var
i: integer;
begin
for i := 1 to Form1.TreeView1.Items.Count - 1 do
begin
Form1.LabelProgress1.Caption := 'Saving image ' + IntToStr(i) +
' of ' + IntToStr(Form1.TreeView1.Items.Count) +
' Press ESC to cancel.';
Form1.LabelProgress1.Update;
{ Get the image from the camera }
iNode := Form1.TreeView1.Items[i];
iImageEnIO.WIAParams.ProcessingBitmap := iImageEnIO.IEBitmap;
iImageEnIO.WIAParams.Transfer(TIEWiaItem(iNode.Data), False);
{ Get the filename }
iFilename := iNode.Text + '.jpg';
iImageEnIO.SaveToFile(IncludeTrailingPathDelimiter(iFolder) +
iFilename);
if Form1.ACancel then
Terminate;
end;
end);
end;
finally
iImageEnIO.Free;
end;
end;
修改
我理解为了正确编写这个线程,其中耗时的部分应该在线程本身而不是在Syncronize中。真正的问题是如何将此代码放在Syncronize循环之外的线程中?
iImageEnIO.WIAParams.ProcessingBitmap := iImageEnIO.IEBitmap;
iImageEnIO.WIAParams.Transfer(TIEWiaItem(iNode.Data), False);
{ Get the filename }
iFilename := iNode.Text + '.jpg';
iImageEnIO.SaveToFile(IncludeTrailingPathDelimiter(iFolder) + iFilename);
也许将图像存储在TBitmap数组中然后访问线程中的循环中的位图以将位图保存到磁盘?
我认为我需要做的是在Syncronize中获取文件夹,文件名和位图,但将位图保存在线程本身中。我只是看不出怎么做,因为有一个循环来获取图像,并且将图像保存到磁盘的调用也需要在循环中?
答案 0 :(得分:1)
您正在UI线程上运行代码。这就是Synchronize
的作用。因此,由于UI线程繁忙,因此无法抽取消息队列。因此,您的排队输入事件不会被处理。
基本上你的线程编码不正确。由于所有工作都同步到UI线程,因此您对线程的使用会给您带来复杂性,而不会带来任何好处。
如果要将长时间运行的任务放在后台线程上,则需要在该线程上执行该工作。您需要在后台线程上完成所有耗时的工作,但只将UI进度更新放到主线程上。
您可以通过在线程上运行循环和处理图像来完成此操作。只有当您想要显示UI时才使用Synchronize
。
您的ACancel
变量命名错误。将该前缀用于参数。这是一个字段,应该是FCancel
。
那就是说,这个领域毫无意义。您应该删除它并使用内置终止机制。在线程上调用Terminate
。在线程Execute
方法中,像你一样调用Terminate
是没有意义的。当您需要退出时,只需退出该方法即可。而且您知道何时退出,因为Terminated
属性为True
。
答案 1 :(得分:1)
我正在添加第二个答案来解决你的第二个问题。这些应该是两个独立的问题。您有以下情形:
解决这个问题需要做的是解除执行任务所需信息的收集,以及实际执行这些任务。因此,像这样构造代码:
type
TTask = record
FileName: string;
// other information specifying task
end;
procedure TMyForm.SaveButtonClick(Sender: TObject);
var
i: Integer;
Tasks: TArray<TTask>;
begin
SetLength(Tasks, TreeView1.Items.Count);
for i := 0 to high(Tasks) do
Tasks[i] := GetTask(i);//you need to write GetTask
end;
FSaveThread := TSaveThread.Create(Tasks);
end;
您的主题可能如下所示:
type
TSaveThread = class(TThread)
private
FTasks: TArray<TTask>;
protected
procedure Execute; override;
public
constructor Create(const Tasks: TArray<TTask>);
end;
constructor TSaveThread.Create(const Tasks: TArray<TTask>);
begin
inherited Create(False);
FTasks := Tasks;
end;
procedure TSaveThread.Execute;
var
i: Integer;
begin
for i := 0 to high(FTasks) do
begin
if Terminated then
exit;
ProcessTask(FTasks[i]);//again, you need to write this
Sychronize(UpdateUI);
end;
end;
重点是您收集预先执行操作所需的信息。这涉及访问UI,可以在事件处理程序或从它调用的方法中进行。然后,一旦收集了所有信息,就将其传递给线程,该线程可以集中精力完成工作,而不必担心GUI的进一步访问。是的,你会想要进行进度报告,但这很快捷,而且可以在不阻止用户界面的情况下轻松完成。