Signalslots

Zur Vorstellung von Komponenten und Units für Lazarus
schnullerbacke
Beiträge: 1187
Registriert: Mi 13. Dez 2006, 10:58
OS, Lazarus, FPC: Winux (L 1.2.xy FPC 2.6.z)
CPU-Target: AMD A4-6400 APU
Wohnort: Hamburg

Signalslots

Beitrag von schnullerbacke »

Anbei ein 2 Units mit dem man sich mehrer Timer in einer Anwendung ersparen kann. Beide Units müssen beim Programmstart als erste initialisiert werden. Dann kann man die Signalslots in seine Objekte einhängen und die TimerSignals dort verarbeiten. Verbesserungen sind erwünscht. Z.B. ließen sich andere Signals in das System einbauen. Ich hatte nur noch keine Zeit das weiter zu basteln.
Dateianhänge
apphelp.zip
2 Units die Signalverarbeitung für Objekte ermöglichen.
(11.14 KiB) 83-mal heruntergeladen
Humor ist der Knopf, der verhindert, daß uns der Kragen platzt.

(Ringelnatz)

Christian
Beiträge: 6079
Registriert: Do 21. Sep 2006, 07:51
OS, Lazarus, FPC: iWinux (L 1.x.xy FPC 2.y.z)
CPU-Target: AVR,ARM,x86(-64)
Wohnort: Dessau
Kontaktdaten:

Beitrag von Christian »

Was bringt das ?
Versteh den Sinn gerade nicht so ganz TTimer ist erfunden oder ?
W.m.k.A.h.e.m.F.h. -> http://www.gidf.de/

schnullerbacke
Beiträge: 1187
Registriert: Mi 13. Dez 2006, 10:58
OS, Lazarus, FPC: Winux (L 1.2.xy FPC 2.6.z)
CPU-Target: AMD A4-6400 APU
Wohnort: Hamburg

Beitrag von schnullerbacke »

Ach je, soll ja Anwendungen geben die z.B. alle 2,5,10 sec und alle 1,2,5 min einen TimerEvent auslösen. Das wird dann häufig mit mehreren Timern gemacht die aber die Ausführungsgeschwindigkeit kräftig drosseln.

Das System regelt das mit einem Timer und einem Signal-Sceduler der die Signals an die Objekte sendet die auch einen entsprechendn Slot dafür haben. Die Signals sind als Text angebbar, wodurch sich das Ganze nicht nur auf TimerTicks kapriziert. So könnte man auch die Datenübertragung von einem zu einem anderen Objekt lösen ohne das die beiden was voneinander wissen müssen. Nur den passenden Slot müssen sie halt haben und den beim Slothandler anmelden.

#Edit

Man könnte z.B. mal überlegen ob man das ganze als sharedobject bastelt und damit auch Anwendungsübergreifend arbeiten kann.

Und damit hier nicht wieder einer meckert, das stammt noch aus Delphi weil die Timer da sch... waren. Das kann aber auch anders genutzt werden. Den AppTimer könnte man auch weglassen, dann gibt es halt die TimerTicks nicht.
Humor ist der Knopf, der verhindert, daß uns der Kragen platzt.

(Ringelnatz)

Christian
Beiträge: 6079
Registriert: Do 21. Sep 2006, 07:51
OS, Lazarus, FPC: iWinux (L 1.x.xy FPC 2.y.z)
CPU-Target: AVR,ARM,x86(-64)
Wohnort: Dessau
Kontaktdaten:

Beitrag von Christian »

Der, der da gemeckert hat war ich. Problem bei der Sache ist das man sich mit einer alternativen Implementation ein Bein stellt weil man so weiter vom System weg ist, deshalb hab ich "gemeckert" schneller wird man damit nämlich nicht eher langsamer ist ja auch deine Sache.
W.m.k.A.h.e.m.F.h. -> http://www.gidf.de/

