Ошибка «Блокировка объекта не принадлежит» при использовании метода OnPaint - PullRequest
0 голосов
/ 24 апреля 2019

Я пытаюсь нарисовать простое изображение методом 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;

Ответы [ 2 ]

1 голос
/ 25 апреля 2019

Сообщение об ошибке «Блокировка объекта не принадлежит» - это сообщение EMonitorLockException, которое задокументировано, чтобы вызываться «всякий раз, когда поток пытается снять блокировку на не принадлежащем монитору».Поскольку вы не ответили на мой запрос о MCVE, и я не смог воспроизвести эту ошибку, я не могу подтвердить, связана ли она с неудачной установкой блокировки через Canvas.BeginScene или с чем-то еще.

Вы можете использовать TImage или TPaintBox для вашего рисунка.Использование TImage дает много преимуществ, таких как прямая загрузка файла изображения, рисование на этом изображении и сохранение вашего изображения в файл непосредственно в различных форматах, таких как .bmp, .jpg или .png (возможно, и другие).TPaintBox является более легким и не имеет собственного растрового изображения, но использует поверхность родительских компонентов для рисования (следовательно, необходим обработчик OnPaint()).Загрузка из файла / его сохранение в файл должны выполняться, например, с помощью отдельной карты TBitmap.

Так что да, вы можете продолжать использовать элемент управления TImage, если хотите, но в этом случае не используйте событие OnPaint длярисунок как ты сейчас.TImage имеет встроенный механизм для рисования себя, когда это необходимо.Вам нужно только один раз нарисовать рисунок на встроенном растровом холсте.В следующем коде изображение нарисовано в событии ButtonClick().Также обратите внимание, что с изображением TImage вы должны правильно использовать BeginScene - EndScene, как описано в документе.

Вы также должны установить TImage.Bitmap.Size перед тем, как рисовать на нем.Если это не было указано в другом месте в вашем коде того, что вы показали, то это может быть еще одна причина, по которой ваш код не создавал изображения.

Нарисуйте свое изображение на 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;
1 голос
/ 25 апреля 2019

Я думаю, что вы получаете это сообщение об ошибке, потому что вы рисуете на холсте в то время, когда вам не разрешено.Возможные причины этого:

  • Вы рисуете на растровом изображении из события рисования изображения.Изображения предназначены для отображения предварительно сгенерированных или загруженных растровых изображений, и, поскольку изменение растрового изображения должно вызывать событие OnPaint, я считаю плохой идеей вносить эти изменения из этого же события.Требуется бесконечный цикл или другие нежелательные побочные эффекты.
  • Вы неправильно используете BeginScene / EndScene.Вы должны продолжить рисование, только если BeginScene возвращает true.И на самом деле их вообще не нужно вызывать при рисовании на заданном холсте события рисования.
  • Вы (частично) используете глобальный экземпляр формы вместо текущего экземпляра (Self), которыйможет (в зависимости от вашего приложения) привести к рисованию не того экземпляра.

Небольшой отказ от ответственности: я оставил ваш код как можно больше, просто изменил то, что, по моему мнению, могло потенциально вызватьтвоя проблема.Я думаю, что все эти изменения имеют смысл, но я должен признать, что я никогда не делал много рисунков в FMX, так что, возможно, некоторые из них немного наивны или чрезмерно защитны (или явно ошибочны).

Вещи, которые являютсяотличается в этом коде от вашего:

  • Используйте TPaintbox (вам нужно добавить TPaintbox с именем «Paintbox1» и добавить этот метод в его обработчик OnPaint).Краски для прямого нанесения.Вы также можете сохранить изображение, если сможете предварительно отобразить растровое изображение на определенных событиях, таких как запуск приложения, нажатие кнопки, таймер и т. Д.
  • Правильное использование BeginScene и EndScene с блоками if и try..finally.BeginScene даст вам блокировку или нет, и вернет логическое значение в зависимости от успеха.Вы должны продолжить, только если вы действительно получили блокировку, и в этом случае вызывать только EndScene, потому что они подсчитываются, и неправильное выполнение этого может испортить счет и, следовательно, всю дальнейшую отрисовку в вашем приложении.
  • Инсульт настройки также внутри сцены.Не уверен на 100%, если нужно, но, думаю, это тоже часть рисования сцены, верно?
  • Полностью пропущен BeginScene..EndScene.Элемент управления Paintbox или Image уже должен был вызывать это сам.См. FMX.Graphics.TCanvas.BeginScene docs
  • Просто используйте Canvas.Он передается как параметр в обработчик событий, поэтому лучше использовать его, а затем попытаться найти правильный холст самостоятельно.
  • Удалено with.Это немного далеко, но похоже, что вы имели в виду глобальную переменную TTabbedForm, и, поскольку вы находитесь внутри метода TTabbedForm, вы должны иметь возможность использовать свойства и методы текущего экземпляра как есть.или добавьте 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;
...