我正在尝试使用OnPaint方法绘制一个简单的图像。该代码可以很好地进行编译,但是在应用程序启动时,它会显示“对象锁未拥有”错误,并且什么也没有发生。你能告诉我我犯了什么错误吗?该代码显示了我正在使用的OnPaint事件。谢谢大家的帮助。
procedure TTabbedForm.Image1Paint(Sender: TObject; Canvas: TCanvas;
const ARect: TRectF);
var
p1, p2, p3, p4, p5, p6: TPointF;
prst1: TRectF;
i :Integer;
begin
Image1.Bitmap.Canvas.Stroke.Color := TAlphaColors.Black;
Image1.Bitmap.Canvas.Stroke.Thickness := 3;
p1 := TPointF.Create(PX, PY);
Image1.Bitmap.Canvas.BeginScene;
with TabbedForm do begin
for i := 0 to 360 do
if (i mod 15)=0 then
begin
p2 := TPointF.Create(Round(PX+PP*sin(i*pi/180)), Round(PY+PP*cos(i*pi/180)));
Image1.Bitmap.Canvas.DrawLine(p1, p2, 100);
end;
for i := 0 to PP do
if (i mod 20)=0 then
begin
prst1 := TRectF.Create(PX+i,PY+i,PX-i,PY-i);
Image1.Bitmap.Canvas.DrawEllipse(prst1, 100);
end;
for i := 0 to 400 do
if (i mod 20)=0 then
begin
p3 := TPointF.Create(i,2*PP);
p4 := TPointF.Create(i,2*PP+2*PP);
Image1.Bitmap.Canvas.DrawLine(p3, p4, 100);
end;
for i := 0 to 400 do
if (i mod 20)=0 then
begin
p5 := TPointF.Create(0,2*PP+i);
p6 := TPointF.Create(2*PP+2*PP,2*PP+i);
Image1.Bitmap.Canvas.DrawLine(p5, p6, 100);
end;
Image1.Bitmap.Canvas.EndScene;
end;
end;
答案 0 :(得分:0)
我认为您会收到此错误消息,因为您是在不允许的时候在画布上绘画的。造成这种情况的潜在原因是:
小的免责声明:我尽可能地保留了您的代码,只是更改了我认为可能导致您的问题的内容。我认为这些更改都是有道理的,但是我必须承认我从未在FMX中做过很多绘画,所以也许其中一些有些天真或过于保护(或者公然错误)。
此代码与您的代码有不同之处:
if
和try..finally
块的BeginScene和EndScene。 BeginScene将为您提供锁定或不锁定,并根据成功返回布尔值。您仅应在实际获得了锁的情况下继续操作,并且在这种情况下也仅调用EndScene,因为它们被引用计数,并且执行此操作可能会增加引用计数,并因此在您的应用程序中进行所有进一步的绘制。BeginScene..EndScene
。 Paintbox或Image控件本身应该已经调用了它。参见FMX.Graphics.TCanvas.BeginScene docs Canvas
。它作为参数传递给事件处理程序,因此最好使用它,然后自己尝试找到合适的画布。with
。这有点长,但是好像您正在引用全局TTabbedForm
变量一样,并且由于您位于TTabbedForm方法内,因此您应该能够将当前实例的属性和方法用作-is,如果遇到命名冲突,请加上Self.
。最好不要依赖那些用于表单和数据模块的全局变量,并且如果您想拥有多个表单实例,实际上会遇到问题,在这种情况下,原始代码会部分运行错误的实例。procedure TTabbedForm.Paintbox1Paint(
Sender: TObject; Canvas: TCanvas; const ARect: TRectF);
var
p1, p2, p3, p4, p5, p6: TPointF;
prst1: TRectF;
i :Integer;
begin
p1 := TPointF.Create(PX, PY);
Canvas.Stroke.Color := TAlphaColors.Black;
Canvas.Stroke.Thickness := 3;
for i := 0 to 360 do
if (i mod 15)=0 then
begin
p2 := TPointF.Create(Round(PX+PP*sin(i*pi/180)), Round(PY+PP*cos(i*pi/180)));
Canvas.DrawLine(p1, p2, 100);
end;
for i := 0 to PP do
if (i mod 20)=0 then
begin
prst1 := TRectF.Create(PX+i,PY+i,PX-i,PY-i);
Canvas.DrawEllipse(prst1, 100);
end;
for i := 0 to 400 do
if (i mod 20)=0 then
begin
p3 := TPointF.Create(i,2*PP);
p4 := TPointF.Create(i,2*PP+2*PP);
Canvas.DrawLine(p3, p4, 100);
end;
for i := 0 to 400 do
if (i mod 20)=0 then
begin
p5 := TPointF.Create(0,2*PP+i);
p6 := TPointF.Create(2*PP+2*PP,2*PP+i);
Canvas.DrawLine(p5, p6, 100);
end;
end;
答案 1 :(得分:0)
错误消息“对象锁未拥有”是EMonitorLockException
的消息,记录为“每当线程尝试释放非拥有的监视器上的锁时,都会引发”。由于您尚未响应我对MCVE的请求,并且我无法重现此错误,因此我无法确认是否是由于通过Canvas.BeginScene
进行的锁获取失败或其他原因导致的。
您可以在图形中使用TImage
或TPaintBox
。使用TImage
具有许多好处,例如直接加载图像文件,在该图像上绘图以及将图像直接以各种格式保存到文件中,例如.bmp
,.jpg
或{{1 }}(也许其他人也可以)。 .png
更轻巧,没有自己的位图,但使用父组件表面进行绘制(因此需要TPaintBox
处理程序)。从/保存到文件的加载必须完成,例如通过单独的TBitmap。
是的,如果需要,您可以继续使用TImage控件,但是在这种情况下,不要像现在一样使用OnPaint()
事件用于图形。 TImage具有内置的机制,可以在需要时进行自我绘制。您只需要在内置位图画布上绘制一次图形即可。在以下代码中,图像是在OnPaint
事件中绘制的。还要注意,使用TImage必须按照记录正确使用ButtonClick()
-BeginScene
。
在绘制之前,您还必须设置EndScene
。如果未在显示的代码中的其他位置设置此值,则可能是代码未生成图像的另一个原因。
在TImage.Bitmap.Size
上绘制图像,例如在按钮的Image1.Bitmap.Canvas
事件中:
OnClick()