Signalslots
-
- 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
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) 99-mal heruntergeladen
Humor ist der Knopf, der verhindert, daß uns der Kragen platzt.
(Ringelnatz)
(Ringelnatz)
-
- 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
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.
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)
(Ringelnatz)
-
- 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:
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/
-
- 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
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.
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)
(Ringelnatz)
-
- 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:
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.
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/
-
- 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
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.
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)
(Ringelnatz)
-
- 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
Ein TTimer Event wird über die Event-queue gestartet, das heißt: nur nachdem ein anderes Event abgearbeitet ist oder bei "Application.ProcessMessages".Christian hat geschrieben:Was bringt das ?
Versteh den Sinn gerade nicht so ganz TTimer ist erfunden oder ?
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
-
- 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
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
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
-
- 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
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.
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)
(Ringelnatz)
-
- 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
@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:
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
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
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)
(Ringelnatz)