Recent

Author Topic: TPaintBox not cleared  (Read 5808 times)

segfault

  • Full Member
  • ***
  • Posts: 107
TPaintBox not cleared
« on: October 12, 2017, 02:15:26 pm »
I'm writing a list of characters of fixed length to a TPaintBox (when the list length exceeds 10, the first character is removed). Every time one of 2 buttons is clicked the character on the button is drawn on the canvas, which should be cleared prior to drawing the current list. Everything works ok but the canvas isn't being cleared which means that the previous list remains. Here's the code:

Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls,
  9.   ExtCtrls;
  10.  
  11. type
  12.  
  13.   { TForm1 }
  14.  
  15.   TForm1 = class(TForm)
  16.     banker: TButton;
  17.     player: TButton;
  18.     marquee: TPaintBox;
  19.     procedure bankerClick(Sender: TObject);
  20.     procedure FormCreate(Sender: TObject);
  21.     procedure playerClick(Sender: TObject);
  22.   private
  23.     { private declarations }
  24.   public
  25.     { public declarations }
  26.     procedure update_marq;
  27.   end;
  28.  
  29. var
  30.   Form1: TForm1;
  31.   marq_list : string;
  32. implementation
  33.  
  34. {$R *.lfm}
  35.  
  36. { TForm1 }
  37. procedure TForm1.update_marq;
  38. var
  39.   xpos, ypos, i : integer;
  40. begin
  41.   marquee.canvas.Clear;
  42.   xpos := 0;
  43.   ypos := 0;
  44.   marquee.canvas.brush.style := bsclear;
  45.   for i := 1 to length(marq_list) do begin
  46.     if marq_list[i] = '1' then
  47.       marquee.canvas.textout(xpos,ypos,'B')
  48.     else
  49.       marquee.canvas.textout(xpos,ypos,'    P');
  50.     inc(ypos, 12)
  51.   end;
  52. end;
  53.  
  54. procedure TForm1.bankerClick(Sender: TObject);
  55. begin
  56.   marq_list := marq_list + '1';
  57.   if length(marq_list) > 10 then delete(marq_list,1,1);
  58.   update_marq;
  59. end;
  60.  
  61. procedure TForm1.FormCreate(Sender: TObject);
  62. begin
  63.   marq_list := '';
  64. end;
  65.  
  66. procedure TForm1.playerClick(Sender: TObject);
  67. begin
  68.   marq_list := marq_list + '0';
  69.   if length(marq_list) > 10 then delete(marq_list,1,1);
  70.   update_marq;
  71. end;
  72.  
  73. end.
 
screenshot here: https://s1.postimg.org/5d52u155xb/snapshot1.png           
Thanks for any help.

wp

  • Hero Member
  • *****
  • Posts: 11916
Re: TPaintBox not cleared
« Reply #1 on: October 12, 2017, 02:32:00 pm »
A paintbox does not "remember" what you painted in an OnClick event. How could it? It has nothing to store that information. It does have an OnPaint event, though, and this is where you must put your painting code. The OnClick should only contain a call to Paintbox.Invalidate or Paintbox.Refresh ("Invalidate" preferred because it elimates multiple requests for repainting).

Untested...
Code: Pascal  [Select][+][-]
  1. procedure TForm1.MarqueePaint(Sender: TObject);
  2. // instead of "Update_marq", ... or call Update_marq here
  3. var
  4.   xpos, ypos, i : integer;
  5. begin
  6.   marquee.canvas.Clear;
  7.   xpos := 0;
  8.   ypos := 0;
  9.   marquee.canvas.brush.style := bsclear;
  10.   for i := 1 to length(marq_list) do begin
  11.     if marq_list[i] = '1' then
  12.       marquee.canvas.textout(xpos,ypos,'B')
  13.     else
  14.       marquee.canvas.textout(xpos,ypos,'    P');
  15.     inc(ypos, 12)
  16.   end;
  17. end;
  18.  
  19. procedure TForm1.bankerClick(Sender: TObject);
  20. begin
  21.   marq_list := marq_list + '1';
  22.   if length(marq_list) > 10 then delete(marq_list,1,1);
  23.   Marquee.Invalidate;  // Request to repaint the paintbox
  24. end;
  25.  
  26. procedure TForm1.playerClick(Sender: TObject);
  27. begin
  28.   marq_list := marq_list + '0';
  29.   if length(marq_list) > 10 then delete(marq_list,1,1);
  30.   Marquee.Invalidate;  // Request to repaint the paintbox
  31. end;

segfault

  • Full Member
  • ***
  • Posts: 107
Re: TPaintBox not cleared
« Reply #2 on: October 12, 2017, 03:17:11 pm »
Thanks, your code works fine.

A paintbox does not "remember" what you painted in an OnClick event. How could it? It has nothing to store that information.

I find this confusing... why should 'remembering' anything even be relevant if the paintbox is cleared prior to drawing on it? The problem appeared to be that the paintbox was remembering which is why the previous list remained and didn't seem to be cleared even though I had used 'marquee.canvas.Clear'.

But anyway your comment tells me that I don't really understand how objects and the GUI model works at all. Up until now I've only written procedural programs and have been avoiding OOP. It seems I can't avoid it any longer (sigh). Quite frankly, it does my head in. There are lots of tutorials around but they are either very terse and assume too much or else they are too simple. Would it help to read some old Delphi books? Lazarus is based on Delphi 7 right?

I'm finding it all a bit overwhelming.
« Last Edit: October 12, 2017, 03:18:59 pm by segfault »

wp

  • Hero Member
  • *****
  • Posts: 11916
