[英]“Object lock not owned” error while using OnPaint method
I'm trying to draw a simple image with OnPaint method. 我正在尝试使用OnPaint方法绘制一个简单的图像。 The code compiles just fine, but when the application starts, it shows "Object lock not owned" error and nothing else happens. 该代码可以很好地进行编译,但是在应用程序启动时,它会显示“对象锁未拥有”错误,并且什么也没有发生。 Could you please tell me what mistake I made? 你能告诉我我犯了什么错误吗? The code shows the OnPaint event I'm using. 该代码显示了我正在使用的OnPaint事件。 Thank you all for your help. 谢谢大家的帮助。
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;
I think you get this error message, because you're drawing on the canvas at a time when you're not allowed to. 我认为您收到此错误消息,是因为您在不允许的时候在画布上绘画。 Potential causes for this are: 造成这种情况的潜在原因是:
Small disclaimer: I left your code as-is as much as possible, just changed the things that I think could potentially cause your problem. 小免责声明:我尽可能地保留了您的代码,只是更改了我认为可能会导致问题的内容。 I think these changes all make sense, but I must admit I've never done much painting in FMX, so maybe some of these are a bit naive or over-protective (or blatantly wrong). 我认为这些更改都是有道理的,但是我必须承认我从未在FMX中做过很多绘画,因此其中一些可能有些天真或过分保护性(或公然错误)。
Things that are different in this code compared to yours: 与您的代码相比,此代码中的不同之处:
if
and a
try..finally
block.
if
和
try..finally
块。
BeginScene..EndScene
completely. 完全BeginScene..EndScene
。 The Paintbox or Image control should already have called that itself. Paintbox或Image控件本身应该已经调用了它。 See FMX.Graphics.TCanvas.BeginScene docs 请参阅FMX.Graphics.TCanvas.BeginScene文档 Canvas
. 只需使用Canvas
。 It's passed as a parameter to the event handler, so better to use that, then to try and find the right canvas yourself. 它作为参数传递给事件处理程序,因此最好使用它,然后自己尝试找到合适的画布。 with
. 删除了with
。 This is a bit of a long shot, but it looked like you were referring to a global TTabbedForm
variable, and since you are inside a TTabbedForm method, you should be able to use the properties and methods of the current instance as-is, or prepend with Self.
这有点长,但是看起来您是在引用全局TTabbedForm
变量,并且由于您位于TTabbedForm方法内,因此您应该能够按原样使用当前实例的属性和方法,或者在Self.
if you run into naming conflicts. 如果您遇到命名冲突。 It's always better to not rely on those globals for forms and datamodules, and you'll actually run into problems if you want to have multiple instances of your form, in which case your original code would partially operate on the wrong instance. 最好不要依赖那些用于表单和数据模块的全局变量,并且如果您想拥有多个表单实例,实际上会遇到问题,在这种情况下,原始代码将部分在错误的实例上运行。 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;
The error message "Object lock not owned" is the message of EMonitorLockException
, which is documented to be raised "whenever a thread tries to release the lock on a non-owned monitor". 错误消息“对象锁未拥有”是EMonitorLockException
的消息, EMonitorLockException
,该记录将在“每当线程尝试释放非拥有的监视器上的锁时引发”。 Since you have not responded to my request for an MCVE, and I have not been able to reproduce this error, I can not confirm whether it is due to an unsuccessful lock aquisition through Canvas.BeginScene
, or something else. 由于您尚未响应我对MCVE的请求,并且无法重现此错误,因此我无法确认是否是由于Canvas.BeginScene
或其他原因导致的锁Canvas.BeginScene
失败。
You can use either a TImage
or a TPaintBox
for your drawing. 您可以将TImage
或TPaintBox
用于图形。 Using a TImage
provides many benefits such as directly loading an image file, drawing on that image and saving your image to a file directly in various formats, like .bmp
, .jpg
or .png
(maybe others too). 使用TImage
许多好处,例如直接加载图像文件,在该图像上绘图以及将您的图像直接以.bmp
, .jpg
或.png
(也可能是其他格式)的各种格式保存到文件中。 A TPaintBox
is more lightweight and doesnt have an own bitmap, but uses the parent components surface to draw on (therefore the need for an OnPaint()
handler). TPaintBox
更轻巧,没有自己的位图,但使用父组件表面进行绘制(因此需要OnPaint()
处理程序)。 Loading from / saving to file must be done eg through a separate TBitmap. 从/保存到文件的加载必须通过单独的TBitmap完成。
So yes, you may continue to use a TImage control if you want, but in that case, do not use the OnPaint
event for the drawing as you are now. 因此,是的,您可以根据需要继续使用TImage控件,但是在这种情况下,请不要像现在一样使用OnPaint
事件作为图形。 A TImage has a built in mechanism to paint itself when needed. TImage具有内置的机制,可以在需要时进行自我绘制。 You only need to draw your drawing once to the built-in bitmap canvas. 您只需要在内置位图画布上绘制一次图形即可。 In the following code the image is drawn in a ButtonClick()
event. 在以下代码中,图像是在ButtonClick()
事件中绘制的。 Also note, that with the TImage you must use BeginScene
- EndScene
correctly as documented. 另请注意,必须使用TImage正确记录BeginScene
- EndScene
。
You must also set the TImage.Bitmap.Size
before drawing on it. 您还必须在绘制之前设置TImage.Bitmap.Size
。 If this was not set elsewhere in your code of what you have shown, then that may be another reason why your code produced no image. 如果未在显示的代码中的其他位置设置此值,则可能是代码未生成图像的另一个原因。
Draw your image on Image1.Bitmap.Canvas
eg in a OnClick()
event of a button: 在Image1.Bitmap.Canvas
上绘制图像,例如在按钮的OnClick()
事件中:
procedure TTabbedForm.Button1Click(Sender: TObject);
var
p1, p2, p3, p4, p5, p6: TPointF;
prst1: TRectF;
i: integer;
begin
Image1.Bitmap.SetSize(300, 300); // must be set before call to BeginScene
if Image1.Bitmap.Canvas.BeginScene then
try
Image1.Bitmap.Canvas.Stroke.Color := TAlphaColors.Black;
Image1.Bitmap.Canvas.Stroke.Thickness := 1;
p1 := TPointF.Create(px, py);
for i := 0 to 360 do
if (i mod 15) = 0 then
begin
pp := i;
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
...
for i := 0 to 400 do
...
for i := 0 to 400 do
....
finally
Image1.Bitmap.Canvas.EndScene;
end;
end;
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.