多线程处理图像问题,求大神指点迷津!!

holding100 2012-08-17 03:17:36
下面代码是处理图片的代码,大致是通过定位图片中直线坐标,再经过坐标变换求出相对坐标中的涂黑点,然后将涂黑点保存到数据库中,不用线程时,执行该过程一切正常,读取的结果和实际吻合,说明读取正确,因为要处理大量图片,执行时间长,是屏幕锁死会锁死很久。这个应该正常。
现在为了提高处理速度。改为多线程后。线程中
procedure TGetDataThd.Execute;
方法用 synchronize(doreadsp); 去执行发现屏幕依旧锁死,读取结果也是正确的。
procedure TGetDataThd.Execute;
改用 doreadsp; 去执行屏幕不锁死了,但是读取结果不正确。
依然用 synchronize(doreadsp); 去执行。同时开5个线程去运行,屏幕锁死,5顺序顺序执行,并没有达到5个线程并行运行从而提高处理速度的预期效果。
例如有100个图片,每20个图片分一组,分别开5个线程运行,运行时间是20个图片处理时间*5而非预期的运行时间减少为1/5
。查了一些资料和翻了很多遍csdn以前的帖子都找不到答案,求大神指点迷津啊。

下面是主要处理图片的代码,可能一些算法需要优化,现在顾不上了,只求能搞定多线程处理图片的问题。

procedure TGetDataThd.DoReadSp;
const
MCW_EM = DWord($133f);
var
i,j,ret,iztscore,icontentscore,iLineCount:integer;
bTempb,testbmp:Tbitmap;
sCheckInfo,model_cls:string;
strBarcode : String;
DecodePara : PTDECODEPARASTRUCT;
BarCodeInfo : PTTOTALBARCODEINFOSTRUCT;
pInfo: pPTBARCODEINFOSTRUCT;
modelid,checkid,cadreid,cadreidlist,zt_pose_count,cadre_pose_count,content_pose_count,use_opinion_flag,zt_pose,content_pose:string;
overall_flag,content_line_direction,card_rowcount,card_colcount:string;
zt_posetmp,content_posetmp,zt_Score,content_score:string;
//原点和斜率定义
iX,iY,iPoseArray,iPose:integer;
AX,AY,BX,BY,CX,CY,AX0,AY0,BX0,BY0,CX0,CY0,DX0,DY0,ox,oy,LPose:integer;
posestates:string;
BlackFound:boolean;
fK:real;
scheckmodel:string;
sSorPath,sDitPath,deptid:string;
bfound:boolean;
adoquery1,ADOQuery2:Tadoquery;
begin
//search the whole image
Set8087CW(MCW_EM);
j:=iStart;
if iList=1 then
begin
CardDistinNewForm.ProgressBar1.Visible:=true;
CardDistinNewForm.ProgressBar1.Progress:=1;
CardDistinNewForm.ProgressBar1.MaxValue:=iEnd-iStart+1;
end;
if iList=2 then
begin
CardDistinNewForm.ProgressBar2.Visible:=true;
CardDistinNewForm.ProgressBar2.Progress:=1;
CardDistinNewForm.ProgressBar2.MaxValue:=iEnd-iStart+1;
end;
if iList=3 then
begin
CardDistinNewForm.ProgressBar3.Visible:=true;
CardDistinNewForm.ProgressBar3.Progress:=1;
CardDistinNewForm.ProgressBar3.MaxValue:=iEnd-iStart+1;
end;
if iList=4 then
begin
CardDistinNewForm.ProgressBar4.Visible:=true;
CardDistinNewForm.ProgressBar4.Progress:=1;
CardDistinNewForm.ProgressBar4.MaxValue:=iEnd-iStart+1;
end;
try
adoquery1:=Tadoquery.Create(nil);
adoquery2:=Tadoquery.Create(nil);
adoquery1.Connection:=DataConnDataModel.ADOConn;
adoquery2.Connection:=DataConnDataModel.ADOConn;
while J<=iEnd do
begin
sSorPath:=copy(trim(CardDistinNewForm.AdvStringGrid1.cells[2,j]),length(trim(CardDistinNewForm.edit1.Text))+1,length(trim(CardDistinNewForm.AdvStringGrid1.cells[2,j])));
sDitPath:=trim(CardDistinNewForm.edit6.Text)+sSorPath;
bTempb:=Tbitmap.Create;
bTempb:=Pic2Bmp(trim(CardDistinNewForm.AdvStringGrid1.cells[2,j]));
testbmp:=bTempb;
sCheckInfo:='0012-010-001';
modelid:=getModelId(sCheckInfo);
checkid:=getCheckId(sCheckInfo);

