![]() |
Timer in einer Unit
Ich brauche einen Timer in einer Unit oder Klasse (ist noch nicht klar). Bei einem Formular packe ich einfach die Timer Komponente drauf und gut ist es. Nur wie mache ich das wenn ich kein Formular habe?
Ich hab mir die Timer Komponente angeguckt, auf das nötigste, zumindest was ich für nötig halte, gestaucht, und den Code erstellt. Funktionieren tut es, gleich beim ersten Mal. Nur bei mir hat noch nie etwas beim ersten Mal funktioniert, was mich skeptisch macht. Ich brauch es nicht kompliziert, nur eben, dass etwas im Takt ausgeführt wird. In dem Code der TTimer Komponente wird noch auf WM_TIMER geprüft. Ist das nötig? Was mich wundert ist auch IDEvent. In der Api-Hilfe steht, dass der Wert nur von Null verschieden sein soll. Bei der Timer-Komponente steht da einfach ein 1. Aber so wie ich es verstanden haben wird der Wert bei KillTimer benötigt um das Ganze zu stoppen. Wie wird hier also zwischen den einzelnen Timern unterschieden?
Delphi-Quellcode:
const
IDEvent: Integer = 1; //Von Null verschieden var a: Integer = 0; procedure OnTimer; begin Inc(a) end; procedure TimerStop; begin KillTimer(Application.Handle, IDEvent); end; procedure TimerStart; var Interval: Cardinal; begin TimerStop; Interval := 1000; SetTimer(Application.Handle, IDEvent, Interval, @OnTimer); end; |
AW: Timer in einer Unit
Zitat:
Delphi-Quellcode:
MyTimer := TTimer.Create(Self);
|
AW: Timer in einer Unit
Jeder TTimer erstellt sich intern sowieso sein eigenes Form, da es unmöglich ist, abzufragen, ob auf einer Form schon Timer sind und welche IDs Diese belegen, also Nicht-TTimer, so wie Deiner, direkt über die WinAPI.
|
AW: Timer in einer Unit
Ja die WM_TIMER ist wichtig. Damit deine TTimer Komponente auch funktioniert, benötigst du im selben Thread eine Message Queue (
![]() ![]() ![]() Da ich die Window Messages nicht sonderlich mag, implementiere ich mir Timer in Units ohne Formular meistens als Thread, der X Sekunden einfach Sleept. |
AW: Timer in einer Unit
Zitat:
![]() Da er den Timer mit SetTimer(Application.Handle, IDEvent, Interval, @OnTimer); erstellt und somit eine Funktion (@OnTimer) zum Aufrufen mit übergibt, bekommt er gar kein WM_TIMER. Das WM_TIMER-Event wird nur an das Windows-Handle (hier Application.Handle) geschickt, wenn KEINE Funktion angegeben wird, sondern NULL/NIL. Die Implementation von WM_TIMER im TTimer kommt daher, das hier wahrscheinlich ein NIL als Funktion angegeben wird und somit an das TTimer ein WM_TIMER geschickt wird. Die TTimer Komponente erzeugt ein eigenes Windows-Handle und somit ist IDEvent egal, da jeder TTimer eh sein eigenes Handle hat und IDEvent dann im Kontext des Handle nur eindeutig sein muss. Wird hingegen kein Handle übergeben, so muß IDEvent eindeutig sein, um eine Trennung zwischen den verschiedenen Timer zu erreichen. Wenn Du wie hier mit Application.Handle arbeitest, brauchst Du für jeden weiteren Timer eine neue IDEvent um einen weiteren Timer mit dem selben Application.Handle zu erstellen. |
AW: Timer in einer Unit
Ich bezog mich aber doch ausdrücklich auf die TTimer Komponente, welche wohl die Windows Messages verwendet. Die
![]() |
AW: Timer in einer Unit
Sorry, dachte du meintest mit ' deine TTimer' den im ersten Posting gezeigten Quellcode.. :(
|
AW: Timer in einer Unit
Zitat:
|
AW: Timer in einer Unit
Zitat:
|
AW: Timer in einer Unit
Zitat:
Kein hWnd und nIDEvent passt zu keinem existierenden Timer, dann gibt es eine neue Timer ID. Also wird nicht generell ignoriert. |
AW: Timer in einer Unit
Zitat:
Zitat:
|
AW: Timer in einer Unit
Zitat:
![]() an. Zitat:
Und zudem steht auch da, was der Rückgabewert bedeutet: Die Timer ID |
AW: Timer in einer Unit
Also, Beispiel:
Wenn Du 3 unterschiedliche Timer benötigst, dann brauchst du a) 3 verschiedene Handles, dann können die IDs gleich sein aber auf jeden Fall > 0. oder b) bei gleichem oder keinem Handle, z.B. dem Application.Handle, 3 verschiedene IDs Windows MUSS den Timer eindeutig identifizieren können und nutzt hier eine Kombination aus dem Handle UND der ID. Wenn Du bereits einen Timer mit der gleichen Kombination von Handle und ID erzeugt hast und das SetTimer nochmals mit den gleichen Werten aufrufst, dann wird KEIN neuer Timer angelegt, sondern bei dem bestehendem Timer NUR der Interval und ggf. die Funktion ausgetauscht. Nur wenn Du eine NEUE Kombination von Handle und ID verwendest, wird ein neuer Timer erzeugt. So, hoffe nun die Abhängigkeit zwischen Handle und ID verdeutlicht zu haben.. :wink: |
AW: Timer in einer Unit
Zitat:
Das Ergebnis war das, welches ich gepostet habe. Dies entspricht allerdings inhaltlich nicht exakt dem, welches ich aus Deinem Link zu MSDN entnehmen kann. Vielleicht sollte man die F1-Taste doch deaktivieren und nicht mehr erwähnen. Zitat:
|
AW: Timer in einer Unit
Was kann man abschließend zu dem ersten Code sagen? Ist der Ok, bzw. sauber, oder fehlt da noch was? Wie gesagt, im TTimer Klasse wird da noch nach WM_TIMER geprüft, nur erkenne ich die Notwenigkeit nicht. Kann aber an mir liegen.
Unabhängig dessen, weil ich dann doch unsicher war, habe ich nach weitere Methoden getestet. Dieses Mal habe ich den Tipp befolgt doch den TTimer selbst zu nehmen. Nur wie gesagt, es soll ohne Formular funktionieren, bzw. in einer Unit. Ich hab zwei Beispiele mit TTimer. Der eigentliche Aufwand dreht sich eigentlich nur um OnTimer.
Delphi-Quellcode:
Andere Variante. Die Idee fand ich im Internet. Ich weiß nicht ob die besser ist als die erste, sie ist zunindest etwas anders:
type
TMyTimer = class(TTimer) public procedure TimerTick(Sender: TObject); end; var MyTimer: TMyTimer; a: Integer = 0; procedure TMyTimer.TimerTick(Sender: TObject); begin Inc(a); end; procedure MyTimerCreate; begin MyTimer := TMyTimer.Create(nil); with MyTimer do begin Interval := 1000; Enabled := True; OnTimer := TimerTick; end; end; procedure MyTimerFree; begin MyTimer.Free; end; initialization MyTimerCreate; finalization MyTimerFree; end.
Delphi-Quellcode:
Das sind jetzt also drei Varianten. Welche ist die Beste für Timer in einer Unit ohne Formular?
type
TEventHandlers = class procedure TimerTick(Sender : TObject); end; var MyTimer: TTimer; EventHandlers : TEventHandlers; a: Integer = 0; procedure TEventHandlers.TimerTick(Sender: TObject); begin Inc(a); end; procedure MyTimerCreate; begin EventHandlers := TEventHandlers.Create(); MyTimer := TTimer.Create(nil); with MyTimer do begin Interval := 1000; Enabled := True; OnTimer := EventHandlers.TimerTick; end; end; procedure MyTimerFree; begin EventHandlers.Free; MyTimer.Free; end; initialization MyTimerCreate; finalization MyTimerFree; end. |
AW: Timer in einer Unit
Zitat:
|
AW: Timer in einer Unit
Zitat:
|
AW: Timer in einer Unit
Zitat:
Die API Lösung basiert auf MMSystem (TMMTime) gruss |
AW: Timer in einer Unit
Zitat:
|
AW: Timer in einer Unit
@BadenPower
Ist schon klar. Man kann aber auch die Verandatür öffnen und sein Auto im Wohnzimmer parken. Gehen tut es, womit die Parkplatzprobleme vorbei wären, man fragt sich aber trotzdem stets ob es die feine saubere Art ist. |
AW: Timer in einer Unit
Eigentlich würde ich lieber ein OOP-Turorial empfehlen, aber hier mal in Kurz, mit Kommentaren.
Delphi-Quellcode:
type
TMyTimer = class(TTimer) protected procedure DoOnTimer; override; public constructor Create(Owner: TComponent); override; //procedure TimerTick(Sender: TObject); // wenn man schon vererbung benutzt, dann doch bitte richtig -> DoOnTimer end; var MyTimer: TMyTimer; // OK, da mach ich noch mit, aber natürlich nur in Implementation deklariert, aber eigentlich als private Class-Var in die Klasse //a: Integer = 0; // warum ist das global, wo es doch nur im Timer benutzt wird? //procedure TMyTimer.TimerTick(Sender: TObject); procedure TMyTimer.DoOnTimer; begin Tag := Tag + 1; //Inc(FCount); // privates Feld "FCount: Integer" mit Property oder das Entwicklerproperty "Tag" inherited; end; constructor TMyTimerCreate(Owner: TComponent); begin inherited; Interval := 1000; end; //procedure MyTimerCreate; //begin // MyTimer := TMyTimer.Create(nil); // alleine in Prozedur, also sinnlos hier // with MyTimer do // begin // Interval := 1000; // nja, 1000 ist zwar auch default, aber wenn es sein muß, dann wenigstens in den Constructor // Enabled := True; // es ist per Default immer Enabled // OnTimer := TimerTick; // sinnlos // end; //end; //procedure MyTimerFree; //begin // MyTimer.Free; //end; initialization //MyTimerCreate; MyTimer := TMyTimer.Create(nil); finalization MyTimer.Free; //MyTimerFree; end. |
AW: Timer in einer Unit
@himitsu
Danke für die Kommentare, nur... das war kein Auszug aus der fertigen Unit, es war nur ein Test. Mehr nicht. Nur ein Test. Es ist toll, dass du eine Klasse konstruiert hast, nur wozu? Welchen Sinn hat das was du gemacht hast? Was soll ich damit? Was soll ich damit anfangen? Wenn ich eine Klasse brauche, dann nehme ich gleich die TTime Komponente. Letztendlich hast du einen Apfel genommen, ein Herzchen draufgeklebt, und das Ergebnis ist ein Apfel. Bevor du also unnötige Verbesserungsvorschläge machst, frag lieber ob das die Frage ist. |
AW: Timer in einer Unit
Die Klasse hattest du auch schon, nur so, wie der Code war, hätte man TTimer nicht ableiten müssen, da praktisch alle Funktion außerhalb lag. :zwinker:
|
AW: Timer in einer Unit
Du hast also eine Unit und keine Klasse, aber eine Prozedur?
Ich würde keinen Timer ableiten, sondern das über den TEventHandler machen. Aber eigentlich würde ich eine Klasse schreiben und einer der Methoden der Klassen dem 'OnTimer' zuweisen. Aber wenn irgendwo legacy code rummschwirrt, würde ich mir die Mühe auch nicht machen und das kleinste Übel nehmen: Den TEventhandler. Ich hab übrigens auch lange gedacht, so eine Komponente ist ein ziemlich großer Klumpen, denn ich bloß nicht in schlanken Klassen/Units verwenden darf. Aber -pah- drauf gebongt. Erstens sind die nicht groß und zweitens ist das Delphi und wenn Komponenten rumliegen, kann man sie doch benutzen. Ich würde jetzt natürlich auch kein TStringGrid nehmen, um ein Array of Array of String abzulegen, aber so ein Timer ist schon schlank. Aber wenn Du drauf Wert legst: Wieso nicht einen Threaded-Timer? Also schlanker gehts nimmer. |
AW: Timer in einer Unit
Nur weil man kein Formular hat, heißt das ja nicht, dass man keine Komponenten als solche nutzen kann. Im Grunde ist der Fall hier ein Paradebeispiel für ein Datenmodul. Da kommt der Timer drauf, wird ganz normal genutzt, und der Rest der Funktionalität kommt auch in die Datenmodul-Klasse.
|
AW: Timer in einer Unit
@himitsu
Ich will dich nicht dafür kritisieren, weil du etwas richtig machen willst, aber das ist ein Programmiererforum. Warum muss der Code optimiert sein? Kann doch jeder selbst machen. Wenn einer nicht weiß wie er A und B in einer Klasse addieren kann, dann schreibe ich in einer Button1Click Prozedur wie man A und B addiert, denn das ist wohl das Problem. Ich packe das nicht in eine Klasse, denn ich gehe davon aus, dass der Fragesteller weiß wie man eine Klasse schreibt. Ich konzentriere mich auf das Problem. Die Beispiele oben beschreiben mögliche Lösungen des Problems, in Einzelteile zerlegt. Der eine Block beschreibt das eine Teil, der andere ein anderes. Wer wissen will wie man Klassen programmieren kann, der kann fragen wie man Klassen programmieren kann. Wer wissen will wie man das Beispiel eleganter programmieren kann, den kann danach fragen. @Dejan Vu Das was ich vorhabe ist etwas komplexer. Mich interessiert es nicht wirklich wie man mit Inc a erhöhen kann. Vielmehr ist es so, dass ich beim Aufruf einer Prozedur eine weitere für eine bestimmte Zeit getaktet starten will. Womit letztendlich mein erstes Beispiel im Post #1 der optimale wäre, denn da muss ich keine Objekte erzeugen oder freigeben, sondern nur den Timer starten und stoppen. Ist für eine Unit optimaler. Trotzdem habe ich auch andere Möglichkeiten geprüft (und die Ergebnisse gepostet falls einer eine ähnliche Frage hat). @jaenicke Ok, danke. Das mit der Komponente und Formular war etwas was ich zwar immer gemacht habe, aber stets nach anderen Möglichkeiten gesucht habe. |
AW: Timer in einer Unit
Vielleicht nicht optimiert, aber wenigstens "aufgeräumt"? :angle2:
|
AW: Timer in einer Unit
Du erinnerst mich etwas an ein HTML-Forum für Anfänger, in dem ich früher (so vor 10 Jahren) gelegentlich war. Regelmäßig wie das Amen in der Kirche kam dort ein Newbie vorbei der sich an einer Webseite versuchte und etwas in zwei Spalten schreiben wollte. Die simple Lösung die damals üblich war und selbst von wichtigen Portale noch genutzt wurde war die Tabelle. Man nehme einen Tabellen-Tag, einen Zeilen-Tag und zwei Spalten-Tags. Fertig.
Dafür muss man nichts lernen. Das sagt man dem Newbie, zeigt es ihm und er ist glücklich. Der will ja nichts groß lernen, er will eine Webseite mit Infos erstellen. Aber wehe einer kam mit der Tabellenlösung an. Denn da gab es die Experten, und für die war die Tabellenlösung keine Lösung. Um etwas in zwei Spalten zu schreiben muß man erst CSS lernen - das war deren Antwort auf die Frage. Ohne CSS geht es nicht. Da gab es dauerhaft Diskussionen. Ich weiß nicht ob je einer der Newbie seine Webseite vollendet hat oder gar extra wegen der zwei Spalten CSS gelernt hat. Aber das war in dem Forum nie wirklich wichtig. Wichtig war, dass man für Spalten CSS drauf haben sollte. |
AW: Timer in einer Unit
Und wer ist hier bei deinem Vergleich hier der Newbie?
Ein Programmierforum ist sehr wohl und zuallererst dazu da, Code nicht nur zu zeigen, sondern auch zu optimieren. Natürlich soll man die TE nicht überfordern, und insofern wäre ein echter Newbie bestimmt überfordert. Nun zähle ich dich aber nicht so direkt zu den, die froh sind, überhaupt die Compile-Funktion entdeckt zu haben. Wenn Du in 2015 in einem Bauforum das Bohren eines Lochen in einer Wand damit demonstrierst, indem Du eine Lehmwand hochziehst, darf man schon darauf hinweisen, das es auch andere, bessererere Baustoffe gibt. Vor allen Dingen, weil bei einer dermaßen fundamentalen Frage auch Interessierte vorbeischauen, die neben den einfachen Tipps auch Optimierungen zu sehen bekommen. |
AW: Timer in einer Unit
Hier mal ein Minimalst-Beispiel, was auch komplett auf ein Handle verzichtet
Delphi-Quellcode:
unit Unit1;
interface uses {Winapi.}Windows; // Startet den Timer oder // setzt ein neues Intervall für einen aktiven Timer procedure TimerStart( Interval: UINT ); // Stoppt den Timer procedure TimerStop; implementation var nIDEvent: UINT_PTR = 0; // Beim Start kein Timer, also nIDEvent = 0 procedure OnTimer; begin // hier irgendwas machen end; procedure TimerStop; begin if KillTimer( 0, nIDEvent ) then nIDEvent := 0; // Wenn der Timer entfernt werden konnte, dann nIDEvent wieder auf 0 setzen end; procedure TimerStart( Interval: UINT ); begin nIDEvent := SetTimer( 0, nIDEvent, Interval, @OnTimer ); end; end. |
AW: Timer in einer Unit
@Dejan Vu
Vielleicht hast du auch Recht, denn irgendwann wird es dann doch peinlich wenn man merkt, dass jemand denkt, dass man das hier
Delphi-Quellcode:
nur deshalb so geschrieben hat, weil man es nicht besser drauf hat, und nicht, weil dahinter eine Absicht steckt und man an die denkt, die evtl. in den nächsten Jahren danach suchen und eher den Sinn verstehen wollen.
procedure MyTimerCreate;
begin MyTimer := TMyTimer.Create(nil); with MyTimer do begin Interval := 1000; Enabled := True; OnTimer := TimerTick; end; end; initialization MyTimerCreate; Und für die, die noch immer nicht verstanden haben wieso der Code so zerlegt ist und es eine procedure TimerTick gibt, den Grund erkennen die, die das Problem man haben werden. Denn es ist nicht ohne Grund so. Denn gelegentlich braucht man die Prozedur nicht. Also ist sie separat. Ich werde das jetzt aber nicht vertiefen. |
AW: Timer in einer Unit
Hi.hi..
:wink: Nur ne kleine Anmerkung: SetTimer( 0, nIDEvent, Interval, @OnTimer ); benötigt eine nIDEvent > 0 !! Also hier einfach SetTimer( 0, 1, Interval, @OnTimer ); Da mit dieser Minimalroutine eh nur ein Teimer erzeugt werden kann :) Zitat:
|
AW: Timer in einer Unit
Zitat:
Evtl. liegt es auch daran, dass ich es mir angewöhnt habe den gesamten Abschnitt der Dokumentation zu lesen und nicht nur das erste Schlagwort. |
AW: Timer in einer Unit
Schau doch mal in den Post Nr. 5 von diesem Thema, da habe ich versucht es zu erklären...:wink:
![]() Zitat:
OK, da weiß wohl MS selber nicht was es will. A nonzero timer identifier. ..nIDEvent should be 0 if the hWnd is NULL Zitat:
|
AW: Timer in einer Unit
Zitat:
Sprich es heißt nicht, dass der Parameter nicht 0 sein darf, sondern der Identifizierer für einen Timer. In dem Fall muss man das einfach nur wörtlich nehmen... |
AW: Timer in einer Unit
:thumb: aber der Event wird nur 1x ausgelöst, d.h. es handelt sich nicht um einen periodischen Timer (den man aber sehr leicht anhand des Codes erstellen kann)
|
AW: Timer in einer Unit
Zitat:
|
AW: Timer in einer Unit
Zitat:
So ist es halt nur wieder das übliche BlaBla. |
AW: Timer in einer Unit
Ich hatte die MSDN-Beschreibung so verstanden, aber -so im nachhinein- wäre das auch ziemlich blöd, wenn es nur 1x gefeuert würde.
Zitat:
|
AW: Timer in einer Unit
Ich hab das mal übersetzt:
SetTimer
Code:
Die Funktion SetTimer erzeugt ein System-Timer-Ereignis. Ein Time-Out-Wert wird festgelegt. Immer wenn ein Time-Out eintritt, sendet das System eine WM_TIMER-Meldung an die installierte Anwendungswarteschlange, oder übergibt die Meldung an eine anwendungsdefinierte TimerProc-Callback-Funktion.
function SetTimer(Wnd: HWnd; IDEvent: Integer; Elapse: Word; TimerFunc: TFarProc): Word;
Rückgabewert Der Rückgabewert legt den Integer-Bezeichner für das neue Timer-Ereignis fest, wenn der Parameter Wnd Null ist, und die Funktion erfolgreich ausgeführt wurde. Eine Anwendung übergibt diesen Wert an die Funktion KillTimer, um das Timer-Ereignis zu beseitigen. Der Rückgabewert ist ungleich Null, wenn der Parameter Wnd ein gültiges Fenster-Handle ist, und die Funktion erfolgreich ausgeführt wurde. Andernfalls ist der Rückgabewert Null. Hinweis Timer sind eine eingeschränkt verfügbare, globale Ressource. Aus diesem Grund ist es wichtig, daß eine Anwendung den durch die Funktion SetTimer zurückgegeben Wert überprüft, um sicherzustellen, daß ein Timer verfügbar ist. Um eine Timer-Funktion zu installieren, muß TimerFunc eine Adresse der Prozedurinstanz einer Callback-Funktion bestimmen und die Funktion muß in der Moduldefinitionsdatei der Anwendung exportiert werden. Die Adresse sollte mit Hilfe der Funktion MakeProcInstance erzeugt werden. Die Callback-Funktion muß die Pascal-Aufrufkonvention verwenden und als FAR deklariert werden. |
Alle Zeitangaben in WEZ +1. Es ist jetzt 17:19 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz