Lazarus

Free Pascal => Beginners => Topic started by: JLWest on August 09, 2020, 10:01:41 pm

Title: How do you make this go?
Post by: JLWest on August 09, 2020, 10:01:41 pm
@winni
It compiles fine.

I think something in the events of the timer Activate property probably needs to be be added but not sure what.

Is Go a valid command?

Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes,  Controls, Dialogs, Forms,Graphics, ExtCtrls,
  9.   SysUtils, Math;
  10.  
  11. type
  12.  
  13.   { TForm1 }
  14.  
  15.   TForm1 = class(TForm)
  16.     Image1: TImage;
  17.     Timer1: TTimer;
  18.  
  19.     procedure FormCreate(Sender: TObject);
  20.     procedure Image1Paint(Sender: TObject);
  21.     Procedure Timer1Timer (Sender : TObject);
  22.  
  23.  
  24.   private
  25.    AHour : Word;
  26.    AMin : Word;
  27.    ASec : Word;
  28.   public
  29.   end;
  30.  const Deg2Rad = pi/180;
  31.  var
  32.   Form1: TForm1;
  33.  
  34. implementation
  35.  
  36. {$R *.lfm}
  37.  
  38.  procedure TForm1.FormCreate(Sender: TObject);
  39.   begin
  40.    Timer1Timer(Nil);
  41.  end;
  42.  
  43.  Procedure TForm1.Timer1Timer (Sender : TObject);
  44.   var junk : word;
  45.   begin
  46.    DecodeTime(Now,AHour,AMin,ASec,Junk);
  47.    Image1.Invalidate;
  48.  end;
  49.  
  50.  // analog clock, image 90 x 90
  51.  procedure TForm1.Image1Paint(Sender: TObject);
  52.   const ro=20;    // Minutenzeiger
  53.         centerX=45;
  54.         centerY=47;
  55.   var x,y,x1,y1,x2,y2 : integer;
  56.       h,degH, degMin, degSec, sinus, cosinus : single;
  57.   begin
  58.   // hours
  59.     h := Ahour;
  60.     if h >=12 then h := h - 12;
  61.        h := h + AMin/60;
  62.        degH :=  h* 30 - 90;   // Uhr (Nord) fängt 90° vor Lazarus (Ost) an
  63.        math.sincos(degH*Deg2Rad, sinus, cosinus);
  64.        x := round (cosinus*ro*0.75)+centerX;
  65.        y := round (sinus *ro*0.75) +CenterY;
  66.        // minutes
  67.        degMin :=  Amin*6 -90;
  68.        math.sincos(degMin*Deg2Rad, sinus, cosinus);
  69.        x1 := round (cosinus*ro)+centerX;
  70.        y1 := round (sinus*ro)+centerY;
  71.        // seconds
  72.        degSec := ASec*6 -90;
  73.        math.sincos(degSec*Deg2Rad, sinus, cosinus);
  74.        x2 := round (cosinus*ro*1.15)+centerX;
  75.        y2 := round (sinus*ro*1.15)+centerY;
  76.        with Image1.Canvas do begin
  77.           pen.width := 5;
  78.           pen.Color := clBlack;
  79.           pen.EndCap :=   pecRound;
  80.           line(centerX, centerY,x,y);  // hours
  81.           line(centerX, centerY,x1,y1);   // minutes
  82.           pen.width := 1;
  83.           brush.Color := clLime;
  84.           EllipseC(x,y,3,3);    // radium, luminous lime-green, very 60th
  85.           EllipseC(x1,y1,3,3);
  86.           pen.Color := clRed;
  87.           line(centerX, centerY,x2,y2);    // seconds
  88.           pen.Color := clBlack;
  89.           Brush.color := clSilver;
  90.           EllipseC (centerX, centerY,3,3);
  91.        end;
  92.     end;
  93. end.
  94.  
Title: Re: How do you make this go?
Post by: MaxCuriosus on August 09, 2020, 10:21:47 pm
I think you need to add "Image1Paint" as "OnTimer" event handler in your timer.
Title: Re: How do you make this go?
Post by: TRon on August 09, 2020, 10:24:24 pm
Sorry as I am not called winni