schnullerbacke
Beiträge: 1187
Registriert: Mi 13. Dez 2006, 10:58
OS, Lazarus, FPC: Winux (L 1.2.xy FPC 2.6.z)
CPU-Target: AMD A4-6400 APU
Wohnort: Hamburg

Beitrag von schnullerbacke »

Ich nehm aber nicht an, das die Timer von FPC mehrere abgestufte Ticks machen können. Muß man also mehrer benutzen. Das hier benutzt genau einen. Hat weder unter Delphi noch unter Kylix was langsamer gemacht. Den SignalHandler mup man dann während der Idletime laufen lassen. Das klappt eigentlich ganz gut.

Und guck mal genau hin, das implementiert keinen neuen TTimer, das benutzt einen vom System angebotenen und läßt den nur zu bestimmten Zeiten das Signal "abfeuern".

Allerdings könnte man überlegen, den SignalHandler als eigenen Prozess laufen zu lassen und so Signals systemweit zu verteilen. Dann hätte Michael genau was er braucht, da das auch bei Ablegern von TObject funzt.
Agesehen davon, wirst du in einem Programm auch nicht viele Slots kriegen, das hält sich also in Grenzen und ist vor allem ohne Delphi-Spezialitäten gebaut.
Humor ist der Knopf, der verhindert, daß uns der Kragen platzt.

(Ringelnatz)

Christian
Beiträge: 6079
Registriert: Do 21. Sep 2006, 07:51
OS, Lazarus, FPC: iWinux (L 1.x.xy FPC 2.y.z)
CPU-Target: AVR,ARM,x86(-64)
Wohnort: Dessau
Kontaktdaten:

Beitrag von Christian »

Michael hat nicht nach nem narichten system gesucht davon hat das Lazarus Widgetset ein eigenes das auch noch ne ganze ecke optimierter ist sorry.

Die Lazarus timer implementierung benutzt Applikationsweit einen Timer arbeitet sogar ziemlich ähnlich zu deinem prinzip allerdings Plattformabhängig bzw Widgetset etwas unterschiedlich und die Narichtenverwaltung/bearbeitung des Wigsetsets ist hochoptimiert und stark ans System gebunden deshalb ist deine Implementierung zwar für Delphi toll (weiss nicht wie das nach Delphi 3 gelöst war denk dort war das Problem auch schon aus der Welt) and im Lazarus wirkt sie eher bremsend.
W.m.k.A.h.e.m.F.h. -> http://www.gidf.de/

schnullerbacke
Beiträge: 1187
Registriert: Mi 13. Dez 2006, 10:58
OS, Lazarus, FPC: Winux (L 1.2.xy FPC 2.6.z)
CPU-Target: AMD A4-6400 APU
Wohnort: Hamburg

Beitrag von schnullerbacke »

Noch nicht verstanden,

das ist völlig unabhängig von Lazarus, Delphi oder ähnlich. Das benutzt als kleinstes Objekt TObject, bleibt mithin völlig neutral. TObject brauchen die Objekte nur um am Event-Scheduling teilnehmen zu können:

type
TMySignalSlot = procedure(blabal. MyBlaType;...)

Der Signalhandler läuft völlig eigenständig, der könnte auch von einem neuen Objekt ein Signal entgegennehmen und würde das dann über die Applikation verteilen. Die Objekte die das Signal wollen tragen einfach beim Signalhandler einen Slot dafür ein und kriegen dann auch das Event.

(* manchmal hab ich das Gefühl ich schreibe Chinesisch, dabei weist mein Fachabi ne klare 2 in Deutsch aus *)

Der Signalhandler läuft völlig ansychron, der verteilt das Event in jedem Fall an alle die den passenden Slot eigetragen haben. Das kann ím Zweifel auch ein Thread sein, der TimeTicker ist nur eien mögliche Quelle.
Humor ist der Knopf, der verhindert, daß uns der Kragen platzt.

(Ringelnatz)

Christian
Beiträge: 6079
Registriert: Do 21. Sep 2006, 07:51
OS, Lazarus, FPC: iWinux (L 1.x.xy FPC 2.y.z)
CPU-Target: AVR,ARM,x86(-64)
Wohnort: Dessau
Kontaktdaten:

Beitrag von Christian »

Ich hab das schon verstanden nur macht Lazarus das genau so und wesentlich optimierter weil näher am Betriebssystemkern.
W.m.k.A.h.e.m.F.h. -> http://www.gidf.de/

mschnell
Beiträge: 3444
Registriert: Mo 11. Sep 2006, 10:24
OS, Lazarus, FPC: svn (Window32, Linux x64, Linux ARM (QNAP) (cross+nativ)
CPU-Target: X32 / X64 / ARMv5
Wohnort: Krefeld

Beitrag von mschnell »

Christian hat geschrieben:Was bringt das ?
Versteh den Sinn gerade nicht so ganz TTimer ist erfunden oder ?


Ein TTimer Event wird über die Event-queue gestartet, das heißt: nur nachdem ein anderes Event abgearbeitet ist oder bei "Application.ProcessMessages".

Ein Signal-Handler unterbricht ein laufendes Hauptprogramm an jeder beliebigen Stelle (wie ein Thread).

Ich fürcht deshalbe, es ist nicht zulässig in einem Signal-Handler irgendwelche LCL-Aktionen (z.B. Bildschirm-Ausgaben) zu machen, weil er sich ähnlich wie ein TThread verhält).

-Michael

Christian
Beiträge: 6079
Registriert: Do 21. Sep 2006, 07:51
OS, Lazarus, FPC: iWinux (L 1.x.xy FPC 2.y.z)
CPU-Target: AVR,ARM,x86(-64)
Wohnort: Dessau
Kontaktdaten:

Beitrag von Christian »

Daran hab ich noch gar nicht gedacht aber du hast recht das dürfte zusätzlich zu einigen schwerwiegenden Problemen führen
W.m.k.A.h.e.m.F.h. -> http://www.gidf.de/

