Recent

Author Topic: Canvas size confusion  (Read 460 times)

mark.chambers

  • New member
  • *
  • Posts: 9
Canvas size confusion
« on: October 29, 2019, 03:47:14 am »
Hi Folks,

I have a strange problem with Canvas on both a TPanel and a TForm - it seems to change size randomly. This is bad because I'm trying to paint onto it and I need to know how big it is. In the following output I show the location in code and width and height of the panel. The form is actually 602x349. The panel is actually 180x158. As you can see, sometimes the panel thinks it is the size of the form, sometimes the size of the panel and sometimes 0x0.

FormCreate   180, 158
FormShow     180, 158
Panel1Paint  602, 349
Panel1Click  0, 0
FormClick    180, 158
Panel1Click  180, 158
Panel1Paint  602, 349
Panel1Click  0, 0
FormClick    180, 158
Panel1Paint  602, 349

I have looked into the LCL code but got lost quite quickly.
Can anyone suggest what have I have done wrong or a way to ensure the panel size is reported correctly?

Thanks,
Mark

Code: Pascal  [Select]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, Forms, Controls, Graphics, Dialogs, ExtCtrls, StdCtrls;
  9.  
  10. type
  11.  
  12.   { TForm1 }
  13.  
  14.   TForm1 = class(TForm)
  15.     Memo1: TMemo;
  16.     Panel1: TPanel;
  17.     procedure FormClick(Sender: TObject);
  18.     procedure FormCreate(Sender: TObject);
  19.     procedure FormShow(Sender: TObject);
  20.     procedure Panel1Click(Sender: TObject);
  21.     procedure Panel1Paint(Sender: TObject);
  22.   private
  23.     C: TCanvas;
  24.     procedure DoIt;
  25.     procedure Log(S: String); overload;
  26.     procedure Log(S: String; args: array of const); overload;
  27.   public
  28.  
  29.   end;
  30.  
  31. var
  32.   Form1: TForm1;
  33.  
  34. implementation
  35.  
  36. {$R *.frm}
  37.  
  38. { TForm1 }
  39.  
  40. procedure TForm1.Log(S: String);
  41. begin
  42.   Memo1.Lines.Add(S);
  43. end;
  44.  
  45. procedure TForm1.Log(S: String; args: array of const);
  46. begin
  47.   Log(Format(S, args));
  48. end;
  49.  
  50.  
  51. procedure TForm1.FormCreate(Sender: TObject);
  52. begin
  53.   Log('FormCreate');
  54.   C := Panel1.Canvas;
  55.   C.Line(10,10,300,300);
  56.   doit;
  57. end;
  58.  
  59. procedure TForm1.FormClick(Sender: TObject);
  60. begin
  61.   Log('FormClick');
  62.   doit;
  63. end;
  64.  
  65. procedure TForm1.Panel1Click(Sender: TObject);
  66. begin
  67.   Log('Panel1Click');
  68.   doit;
  69. end;
  70.  
  71. procedure TForm1.FormShow(Sender: TObject);
  72. begin
  73.   Log('FormShow');
  74.   doit;
  75. end;
  76.  
  77. procedure TForm1.Panel1Paint(Sender: TObject);
  78. begin
  79.   Log('Panel1Paint');
  80.   doit;
  81. end;
  82.  
  83. procedure TForm1.Doit;
  84. begin
  85.   Log('%d, %d', [Panel1.Canvas.Width, Panel1.Canvas.Height]);
  86.   C.Line(10,10,300,300);
  87. end;
  88.  
  89. end.  
  90.  

Code: Pascal  [Select]
  1. object Form1: TForm1
  2.   Left = 368
  3.   Height = 349
  4.   Top = 230
  5.   Width = 602
  6.   Caption = 'Form1'
  7.   ClientHeight = 349
  8.   ClientWidth = 602
  9.   DesignTimePPI = 120
  10.   OnClick = FormClick
  11.   OnCreate = FormCreate
  12.   OnShow = FormShow
  13.   LCLVersion = '6.8'
  14.   object Panel1: TPanel
  15.     Left = 408
  16.     Height = 158
  17.     Top = 104
  18.     Width = 180
  19.     Caption = 'Panel1'
  20.     TabOrder = 0
  21.     OnClick = Panel1Click
  22.     OnPaint = Panel1Paint
  23.   end
  24.   object Memo1: TMemo
  25.     Left = 4
  26.     Height = 333
  27.     Top = 12
  28.     Width = 375
  29.     Lines.Strings = (
  30.       'Memo1'
  31.     )
  32.     TabOrder = 1
  33.   end
  34. end
  35.  

Handoko

  • Hero Member
  • *****
  • Posts: 3237
  • My goal: build my own game engine using Lazarus
Re: Canvas size confusion
« Reply #1 on: October 29, 2019, 06:43:43 am »
Instead of paint on TPanel's canvas, you should use TPaintBox.

mark.chambers

  • New member
  • *
  • Posts: 9
Re: Canvas size confusion
« Reply #2 on: October 29, 2019, 07:14:28 am »
Thanks Handoko,

I tried your suggestion of using a TPaintBox instead of a TPanel. The Canvas width and height are reported as either 0x0 or the Form's width and height, rather then the dimensions of the TPaintBox.

So, no different.  Any other ideas?

Regards,
Mark

howardpc

  • Hero Member
  • *****
  • Posts: 3203
