Recent

Author Topic: Best way to exchange data between a form and a unit  (Read 55766 times)

cdbc

  • Hero Member
  • *****
  • Posts: 1806
    • http://www.cdbc.dk
Re: Best way to exchange data between a form and a unit
« Reply #105 on: February 09, 2024, 05:07:10 pm »
Hi
That looks good, me likey  8-)
It depends on the size of the project... complexity, but for small(ish) projects, it's ok to keep the /common/ types and definitions in 1 file. For bigger projects I like to split it up in e.g.:
1. layer a /super/unit with baseclass(es) often 'abstract'+interface(s) of that
2. layer: oef.intf, oef.decl - they can see & use the classes/interfaces above
3. layer: model.impl, presenter.impl, settings.impl, dbase.impl etc...
4. layer: view.main, view.dataedit, view.datareport, etc...
It is possible to let an abstract class implement an interface, so that they're both /virtual/ in a 'super'-unit, that way you can work around circular references (they're a PITA).
Here's a model.intf file of a tiny project of mine:
Code: Pascal  [Select][+][-]
  1. unit model.intf;
  2. {$mode ObjFPC}{$H+}
  3. {-$define usepli}
  4. interface
  5. uses bc_datetime,model.memitem,bc_pluginapih,help.crt;
  6. type
  7.   { alias from memitem }
  8.   InvnItem = model.memitem.InvnItem;
  9.   ImemItem = model.memitem.ImemItem;
  10.   ImemTransaction = model.memitem.ImemTransaction;
  11.   { alias from bc_datetime }
  12.   IbcDateTime = bc_datetime.IbcDateTime;
  13. {$interfaces corba}
  14.   ImemItemList = interface(IbcIterable)['{9A14F454-7F13-497C-AD49-71B8A69E35B7}']
  15.     function get_Item(anIndex: ptrint): TmemBaseItem;
  16.     procedure set_Item(anIndex: ptrint;aValue: TmemBaseItem);
  17.     { adds and returns a new item of type provided in constructor }
  18.     function AddItem: TmemBaseItem;
  19.     { clears the list of any items }
  20.     procedure Clear;
  21.     { deletes and frees the item @ index }
  22.     procedure DeleteItem(anIndex: ptrint);
  23.     { removes and returns the item with "anID", if found in list else returns NIL;
  24.       param: "anID"(to find) ~ if -1 is passed, it will return the first item in list or nil on empty }
  25.     function ExtractItem(anID: ptrint): TmemBaseItem;
  26.     { returns the index of "anItem" in list, or returns -1 on NOT found }
  27.     function IndexOf(anItem: TmemBaseItem): ptrint;
  28.     { positions the cursor just before the first item, used with "MoveNext" & "Peek" }
  29.     procedure MoveBeforeFirst;
  30.     { steps to the next item in the list, used with "MoveBeforeFirst" & "Peek" }
  31.     function MoveNext: boolean;
  32.     { provides access to the current item @ cursor, used with "MoveBeforeFirst" & "MoveNext" }
  33.     function Peek: TmemBaseItem;
  34.     { provides access to the individual items, default property }
  35.     property Items[anIndex: ptrint]: TmemBaseItem read get_Item write set_Item; default;
  36.   end; { ImemItemList }
  37.   ItiItem = interface['{6AE7CCF8-911A-4E8B-A94F-2893F8C0DEBC}']
  38.     function get_Dati: IbcDateTime;
  39.     function get_ID: ptrint;
  40.     function get_Spare: string;
  41.     function get_Title: string;
  42.     function Obj: TObject;
  43.     procedure set_ID(aValue: ptrint);
  44.     procedure set_Spare(aValue: string);
  45.     procedure set_Title(aValue: string);
  46.     procedure Assign(const aSrc: ItiItem);
  47.     property DateTime: IbcDateTime read get_Dati;
  48.     property ID: ptrint read get_ID write set_ID;
  49.     property Spare: string read get_Spare write set_Spare;
  50.     property Title: string read get_Title write set_Title;
  51.   end; { ItiItem }
  52.   { ItiItemList }
  53.   ItiItemList = interface(IbcIterable)['{0B7E0D98-8CC5-4026-BA11-E4D1D57D354F}']
  54.     function get_Item(anIdx: ptrint): ItiItem;
  55.     procedure set_Item(anIdx: ptrint;aValue: ItiItem);
  56.     function AddItem(anItem: ItiItem): ptrint;
  57.     function AddItemSorted(anItem: ItiItem): ptrint;
  58.     function AddNewItem: ItiItem;
  59.     procedure Clear;
  60.     procedure DeleteItem(anIndex: ptrint);
  61.     function GetItemFromID(anID: ptrint): ItiItem;
  62.     function GetItemFromTitle(const aTitle: string): ItiItem;
  63.     function IndexOf(anItem: ItiItem): ptrint;
  64.     procedure UpdateItem(var anItem: ItiItem); { free's the incoming item afterwards!!! }
  65.     property Count: ptrint read CountItems;
  66.     property Items[anIdx: ptrint]: ItiItem read get_Item write set_Item; default;
  67.   end; { ItiItemList }
  68.  
  69.  
  70.   { ImemImporter is a service that imports memento-items from an external source,
  71.     takes the name of a source- table/file/webaddress and imports the data into the
  72.     "aTarget" list in raw text-format, returns the number of items imported as result;
  73.     params: NO creation-params just do:
  74.     pointer(fImporter):= PluginMgr.GetPliSvcDefaultWeak('ImemImporter');
  75.     ...use it...and remember to:
  76.     fImporter.Obj.Free; fImporter:= nil; when you're done }
  77.   ImemImporter = interface(IbcCorba)['{3601C1ED-789A-4D41-B38C-00D497E564B8}']
  78.     { takes the name of a source- table/file/webaddress and imports the data into the
  79.       "aTarget" list in raw text-format, returns the number of items imported as result }
  80.     function ImportData(const aSourceName: shortstring;aTarget: ImemItemList): ptrint;
  81.   end; { ImemImporter }
  82.   { the actual model/datastore api, service gets automagically destroyed on program-end }
  83.   ImemModelMain = interface(IbcCorba)['{5AE326B6-AA94-43FA-BDF6-A05A5F3B0636}']
  84.     function get_Databasename: string;
  85.     procedure set_Databasename(aValue: string);
  86.     function AddMemItem(const aMemItem: ImemItem): ptrint;
  87.     function GetMemItemByID(anID: ptrint;out aMemItem: ImemItem): boolean; overload;
  88.     function GetMemItemByID(anID: ptrint): ImemItem; overload;
  89.     function GetMemItemList(anItemList: ImemItemList): boolean;
  90.     function GetMemTitleIdList(aTitleIdList: ItiItemList): boolean;
  91.     function ImportMementos(const aSourceName: shortstring): ptrint; // returns -97..0 on error, positive is count of items
  92.     procedure RemoveMemItemByID(anID: ptrint);
  93.     procedure UpdateMemItem(const aMemItem: ImemItem);
  94.     property Databasename: string read get_Databasename write set_Databasename;
  95.   end; { ImemModel }
  96.   { ImemTransactionManager reacts to user actions }
  97.   ImemTransactionManager = interface(IbcCorba)['{9118526B-15B6-453C-BE15-16EECE8251C3}']
  98.     procedure EditMementoByID(const anId: ptrint);
  99.     procedure FetchMementoByID(const anIdStr: string);
  100.     procedure FetchTitleList;
  101.     function FetchSearchList(const aSearchMask: string;out aList: ItiItemList): boolean;
  102.     procedure ImportMementos(const aSourceName: shortstring);
  103.     procedure CommitTransaction;
  104.     function InTransaction: boolean;
  105.     procedure RollbackTransaction;
  106.     function StartTransaction: ImemTransaction;
  107.   end; { ImemTransactionManager }
  108.   {  }
  109.   ImemViewModelMain = interface(IbcCorba)['{DD9FD4C9-C5E8-4A34-A3BF-BA0FF0B87E1D}']
  110.  
  111.     function get_Provider: IobsProvider;
  112.     function get_TitleIdList: ItiItemList;
  113.     function get_TrxMan: ImemTransactionManager;
  114.     procedure set_Provider(aValue: IobsProvider);
  115.  
  116.     function DataStoreName: string;
  117.     function HandlePliNotify(aHandle,aMsg,aWParam,aLParam: ptrint;const aText: shortstring): ptrint;
  118.     property Provider: IobsProvider read get_Provider write set_Provider;
  119.     property TitleIdList: ItiItemList read get_TitleIdList;
  120.     property TrxManager: ImemTransactionManager read get_TrxMan;
  121.   end; { ImemViewModelMain }
  122.  
  123.   {  }
  124.   ImemViewMain = interface(IbcCorba)['{14E0C6AF-65E9-4F1A-950C-3FF8115858AF}']
  125.  
  126.     function get_Observer: IobsSubscriber;
  127.     function get_Form: TcrtView;
  128.     function get_ViewModel: ImemViewModelMain;
  129.     procedure set_Observer(aValue: IobsSubscriber);
  130.     procedure set_Form(aValue: TcrtView);
  131.     procedure set_ViewModel(aValue: ImemViewModelMain);
  132.     procedure HandleObsNotify(aReason: ptrint; aNotifyClass: TObject; UserData: pointer);
  133.                                                                  
  134.     procedure Show;
  135.  
  136.     property Observer: IobsSubscriber read get_Observer write set_Observer;
  137.  
  138.     property Form: TcrtView read get_Form write set_Form;
  139.     property ViewModel: ImemViewModelMain read get_ViewModel write set_ViewModel;
  140.   end; { ImemViewMain }
  141. {$interfaces com}
  142.  
  143.  
  144. implementation
  145.  
  146.  
  147. end.
  148.  
As you can see I use interfaces because they make it easy to implement stuff, incl. plugins...
The "model.memitem" is the 'super'unit, in my case...
edit: Come to see the code again... the 'ImemViewMain' shouldn't be there, but in the view-unit. I just stuck it in there 'cause it looked neat & tidy   :D
I think you're on a good way, keep it up(and disciplined)  8)
Regards Benny
« Last Edit: February 09, 2024, 05:58:19 pm by cdbc »
If it ain't broke, don't fix it ;)
PCLinuxOS(rolling release) 64bit -> KDE5 -> FPC 3.2.2 -> Lazarus 2.2.6 up until Jan 2024 from then on it's: KDE5/QT5 -> FPC 3.3.1 -> Lazarus 3.0

Hansvb

  • Hero Member
  • *****
  • Posts: 727
Re: Best way to exchange data between a form and a unit
« Reply #106 on: February 11, 2024, 05:29:36 pm »
Quote
(they're a PITA)
I had to google this  :-X

Quote
presenter.trxman.pas -> has implementations of transactions, ref in tpresenter
This puzzles me. If I want to create the transaction manager it must have a presenter and a model. (fModel: IModel; fOwner: IPresenter;). I don't see how I can create a transactionmanager ref in the presenter.


cdbc

  • Hero Member
  • *****
  • Posts: 1806
    • http://www.cdbc.dk
Re: Best way to exchange data between a form and a unit
« Reply #107 on: February 11, 2024, 05:59:53 pm »
Hi
Here's the presenter from hvb_play:
Code: Pascal  [Select][+][-]
  1.   IPresenter = interface['{973EB39F-B26E-469E-A449-6A1970D1EA84}']
  2.     function get_LogActions: boolean;
  3.     function get_Model: IModel;
  4.     function get_Provider: IobsProvider;
  5.     function get_TrxMan: TTransactionManager;
  6.     procedure set_LogActions(aValue: boolean);
  7.  
  8.     procedure AdjustProgress(aSender: TObject;aMin,aMax,aPosition: integer);
  9.     procedure ChangeModel(aValue: IModel);
  10.     procedure CreateDirectories(const aRootDir: string;aDirList: TSTrings = nil); deprecated 'use TrxMan instead';
  11.     procedure GetStaticTexts(aRegion: word);
  12.     function Obj: TObject;
  13.     procedure TestCaptionAgain;
  14.  
  15.     property LogActions: boolean read get_LogActions write set_LogActions;
  16.     property Model: IModel read get_Model;
  17.     property Provider: IobsProvider read get_Provider;
  18.     property TrxMan: TTransactionManager read get_TrxMan; //HERE
  19.   end; // right above HERE ^
and:
Code: Pascal  [Select][+][-]
  1. constructor TPresenter.Create;
  2. begin
  3.   inherited Create;
  4.   fProvider:= CreateObsProvider; // let factory func create it for us
  5.   fModel:= TModel.Create(Self); // for ease of use we do this, for now
  6.   fTrxMan:= TTransactionManager.Create(Self,fModel); //HERE
  7. end;
  8.  
  9. destructor TPresenter.Destroy;
  10. begin
  11.   fTrxMan.Free; //HERE
  12.   fModel.Free;
  13.   fProvider.Free;
  14.   inherited Destroy;
  15. end;
You already know this  ;D
Regards Benny
« Last Edit: February 11, 2024, 06:01:40 pm by cdbc »
If it ain't broke, don't fix it ;)
PCLinuxOS(rolling release) 64bit -> KDE5 -> FPC 3.2.2 -> Lazarus 2.2.6 up until Jan 2024 from then on it's: KDE5/QT5 -> FPC 3.3.1 -> Lazarus 3.0

Hansvb

  • Hero Member
  • *****
  • Posts: 727
Re: Best way to exchange data between a form and a unit
« Reply #108 on: February 11, 2024, 06:11:36 pm »
That works when there is one types file with all the interfaces in it. I tried to split it up to a file for the presenter interface and a file for the model interface. Then this will not work because I can not declare the transaction manager in the presenter because it does not know the model at that point and I can't make a "base" class because than the presenter and the model are not known at that point. That is what i was trying to do.

cdbc

  • Hero Member
  • *****
  • Posts: 1806
    • http://www.cdbc.dk
Re: Best way to exchange data between a form and a unit
« Reply #109 on: February 11, 2024, 06:12:47 pm »
Hi
Use interfaces to connect the different classes and keep your interfaces together, so they can reference each other... e.g.: ITransactionManager & ITransaction + descendants of ITransaction...  8)
If need be, stick it in the superunit,  %)
BUT be careful not to 'bloat' that instead...  >:(
Regards Benny
If it ain't broke, don't fix it ;)
PCLinuxOS(rolling release) 64bit -> KDE5 -> FPC 3.2.2 -> Lazarus 2.2.6 up until Jan 2024 from then on it's: KDE5/QT5 -> FPC 3.3.1 -> Lazarus 3.0