I think something in the events of the timer Activate property probably needs to be be added but not sure what.
You should enable the timer and make sure the interval is set to 1000 milliseconds (= 1 second) or less for the clock to be (re)drawn at least every second.
Title: Re: How do you make this go?
Post by: TRon on August 09, 2020, 10:31:41 pm
I think you need to add "Image1Paint" as "OnTimer" event handler in your timer.
Ontimer events are usually named TimerXTimer by codetools/inspector, where x stands for the number (in case you have multiple timer controls situated on your form).

The timer event in the code is calling the method Invalidate of the image control, which in turn should trigger a paint event (for that image).
Title: Re: How do you make this go?
Post by: JLWest on August 09, 2020, 10:32:12 pm
Sorry TRon

Yea, the timer is enabled and set to 1000
Needs something more.


Sorry as I am not called winni

I think something in the events of the timer Activate property probably needs to be be added but not sure what.
You should enable the timer and make sure the interval is set to 1000 milliseconds (= 1 second) or less for the clock to be (re)drawn at least every second.
Title: Re: How do you make this go?
Post by: Blaazen on August 09, 2020, 10:52:03 pm
Try to replace TImage with TPaintBox. It worked for me. But still, there is something wierd with TImage, it should work too.
Title: Re: How do you make this go?
Post by: TRon on August 09, 2020, 11:07:11 pm
It worked for me. But still, there is something wierd with TImage, it should work too.
It did not for me. The paint event does not seem to be triggered by the invalidate from the timer. Refresh also wasn't able to do the trick for me. There definitely seems something weird going on there unless I completely misunderstood the workings of TImage.

In case it matters, linux, gtk2, laz 2.0.8

@JLWest,
Sorry , there seems to be something wrong there. Best would be to try to follow blaazen's advice to work around the problem by using a paintbox.
Title: Re: How do you make this go?
Post by: JLWest on August 09, 2020, 11:14:00 pm
Thanks, I was thinking I did something wrong.
Title: Re: How do you make this go?
Post by: MaxCuriosus on August 10, 2020, 12:12:07 am

Ontimer events are usually named TimerXTimer by codetools/inspector, where x stands for the number (in case you have multiple timer controls situated on your form).

The timer event in the code is calling the method Invalidate of the image control, which in turn should trigger a paint event (for that image).

TRon,
Thank you for your explanation.
I had recently built a similar analog clock whitout using the "Timer1Timer" procedure.
Title: Re: How do you make this go?
Post by: TRon on August 10, 2020, 12:24:46 am
Just for the record, you can't just copy-paste the code as shown. You must use the object inspector to create/link the events to their corresponding implementations in code.

@MaxCurious:
You're welcome  :)

For sure there are other methods to 'update' for example an analogue clock. OnIdle is another event that could be used for that (to not over-abuse OnIdle you should check if the time differs from the previous drawn time and only actually draw it when there need to do so).

How did you end up updating/(re)drawing your clock ?
Title: Re: How do you make this go?
Post by: TRon on August 10, 2020, 12:54:30 am
Splendid.... whatever you try, casting to an ancestor or whatever, not able to draw the image whatsoever.

However, when calling onpaint event manually, the onpaint event gets invoked and the image draws itself ...... twice  %)
Title: Re: How do you make this go?
Post by: winni on August 10, 2020, 01:30:37 am
Hi!

I don't know what you gonna do.
Are some procedures  not connected to some events?

The code is part of my  self made alarm clock and it works since years.
Exact the way it is shown by me an JLWest.
And it survided fpc and Lazarus updates.

Just made a screenshot to convince you.
No PaintBox, no update - just as I wrote it.

Winni
Title: Re: How do you make this go?
Post by: jamie on August 10, 2020, 02:07:16 am
I think the issue is that you need a Picture loaded of the same size as the drawing surface to start with.
 Without this there is no drawing surface size.. the Canvas has none...

 I just set the Picture.Bitmap.SetSize(Width,Height); in the FormOnCreate and that creates a black background image.

 Then the OnPaint seems to be getting called..

 Using the canvas of the control you are drawing on the surface not the Bitmap.

 If you want it to be fixed in the image then Picture.Bitmap.Canvas…..
etc

Title: Re: How do you make this go?
Post by: TRon on August 10, 2020, 02:14:09 am
Just made a screenshot to convince you.
I post one in return to convince you and tell you that I am not convinced  :P