ADOQuery1.Close;
ADOQuery1.SQL.Text:='select cadre_list_id d1,zt_pose as d2,content_pose as d3'
+' from card_content_pose where model_id='''+trim(sCheckInfo) +''' order by cadre_list_id';
ADOQuery1.Open;
if adoquery1.Eof=false then
scheckmodel:=trim(sCheckInfo) //考核卡信息
else
begin
cadreid:=copy(sCheckInfo,length(sCheckInfo)-2,3) ;
adoquery1.Close;
adoquery1.SQL.Text:='select dpt_id as d1 from objece_set where check_id='''
+checkid+''' and card_id='''+cadreid+'''';
adoquery1.Open;
if adoquery1.Eof=false then
deptid:=trim(adoquery1.fieldbyname('d1').AsString);
adoquery1.Close;
ADOQuery1.SQL.Text:='select cadre_list_id d1,zt_pose as d2,content_pose as d3'
+' from card_content_pose where model_id='''+sCheckInfo+''' order by cadre_list_id';
ADOQuery1.Open;
if adoquery1.Eof=false then
scheckmodel:=sCheckInfo
else
scheckmodel:=copy(sCheckInfo,1,8);

end;
cadreidlist:=getCadreList(sCheckInfo);
ADOQuery1.Close;
ADOQuery1.SQL.Text:='select list_id d1,zt_pose_count as d2,cadre_pose_count as d3,content_pose_count as d4,'
+'use_opinion_flag as d5,overall_flag as d6,content_line_direction as d7,card_rowcount as d8,card_colcount as d9,model_cls as d10'
+' from card_model where list_id='''+modelid+'''';
ADOQuery1.Open;
if ADOQuery1.Eof=false then
begin
zt_pose_count:=trim(ADOQuery1.fieldbyname('d2').AsString);
cadre_pose_count:=trim(ADOQuery1.fieldbyname('d3').AsString);
content_pose_count:=trim(ADOQuery1.fieldbyname('d4').AsString);
use_opinion_flag:=trim(ADOQuery1.fieldbyname('d5').AsString);
overall_flag:=trim(ADOQuery1.fieldbyname('d6').AsString);
content_line_direction:=trim(ADOQuery1.fieldbyname('d7').AsString);
card_rowcount:=trim(ADOQuery1.fieldbyname('d8').AsString);
card_colcount:=trim(ADOQuery1.fieldbyname('d9').AsString);
model_cls:=trim(ADOQuery1.fieldbyname('d10').AsString);
end;
//************开始*****************获得图片原点和X轴斜率


//从左到右
iX:=40;
posestates:='0';
LPose:=0;
iPose:=0;

while iX<testbmp.Width-40 do
begin
iY:=testbmp.Height-40;
BlackFound:=false;
while iY>testbmp.Height-600 do
begin
// memo1.Lines.Add(ColorToString(testbmp.Canvas.Pixels[ix,iy])+'('+inttostr(ix)+','+inttostr(iy)+')');
//CardDistinNewForm.Memo1.Lines.Add(inttostr(ix)+','+inttostr(iY));
if testbmp.Canvas.Pixels[ix,iy]=clBlack then
begin

if posestates='0' then
begin
posestates:='1';
AX:=iX;
AY:=iY;
BX:=iX;
BY:=iY;
end;
if posestates='1' then
begin
CX:=iX;
CY:=iY;
end;
BlackFound:=true;
break;
end;
iY:=iY-1;
end;
if BlackFound=true then
begin
if ABS(CY-BY)<=1 then
begin
BX:=CX;
BY:=CY;
LPose:= LPose+1;
if LPose>15 then
begin
iPose:=iPose+1;
if iPose=1 then
begin
AX0:=round((AX+BX)/2+0.1);
AY0:=round((AY+BY)/2+0.1);
end;
if iPose>1 then
begin
BX0:=round((AX+BX)/2+0.1);
BY0:=round((AY+BY)/2+0.1);
end;
end;
end
else
begin
posestates:='0';
LPose:=0;
end;
end
else
begin
posestates:='0';
LPose:=0;
end;
iX:=iX+1;
end;
ix:=145;
iy:=139;

...全文
250 21 打赏 收藏 转发到动态 举报
写回复
用AI写文章
21 条回复
切换为时间正序
请发表友善的回复…
发表回复
holding100 2012-08-20
  • 打赏
  • 举报
回复
老兄方便留个QQ吗?可以方便交流请教。
holding100 2012-08-20
  • 打赏
  • 举报
回复
又试了下,将doreadsp中的和界面相关的代码都去掉,还试老问题,处理结果不对,为什么用synchronize来调用就结果对呢?不用synchronize就不对。已经去掉了在处理过程中修改主界面的代码了。线程执行的方法和普通主进程执行的时候是不是需要特殊处理下,同样的代码在主进程执行就没问题,放到线程中就处理结果错误,中间也不报错能执行下去,就是结果错误。
我图片处理的时候改用
fileList := TStringList.Create;
AppPath:= 'D:\pic\bmp\';
ext:= '*.bmp ';
if FindFirst(AppPath+ext,faAnyFile-faDirectory, SearchRec) = 0 then
begin
repeat
FileList.Add(AppPath+SearchRec.Name);
until FindNext(SearchRec) <> 0;
FindClose(SearchRec);
end;

j:=0;
iEnd:=FileList.Count;
while J<iEnd do
begin
testbmp:=Tbitmap.Create;
testbmp.LoadFromFile(trim(FileList.Strings[j]));
。。。。。
对testbmp进行处理过程
。。。。。
j:=j+1;
testbmp.Free;
end;


下面是图片格式转换函数将非bmp格式都转为bmp格式
function TGetDataThd.Pic2Bmp(PicFileName: String): TBitmap;
var
Str:String;
Icon: TIcon;
Jpeg1:TJPEGIMAGE;
MetaFile:TMetafile;
begin
result:=TBitMap.create;
result.PixelFormat:=pf1bit;
Str := ExtractFileExt(PicFileName);
Str :=Uppercase(Copy(Str,2,3));
if (Str='BMP') then
result.LoadFromFile(PicFileName)
else //Jpeg转换成BMP
if (Str='JPG') or (Str='JPEG') then
begin
Jpeg1 := TJPEGIMAGE.Create;
Jpeg1.LoadFromFile(PicFileName);
result.Assign(Jpeg1);
Jpeg1.free;
end
else //ICO 转换成BMP
if (Str='ICO') or (Str='ico') then
begin
Icon := TIcon.Create;
Icon.LoadFromFile(PicFileName);
result := TBitmap.Create;
result.Width := Icon.Width;
result.Height := Icon.Height;
result.Canvas.Draw(0, 0, Icon);
Icon.Free;

end
else //WMF转换成BMP
if Str='WMF' then
begin
Metafile:=TMetaFile.create;

MetaFile.LoadFromFile(PicFileName);
with result do
begin
Height:=Metafile.Height;
Width:=Metafile.Width;
Canvas.Draw(0,0,MetaFile);

end;
MetaFile.Free;
end;

end;
中间对testbmp进行处理操作,
主要是根据预先设定的位置坐标对该坐标点是否涂黑进行判断

if testbmp.Canvas.Pixels[ix,iy]=clBlack then
result:='A';
预设的点很多,将这些预设的点判断是否涂黑后将结果组合在一起如:“ABDAACCAAAAACCCAADDDAAAADDDABBB"保存到数据库中。
中间这段处理过程在主进程中执行没问题,在线程中用synchronize来调用也没问题,都可以执行的很好,得出正确的处理结果,但是放线程中不用synchronize来调用,图片处理判断就有问题,的到结果是错误的,比如20张图片,有可能最后一张处理的正确,而前面的图片处理结果都错误。
haitao 2012-08-20
  • 打赏
  • 举报
回复
adoquery的Connect属性赋值也是在线程中的处理函数中赋的值。取坐标和存结果分别用的不同的adoquery,并且其Connect属性也是分别用的不同的链接

——那就是要主程序先CoInitializeEx一次了。。。。。
holding100 2012-08-20
  • 打赏
  • 举报
回复
主程序是有选择目录的Edit控件,但是我为了测试减少不必要的不确定性,我直接在线程中指定目录了。在测试中并没有传到线程中目录参数。坐标位置是用adoquery获得的,这个也是在线程中的处理函数新创建的,包括adoquery的Connect属性赋值也是在线程中的处理函数中赋的值。取坐标和存结果分别用的不同的adoquery,并且其Connect属性也是分别用的不同的链接。我曾经做过实验,同样类似的调用方法,一个是处理图片的,一个是普通的计算,不用synchronize调用时,图片的处理的有问题,简单计算的却没有问题,我在想是不是和处理时间有关系,或者图像处理有什么特殊的方法搞定,光看帮助和例程搞不定啊,从网上搜也搜不可以的方法。这个问题搞了快半个月,解决不了。死的心都有了。。。。
haitao 2012-08-20
  • 打赏
  • 举报
回复
关键是子线程有没有使用到主界面的控件。。。。。

如果子线程自己扫描目录,那各个子线程怎么知道该扫描哪些不同的目录名?
还有位置坐标是固定的,还是怎么从主线程传进去的。。。。

否则,好像没什么不安全的地方了
holding100 2012-08-20
  • 打赏
  • 举报
回复
主进程现在什么也不做,只是创建线程,处理都在线程中进程的
holding100 2012-08-20
  • 打赏
  • 举报
回复
不是,是在线程的中doReadSP函数创建,这个函数是主要批量处理图片的函数
haitao 2012-08-20
  • 打赏
  • 举报
回复
fileList := TStringList.Create;是在主线程里创建?收集了所有待处理文件列表,给各个线程取用?
如果这样,问题可能在TStringList,它是 非线程安全的

如果各个线程分别扫描不同的目录到自己的TStringList,那这一点就没问题
holding100 2012-08-20
  • 打赏
  • 举报
回复
问题解决了,就是bmp图片在线程中处理的问题,在图片处理前加bmp.Canvas.Lock 在图片处理完加bmp.Canvas.unLock 就ok,为什么图片处理和数值计算对内存的使用有区别呢。。
太多的未知。。。一个小程序员的烦恼。
感谢sz_haitao兄这几天一直陪我思考。
结贴吧,可以好好睡个觉了。
haitao 2012-08-20
  • 打赏
  • 举报
回复
如果线程自己创建的bmp,Bmp.Canvas应该安全的,除非它的操作还涉及主form的控件?
holding100 2012-08-20
  • 打赏
  • 举报
回复
不是这个问题,我跟踪过,adoquery查询与更新一切正常,似乎快找到问题所在了,我跟踪到了在线程中bmp图片处理中 用Bmp.Canvas.*后,内存就乱了,图片中像素没了,我再试试,看看是不是这个问题造成的。
holding100 2012-08-19
  • 打赏
  • 举报
回复
感谢兄台这么晚了还回复指点。关键是第2点处理图片的时候会出问题,第3点小弟同时开5个线程会产生ADO连接占用问题,经过昨晚试验需要每个ADOQUERY都用独立的连接可以解决。
关键是第二点处理图片时候,只开一个线程的时候如果不用synchronize同步也会产生处理结果错误的问题。只开单线程处理时候用了synchronize读取结果没问题? 按照以前同学的解答应该是处理图片时候没同步会造成的内存地址混乱从而产生读取错误。怎么能保证“数据空间独立”呢?用了synchronize可能保证独立了,但是会造成读取界面锁死(主进程锁死)直到线程执行完,并且开多线程会顺序执行,实现不了并行处理。关键是兄台说的“数据空间独立”,刚开始学习用线程。老大能说的详细点吗?
haitao 2012-08-19
  • 打赏
  • 举报
回复
每个线程处理一个图片分几个步骤:
1、取图片,本地,无须synchronize同步,费时x秒(与图片文件大小有关,假设x=1)
2、处理,数据空间独立,无须synchronize同步,费时y秒(y=50)
3、保存,与生成的字符串大小有关,共用ado连接则需要synchronize同步,费时z秒(与字符串大小有关,假设z=3)

则只有3是需要排队执行的,1、2应该可以同时进行。不过,如果cpu单核或并行差,1、2的并行也会互等

如果ado是每个线程独立有一个连接,则3也可以并行,这样需要程序先CoInitializeEx一次。。。。搜一下就知道了
haitao 2012-08-19
  • 打赏
  • 举报
回复
看来2就不能独立?用了synchronize不是保证独立了,而串行了!
2如果数据变量、处理函数都是与主界面无关的,则就算“数据空间独立”,无须synchronize同步,也就能并行了
holding100 2012-08-18
  • 打赏
  • 举报
回复
多个线程处理多个图片,比如开5个线程分别处理5个图片,将5个图片的读取结果都存入数据库中。
比如有100个图片顺序排列,第一个线程处理1-20个,第二个线程处理21-40,第三个线程处理41-60第4个线程处理第61-80,第5个线程处理81-100个图片。
如果在线程单元中Execute进程用synchronize(doreadsp)调用时候是第一线程处理完1-20个图片后第二个线程才启动处理第21-40个图片,以此类推,这5个线程是顺序执行的,不是并行同时运行的。
主进程的调用如下:
FModThread1:=TGetDataThd.Create(1,20,1);
FModThread1:=TGetDataThd.Create(21,40,1);
FModThread1:=TGetDataThd.Create(41,60,1);
FModThread1:=TGetDataThd.Create(61,80,1);
FModThread1:=TGetDataThd.Create(81,100,1);

如果线程单元中的Execute进程用synchronize(doreadsp)调用
读取结果是对的,但是上面5个线程是依次执行,没有达到同时执行的效率。
如果线程单元中的Execute进程用doreadsp调用
读取结果是错的,这时上面5个线程是同时进行的,但是因为没有同步过,可能每个线程都访问相同内存地址造成读取结果错误。

但是如果用synchronize同步doreadsp 读取结果虽然对了,但是这5个线程不是同时工作,而是顺序工作,失去了多线程并行处理提高效率的设计初衷,该怎么处理才能实现并行处理提高效率并且又不内存访问冲突,读取结果能正确呢?
想了好几天办法,总是行不通,万能的csdner,帮帮我这个迷途小Coder吧
holding100 2012-08-18
  • 打赏
  • 举报
回复
图片是用扫描仪扫的存在本地硬盘目录中的。每个图片处理完得到的结果是类似ADAAAGAA*DAABBBB的一个字符串存入到数据库中,每张图片的处理时间大概50秒,每个线程在处理图片的时间创建ADOquery建立连接。公用一个连接。
“多线程操作ado,需要先执行coinit...."是什么意思?没这样用过,能说的具体点吗?
haitao 2012-08-18
  • 打赏
  • 举报
回复
取得本地还是数据库的图片?处理后保存到数据库?
取、处理、保存的平均时间开销各是多少?
数据库连接是每个线程有自己的连接,还是共用一个连接?
多线程操作ado,需要先执行coinit....
haitao 2012-08-17
  • 打赏
  • 举报
回复
代码太长

多个线程是处理同一个图形对象、区域?
最好各复杂一份,仅仅处理自己的图形资源、对象
处理完了,再合并到目标图形资源、对象
holding100 2012-08-17
  • 打赏
  • 举报
回复
求大神指点迷津啊
holding100 2012-08-17
  • 打赏
  • 举报
回复
代码接上面
else
begin
cadreid:=cadreidlist;
cadreidlist:='';
end;
zt_pose:=trim(ADOQuery1.fieldbyname('d2').AsString);
content_pose:=trim(ADOQuery1.fieldbyname('d3').AsString);
iztscore:=1;
zt_pose:=PassCadre(zt_pose);
while zt_pose<>'' do
begin
if pos(';',zt_pose)>0 then
zt_posetmp:=copy(zt_pose,1,pos(';',zt_pose)-1)
else
zt_posetmp:=zt_pose ;
if doScore(testbmp,getxpose(zt_posetmp,ox,oy,fK),getypose(zt_posetmp,ox,oy,fK))=true then
break;
if pos(';',zt_pose)>0 then
zt_pose:=copy(zt_pose,pos(';',zt_pose)+1,length(zt_pose)-pos(';',zt_pose))
else
zt_pose:='';
iztscore:=iztscore+1;
end;
zt_Score:=getScore(iztscore);
content_pose:=PassCadre(content_pose);
content_score:='';
content_posetmp:=''; //临时字符串
while content_pose<>'' do
begin
icontentscore:=1;
if pos('|',content_pose)>0 then
content_posetmp:=copy(content_pose,1,pos('|',content_pose)-1)
else
content_posetmp:=content_pose;
while content_posetmp<>'' do
begin

if pos(';',content_posetmp)>0 then
zt_posetmp:=copy(content_posetmp,1,pos(';',content_posetmp)-1)
else
zt_posetmp:=content_posetmp;
if doScore(testbmp,getxpose(zt_posetmp,ox,oy,fK),getypose(zt_posetmp,ox,oy,fK))=true then
break;
if pos(';',content_posetmp)>0 then
content_posetmp:=copy(content_posetmp,pos(';',content_posetmp)+1,length(content_posetmp)-pos(';',content_posetmp))
else
content_posetmp:='';
icontentscore:=icontentscore+1;
end;
if icontentscore=5 then
icontentscore:=15;
content_score:=content_score+getScore(icontentscore);
if pos('|',content_pose)>0 then
content_pose:=copy(content_pose,pos('|',content_pose)+1,length(content_pose)-pos('|',content_pose))
else
content_pose:=''
end;
ADOQuery2.Close;
if model_cls='1' then
ADOQuery2.SQL.Text:='insert into card27 (check_id,zt_id,dx_id1,c1,d1,e1,sfilename) values ('''+checkid+''','''+zt_Score+''','''+cadreid+''','''
+copy(content_score,1,length(content_score)-2)+''','''+copy(content_score,length(content_score)-1,1)
+''','''+copy(content_score,length(content_score),1)+''','''+trim(CardDistinNewForm.AdvStringGrid1.cells[2,j])+''')';
if model_cls='2' then
ADOQuery2.SQL.Text:='insert into card26 (check_id,zt_id,dx_id1,c1,d1,sfilename) values ('''+checkid+''','''+zt_Score+''','''+cadreid+''','''
+copy(content_score,1,length(content_score)-1)+''','''+copy(content_score,length(content_score),1)+''','''+trim(CardDistinNewForm.AdvStringGrid1.cells[2,j])+''')';
if ((model_cls='3') or (model_cls='4')) then
ADOQuery2.SQL.Text:='insert into new_card26a (check_id,zt_id,dx_id1,c1,sfilename) values ('''+checkid+''','''+zt_Score+''','''+cadreid+''','''
+content_score+''','''+trim(CardDistinNewForm.AdvStringGrid1.cells[2,j])+''')';
if ((model_cls='5') or (model_cls='6')) then
ADOQuery2.SQL.Text:='insert into new_card26 (check_id,zt_id,dx_id1,c1,sfilename) values ('''+checkid+''','''+zt_Score+''','''+cadreid+''','''
+content_score+''','''+trim(CardDistinNewForm.AdvStringGrid1.cells[2,j])+''')';

ADOQuery2.ExecSQL;
ADOQuery1.Next;
end;
end;
CardDistinNewForm.AdvStringGrid1.Cells[1,J]:='Do';
if iList=1 then
CardDistinNewForm.ProgressBar1.Progress := CardDistinNewForm.ProgressBar1.Progress + 1;
if iList=2 then
CardDistinNewForm.ProgressBar2.Progress := CardDistinNewForm.ProgressBar2.Progress + 1;
if iList=3 then
CardDistinNewForm.ProgressBar3.Progress := CardDistinNewForm.ProgressBar3.Progress + 1;
if iList=4 then
CardDistinNewForm.ProgressBar4.Progress := CardDistinNewForm.ProgressBar4.Progress + 1;

j:=j+1;
bTempb.Free;
end;
finally
FreeAndNil(adoquery1);
FreeAndNil(adoquery2);
end;
if iList=1 then
begin
CardDistinNewForm.ProgressBar1.Progress:=iEnd+1;
CardDistinNewForm.ProgressBar1.Visible:=false;
end;
if iList=2 then
begin
CardDistinNewForm.ProgressBar2.Progress:=iEnd+1;
CardDistinNewForm.ProgressBar2.Visible:=false;
end;
if iList=3 then
begin
CardDistinNewForm.ProgressBar3.Progress:=iEnd+1;
CardDistinNewForm.ProgressBar3.Visible:=false;
end;
if iList=4 then
begin
CardDistinNewForm.ProgressBar4.Progress:=iEnd+1;
CardDistinNewForm.ProgressBar4.Visible:=false;
end;
end;
加载更多回复(1)

1,183

社区成员

发帖
与我相关
我的任务
社区描述
Delphi GAME,图形处理/多媒体
社区管理员
  • GAME,图形处理/多媒体社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