cdbc

  • Hero Member
  • *****
  • Posts: 1806
    • http://www.cdbc.dk
Re: Best way to exchange data between a form and a unit
« Reply #110 on: February 11, 2024, 06:20:04 pm »
Hi
Look at my 'model.intf' above, do you see how many different interfaces are in action, all declared in this one unit, either physical or by /aliasing/ from other units. Then keep your params interfaces too...
The only class-types in that unit are from the /super/unit or from built-in RTL-classes...
Regards Benny
If it ain't broke, don't fix it ;)
PCLinuxOS(rolling release) 64bit -> KDE5 -> FPC 3.2.2 -> Lazarus 2.2.6 up until Jan 2024 from then on it's: KDE5/QT5 -> FPC 3.3.1 -> Lazarus 3.0

Hansvb

  • Hero Member
  • *****
  • Posts: 727
Re: Best way to exchange data between a form and a unit
« Reply #111 on: February 11, 2024, 07:57:36 pm »
Ok, i think I've got it. TTransaction sits now in it's own unit. TTransactionManager sits in the presenter unit but not in the interface unit of the presenter. Because the manager is only used internally in the presenter so it doesn't have to be in the interface. (I kept making a mistake there).
Now I can still keep the presenter and model units split into an .intf unit and a regular unit.

