Recent

Author Topic: [SOLVED] TNotifyEvent in nonUI. How to watch a variable with event  (Read 3152 times)

AL

  • Sr. Member
  • ****
  • Posts: 261
I would like to watch the value of a variable in memory and trigger an event when the value change.
So far my code does not work and I did not found a complete example of someting similar.
Any help would be much appreciated.
My code so far:

I get an error on FOnchanged := @ProcessEvent (sender);

If I comment that then I get a sigsev and error on  MyData := Val ; 

Code: Pascal  [Select][+][-]
  1. program Project1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. uses
  6.   {$IFDEF UNIX}
  7.   cthreads,
  8.   {$ENDIF}
  9.   Classes
  10.   { you can add units after this };
  11.  
  12. Type TWatchDog = Class (TObject)
  13.  Private
  14.     FOnChanged : TNotifyEvent ;
  15.     FMYData    : Boolean ;
  16.     Procedure SetMyData ( Val: Boolean);
  17.   Public
  18.     procedure Create(Sender: TObject);
  19.     Property OnChanged : TNotifyEvent Read FOnchanged write FOnChanged ;
  20.     Property MyData : Boolean Read FMyData Write SetMyData ;
  21.     Procedure ProcessEvent(Sender: TObject);
  22. end;
  23. Var WatchDog : TWatchDog ;
  24.  
  25. Procedure   TWatchDog.SetMyData(val:Boolean);
  26.  Begin
  27.       MyData := Val ;
  28.       if Assigned(FOnChanged) then FOnChanged(Self);
  29.  
  30.  end;
  31. Procedure TWatchDog.ProcessEvent(Sender: TObject);
  32. begin
  33.      //Do some Stuff
  34.      writeln('Event has been fired');
  35.      readln;
  36. End;
  37.  
  38. Procedure TWatchDog.Create (Sender: TObject);
  39. begin
  40.      FOnchanged := @ProcessEvent (sender);
  41. end;
  42.  
  43.  
  44.  
  45. begin
  46.   Watchdog.create (Watchdog);
  47.   Watchdog.SetMyData(false);
  48.   writeln('set to false');
  49.   readln;
  50.   Watchdog.SetMyData(True);
  51.   writeln('set to True');
  52.   readln;
  53.  
  54. end.
  55.                        
« Last Edit: February 15, 2020, 05:36:16 pm by AL »
Laz 3.1, fpc 3.2.2, Win10
Laz 3.1  fpc 3.2.2, MacOS Monterey running on VMWare/Win 10
Laz 3.1  fpc 3.2.2 Ubuntu 20.04

trev

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 2020
  • Former Delphi 1-7, 10.2 user
Re: TNotifyEvent in nonUI. How to watch a variable with event
« Reply #1 on: February 15, 2020, 05:29:46 am »
Regarding the sigsev: Val is a keyword in Pascal  :o  Change it to Value.


Thaddy

  • Hero Member
  • *****
  • Posts: 14213
  • Probably until I exterminate Putin.
Re: TNotifyEvent in nonUI. How to watch a variable with event
« Reply #2 on: February 15, 2020, 07:07:47 am »
Or &val...(ampersand to escape keyword)
Anyway: under windows you typically would use AllocateHwnd to create a handle for a non-gui application. Many examples available.
A more cross platform solution is to use the provided Observer/Observed from TPersistent: https://www.freepascal.org/docs-html/rtl/classes/ifpobserver.html
And an example here: http://free-pascal-general.1045716.n5.nabble.com/attachment/5711985/0/fpobserver_demo.pas

The latter is a better way than AllocateHwnd(),  it is a feature that is under the radar of many fpc users but very powerful.
« Last Edit: February 15, 2020, 07:12:36 am by Thaddy »
Specialize a type, not a var.

dpremus

  • New Member
  • *
  • Posts: 32
Re: TNotifyEvent in nonUI. How to watch a variable with event
« Reply #3 on: February 15, 2020, 07:56:01 am »
Here is your example that works ...

