Recent

Author Topic: Три вопроса новичка: OnPaint, ImageList и логика программы  (Read 1600 times)

majolika

  • Jr. Member
  • **
  • Posts: 73
Привет!
Имеется три новичковых вопроса о рисовании на контроле. Ради самообразования я пишу «сапёра».

Вопрос 1: OnPaint
Когда я нажимаю кнопку «старт», то показываю минное поле и заполняю его квадратами. Затем, когда я кликаю на минном поле и рисую мину. Но минное поле полностью перерисовывается, все квадраты — как ветром сдувает, и поверх рисуется мина, которая тоже исчезает при следующем клике.
Почему?

Вопрос 2: ImageList
ImageList — удобный контрол, но я обнаружил одну странную вещь: когда ярисую одну и ту же картинку при помощи ImageList и при помощи Canvas.Draw, это даёт разные результаты.
Почему?

Code: Pascal  [Select][+][-]
  1. ImageList1.Resolution[24].Draw(Bevel1.Canvas,  x, y, 0, True);
Code: Pascal  [Select][+][-]
  1. Field.Canvas.Draw(x, y, Picture_Loaded_From_TImage_Placed_On_The_Form);

На приаттаченной картинке показана разница минного поля:
фрагмент слева, более яркое минное поле — ImageList
фрагмент справа, более тёмное минное поле — Canvas.Draw

(просто на форму кидаю невидимый Timage, в Form.OnCreate подгружаю как TBitmap и рисую)
Пятой точкой чую, что собака зарыта в том, что TImage и TImageList как-то по-разному работают с загруженными изображениями, но, блин, где логика?!
«Изображение» и «список изображений» — исходя из названия, одно и то же, только в первом случае — в единственном экземпляре, во втором — в ассортименте, так сказать.

Вопрос 3: логика программы
Первый клик на кнопке «старт» работает отлично, а последующие — никак не работают.
Почему?

Ниже минимальный proof-of-concept код:

Code: Pascal  [Select][+][-]
  1. unit test_unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls, ExtCtrls;
  9.  
  10. type
  11.  
  12.   { TForm1 }
  13.  
  14.   TForm1 = class(TForm)
  15.     Bevel1: TBevel;
  16.     Button1: TButton;
  17.     ImageList1: TImageList;
  18.     procedure Bevel1MouseUp(Sender: TObject; Button: TMouseButton;
  19.       Shift: TShiftState; X, Y: Integer);
  20.     procedure Bevel1Paint(Sender: TObject);
  21.     procedure Button1Click(Sender: TObject);
  22.   private
  23.  
  24.   public
  25.  
  26.   end;
  27.  
  28. var
  29.   Form1: TForm1;
  30.   BoxSize: Integer = 24;
  31.   FieldRow: integer = 10;
  32.   FieldCol: integer = 10;
  33.   Button1Clicked: Boolean = False;
  34.   DrawABomb: Boolean = False;
  35.   CoordX, CoordY: Integer;
  36.  
  37. implementation
  38.  
  39. {$R *.lfm}
  40.  
  41. { TForm1 }
  42.  
  43. procedure TForm1.Button1Click(Sender: TObject);
  44. begin
  45.   Button1Clicked := True;
  46.   Bevel1.Width := FieldCol * BoxSize;
  47.   Bevel1.Height := FieldRow * BoxSize;
  48.   Bevel1.Show;
  49. end;
  50.  
  51. procedure TForm1.Bevel1Paint(Sender: TObject);
  52. var
  53.   x, y: Integer;
  54. begin
  55.   if Button1Clicked then begin
  56.      for x := 0 to FieldCol - 1 do begin
  57.          for y := 0 to FieldRow - 1 do begin
  58.              ImageList1.Resolution[24].Draw(Bevel1.Canvas,
  59.              (x * BoxSize), (y * BoxSize), 0, True);
  60.          end;
  61.      end;
  62.      Button1Clicked := False;
  63.   end;
  64.   if DrawABomb then begin
  65.      ImageList1.Resolution[24].Draw(Bevel1.Canvas, CoordX, CoordY, 1, True);
  66.      DrawABomb := False;
  67.   end;
  68. end;
  69.  
  70. procedure TForm1.Bevel1MouseUp(Sender: TObject; Button: TMouseButton;
  71.           Shift: TShiftState; X, Y: Integer);
  72. begin
  73.   if Button = mbLeft then begin
  74.      DrawABomb := True;
  75.      CoordX := X; CoordY := Y;
  76.      Bevel1.Repaint;
  77.   end;
  78. end;
  79.  
  80. end.
  81.  