cdbc

  • Hero Member
  • *****
  • Posts: 1806
    • http://www.cdbc.dk
Re: Best way to exchange data between a form and a unit
« Reply #112 on: February 11, 2024, 08:26:10 pm »
Hi
Well, then I'm looking forward to seeing your layout, because I find it dang hard to explain how to get it just right, you know...
It depends on what you want to do...  :)
Regards Benny
If it ain't broke, don't fix it ;)
PCLinuxOS(rolling release) 64bit -> KDE5 -> FPC 3.2.2 -> Lazarus 2.2.6 up until Jan 2024 from then on it's: KDE5/QT5 -> FPC 3.3.1 -> Lazarus 3.0

Hansvb

  • Hero Member
  • *****
  • Posts: 727
Re: Best way to exchange data between a form and a unit
« Reply #113 on: February 11, 2024, 08:31:54 pm »
I just closed my pc. I will upload it tomorrow.

cdbc

  • Hero Member
  • *****
  • Posts: 1806
    • http://www.cdbc.dk
Re: Best way to exchange data between a form and a unit
« Reply #114 on: February 11, 2024, 08:32:58 pm »
Okidoki  :D
If it ain't broke, don't fix it ;)
PCLinuxOS(rolling release) 64bit -> KDE5 -> FPC 3.2.2 -> Lazarus 2.2.6 up until Jan 2024 from then on it's: KDE5/QT5 -> FPC 3.3.1 -> Lazarus 3.0