Code: Pascal  [Select][+][-]
  1. program Project1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. uses
  6.   {$IFDEF UNIX}
  7.   cthreads,
  8.   {$ENDIF}
  9.   Classes
  10.   { you can add units after this };
  11.  
  12. Type TWatchDog = Class (TObject)
  13.  Private
  14.     FOnChanged : TNotifyEvent;
  15.     FMYData    : Boolean;
  16.  
  17.     Procedure SetMyData (Value: Boolean);
  18.   Public
  19.     procedure AfterConstruction; override;
  20.  
  21.     Property  OnChanged : TNotifyEvent Read FOnchanged write FOnChanged ;
  22.     Property  MyData    : Boolean      Read FMyData    Write SetMyData ;
  23.     Procedure ProcessEvent(Sender: TObject);
  24. end;
  25.  
  26.  
  27. Var WatchDog : TWatchDog ;
  28.  
  29.  
  30. procedure TWatchDog.AfterConstruction;
  31. begin
  32.   inherited;
  33.  
  34.   // Intialization
  35.   // It's a good habit to always initialize your variables
  36.   FMyData := False;
  37. end;
  38.  
  39. Procedure TWatchDog.SetMyData(Value: Boolean);
  40. begin
  41.   If MyData = Value Then Exit;
  42.   FMyData := Value;
  43.  
  44.   if Assigned(FOnChanged) then
  45.     FOnChanged(Self);
  46. end;
  47.  
  48. Procedure TWatchDog.ProcessEvent(Sender: TObject);
  49. begin
  50.   //Do some Stuff
  51.   writeln('Change event has been fired');
  52.   readln;
  53. End;
  54.  
  55.  
  56. begin
  57.   WatchDog := TWatchDog.Create;
  58.   WatchDog.OnChanged := @WatchDog.ProcessEvent;
  59.  
  60.   writeln('set to false');
  61.  
  62.   // MyData is already false (default value),
  63.   // so change event will not triggerd
  64.   Watchdog.MyData := false;
  65.   readln;
  66.  
  67.   writeln('set to True');
  68.   Watchdog.MyData := true;
  69.   readln;
  70.  
  71.   WatchDog.Free;
  72. end.
  73.  


But in your example, you have ProcessEvent procedure in the same class so you don't
need event at all, you can simply directly call your ProcessEvent procedure.

Here is an example:

Code: Pascal  [Select][+][-]
  1. program Project1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. uses
  6.   {$IFDEF UNIX}
  7.   cthreads,
  8.   {$ENDIF}
  9.   Classes
  10.   { you can add units after this };
  11.  
  12. Type TWatchDog = Class (TObject)
  13.  Private
  14.     FOnChanged : TNotifyEvent;
  15.     FMYData    : Boolean;
  16.  
  17.     Procedure SetMyData (Value: Boolean);
  18.   Public
  19.     procedure AfterConstruction; override;
  20.  
  21.     Property  OnChanged : TNotifyEvent Read FOnchanged write FOnChanged ;
  22.     Property  MyData    : Boolean      Read FMyData    Write SetMyData ;
  23.     Procedure ProcessEvent;
  24. end;
  25.  
  26.  
  27. Var WatchDog : TWatchDog ;
  28.  
  29.  
  30. procedure TWatchDog.AfterConstruction;
  31. begin
  32.   inherited;
  33.  
  34.   // Intialization
  35.   // It's a good habit to always initialize your variables
  36.   FMyData := False;
  37. end;
  38.  
  39. Procedure TWatchDog.SetMyData(Value: Boolean);
  40. begin
  41.   If MyData = Value Then Exit;
  42.   FMyData := Value;
  43.  
  44.   ProcessEvent;
  45. end;
  46.  
  47. Procedure TWatchDog.ProcessEvent;
  48. begin
  49.   //Do some Stuff
  50.   writeln('Change event has been fired');
  51.   readln;
  52. End;
  53.  
  54.  
  55. begin
  56.   WatchDog := TWatchDog.Create;
  57.  
  58.   writeln('set to false');
  59.  
  60.   // MyData is already false (default value),
  61.   // so change event will not triggerd
  62.   Watchdog.MyData := false;
  63.   readln;
  64.  
  65.   writeln('set to True');
  66.   Watchdog.MyData := true;
  67.   readln;
  68.  
  69.   WatchDog.Free;
  70. end.
  71.  



Here is some real-world example that makes sense,
where you have an event handler in another class.