Not calling the onpaint handler manually does not invoke it, in any way. Calling it manually invokes it twice. I seem to missing your platform/lazversion and used widgetset.
Title: Re: How do you make this go?
Post by: jamie on August 10, 2020, 02:27:11 am
Well, I did the test over here and without the image the OnPaint event does not get called using a Invalidate, up etc.

But after setting the size of the bitmap to something that exists it does now..

Personally I don't see why they limit this but that is the way its done..

Also, it could be possible the cliprect is not yet set.

Platform is Windows 10
Title: Re: How do you make this go?
Post by: TRon on August 10, 2020, 02:27:47 am
I think the issue is that you need a Picture loaded of the same size as the drawing surface to start with.
 Without this there is no drawing surface size.. the Canvas has none...
I can't recall that ever being a requirement on windows.... (Delphi to be specific).

however...
Quote
I just set the Picture.Bitmap.SetSize(Width,Height); in the FormOnCreate and that creates a black background image.
... that seem to be the trick.

Quote
Then the OnPaint seems to be getting called..
Indeed, I am able to confirm.... and only once (instead of twice)  :)

Thank you jamie !
Title: Re: How do you make this go?
Post by: jamie on August 10, 2020, 02:39:35 am
That's no problem..

Hey, I don't drink that much but I do play a lot of tennis so if you are ever in this area I can show you how I bang my balls around!  :)
Title: Re: How do you make this go?
Post by: TRon on August 10, 2020, 02:42:58 am
Hey, I don't drink that much but I do play a lot of tennis so if you are ever in this area I can show you how I bang my balls around!  :)
Nutbush ?  :D
Title: Re: How do you make this go?
Post by: TRon on August 10, 2020, 02:47:14 am
@JLWest:
In case you have experienced the same issue that I had, then you can 'fix' that by changing your FormCreate event to read:
Code: Pascal  [Select][+][-]
  1. procedure TForm1.FormCreate(Sender: TObject);
  2. begin
  3.   Image1.Picture.Bitmap.SetSize(Image1.Width, Image1.Height);
  4.   Timer1Timer(Nil);
  5. end;
Title: Re: How do you make this go?
Post by: JLWest on August 10, 2020, 04:50:20 am
I get a black box.

Image is 90X90
Timer is enabled and set to 1000
OnTimer event is set to Timer1Timer

Changed OnCreate to
    Image1.Picture.Bitmap.SetSize(Image1.Width, Image1.Height);
   Timer1Timer(Nil);
 
                                     
Just a black box is all I get.

Title: Re: How do you make this go?
Post by: TRon on August 10, 2020, 04:57:16 am
@JLWest
Could you post your project .lfm file ?
Title: Re: How do you make this go?
Post by: JLWest on August 10, 2020, 05:03:18 am
@Winni Because  you only gave me a png of the butt ugliest clock in the world I guess I'll have to make i,440 copies of it with the hands covering a 24hr period and just go thru a giant if statement.

if Now := l2:01 then begin
  Clock1200.visible := False;
  Clock1201.Visible := True;
ect.
ect..
end if;

Hi!

I don't know what you gonna do.
Are some procedures  not connected to some events?

The code is part of my  self made alarm clock and it works since years.
Exact the way it is shown by me an JLWest.
And it survided fpc and Lazarus updates.

Just made a screenshot to convince you.
No PaintBox, no update - just as I wrote it.

Winni
Title: Re: How do you make this go?
Post by: JLWest on August 10, 2020, 05:04:46 am
@JLWest
Could you post your project .lfm file ?

Yea, As soon as I make 1239  copies of winni's clock.
Title: Re: How do you make this go?
Post by: JLWest on August 10, 2020, 05:08:09 am
Here you go.
Title: Re: How do you make this go?
Post by: TRon on August 10, 2020, 05:09:43 am
Yea, As soon as I make 1239  copies of winni's clock.
I am focusing on the code you showed. That draws a hour, minutes and seconds line using different colours.

There is no need for (additional) images in order for that code to work. It is however a small image, and is using some odd colours.

It is about the basic principle of calculating the positions and drawing of the clock-hands.

I even got it working in transparency mode now, so we should be able to get it working for you as well.
Title: Re: How do you make this go?
Post by: TRon on August 10, 2020, 05:14:09 am
Here you go.
1. Open your project
2. click on the image, so that it gets selected in the object inspector.
3. in the object inspector, click on the events tab
4. search for the event OnPaint
5. double click the event, a new event will be added to your code
6. copy your code from your (old) function Image1Paint into the new event function
7. remove the old image1paint code/function
8. save your project
9. compile and run your project