Hansvb

  • Hero Member
  • *****
  • Posts: 727
Re: Best way to exchange data between a form and a unit
« Reply #115 on: February 11, 2024, 08:45:18 pm »
Sorry, couldn't resist and because you already spent so much time on me. I Turned on the PC for a moment, see the attachment.


cdbc

  • Hero Member
  • *****
  • Posts: 1806
    • http://www.cdbc.dk
Re: Best way to exchange data between a form and a unit
« Reply #116 on: February 11, 2024, 08:53:47 pm »
Hi
 :D Will do ...and you're welcome  8-)
Regards Benny
If it ain't broke, don't fix it ;)
PCLinuxOS(rolling release) 64bit -> KDE5 -> FPC 3.2.2 -> Lazarus 2.2.6 up until Jan 2024 from then on it's: KDE5/QT5 -> FPC 3.3.1 -> Lazarus 3.0

janasoft

  • New Member
  • *
  • Posts: 40
Re: Best way to exchange data between a form and a unit
« Reply #117 on: February 11, 2024, 11:11:27 pm »
Hi
I'm following this thread with a lot of interest.
I would like to thank everyone who is putting their knowledge at the service of the community, especially to @cdbc.
Regards
Lazarus 2.2.6 (rev lazarus_2_2_6) FPC 3.2.2 x86_64-win64-win32/win64