Code: Pascal  [Select][+][-]
  1. program Project1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. uses
  6.   {$IFDEF UNIX}
  7.   cthreads,
  8.   {$ENDIF}
  9.   Classes
  10.   { you can add units after this };
  11.  
  12. Type TWatchDog = Class(TObject)
  13.  Private
  14.     FOnChanged   : TNotifyEvent;
  15.     FMYData      : Boolean;
  16.     FIsMyDataInitInitalized: Boolean;
  17.  
  18.     Procedure SetMyData (Value: Boolean);
  19.   Public
  20.     procedure AfterConstruction; override;
  21.  
  22.     Property  OnChanged : TNotifyEvent Read FOnchanged write FOnChanged ;
  23.     Property  MyData    : Boolean      Read FMyData    Write SetMyData ;
  24. end;
  25.  
  26.  
  27. Type TSomeWorkingClass = Class(TObject)
  28. public
  29.   Procedure ProcessEvent(Sender: TObject);
  30. end;
  31.  
  32.  
  33. Var WatchDog     : TWatchDog;
  34. Var WorkingClass : TSomeWorkingClass;
  35.  
  36.  
  37. procedure TWatchDog.AfterConstruction;
  38. begin
  39.   inherited;
  40.  
  41.   FMyData        := False;
  42.   FIsMyDataInitInitalized  := False;
  43. end;
  44.  
  45. Procedure TWatchDog.SetMyData(Value: Boolean);
  46. begin
  47.   If (MyData = Value) and (FIsMyDataInitInitalized) Then Exit;
  48.   FMyData := Value;
  49.   FIsMyDataInitInitalized := TRUE;
  50.  
  51.   If Assigned(FOnChanged) Then
  52.     Self.OnChanged(Self);
  53. end;
  54.  
  55.  
  56.  
  57. Procedure TSomeWorkingClass.ProcessEvent(Sender: TObject);
  58. begin
  59.   //Do some Stuff
  60.   writeln('Change event has been fired');
  61. end;
  62.  
  63.  
  64. begin
  65.   WatchDog     := TWatchDog.Create;
  66.   WorkingClass := TSomeWorkingClass.Create;
  67.  
  68.   Try
  69.     WatchDog.OnChanged := @WorkingClass.ProcessEvent;
  70.  
  71.     writeln('set to false');
  72.     Watchdog.MyData := false;
  73.     readln;
  74.  
  75.     writeln('set to True');
  76.     Watchdog.MyData := true;
  77.     readln;
  78.  
  79.   finally
  80.     WatchDog.Free;
  81.     WorkingClass.Free;
  82.   end;
  83. end.




zamronypj

  • Full Member
  • ***
  • Posts: 133
    • Fano Framework, Free Pascal web application framework
Re: TNotifyEvent in nonUI. How to watch a variable with event
« Reply #4 on: February 15, 2020, 10:58:24 am »
Regarding the sigsev: Val is a keyword in Pascal  :o  Change it to Value.

val is not keyword, it is procedure name

https://www.freepascal.org/docs-html/rtl/system/val.html
Fano Framework, Free Pascal web application framework https://fanoframework.github.io
Apache module executes Pascal program like scripting language https://zamronypj.github.io/mod_pascal/
Github https://github.com/zamronypj

Thaddy

  • Hero Member
  • *****
  • Posts: 14213
  • Probably until I exterminate Putin.
Re: TNotifyEvent in nonUI. How to watch a variable with event
« Reply #5 on: February 15, 2020, 12:36:54 pm »
https://www.freepascal.org/docs-html/rtl/system/val.html
Be careful. It is actually a compiler intrinsic as can be seen in the declaration: it is in the system.fpd file...Which means it is only documented as a procedure type.
You can overload it, though, that is true.
« Last Edit: February 15, 2020, 12:39:14 pm by Thaddy »
Specialize a type, not a var.

AL

  • Sr. Member
  • ****
  • Posts: 261
Re: TNotifyEvent in nonUI. How to watch a variable with event
« Reply #6 on: February 15, 2020, 03:38:08 pm »
Regarding the sigsev: Val is a keyword in Pascal  :o  Change it to Value.
Oh boy, I was weak there.  I have used the val procedure countless times.  Surprising that the compiler did not give a better warning.

Thank you all for educating me! This is my first Class created!
I now have some reading to do.
I need an option that will be cross platform (Win + OS X specifically).
This forum is great!
Laz 3.1, fpc 3.2.2, Win10
Laz 3.1  fpc 3.2.2, MacOS Monterey running on VMWare/Win 10
Laz 3.1  fpc 3.2.2 Ubuntu 20.04

PascalDragon

  • Hero Member
  • *****
  • Posts: 5446
  • Compiler Developer
Re: [SOLVED] TNotifyEvent in nonUI. How to watch a variable with event
« Reply #7 on: February 15, 2020, 07:51:44 pm »
If I comment that then I get a sigsev and error on  MyData := Val ; 