mschnell
Beiträge: 3444
Registriert: Mo 11. Sep 2006, 10:24
OS, Lazarus, FPC: svn (Window32, Linux x64, Linux ARM (QNAP) (cross+nativ)
CPU-Target: X32 / X64 / ARMv5
Wohnort: Krefeld

Beitrag von mschnell »

Ich habe gerade 'mal (in Delphi) versucht, die signalhandler Unit zu benutzen.

Ich bekomme es auch kompiliert und habe gesehen, dass automatisch eine Instanz angelegt wird, die ich dann benutzen kann.

Ich habe ein signal-Objekt angelegt und dort eine event-prozedur eingetragen. Das scheint soweit zu klappen. Allerdings wird das Event nicht aufgerufen, wenn ich das Signal auslöse.

Eine kurze Beschreibung der einzelnen Klassen und Funktionen wäre sehr hilfreich.

P.S.: Ich habe natürlich gesehen, dass mein Kommentar bezüglich Linux-Signale und die Warnung bezüglich der LCL hier Unsinn war.

-Michael

schnullerbacke
Beiträge: 1187
Registriert: Mi 13. Dez 2006, 10:58
OS, Lazarus, FPC: Winux (L 1.2.xy FPC 2.6.z)
CPU-Target: AMD A4-6400 APU
Wohnort: Hamburg

Beitrag von schnullerbacke »

Du mußt noch die Verbindung zum Main-Thread herstellen. Dazu mußt du:

Application.OnIdle

den Signalverteiler einhängen. Damit arbeitet der immer nur während der Idle-Time.

@Christian

Nun verrat mir endlich mal wo ich diese erstaunlichen Fähigkeiten bei FCL oder LCL finde. Googeln hat zu nix geführt, deswegen hab ich für sowas erstmal mein eigenes Produkt vorgeschlagen. Falls du damit allerdings das Message-Handling meinst, die die WM_blabla-Events verteilen dann kann ich mir das schenken. Die funktionierten bei Delphi nur für visuelle Objekte, deswegen der eigene Signal-Handler.
Humor ist der Knopf, der verhindert, daß uns der Kragen platzt.

(Ringelnatz)

Christian
Beiträge: 6079
Registriert: Do 21. Sep 2006, 07:51
OS, Lazarus, FPC: iWinux (L 1.x.xy FPC 2.y.z)
CPU-Target: AVR,ARM,x86(-64)
Wohnort: Dessau
Kontaktdaten:

Beitrag von Christian »

Ich denk die funktionieren beim Lazarus auch für nichtvisuelle Anwendungen und Objekte ich habe jedenfalls TTimer auch schon in Kommandozeilen Programmen eingesetzt.
W.m.k.A.h.e.m.F.h. -> http://www.gidf.de/

schnullerbacke
Beiträge: 1187
Registriert: Mi 13. Dez 2006, 10:58
OS, Lazarus, FPC: Winux (L 1.2.xy FPC 2.6.z)
CPU-Target: AMD A4-6400 APU
Wohnort: Hamburg

Beitrag von schnullerbacke »

@Christian

Es geht auch nicht um TTimer, der TTimeTicker benutzt nur lediglich einen TTimer und bildet daraus eben mehrere Signale. Dabei hab ich die gebräuchlichsten so gewählt, das man alle anderen Zeiten durch entsprechende Counter selbst bilden kann.

Um aber an die Signals zu kommen muß ich die Apllikationsweit verteilen. Das hab ich bei Delphi mit den Methoden des Message- und Command-Handling versucht und bin kläglich gescheitert. Das funzt bei denen nur bei bestimmten Objekten ab TPersistent.

Daraus entstand der SignalHandler, der nimmt von einem beliebigen Objekt ein Signal entgegen und trägt ihn in seine Liste ein. Wie findet man in TimeTicker.pas. Dann sendet ein beliebiges Objekt das entsprechende Signal mit AppSignalHandler.SendSignal (ebenfalls in TTimeTicker nachvollziehen).

Alle Objekt in der Anwendung die am Signalhandling teilnehmen wollen müssen nun noch einen entsprechenden SignalSlot für das gewünschte Signal (als string angebbar) beim AppSignalHandler registrieren. Der bindet das an das entsprechende Signal und wenn ein solches vorliegt dann verteilt er das an die registrierten Slots.

Ich würde euch ja mein Testproggi zur Verfügung stellen, dafür muß ich aber erstmal die eigenen Objekte nach LAZARUS portieren. Das ist leider nicht ganz so einfach. Damit das aber funzt hier ein Beispiel wie man den Slot einträgt:

Code: Alles auswählen

{-----------------------------------------------------------------------------
  Class:     TfoChatServerForm
  Methode:   CM1minTick
  Author:    hardy
  Date:      11-Jun-2006
  Arguments: ASignal: TAppSignalStruct
  Subject  : So oder ähnlich muß der SignalSlot vereinbart
             werden * je nach Objekt und Signal *
-----------------------------------------------------------------------------}

procedure TfoChatServerForm.CM1minTick(ASignal: TAppSignalStruct);
const
  cProcName = 'uFoChatServerForm.TfoChatServerForm.CM1minTick';
 
  function SetErrorParams: string;
  begin
    Result:= '';
    // 'ASignal: TAppSignalStruct'
  end; // of function SetErrorParams: string
 
begin
  { procedure body }
  sbStateBar.Panels[0].Text:= DateTimeToStr(Now);
end; // of TfoChatServerForm.CM1minTick
 
{-----------------------------------------------------------------------------
  Class:     TfoChatServerForm
  Methode:   FormCreate
  Author:    root
  Date:      08-Jun-2006
  Arguments: Sender: TObject
-----------------------------------------------------------------------------}

procedure TfoChatServerForm.FormCreate(Sender: TObject);
const
  cProcName = 'uFoChatServerForm.TfoChatServerForm.FormCreate';
 
  function SetErrorParams: string;
  begin
    Result:= '';
    // 'Sender: TObject'
  end; // of function SetErrorParams: string
 
var
  ASlot: TSignalSlotStruct;
begin
  { procedure body }
  tbtnSrvConnect.Hint:= 'connect Server';
  tbtnSrvConnect.ShowHint:= true;
  FCommandPrefix:= tcpChatSrv.CommandPrefix;
  FRoomObjList := TList.Create;
  FsbRoomObj_y0:= 4;
  FsbRoomObj_x0:= 4;
  FfrmLogCommands:= TfrmLogCommands.Create(sbChatRoomObjects);
  FfrmLogCommands.Left:= FsbRoomObj_x0;
  FfrmLogCommands.Top := FsbRoomObj_y0;
  FfrmLogCommands.Parent:= sbChatRoomObjects;
  FfrmLogCommands.OnMinimize:= UpdateObjectScroller;
  FfrmLogCommands.OnMaximize:= UpdateObjectScroller;
  FfrmLogCommands.EnableTicker:= true;
  FRoomObjList.Add(pointer(FfrmLogCommands));
  FsbRoomObj_y0:= FsbRoomObj_y0 + FfrmLogCommands.Height + 4;
  SetFormCaption;
  FDataModul:= TrbmsDataModul.Create(Self);
  sbStateBar.Panels[0].Text:= DateTimeToStr(Now);
  //
  // hier ist die entscheidende Passage
  //
  if (AppSignalHandler <> nil) then begin
    ASlot:= TSignalSlotStruct.Create;
    ASlot.Signal    := 'CM_1minTick';
    ASlot.Receiver  := Self;
    ASlot.SignalSlot:= CM1minTick;
    AppSignalHandler.AddSignalSlot(ASlot.Signal, ASlot);
  end; // of if (AppSignalHandler <> nil) then begin
  AppTimeTicker.Enabled:= true;
  FGuestsAllowed:= true;
end; // of TfoChatServerForm.FormCreate


Damit das ganze läuft muß der SignalHandler als erste Unit im Projekt geladen werden. Der aktiviert sich dann selbst. Der TimeTicker sollte als 2tes Objekt im Projekt stehen kann aber auch später passieren, zB. MainForm.

Nun hab ich noch die braune Idee gehabt, den SignalHandler so zu erweitern, das man zur Entwurfszeit bereits entsprechende Signals registrieren kann und dann an den verbunden Objekten den Slot automatisch erzeugen lassen kann. Nur der nötige Code für das Objekt muß dann noch eingtragen werden. Das heißt der wird ein nichtvisuelles Objekt das man auf der MainForm einklinkt und ein Gegenstück für die Slots muß gebaut werden, das man dann auf anderen visuellen Objekten einklinken kann. Bei allen anderen Objekten muß man das dann halt von Hand machen.

Der SignalHandler muß auch nirgends eingehängt werden, das war Quark. Nur initialisiert muß er sein. Der tut also nur dann was, wenn auch ein Signal anliegt.

//Edit: ich hab mal Pascal gesetzt, anstat Plain-Code; monta
Humor ist der Knopf, der verhindert, daß uns der Kragen platzt.

(Ringelnatz)

Christian
Beiträge: 6079
Registriert: Do 21. Sep 2006, 07:51
OS, Lazarus, FPC: iWinux (L 1.x.xy FPC 2.y.z)
CPU-Target: AVR,ARM,x86(-64)
Wohnort: Dessau
Kontaktdaten:

Beitrag von Christian »

Och mann schnullerbacke TTimer benutzt aber Messages unter Lazarus hatte ich auch schon 2x erzählt. Also muss das Messeging System auch ohne GUI funktionieren.
W.m.k.A.h.e.m.F.h. -> http://www.gidf.de/

Antworten