摘要 图形应用的开发是Window下大多数应用程序开发的重要内容,而多线程的图形显示(多区域的图形同时显示)是应用开发中的难点,本文将就这一难点的解决提供方法,以期达到抛砖引玉的目的,方便大家的应用开发。
关键词 多线程 图形显示 同步效果 delphi
一、引言
图形操作在各种应用系统中均有所涉及,好的图形效果可以给用户留下较为深刻的印象,在各种显示领域的系统开发中均要求对图形作出各种各样的显示效果,有的还要求能多个区域同时显示不同的图形和效果,但由于一般开发采用计时器作为控制作图的控件,多个区域进行效果时,不能实现真正意义上的同步,会产生停顿和相互间影响等问题,而采用多线程技术则能很好的解决以上的问题。本文就将对实现多线程的图形操作进行论述。
二、多线程及设计思路
要说明多线程的问题,首先必须要理解什么叫“线程”。通俗地理解,线程是独立执行以完成某种任务的一段程序,一个线程可以和其它线程并行地执行不同的任务,但又彼此配合以协同完成一个复杂的任务。线程是系统中任务运行的最小时间划分单位。操作系统将CPU的时间分成很短的片段分配给每个线程。系统不停的在各个线程间进行切换。由于系统为每个线程所划分的时间片段都很小,所以看上去好象是多个线程同时在运行。在程序中实现多线程,就指一个应用程序内部实现的多任务操作。在本文中指同一时间进行多个图形的同步效果显示。Win32等操作系统中就提供了多线程的能力。采用多线程技术就能使不同区域的多个图形同时进行显示,每个图形操作相对应一个线程。
在delphi中,有关线程的API封装到一个叫Tthread的Object Pascal的对象中。Ttread是直接从Tobject继承下来的,因此,它不是元件。同时,由于Execute()是抽象的,我们不能创建Tthread实例,而只能创建Tthread派生类的实例。
线程对象的定义如下所示:
Tpye
TMyThread=class(Tthread)
Private
{private declarations}
protected
procedure Execute;override;
end;
在Tthread的派生类中唯一要重载的方法就是Execute()。所有图形的操作应该在Execute()方法中设置。但要使上述代码能够执行,还需要调用线程对象的Create(),创建线程执行线程代码。这个比较简单。
Procedure Tform1.Button1Click(Sender:Tobject)
Var
NewThread:TMyThread;
Begin
NewThread:=TMyThread.Create(False);
或 NewThread:=TMyThread.Create(F1,(F2…));
End;
当一个线程对象的Execute ()执行完毕,这个线程就算终止了。此时,会调用Delphi的一个标准的例程 EndThread()。进而调用一个叫ExitThread()的API函数。ExitThread()的作用是清除线程所占用的栈。此外,如果不需要这个对象,也要把这个对象从内存中清除。尽管当进程终止时会自动清除所有的线程对象,最为方便的方法就是将它的FreeOnTerminate特性设为 True,而且,只要在Execute()退出前设置就行了。
以上简述了Delphi 中线程的基本实现,包括线程的创建、实现和终止。对图形操作还有一个问题,在Delphi中设计VCL的时候,只考虑了同一时刻只有一个线程来访问它,因此,多个线程同时访问VCL是不允许的。但访问Tcanvas却是个例外,它允许多个线程同时访问,这由于Tcanvas中新增加的Lock()和 UnLock()方法。Lock()方法类似于线程中的阻塞,当一个线程调用了Lock()后,该线程可以独占图形对象,而不让其他线程访问图形对象,而调用UnLock()后,即释放了阻塞。有关阻塞等线程其他的内容,限于篇幅,不再做过多说明。在Win98下,可不用调用Lock()和Unlock ()方法。
三、程序实现及说明
下面的程序主要实现了在Form的4个区域同步显示图形,图形显示效果列举了几种,以供大家参考,大家也可以自行添加其他的效果。另外,程序兼容了Bmp和Jpg两中图形文件的显示。稍加修改,就可实现多区域播放列表的显示。
首先,建立一个Form,添加入两个按钮,按钮功能分为开始演示和暂停演示。接着,添加入4个Timer 控件,分别控制4个播放区域的图形显示。整体界面完成后,就可以具体编程工作了。
1.线程对象的定义:
tdrawthread=class(tthread)
private
pi,pj:Tpoint; //显示区域起始点和结束点坐标
sTranpath:string; //待显示图片的路径
iTranTime:integer; //图形停留显示的时间
iTranMode:integer; //图形显示的方式
iTranIndex:integer; //图形显示的区域(主要为了循环显示)
public
constructor create(pS:Tpoint;pE:Tpoint;sBmppath:string;iDispTime:integer;iMode:integer;iNo:integer);
procedure execute;override;
end;
2.线程对象的实现
constructor tdrawthread.create(pS:Tpoint;pE:Tpoint;sBmppath:string;iDispTime:integer;iMode:integer;iNo:integer); //图形效果的各种显示参数,定义见上
begin
pi:=pS;
pj:=pE;
sTranpath:=sBmppath;
iTranTime:=iDispTime;
iTranMode:=iMode;
iTranIndex:=iNo;
inherited create(false);
end;
procedure tdrawthread.execute; //具体显示的实现
var
Bitmapi:Tbitmap;
Bitmapj:Tbitmap;
Jpegi: TJPEGImage;
xgroup,xcount:integer;
i,j:integer;
begin
FreeOnTerminate:=true; //线程的终止
if fileexists(sTranpath) then
begin
Bitmapi:=Tbitmap.Create;
if LowerCase(copy(sTranpath,length(sTranpath)-2,3))='jpg' then
begin //兼容JPG图形文件的显示
Jpegi := TJPEGImage.Create ;
Jpegi.LoadFromFile(sTranpath);
Bitmapi.Assign(Jpegi);
Jpegi.free;
end
else
Bitmapi.LoadFromFile(sTranpath);
if bStop then exit; //参数控制退出系统
Bitmapi.Canvas.Lock; //图形对象进行上锁操作
form1.Canvas.Brush.Color:=clblack; //Form背景擦除操作
form1.Canvas.FillRect(rect(pi.X,pi.y,pj.x,pj.Y));
case iTranMode of //进行各种图形效果的显示
0:begin //显现
form1.Canvas.CopyRect(rect(pi.x,pi.y,pj.x,pj.y),Bitmapi.canvas,rect(0,0,pj.x-pi.x,pj.y-pi.y));
end;
1:begin //左移
for i:=0 to pj.x-pi.x do
begin
application.ProcessMessages;
if bStop then
begin //如果退出系统,各图形对象进行解锁操作
if form1.Canvas.LockCount>0 then form1.Canvas.Unlock;
if Bitmapi.Canvas.LockCount>0 then Bitmapi.Canvas.Unlock;
exit;
end;
form1.Canvas.Lock;
form1.Canvas.CopyRect(rect(pj.x-i,pi.y,pj.x,pj.y),Bitmapi.Canvas,rect(0,0,i,pj.Y-pi.y));
form1.Canvas.Unlock;
sleep(2);
end;
form1.Canvas.Lock;
form1.Canvas.CopyRect(rect(pi.x,pi.y,pj.x,pj.y),Bitmapi.canvas,rect(0,0,pj.x-pi.x,pj.y-pi.y));
form1.Canvas.Unlock;
end;
2:begin //右移