Re: TPaintBox not cleared
« Reply #3 on: October 12, 2017, 05:04:11 pm »
I find this confusing... why should 'remembering' anything even be relevant if the paintbox is cleared prior to drawing on it? The problem appeared to be that the paintbox was remembering which is why the previous list remained and didn't seem to be cleared even though I had used 'marquee.canvas.Clear'.
What I wanted to say is: You never should put any drawing code into any event except for OnPaint. Suppose your OnClick handler fills the background of the paintbox in red color, nothing else, just for simplicity. When the user clicks the button the paintbox will be red - fine. But now the user decides to resize the form. When this happens Windows requests your program to repaint itself again to adapt to the new size. But the paintbox does not "know" how to repaint itself - it had been painted in the button's OnClick event, but this is long gone; therefore the paintbox is erased (this is the standard behavior built into the paintbox). If, on the other hand, you had put the painting code into the OnPaint event of the paintbox it would be called automatically, and the paintbox would be red again. Just look at the attached demo.

Would it help to read some old Delphi books? Lazarus is based on Delphi 7 right?
Lazarus is not "based" on Delphi 7, but it gets close. Although there may be subtle differences here and there, a well-written Delphi book will help you more than a poorly-written Lazarus article somewhere in the net. (Of course I don't want to say that all Lazarus articles are poorly written).

segfault

  • Full Member
  • ***
  • Posts: 107
Re: TPaintBox not cleared
« Reply #4 on: October 12, 2017, 07:54:11 pm »
Thanks for the nice demo which makes the point nicely. However I'm still a little puzzled why my original code appeared to work ok but didn't clear the canvas in spite of the canvas.clear statement. Just trying to understand.  :)

Although there may be subtle differences here and there, a well-written Delphi book will help you more than a poorly-written Lazarus article somewhere in the net. (Of course I don't want to say that all Lazarus articles are poorly written).

Not complaining about the Lazarus/FP documentation which is good and comprehensive, but I'm surprised there isn't at least one book aimed at beginners. According to the wiki there is 'Lazarus : The Complete Guide', but the link is dead. I also found this thread but no news of when there will be a second edition or replacement-

http://forum.lazarus.freepascal.org/index.php/topic,34613.msg227166.html#msg227166

Judging by the number of views of that thread there appears to be a demand for such a book...

wp

  • Hero Member
  • *****
  • Posts: 11916
Re: TPaintBox not cleared
« Reply #5 on: October 12, 2017, 07:59:23 pm »
However I'm still a little puzzled why my original code appeared to work ok but didn't clear the canvas in spite of the canvas.clear statement. Just trying to understand.
Me too. Could you put that code into a little project to upload here? (Please only -pas, .lfm, .lpi and .lpr files packed into a common zip which you can upload from "Attachments and other options" below the edit field here).

segfault

  • Full Member
  • ***
  • Posts: 107
Re: TPaintBox not cleared
« Reply #6 on: October 13, 2017, 10:17:43 am »
Ok, attached. Also I notice that although the program seems to work (apart from the canvas not being cleared), the marquee (paintbox) should be white but it's not.

wp

  • Hero Member
  • *****
  • Posts: 11916
Re: TPaintBox not cleared
« Reply #7 on: October 13, 2017, 11:14:58 am »
All these observations are a consequence of the fact that you don't provide an OnPaint event handler. Call update_marq from the the OnPaint handler (double-click on "OnPaint" of the paintbox), replace the call the update_marq in the buttons' OnClick by marquee.Invalidate, and everything will work as expected:

Code: Pascal  [Select][+][-]
  1. procedure TForm1.bankerClick(Sender: TObject);
  2. begin
  3.   marq_list := marq_list + '1';
  4.   if length(marq_list) > 10 then delete(marq_list,1,1);
  5.   marquee.Invalidate;  // replaces: "update_marq"
  6. end;
  7.  
  8. procedure TForm1.FormCreate(Sender: TObject);
  9. begin
  10.   marq_list := '';
  11. end;
  12.  
  13. procedure TForm1.marqueePaint(Sender: TObject);  // new
  14. begin
  15.   update_marq;
  16. end;
  17.  
  18. procedure TForm1.playerClick(Sender: TObject);
  19. begin
  20.   marq_list := marq_list + '0';
  21.   if length(marq_list) > 10 then delete(marq_list,1,1);
  22.   marquee.Invalidate;  // replaces "update_marq"
  23. end;
  24.  
  25. procedure TForm1.update_marq;
  26. var
  27.   xpos, ypos, i : integer;
  28. begin
  29.   marquee.canvas.Clear;
  30.   xpos := 0;
  31.   ypos := 0;
  32.   marquee.canvas.brush.style := bsclear;
  33.   for i := 1 to length(marq_list) do begin
  34.     if marq_list[i] = '1' then begin
  35.       marquee.canvas.textout(xpos,ypos,'B');
  36.     end
  37.     else
  38.       marquee.canvas.textout(xpos,ypos,'    P');
  39.     inc(ypos, 12)
  40.   end;
  41. end;

jamie

  • Hero Member
  • *****
  • Posts: 6130
Re: TPaintBox not cleared
« Reply #8 on: October 14, 2017, 03:18:01 am »
I think what the issue here is the fact that the surface isn't getting cleaned from the
prior paint operation. this will cause different characters to stack on top of each other unless a full repaint is done which requires a page to be erased.

 A Tbitmap should be used to build the image in the background the same way it is
being done now, but only clear the surface first using FillREct prior to doing the surface
drawing. Then in the Onpaint handler of the PaintBox, simply need to canvas.Draw it on
there.

 Of course this could be over their head at this time.

The only true wisdom is knowing you know nothing

 

TinyPortal © 2005-2018