Re: Canvas size confusion
« Reply #3 on: October 29, 2019, 09:54:06 am »
Try this, removing C, your independent reference to a canvas.
Code: Pascal  [Select]
  1.   TForm1 = class(TForm)
  2.     Memo1: TMemo;
  3.     Panel1: TPanel;
  4.     procedure FormClick(Sender: TObject);
  5.     procedure FormCreate(Sender: TObject);
  6.     procedure FormShow(Sender: TObject);
  7.     procedure Panel1Click(Sender: TObject);
  8.     procedure Panel1Paint(Sender: TObject);
  9.   private
  10.     procedure DoIt;
  11.     procedure Log(S: String); overload;
  12.     procedure Log(S: String; args: array of const); overload;
  13.   end;
  14.  
  15. var
  16.   Form1: TForm1;
  17.  
  18. implementation
  19.  
  20. {$R *.lfm}
  21.  
  22. procedure TForm1.FormCreate(Sender: TObject);
  23. begin
  24.   Log('FormCreate');
  25.   DoIt;
  26. end;
  27.  
  28. procedure TForm1.FormClick(Sender: TObject);
  29. begin
  30.   Log('FormClick');
  31.   DoIt;
  32. end;
  33.  
  34. procedure TForm1.FormShow(Sender: TObject);
  35. begin
  36.   Log('FormClick');
  37.   DoIt;
  38. end;
  39.  
  40. procedure TForm1.Panel1Click(Sender: TObject);
  41. begin
  42.   Log('Panel1Click');
  43.   DoIt;
  44. end;
  45.  
  46. procedure TForm1.Panel1Paint(Sender: TObject);
  47. begin
  48.   Log('Panel1Paint');
  49.   DoIt;
  50. end;
  51.  
  52. procedure TForm1.DoIt;
  53. const
  54.   t: Integer = 10;
  55. begin
  56.   Panel1.Canvas.Clear;
  57.   Log('%d, %d', [Panel1.Canvas.Width, Panel1.Canvas.Height]);
  58.   Inc(t, 10);
  59.   Panel1.Canvas.Line(10,t,300,300);
  60. end;
  61.  
  62. procedure TForm1.Log(S: String);
  63. begin
  64.   Memo1.Lines.Add(S);
  65. end;
  66.  
  67. procedure TForm1.Log(S: String; args: array of const);
  68. begin
  69.   Memo1.Lines.Add(S, args);
  70. end;

wp

  • Hero Member
  • *****
  • Posts: 6485
Re: Canvas size confusion
« Reply #4 on: October 29, 2019, 10:17:39 am »
In Delphi there is no such thing as TCanvas.Width or TCanvas.Height - the effective size of the canvas is determined by the class to which the canvas belongs, e.g. TBitmap.Width, TForm.Width, TPaintbox.Width. I know that Lazarus does provide these properties for TCanvas, but I am not sure whether the values are always reliable because the Canvas is tied to its Handle and this is usually valid only withing a painting operation. In fact, your DoIt method is drawing outside paint operations which is even prohibited in some widgetsets.
Lazarus trunk / fpc 3.0.4 / all 32-bit on Win-10

Thaddy

  • Hero Member
  • *****
  • Posts: 9293
Re: Canvas size confusion
« Reply #5 on: October 29, 2019, 10:55:31 am »
In Delphi there is no such thing as TCanvas.Width or TCanvas.Height - the effective size of the canvas is determined by the class to which the canvas belongs, e.g. TBitmap.Width, TForm.Width, TPaintbox.Width. I know that Lazarus does provide these properties for TCanvas, but I am not sure whether the values are always reliable because the Canvas is tied to its Handle and this is usually valid only withing a painting operation. In fact, your DoIt method is drawing outside paint operations which is even prohibited in some widgetsets.
Not even that: the paint area is determined by ClientHeight and ClientWidth.
also related to equus asinus.

kupferstecher

  • Sr. Member
  • ****
  • Posts: 324
Re: Canvas size confusion
« Reply #6 on: October 29, 2019, 11:00:27 am »
For self-drawing I use TCustomControl, works for me.

Description here:
https://wiki.lazarus.freepascal.org/Developing_with_Graphics#Create_a_custom_control_which_draws_itself

Thaddy

  • Hero Member
  • *****
  • Posts: 9293
Re: Canvas size confusion
« Reply #7 on: October 29, 2019, 11:04:55 am »
Yup. Good tip.
also related to equus asinus.

mark.chambers

  • New member
  • *
  • Posts: 9
Re: Canvas size confusion
« Reply #8 on: October 29, 2019, 11:09:27 am »
OK, I see. So I need to use the Canvas's owner's ClientHeight and ClientWidth, not the Height and Width on the Canvas (which sound like they shouldn't even exist).

What do you mean though by "drawing outside paint operations"?

kupferstecher

  • Sr. Member
  • ****
  • Posts: 324
Re: Canvas size confusion
« Reply #9 on: October 29, 2019, 11:25:32 am »
You should read the link that I postet. You can't randomly draw on the canvas, because you don't know when the OS reads from it for displaying the contents.

In short: Draw on the canvas of a TBitmap and assign it to the control's canvas ONLY within the Paint-Event.

kupferstecher

  • Sr. Member
  • ****
  • Posts: 324
Re: Canvas size confusion
« Reply #10 on: October 29, 2019, 11:34:19 am »
In addition:
If you want to change the content, i.e. force a redraw, the you should call the control's Invalidate-Method:
 AControl.Invalidate;

Then the Paint-method will be called by the system, where you draw the updated contents.

mark.chambers

  • New member
  • *
  • Posts: 9
Re: Canvas size confusion
« Reply #11 on: October 29, 2019, 12:33:44 pm »
Read and understood. I have a double buffered example going now.

Thanks kupferstecher.