Lazarus 3.8 (rev lazarus_3_8) FPC 3.2.2 x86_64-win64-win32/win64

Seenkao

  • Hero Member
  • *****
  • Posts: 674
    • New ZenGL.
Quote
Но минное поле полностью перерисовывается, все квадраты — как ветром сдувает, и поверх рисуется мина, которая тоже исчезает при следующем клике.
При каждом клике, поле должно перерисовываться, это по сути правильно. Не хотите чтоб перерисовывалось поле, значит надо отрисовывать определённый квадрат (квадраты) который открыли. А это другая логика, отличная от той, что используете вы.

На вашу логику надо накладывать всё, что уже открыто.
Rus: Стремлюсь к созданию минимальных и достаточно быстрых приложений.

Eng: I strive to create applications that are minimal and reasonably fast.
Working on ZenGL

majolika

  • Jr. Member
  • **
  • Posts: 73
Спасибо, Seenkao!
Но мне уже помогли со всеми возникавшими вопросами в англоязычной части форума. Там всё несколько оживлённее, нежели тут, в русскоязычной разделе.
Я рисовал прямо на контроле, что в данном случае не очень-то правильно.
В общем, я уже почти со всем разобрался и почти завершил свою первую программу на Lazarus: вариацию Сапёра с элементами RPG. Сейчас навожу последние штрихи.
« Last Edit: February 20, 2025, 09:59:32 am by majolika »
Lazarus 3.8 (rev lazarus_3_8) FPC 3.2.2 x86_64-win64-win32/win64

majolika

  • Jr. Member
  • **
  • Posts: 73
Но с одной штукой я пока не разобрался. Может, у вас найдётся время помочь?

Я там рисую маленькую рукотворную анимацию при помощи таймера.
Конструкция базово такая:

Code: Pascal  [Select][+][-]
  1. procedure StartAnimation;
  2.   AnimationFrame := 0;
  3.   Timer1.Enabled := True;
  4.  
  5. procedure TForm1.Timer1Timer;
  6.   // рисуем кадр анимации
  7.   if AnimationFrame > AnimationFramesCount then
  8.      StopAnimation;
  9.  
  10. procedure StopAnimation;
  11.   Timer1.Enabled := False;

И при определённых условиях в обработчике событий мыши вызывается StartAnimation.

Проблема в том, что после вызова StartAnimation цикл обработки событий спокойно продолжается. А мне надо как-то его притормозить, поставить на паузу, пока анимация не отрисуется, т.е. чтобы программа ждала конца анимации и уже потом продолжала работать.
Щас, пока писал, пришла в голову мысль, что, может, просто вставить пустой цикл, выполняющийся до тех пор, пока Timer.Enabled не станет False?
Но это какое-то топорное решение (если оно, вообще, будет работать). Может, есть какое-то более правильное решение?
« Last Edit: February 20, 2025, 10:18:35 am by majolika »
Lazarus 3.8 (rev lazarus_3_8) FPC 3.2.2 x86_64-win64-win32/win64

Seenkao

  • Hero Member
  • *****
  • Posts: 674
    • New ZenGL.
Надо создать флаг, который будет включаться, когда проигрывается анимация. Если флаг включен, то значит события ни какие не обрабатываем и выходим из обработки. Если выключен, то обработка событий происходит.
Rus: Стремлюсь к созданию минимальных и достаточно быстрых приложений.

Eng: I strive to create applications that are minimal and reasonably fast.
Working on ZenGL

majolika

  • Jr. Member
  • **
  • Posts: 73
Надо создать флаг, который будет включаться, когда проигрывается анимация.
А Timer.Enabled не может служить таким флагом?

значит события ни какие не обрабатываем и выходим из обработки
А это как? С помощью Application.ProcessMessages?
Я пока так и не понял, как именно эта штука работает.
« Last Edit: February 20, 2025, 11:42:47 am by majolika »
Lazarus 3.8 (rev lazarus_3_8) FPC 3.2.2 x86_64-win64-win32/win64

Seenkao

  • Hero Member
  • *****
  • Posts: 674
    • New ZenGL.
Quote
А Timer.Enabled не может служить таким флагом?
Можно, но не желательно. Лучше ввести свой собственный флаг (булеву переменную).