Does it now work for you ?

Title: Re: How do you make this go?
Post by: JLWest on August 10, 2020, 05:51:22 am
I got a black box with 3 green dots.I can't tell if anything is moving. Maybe.
Title: Re: How do you make this go?
Post by: TRon on August 10, 2020, 05:55:09 am
Good, we are getting there  :)

There is something moving but I do not blame in case you are unable to see that properly. The seconds clock-hand is a single line of pixels (one pixel width) in red.

I've attached my project that increases the size of the clock a little, still using a single pixel width line for the seconds clock-hand though.
Title: Re: How do you make this go?
Post by: JLWest on August 10, 2020, 06:01:00 am
Yea, got it.
Title: Re: How do you make this go?
Post by: JLWest on August 10, 2020, 06:10:45 am
TRon, it's not to bad. The clock without a frame.

Ok here is what I will do.

Create a listbox and put about 20 airport records in the listbox.

Then I'll post it on my GDrive.

and give you, winni and handako a link.

Have you read the other post where handako has a working prototye of a clock.
Title: Re: How do you make this go?
Post by: TRon on August 10, 2020, 08:23:10 am
Have you read the other post where handako has a working prototye of a clock.
I have now  :)
Title: Re: How do you make this go?
Post by: winni on August 10, 2020, 12:09:59 pm
Hi!

The whole mystery with the not working OnPaint for the Image1 is the crazy behaviour of the TImage with Lazarus:

You have to "initialize" it which is nowhere documented.

Solution 1: Put a background picture into image1 - like I did.
Solution 2: "initialize" image1 in the procedure FormCreate:

Code: Pascal  [Select][+][-]
  1. Image1.Canvas.Brush.color := clWhite;
  2. Image1.Canvas.Fillrect (Rect(0,0,Image1.width, Image1.Height));
  3.  
That is the whole secret.
And that lousy behaviour of the TImage should be documented somewhere. Since 20 years.

Winni

Title: Re: How do you make this go?
Post by: TRon on August 11, 2020, 12:30:17 am
The whole mystery with the not working OnPaint for the Image1 is the crazy behaviour of the TImage with Lazarus:

You have to "initialize" it which is nowhere documented.
Yeah. I was unaware of that fact and was unable to find/locate that (small) piece of information. Now we know  :)

Thank you winni
Title: Re: How do you make this go?
Post by: lucamar on August 11, 2020, 12:49:27 pm
The whole mystery with the not working OnPaint for the Image1 is the crazy behaviour of the TImage with Lazarus [...]

It's not really crazy or a mistery. A TImage is basically a "presentation" control meant to show whatever is loaded into its Picture property while the proper control to draw something is TPaintBox.

Granted, one can make do with a TImage and its Canvas, but then one has to realize that if there is no Picture the Canvas is not "initialized". That's all there is to it. :)
Title: Re: How do you make this go?
Post by: winni on August 11, 2020, 01:38:59 pm
Hi lucamar!

But what if you just start to create a picture??
Then you got that trap where Tron and others stepped in.

There should be a createPicture or InitPicture for those cases.
But the initialization of the picture is completly hidden so it is not obvious when it is done!

Winni
Title: Re: How do you make this go?
Post by: lucamar on August 11, 2020, 02:05:32 pm
But what if you just start to create a picture??

Then you use a TBitmap (or any other similar class) if you're doing it in code or use a TPaintBox if it's drawn by the user (like, say, in a "Paint" kind of program).

Painting in a TImage's canvas is really no better than painting in, say, a TPanel's canvas and it's, indeed, worse because at least the TPanel does have an initialized canvas while a TImage doesn't until either you load a Picture or initialize it yourself. And worse of all: as soon as you try almost any operation with picture (for example to save whatever you've just drawn) the TImage canvas is redrawn to synch with the Picture, so you lose whatever you draw there.

I'm not saying it can't be done and, indeed, there are lots of code snippets out there using that "solution"; I just think that taking all into account it's not reallya good idea unless one does know exactly what one's doing and, more important, why.

IMHO, of course ... ;)
TinyPortal © 2005-2018