cdbc

  • Hero Member
  • *****
  • Posts: 1806
    • http://www.cdbc.dk
Re: Best way to exchange data between a form and a unit
« Reply #118 on: February 12, 2024, 12:12:09 am »
Hi
@janasoft: You are very welcome  :)
Please speak up / chime in, if you've got questions or comments...
Regards Benny
If it ain't broke, don't fix it ;)
PCLinuxOS(rolling release) 64bit -> KDE5 -> FPC 3.2.2 -> Lazarus 2.2.6 up until Jan 2024 from then on it's: KDE5/QT5 -> FPC 3.3.1 -> Lazarus 3.0

cdbc

  • Hero Member
  • *****
  • Posts: 1806
    • http://www.cdbc.dk
Re: Best way to exchange data between a form and a unit
« Reply #119 on: February 12, 2024, 11:24:41 am »
Hi
@Hansvb: No wonder you're a bit bewildered... Your code is a right mess, sorry. I'll see what I can do to get you back on track... :)
So, you've got the concept down, now we have to figure out a basic layout, a template of sorts, if you will, that you can use again...
Hang on a bit, I'm letting it stew  :D
Btw. You have a couple of errors, that prevent the sample I got, from running...
Code: Pascal  [Select][+][-]
  1. // from types:
  2. { notifications for this reason carries a 'PDirectories' record in 'Userdata' & optionally an object in 'aNotifyClass' }
  3.   prCreateDirs = prUser + 1;
  4. // from presenter.intf:
  5.   { notifications for this reason carries a 'PStaticTextsAll' record in 'Userdata' }
  6.   prStaticTexts = prUser + 1;
  7. ///// That's an absolute NOGO! \\\\\
  8. // to have 2 constants with the same value, the result is that the view
  9. // tries to show statictexts when it gets fed a PDirectories => Crash & Burn!
  10. // so change: prStaticTexts = prUser + 1;
  11. // to: prStaticTexts = prUser + 2;
  12. // then after you've implemented:
  13. procedure TVwMain.DoCreateDirs(anObj: TObject; aDirs: PDirectories); { added by /bc 110224 }
  14. begin
  15.   StatusBar1.Panels[0].Text:= ' CreateDirs returned >> ' + aDirs^.dirSuccesMsg + ' <<';
  16. end;  
  17. // and
  18. procedure TVwMain.HandleObsNotify(aReason: TProviderReason;
  19.   aNotifyClass: TObject; UserData: pointer);
  20. begin
  21.   case aReason of
  22.     prCreateDirs: DoCreateDirs(aNotifyClass,UserData); { added by /bc 110224 }
  23.     prStaticTexts: DoSetStaticTexts(UserData);
  24.   end;
  25. end;
  26. // it works, with NO CRASH.
  27.  
Regards Benny
If it ain't broke, don't fix it ;)
PCLinuxOS(rolling release) 64bit -> KDE5 -> FPC 3.2.2 -> Lazarus 2.2.6 up until Jan 2024 from then on it's: KDE5/QT5 -> FPC 3.3.1 -> Lazarus 3.0

 

TinyPortal © 2005-2018