You need to write FMyData := Val, otherwise you get an infinite recursion as MyData := Val will in fact be SetMyData(Val).

Regarding the sigsev: Val is a keyword in Pascal  :o  Change it to Value.

As zamronypj and Thaddy wrote Val is not a keyword, but a compiler intrinsic. Those can be reused in custom code and you can still use them using for example System.Val. The main difference to ordinary procedures is that they don't participate in overload selection.

Or &val...(ampersand to escape keyword)

The ampersand is only useful on keyword, not intrinsics... (though its usage won't result in a compiler error)

AL

  • Sr. Member
  • ****
  • Posts: 261
Re: TNotifyEvent in nonUI. How to watch a variable with event
« Reply #8 on: February 15, 2020, 11:30:31 pm »
A more cross platform solution is to use the provided Observer/Observed from TPersistent: https://www.freepascal.org/docs-html/rtl/classes/ifpobserver.html
And an example here: http://free-pascal-general.1045716.n5.nabble.com/attachment/5711985/0/fpobserver_demo.pas

The latter is a better way than AllocateHwnd(),  it is a feature that is under the radar of many fpc users but very powerful.
This is really what I wanted to accomplish.  Observe a variable and do something when changed.
What kind of variables can be observed?  Can we observe a Boolean or an Integer or an Array of Integer for example?
Laz 3.1, fpc 3.2.2, Win10
Laz 3.1  fpc 3.2.2, MacOS Monterey running on VMWare/Win 10
Laz 3.1  fpc 3.2.2 Ubuntu 20.04

winni

  • Hero Member
  • *****
  • Posts: 3197
Re: [SOLVED] TNotifyEvent in nonUI. How to watch a variable with event
« Reply #9 on: February 16, 2020, 12:32:42 am »
Hi!

The observed Data is just a pointer - you can try it with every kind of variable.

And second it owns the function

Code: Pascal  [Select][+][-]
  1. Function Supports(...) : boolean;

whichs overloaded in different ways.

Winni

jamie

  • Hero Member
  • *****
  • Posts: 6091
Re: [SOLVED] TNotifyEvent in nonUI. How to watch a variable with event
« Reply #10 on: February 16, 2020, 05:03:15 am »
if it was me doing a GUI app for example... I would employ a thread and post a message to the main window when the value changed
The only true wisdom is knowing you know nothing

PascalDragon

  • Hero Member
  • *****
  • Posts: 5446
  • Compiler Developer
Re: TNotifyEvent in nonUI. How to watch a variable with event
« Reply #11 on: February 16, 2020, 11:21:02 am »
A more cross platform solution is to use the provided Observer/Observed from TPersistent: https://www.freepascal.org/docs-html/rtl/classes/ifpobserver.html
And an example here: http://free-pascal-general.1045716.n5.nabble.com/attachment/5711985/0/fpobserver_demo.pas

The latter is a better way than AllocateHwnd(),  it is a feature that is under the radar of many fpc users but very powerful.
This is really what I wanted to accomplish.  Observe a variable and do something when changed.
What kind of variables can be observed?  Can we observe a Boolean or an Integer or an Array of Integer for example?

You still need to manually notify the observers inside each property setter.

AL

  • Sr. Member
  • ****
  • Posts: 261
Re: [SOLVED] TNotifyEvent in nonUI. How to watch a variable with event
« Reply #12 on: March 03, 2020, 04:35:25 am »
Hi!

The observed Data is just a pointer - you can try it with every kind of variable.

And second it owns the function

Code: Pascal  [Select][+][-]
  1. Function Supports(...) : boolean;

whichs overloaded in different ways.

Winni

As far as I understand, the observed data need to be of Tcomponent.
I tried with regular variables and there is no support.
Laz 3.1, fpc 3.2.2, Win10
Laz 3.1  fpc 3.2.2, MacOS Monterey running on VMWare/Win 10
Laz 3.1  fpc 3.2.2 Ubuntu 20.04

lucamar

  • Hero Member
  • *****
  • Posts: 4219
Re: [SOLVED] TNotifyEvent in nonUI. How to watch a variable with event
« Reply #13 on: March 03, 2020, 01:47:58 pm »
As far as I understand, the observed data need to be of Tcomponent.
I tried with regular variables and there is no support.

Not really; it just have to be an object implementing the IFPObserved interface, like TPersistent does. But yes, no support for regular, independent vars AFAIK.
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.

 

TinyPortal © 2005-2018