Quote
А это как? С помощью Application.ProcessMessages?
в процедуре обработки, например клавиш или мыши, пишем:
  Если флаг, то
  выходим.

Вот и вся проблема.
Rus: Стремлюсь к созданию минимальных и достаточно быстрых приложений.

Eng: I strive to create applications that are minimal and reasonably fast.
Working on ZenGL

majolika

  • Jr. Member
  • **
  • Posts: 73
в процедуре обработки, например клавиш или мыши, пишем:
  Если флаг, то
  выходим.
Понял. Спасибо!
Но это будет работать только внутри контрола, к которому привязан обработчик событий (в моём случае — PaintBox).
С одной стороны — это уже полдела и хрен бы с ним, с остальным; а с другой стороны — хочется, чтобы вся программа замирала.
Но в любом случае — спасибо!
Lazarus 3.8 (rev lazarus_3_8) FPC 3.2.2 x86_64-win64-win32/win64

Nimbus

  • New Member
  • *
  • Posts: 15
Как простейшее решение, вы можете блокировать события отдельных контролов (либо всей формы), устанавливая их значение Enabled := False на нужное время.

majolika

  • Jr. Member
  • **
  • Posts: 73
Как простейшее решение, вы можете блокировать события отдельных контролов (либо всей формы), устанавливая их значение Enabled := False на нужное время.
Всю форму блокировать таким способом я пробовал. Она дёргаться начинает, если кликнуть в процессе анимации. Выглядит не очень.
А вот PaintBox блокировать на время анимации — это интересная мысль. Надо попробовать. Спасибо за подсказку! :)

У меня главная беда-то какая: когда срабатывает триггер проигрыша/победы, выводится соответствующее окошко, а вот анимация получения урона продолжает проигрываться и может вылезать из-под окошка проигрыша/победы. Там анимации-то — меньше секунды, но всё равно как-то не очень хорошо.
« Last Edit: February 20, 2025, 07:21:24 pm by majolika »
Lazarus 3.8 (rev lazarus_3_8) FPC 3.2.2 x86_64-win64-win32/win64

Nimbus

  • New Member
  • *
  • Posts: 15
У меня главная беда-то какая: когда срабатывает триггер проигрыша/победы, выводится соответствующее окошко, а вот анимация получения урона продолжает проигрываться и может вылезать из-под окошка проигрыша/победы. Там анимации-то — меньше секунды, но всё равно как-то не очень хорошо.

Можно ведь прекратить анимацию перед выводом окна, раз она мешает.
Либо задержать окно, до завершения анимации.

majolika

  • Jr. Member
  • **
  • Posts: 73
задержать окно, до завершения анимации.
Спасибо, капитан Очевидность!
В этом как раз вопрос и состоял.
Lazarus 3.8 (rev lazarus_3_8) FPC 3.2.2 x86_64-win64-win32/win64

Seenkao

  • Hero Member
  • *****
  • Posts: 674
    • New ZenGL.
Перед открытием окна победы/проигрыша, так же проверять флаг, проигрывается анимация или нет.
Либо, если открывается окно победы/поражения, анимация принудительно завершается.
Rus: Стремлюсь к созданию минимальных и достаточно быстрых приложений.

Eng: I strive to create applications that are minimal and reasonably fast.
Working on ZenGL

majolika

  • Jr. Member
  • **
  • Posts: 73
Перед открытием окна победы/проигрыша, так же проверять флаг, проигрывается анимация или нет.
Либо, если открывается окно победы/поражения, анимация принудительно завершается.
Да что ж всё так криво и костыльно-то с этим таймером, блин!
Lazarus 3.8 (rev lazarus_3_8) FPC 3.2.2 x86_64-win64-win32/win64

Nimbus

  • New Member
  • *
  • Posts: 15
Спасибо, капитан Очевидность!
В этом как раз вопрос и состоял.

Элементарное решение - цикл.

Code: Pascal  [Select][+][-]
  1. { Ожидание завершения анимации }
  2. while Timer1.Enabled do
  3. begin
  4.   Application.ProcessMessages;
  5.   Sleep(5);
  6. end;
  7.  
  8. { Вывод окна }
  9. ...

Либо как советует Seenkao.
Что-либо более сложное уже будет зависеть от структуры вашего приложения.

 

TinyPortal © 2005-2018