Recent

Author Topic: Invalid Function Call  (Read 18896 times)

Birger52

  • Sr. Member
  • ****
  • Posts: 309
Invalid Function Call
« on: August 18, 2018, 07:59:29 pm »
Win7 64bit, Laz 1.8.4 FPC 3.0.4
New to Lazarus, but not to programming.
(Actually Borland Delhi was my first OOP after Commodore 64, and Turbo Basic.
I know many things have changed.)

Creating my own things.
I'm getting an "EInvalidOperation" exception: "Control '' has no parent window" it claims. (image)

Exception do give a filename and a location - (don't know if theres a call stack somewhere...) - but no referance to my own code.

So I set breakpoints, and inspect variables and values.
And I find the excpetion occurs in my code, at a line that says (image)
  lh := Self.Canvas.TextHeight('Tekstgy');   
(Purpose is to get the height of a text in the control, for later adding a suitable number of labels...)
Self DO have a name - not sure why the exception claims it does not..

Anyhow - image show clearly, that there apparently is an attempt to call something other than what is in the sourcecode...
Self.Canvas.TextHeightTextHeightTextHeight
is not really what it says right there....

So the error in my code, comes from?
any explanation?
Or hint on how to avoid this kind of things?
Lazarus 2.0.8 FPC 3.0.4
Win7 64bit
Playing and learning - strictly for my own pleasure.

Birger52

  • Sr. Member
  • ****
  • Posts: 309
Re: Invalid Function Call
« Reply #1 on: August 18, 2018, 08:11:46 pm »
Seems this happens, because the procedure is called from within the controls constructor.
Lazarus 2.0.8 FPC 3.0.4
Win7 64bit
Playing and learning - strictly for my own pleasure.

Handoko

  • Hero Member
  • *****
  • Posts: 5131
  • My goal: build my own game engine using Lazarus
Re: Invalid Function Call
« Reply #2 on: August 18, 2018, 08:18:14 pm »
Any source code for us to test?

Blaazen

  • Hero Member
  • *****
  • Posts: 3237
  • POKE 54296,15
    • Eye-Candy Controls
Re: Invalid Function Call
« Reply #3 on: August 18, 2018, 08:22:07 pm »
What is TLabelList? It is Dephi visual component ported to Lazarus? And is created at design-time or run-time?

This kind of bugs happen because LCL itself is abstract and Handle is not yet allocated (later than in Delphi).

Solution is usually checking:
Code: Pascal  [Select][+][-]
  1. if HandeAllocated then
  2.   lh := Self.Canvas.TextHeight('Tekstgy');  
  3.  
or move code to method InitializeWnd, message method WMSize or similar.
Lazarus 2.3.0 (rev main-2_3-2863...) FPC 3.3.1 x86_64-linux-qt Chakra, Qt 4.8.7/5.13.2, Plasma 5.17.3
Lazarus 1.8.2 r57369 FPC 3.0.4 i386-win32-win32/win64 Wine 3.21

Try Eye-Candy Controls: https://sourceforge.net/projects/eccontrols/files/

lucamar

  • Hero Member
  • *****
  • Posts: 4219
Re: Invalid Function Call
« Reply #4 on: August 18, 2018, 08:23:08 pm »
I would need to see your whole code, specially where you create your control, but my guess (and it is *just* a guess) is that you need to assign it a Parent. For example, using a simple label created dynamically inside a standard form:

Code: Pascal  [Select][+][-]
  1. interface
  2.  
  3. {...}
  4.  
  5. type
  6.   TForm1 = class(TForm)
  7.     <... automatic declarations ...>
  8.   public
  9.     MeLabel: TLabel;
  10.     procedure MakeMeLabel;
  11.   end;
  12.  
  13. implementation
  14.  
  15. {...}
  16.  
  17. procedure TForm1.MakeMeLabel;
  18. begin
  19.   MeLabel := TLabel.Create(Self); {<- *Owner* is now the current TForm1 instance}
  20.   MeLabel.Parent := Self;         {<- but it also *needs* a Parent to know where
  21.                                       to draw itself (among other things) }
  22.   MeLabel.Caption := 'I''m just a humble label';
  23. end;
  24.  
  25. {...}
  26.  
« Last Edit: August 18, 2018, 08:26:35 pm by lucamar »
Turbo Pascal 3 CP/M - Amstrad PCW 8256 (512 KB !!!) :P
Lazarus/FPC 2.0.8/3.0.4 & 2.0.12/3.2.0 - 32/64 bits on:
(K|L|X)Ubuntu 12..18, Windows XP, 7, 10 and various DOSes.

Birger52

  • Sr. Member
  • ****
  • Posts: 309
Re: Invalid Function Call
« Reply #5 on: August 18, 2018, 10:47:47 pm »
TLabelList is a descendent of TPanel, created in Lazarus.
It is inserted into another version of TPanel at designtime.

Kind of thought Parent was set, when component placed at designtime.
But setting Parent does cure the problem.

Leaving the odd thing, that TPanels constructor Create requires a TComponent, but Parent is a WinControl, so the Parent must be typecasted...
Code: Pascal  [Select][+][-]
  1. constructor TLabelList.Create(TheOwner: TComponent);
  2. begin
  3.   inherited Create(TheOwner);
  4.   Parent := TWinControl(TheOwner);
  5. ...
  6.  

Lazarus 2.0.8 FPC 3.0.4
Win7 64bit
Playing and learning - strictly for my own pleasure.

taazz

  • Hero Member
  • *****
  • Posts: 5368
Re: Invalid Function Call
« Reply #6 on: August 19, 2018, 04:28:52 am »
TLabelList is a descendent of TPanel, created in Lazarus.
It is inserted into another version of TPanel at designtime.

Kind of thought Parent was set, when component placed at designtime.
But setting Parent does cure the problem.

Leaving the odd thing, that TPanels constructor Create requires a TComponent, but Parent is a WinControl, so the Parent must be typecasted...
Code: Pascal  [Select][+][-]
  1. constructor TLabelList.Create(TheOwner: TComponent);
  2. begin
  3.   inherited Create(TheOwner);
  4.   Parent := TWinControl(TheOwner);
  5. ...
  6.  
I suggest to change the height calculation to use an independent method when the parent is not set or the window is not allocated yet to avoid setting the parent multiple times with out reason.
Good judgement is the result of experience … Experience is the result of bad judgement.

OS : Windows 7 64 bit
Laz: Lazarus 1.4.4 FPC 2.6.4 i386-win32-win32/win64

Birger52

  • Sr. Member
  • ****
  • Posts: 309
Re: Invalid Function Call
« Reply #7 on: August 19, 2018, 11:59:40 am »
Parent is only assigned once, in the construcor.

I was looking for an independent method, of calculating the resulting pixelheight of a given font.
But i can't seem to find one...
Lazarus 2.0.8 FPC 3.0.4
Win7 64bit
Playing and learning - strictly for my own pleasure.

Handoko

  • Hero Member
  • *****
  • Posts: 5131
  • My goal: build my own game engine using Lazarus
Re: Invalid Function Call
« Reply #8 on: August 19, 2018, 12:48:29 pm »
I checked the source code I ever wrote to generate OpenGL texture from system font:

Code: Pascal  [Select][+][-]
  1. procedure TGSLabel.GenerateTexture;
  2. var
  3.   Bitmap: TBitmap;
  4.   ScanData: PRGBQuad;
  5.   TextSize: TSize;
  6.   NewWidth, X, Y, i: integer;
  7. begin
  8.  
  9.   // Draw the text on a temporary bitmap
  10.   Bitmap. := TBitmap.Create;
  11.   with Bitmap, Bitmap.Canvas, TextSize do
  12.   begin
  13.     PixelFormat := pf32bit;
  14.     AntialiasingMode := amOn;
  15.     Font.Size := Round(FGraphicsScreen.FOutput.Height * FHreal / FFontQuality);
  16.     Font.Quality := fqAntialiased; // Windows favors fqAntialiased
  17.     TextSize := TextExtent(FCaption);
  18.     Inc(cx, 2);
  19.     Inc(cy, 2);
  20.     SetSize(cx, cy);
  21.     Brush.Color := clBlack;
  22.     Clear;
  23.     Font.Color := clWhite;
  24.     TextOut(2, 0, FCaption);
  25.     if Assigned(FTextureBuffer) then
  26.       SetLength(FTextureBuffer, 0);
  27.     SetLength(FTextureBuffer, cx * cy);
  28.     FTextureAspect := cx / cy;
  29.     FTextureHeight := cy;
  30.     ;
  31.     FTextureWidth := cx;
  32.     NewWidth := cx - 1;
  33.   end;
  34.  
  35.   // Map the temporary bitmap data to the output
  36.   i := NewWidth;
  37.   for Y := (FTextureHeight - 1) downto 1 do
  38.   begin
  39.     ScanData := Bitmap.ScanLine[Y];
  40.     for X := 0 to NewWidth do
  41.     begin
  42.       if (X > 0) and (X < NewWidth) then
  43.         FTextureBuffer[i] := FColorText or (ScanData^.rgbBlue shl 24);
  44.       Inc(ScanData);
  45.       Inc(i);
  46.     end;
  47.   end;
  48.  
  49.   // Finalization
  50.   Bitmap.Free;
  51.   CalculateTextPosition;
  52.  
  53. end;

You can use TextExtent(FCaption); (see line #17), the result is in cx and cy.

taazz

  • Hero Member
  • *****
  • Posts: 5368
Re: Invalid Function Call
« Reply #9 on: August 19, 2018, 01:01:10 pm »
Parent is only assigned once, in the construcor.
and once the lfm is loaded from the resources. The constructor is the wrong place to assign the parent especially with the assumption that the owner is a container control.
I was looking for an independent method, of calculating the resulting pixelheight of a given font.
But i can't seem to find one...
here is a quick and dirty one, there are others a bit more elaborate too.
Code: Pascal  [Select][+][-]
  1. function TextHeight(const aFont:TFont; const aText:String):Integer;
  2. var
  3.   vBmp:TBitmap;
  4. begin
  5.   Result := 0;
  6.   vBmp := TBitmap.Create;
  7.   try
  8.     vBmp.Canvas.Font := aFont;
  9.     Result := vBmp.Canvas.TextHeight(aText);
  10.   finally
  11.     vBmp.Free;
  12.   end;
  13. end;
  14. function TextExtent(const aFont:TFont; const aText:String):TSize;
  15. var
  16.   vBmp:TBitmap;
  17. begin
  18.   Result := 0;
  19.   vBmp := TBitmap.Create;
  20.   try
  21.     vBmp.Canvas.Font := aFont;
  22.     Result := vBmp.Canvas.TextExtent(aText);
  23.   finally
  24.     vBmp.Free;
  25.   end;
  26. end;    
  27.  

EDIT:
Added a function that returns both the height and the width.
« Last Edit: August 19, 2018, 01:26:57 pm by taazz »
Good judgement is the result of experience … Experience is the result of bad judgement.

OS : Windows 7 64 bit
Laz: Lazarus 1.4.4 FPC 2.6.4 i386-win32-win32/win64

lucamar

  • Hero Member
  • *****
  • Posts: 4219
Re: Invalid Function Call
« Reply #10 on: August 19, 2018, 02:09:27 pm »
Kind of thought Parent was set, when component placed at designtime.

It should be ... though I don't know enough of Lazarus interiorities to say whether it's really so.

Leaving the odd thing, that TPanels constructor Create requires a TComponent, but Parent is a WinControl, so the Parent must be typecasted...

Simplifying a lot, Owner is responsible of the management of its contained components (creation, destruction, etc.), while Parent is responsible of its visual characteristics (placement on the screen, look, etc.) so it needs the features of visual controls i.e. TWinControl descendants.
Turbo Pascal 3 CP/M - Amstrad PCW 8256 (512 KB !!!) :P
Lazarus/FPC 2.0.8/3.0.4 & 2.0.12/3.2.0 - 32/64 bits on:
(K|L|X)Ubuntu 12..18, Windows XP, 7, 10 and various DOSes.

Handoko

  • Hero Member
  • *****
  • Posts: 5131
  • My goal: build my own game engine using Lazarus
Re: Invalid Function Call
« Reply #11 on: August 19, 2018, 02:38:01 pm »
Kind of thought Parent was set, when component placed at designtime.

It should be ... though I don't know enough of Lazarus interiorities to say whether it's really so.

If I understood correctly, what @taazz meant is the constructor has already called because you have this: {$R *.lfm} in the source code.

Birger52

  • Sr. Member
  • ****
  • Posts: 309
Re: Invalid Function Call
« Reply #12 on: August 19, 2018, 09:38:12 pm »
If Owner is responsible for creating and free the object, and Parent is responsible for drawing resizing etc, the contructor is imho. the right place to put the Parnet assignment.
It is a Panel inserted in another panel at designtime. The first panel will be responsible for creating, deleting and drawing the second. And it is not ging to change.
So I do not really see any reason for doing it any other way.

As for the textheight - I don't really see the suggested solution being simpler in any way.

Lazarus 2.0.8 FPC 3.0.4
Win7 64bit
Playing and learning - strictly for my own pleasure.

taazz

  • Hero Member
  • *****
  • Posts: 5368
Re: Invalid Function Call
« Reply #13 on: August 19, 2018, 09:59:07 pm »
If Owner is responsible for creating and free the object, and Parent is responsible for drawing resizing etc, the contructor is imho. the right place to put the Parnet assignment.
Not even close owner can be any TComponent descendant including a run time factory that dynamically creates control based on a xml description and destroys them as needed.
It is a Panel inserted in another panel at designtime. The first panel will be responsible for creating, deleting and drawing the second. And it is not ging to change.
Wrong again. The owner is not the panel you placed your control in, but the form containing that first panel the form is responsible for creating it and assign to it the parent.
So I do not really see any reason for doing it any other way.
Your limited imagination does not make for a good argument of writing bad code. I really do not care what you do with your code, I only inform you of why it is dangerous if you fill safe with your choices I'm happy for you.
As for the textheight - I don't really see the suggested solution being simpler in any way.
I only said its interdependent of the underline canvas its not that complicated though.

Please, fill free to ignore me, its your code and you know better how its going to be used, I'm only expressing my opinion fill free to ignore it.
Good judgement is the result of experience … Experience is the result of bad judgement.

OS : Windows 7 64 bit
Laz: Lazarus 1.4.4 FPC 2.6.4 i386-win32-win32/win64

engkin

  • Hero Member
  • *****
  • Posts: 3112
Re: Invalid Function Call
« Reply #14 on: August 19, 2018, 10:06:46 pm »
@Birger52,
For a visual control, like yours, the published properties get assigned their values *after* the constructor. At the end of the process LOADED gets called. Typically you should override LOADED and deal with adding initial labels there, but only if the control does not have some already. In LOADED the parent, where you dropped your control at design time, had already been assigned.

Listen to Taazz, he is a master at this who is willing to teach if people are willing to learn.

 

TinyPortal © 2005-2018