[gelöst] Aus eigener Prozedure Button ausführen?

Für Fragen zur Programmiersprache auf welcher Lazarus aufbaut
Antworten
Erwin
Beiträge: 286
Registriert: Mi 16. Sep 2009, 14:15
OS, Lazarus, FPC: Xubuntu 22.04 / x86_64_linux-gtk 2 / L 2.2.0 / FPC 3.2.2

[gelöst] Aus eigener Prozedure Button ausführen?

Beitrag von Erwin »

Hallo Zusammen.

Auf der Suche nach der Lösung eines Problems (Button / Buttons aus eigener Prozedure heraus auszuführen) habe ich leider zu viele Antworten gefunden. Weshalb ich mich auch da wiederum frage, welche Lösung ist die Bessere?
Weil ...

Code: Alles auswählen

 
procedure FEigene;
begin
  Button1Click(Sender); // geht nicht
end;

geht ja nicht. Die eigenen Prozedure kennt keinen 'sender'. Der Teil war leider sehr leicht heraus zu finden.
Dazu habe ich dann 2 Lösungen in den Forums gefunden:

Code: Alles auswählen

 
procedure FEigene;
begin
  Button1Click(nil); // 1: also anstatt 'Sender' dann 'nil'.
  Button1.Click; // 2: Also mit Punkt dazwischen. Wird also Click aufgerufen?
end;

Was von den Beiden ist besser für so was geeignet?
1, also das mit nil statt sender?
2, das mit der Methode (?) Click?

Oder wäre es am besten, wie einer dazu meinte, dass man in solchen Fällen den Inhalt von Button1 in eine eigene Prozedure packt, und diese dann sowohl mit dem Button als auch dann mit der anderen eigenen Prozedure aufruft?

Danke.
Zuletzt geändert von Erwin am Do 28. Jun 2018, 13:41, insgesamt 1-mal geändert.
Lazarus 2.2.0 / FP 3.2.4

Mathias
Beiträge: 6164
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Re: Aus eigener Prozedure Button ausführen?

Beitrag von Mathias »

1, also das mit nil statt sender?

Es muss nicht unbedingt nil sein.

Code: Alles auswählen

procedure TForm1.Button1Click(Sender: TObject);
begin
  if Sender = Button2 then begin
    Color := clRed;
  end else begin
    Color := clGreen;
  end;
end;
 
procedure TForm1.Button2Click(Sender: TObject);
begin
  Button1Click(Sender);
end;

Aber Schlussendlich, welche Variante das man wählt kommt immer auf die Situation an.
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

wp_xyz
Beiträge: 4869
Registriert: Fr 8. Apr 2011, 09:01

Re: Aus eigener Prozedure Button ausführen?

Beitrag von wp_xyz »

Erwin hat geschrieben:Dazu habe ich dann 2 Lösungen in den Forums gefunden:

Code: Alles auswählen

 
procedure FEigene;
begin
  Button1Click(nil); // 1: also anstatt 'Sender' dann 'nil'.
  Button1.Click; // 2: Also mit Punkt dazwischen. Wird also Click aufgerufen?
end;

Was von den Beiden ist besser für so was geeignet?
1, also das mit nil statt sender?
2, das mit der Methode (?) Click?

Oder wäre es am besten, wie einer dazu meinte, dass man in solchen Fällen den Inhalt von Button1 in eine eigene Prozedure packt, und diese dann sowohl mit dem Button als auch dann mit der anderen eigenen Prozedure aufruft?

Es widerstrebt mir, die "Click"-Methode aufzurufen - das ist letztendlich eine interne Routine, die ist zwar öffentlich zugänglich (da ist m.E. ein Fehler), aber sie macht in der Regel viel mehr als du eigentlich willst, und vielleicht passieren auch Sachen, die du gar nicht erwartest.

Den Event-Handler "Button1Click" würde ich schon eher aufrufen, er ist ja dafür gedacht, den Benutzercode auszuführen, der beim Klicken des Buttons ablaufen soll. Sender ist in der Regel nil, es sei denn er wird in der Button1Click-Routine explizit verwendet, dann musst du halt gucken.

Aber auch das gefällt mir eigentlich nicht. Stelle dir vor, du willst beim Button-Click eine Datei laden. Wenn du nach Jahren dein Programm wieder siehst und suchst, wo die Datei geladen wird, tust du dich sehr schwer, das in einer Routine "Button1Click" zu finden. Besser wird es, wenn der Button BtnLoad heißt o.ä. Aber am besten ist es, wenn du eine Prozedur "LoadFile" hast. Dann ist alles klar - diese ist für das laden der Datei zuständig. Diese Prozedur wird im Button1Click aufgerufen, und die kannst du selbst aufrufen, wenn du unabhängig von einem Button-Click eine Datei laden willst.

Meiner Meinung nach ist das richtige Vorgehen also, zumindest bei längeren und wichtigen Code-Blöcken, den OnClick-Code in eine eigene Prozedur auszulagen, die man dann unabhängig vom physikalischen Klick-Vorgang aufrufen kann.

Warf
Beiträge: 1908
Registriert: Di 23. Sep 2014, 17:46
OS, Lazarus, FPC: Win10 | Linux
CPU-Target: x86_64

Re: Aus eigener Prozedure Button ausführen?

Beitrag von Warf »

Kompromiss zwischen den beiden:

Code: Alles auswählen

if Assigned(Button1.OnClick) then Button1.OnClick(Button1);

Somit muss man nicht den Code ändern wenn man im Objektinspektor eine andere Funktion als Event auswählt, verwendet aber nicht die Interne Methode Click (sollte die nicht eh protected sein?)

MmVisual
Beiträge: 1445
Registriert: Fr 10. Okt 2008, 23:54
OS, Lazarus, FPC: Winuxarm (L 3.0 FPC 3.2)
CPU-Target: 32/64Bit

Re: Aus eigener Prozedure Button ausführen?

Beitrag von MmVisual »

Button1Click(nil) ;
Button1Click(Button1) ;
Button1Click(TObject(1));
Button1Click(Self);

Alles geht, es kommt immer darauf an was man mit dem Sender in der Funktion Button1Click alles machen will.

Procedure Tform1. Butt(Sender: TObject);
Begin
If Sender is TButton Then
TButton(Sender).Caption := 'gugus'
Else If Sender = TObject(1) Then
ShowMessage('Object ' + IntToStr(Integer(Sender)))
Else If Sender=nil Then
ShowMessage('nix gugus');
End;

Button1.OnClick := @Butt;
Button2.OnClick := @Butt;


Dann auf die Tasten drücken, die ändern dann Caption

Butt(nil);
Zeigt die messagebox.

Man kann somit eine Funktion mehreren Steuerelementen zuordnen und innerhalb dieser unterscheiden was man anders ausführen möchte.

Sender ist nur eine stink normale Variable und enthält einen Zeiger auf das aufgerufene Steuerelement, diese kann jederzeit für eigene Zwecke genutzt werden, so wie in dem Beispiel wo eine Zahl 1 übergeben wird.

PS: hab grad kein PC da, können Tippfehler drin sein...
EleLa - Elektronik Lagerverwaltung - www.elela.de

Erwin
Beiträge: 286
Registriert: Mi 16. Sep 2009, 14:15
OS, Lazarus, FPC: Xubuntu 22.04 / x86_64_linux-gtk 2 / L 2.2.0 / FPC 3.2.2

Re: Aus eigener Prozedure Button ausführen?

Beitrag von Erwin »

Mathias, in Deinem Beispiel wird aber aus einem anderen Button heraus ein Button aufgerufen. Mir geht es darum, aus eigener Prozdure ein Button auszuführen. Aber informativ ist es dennoch. Auf diese weise kann man also direkt (ohne Variable) überprüfen, welcher Button gedrückt wurde?

2-3 Möglichkeiten waren es am Anfang. Jetzt sind es sogar 6-7!?

Code: Alles auswählen

 
Button1Click(nil); // 1 (alt)
Button1.Click; // 2 (alt)
if Assigned(Button1.OnClick) then Button1.OnClick(Button1); // 3
// oder gleich
Button1.OnClick(Button1); // 3 1/2
Button1Click(Button1); // 4
Button1Click(TObject(1)); // 5
Button1Click(Self); // 6
 


Warf, wieso verwendest Du 3 (if Assigned(Button1.OnClick) then Button1.OnClick(Button1);),
anstatt 3 1/2 (Button1.OnClick(Button1);)?
Bei mir funktioniert nämlich auch das kürzere. Verwirrend beim Schreiben ist aber, dass wenn man dann ab 'Button1.' bei der Auswahl dann 'OnClick' wählt, der Lazarus selbst ':=' Hinzufügt? So was bringt einem total vom eigentlichen Code ab.

wp_xyz, einen Teil Deiner Ausführung verstehe ich nicht. Nämlich das ab dem 'Button-Click eine Datei laden'? Da geht es doch nur noch um Namensgebung? Was hat das mit meiner Frage zu tun?
Was den Sender betrifft: Dann ist das also selber ein Methode/Variable, die meist den Wert nil enthält? Und wenn man statt Sender nil nimmt, teilt man im meist das selbe wie Sender mit, aber eben direkter. So wie 'text' direkter ist, als TextVariable?
Also wenn ich mir all die Möglichkeiten ansehe, komme ich auch langsam zu dem Schluss, dass es besser ist, all jene Buttons-Inhalte, die mehrmals (also von anderen Buttons, Prozedure etc.) gebraucht werden kann, in eine eigene Prozedure auszulagern.
Lazarus 2.2.0 / FP 3.2.4

wp_xyz
Beiträge: 4869
Registriert: Fr 8. Apr 2011, 09:01

Re: Aus eigener Prozedure Button ausführen?

Beitrag von wp_xyz »

Erwin hat geschrieben:wp_xyz, einen Teil Deiner Ausführung verstehe ich nicht. Nämlich das ab dem 'Button-Click eine Datei laden'? Da geht es doch nur noch um Namensgebung? Was hat das mit meiner Frage zu tun?

Eigentlich nichts, aber das ist zum einen so zentral, dass man es nicht oft genug wiederholen kann. Und zum anderem wird in diesem Thread immer nur von Button1Click geredet, so dass man auf die Idee kommen kann, dass es tatsächlich bei diesem Namen bleibt.

Erwin hat geschrieben:Was den Sender betrifft: Dann ist das also selber ein Methode/Variable, die meist den Wert nil enthält? Und wenn man statt Sender nil nimmt, teilt man im meist das selbe wie Sender mit, aber eben direkter. So wie 'text' direkter ist, als TextVariable?

Sender ist eine Variable (keine Methode) und enthält das Objekt, das das Ereignis erzeugt hat und "aussendet". Bei einem Button-Click wird dort der Zeiger auf den Button eingetragen, der geklickt wurde. Im der Ereignisbehandlungsroutine (TForm1.Button1Click) kannst du diese Information verwenden - oder nicht. In den meisten Fällen wird Sender ignoriert, manchmal wird aber ein Eventh-Handler von mehreren Objekten gemeinsam verwendet, um Code sparen - dann kann man am Sender unterscheiden, was genau passieren soll. Wenn du den Eventhandler unabhängig von einem Click-Ereignis aufrufen willst, musst du dir den Code ansehen und prüfen, ob und wie "Sender" dort verwendet wird; dementsprechend muss die aufrufende Routine den richtigen Sender einsetzen. In - ich würde sagen - 95% aller Fälle wird nil genügen, weil der Sender im Button1OnClick gar nicht verwendet wird.

Warf
Beiträge: 1908
Registriert: Di 23. Sep 2014, 17:46
OS, Lazarus, FPC: Win10 | Linux
CPU-Target: x86_64

Re: Aus eigener Prozedure Button ausführen?

Beitrag von Warf »

MmVisual hat geschrieben:

Code: Alles auswählen

Procedure Tform1. Butt(Sender: TObject);
Begin
  If Sender is TButton Then
    TButton(Sender).Caption := 'gugus'
  Else If Sender = TObject(1) Then
    ShowMessage('Object ' + IntToStr(Integer(Sender)))
  Else If Sender=nil Then
    ShowMessage('nix gugus');
End;

Das kann glaub ich ganz schön in die Hose gehen, afaik muss für den is Operator der Pointer Dereferenziert werden, das heißt wenn es TObject(1) ist wird der Pointer(1) dereferenziert, was im best case funktioniert und keinen Button findet, es kann aber auch sein das es funktioniert, da irgendwas findet was aussieht wie ein Button und dann TButton(Sender).Caption := 'gugus' ausführt, oder es knallt mit einer Zugriffsverletzung. Für Nil gilt natürlich genau das gleiche, nur das es dann immer knallen müsste
Daher eher:

Code: Alles auswählen

Procedure Tform1. Butt(Sender: TObject);
Begin
  If not Assigned(Sender) then // Alternative zu Sender = nil
    ShowMessage('nix gugus')
  Else If IntPtr(Sender) = 1 Then
    ShowMessage('Object ' + IntToStr(IntPtr(Sender)))
  Else If Sender is TButton Then
    (Sender As TButton).Caption := 'gugus'; // Besserer Stil (auch wenn es hier keinen unterschied machen sollte)
End;


Erwin hat geschrieben:Warf, wieso verwendest Du 3 (if Assigned(Button1.OnClick) then Button1.OnClick(Button1);),
anstatt 3 1/2 (Button1.OnClick(Button1);)?
Bei mir funktioniert nämlich auch das kürzere. Verwirrend beim Schreiben ist aber, dass wenn man dann ab 'Button1.' bei der Auswahl dann 'OnClick' wählt, der Lazarus selbst ':=' Hinzufügt? So was bringt einem total vom eigentlichen Code ab.

Um zu überprüfen ob das Event überhaupt gesetzt ist. Grade wenn du die Events dynamisch befüllst kann es vorkommen das du eventuell mal kurz kein Event drin hast (eventuell sogar gewollt) und du willst ja nicht das es dann knallt.
Das := macht Lazarus übrigens hin da OnClick eine Read-Write property ist der man auch Events zuweisen kann:

Code: Alles auswählen

procedure TForm1.Button1Click(Sender: TObject);
begin
  ShowMessage('Funk 1');
  (Sender as TButton).OnClick := @Button2Click;
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
  ShowMessage('Funk 2');
  (Sender as TButton).OnClick := @Button1Click;
end;

Und bei Schreibbaren Properties macht lazarus automatisch := wen sie am Zeilenanfang stehen (macht ja bei allem außer Funktionszeigern auch irgendwie sinn)

Erwin
Beiträge: 286
Registriert: Mi 16. Sep 2009, 14:15
OS, Lazarus, FPC: Xubuntu 22.04 / x86_64_linux-gtk 2 / L 2.2.0 / FPC 3.2.2

Re: Aus eigener Prozedure Button ausführen?

Beitrag von Erwin »

wp_xyz, das hört sich so an, als würde in der Variable Sender meist der eigene gedrückte Button drin stehen?
Was die Namen betrifft: Nur keine Angst, so was wie Button1 gibt es bei mir nicht. Bei mir wird alles umbenannt (zb. LadenB, LadenDateiB, etc.).. Aber leider heißt es sogar in meinen dicksten Buch, beim ersten Programmbeispiel, dass man vorerst keine Namen vergeben muss ... . Fürchterlich, weil so was sollte man sich von Anfang an angewöhnen, sonst geht es schwerer ins Blut über. Schon traurig, was es alles für Autoren gibt.

Warf, ich habe es sogar abgeschrieben, damit mehr davon hängen bleibt. Und dennoch hatte ich das all bekannte if then nicht richtig registriert. :roll:
Wird vermutlich am Assigned gelegen haben. Kenne den Befehl nicht wirklich, und somit schwer nachvollziehbar, ob und wie man so was vergleichen kann?
Einige (oder gar alle) Alternativen sind also Anfällig für den Knall? Natürlich erst recht, wenn man sie nicht exakt richtig wiedergibt?

Wenn ich das jetzt Richtig verstanden habe, will man aus der eigenen Prozedure heraus ein Button-Ereignis auslösen, kann es eventuell dabei während des Programm läuft, was schief gehen? Irgendwie habe ich den Eindruck, auch zwischen den Zeilen gelesen zu haben, dass so was auch dann passieren könnte, wenn aus einem Button-Ereignis heraus ein anderes Button-Ereignis ausgelöst wird?
Wird also das Vernünftigste sein, so bald innerhalb eines Objekts (Button) ein Code mehrmals gebraucht wird, dann gleich alles in eine eigene Prozedure zu packen?
Lazarus 2.2.0 / FP 3.2.4

Epcop
Beiträge: 140
Registriert: Di 29. Mai 2012, 09:36

Re: Aus eigener Prozedure Button ausführen?

Beitrag von Epcop »

Erwin hat geschrieben:

Code: Alles auswählen

 
procedure FEigene;
begin
  Form1.Button1Click(Sender); // jetzt geht es !?
end;


Erwin
Beiträge: 286
Registriert: Mi 16. Sep 2009, 14:15
OS, Lazarus, FPC: Xubuntu 22.04 / x86_64_linux-gtk 2 / L 2.2.0 / FPC 3.2.2

Re: Aus eigener Prozedure Button ausführen?

Beitrag von Erwin »

Epcop, was soll das? Wo bitte schön soll ich das geschrieben haben, mit dem 'jetzt geht es !?'.
Lazarus 2.2.0 / FP 3.2.4

Mathias
Beiträge: 6164
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Re: Aus eigener Prozedure Button ausführen?

Beitrag von Mathias »

Wo hast du jetzt den Sender gefunden, hast du diesen global deklariert ?
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

Erwin
Beiträge: 286
Registriert: Mi 16. Sep 2009, 14:15
OS, Lazarus, FPC: Xubuntu 22.04 / x86_64_linux-gtk 2 / L 2.2.0 / FPC 3.2.2

Re: Aus eigener Prozedure Button ausführen?

Beitrag von Erwin »

Mathias, ich habe das nie so geschrieben. Jedenfalls kann ich mich weder daran erinnern, noch finde ich solche einen Beitrag von mir. Epcop hat das Zitat offenbar absichtlich verfälscht, um zu stören.
Lazarus 2.2.0 / FP 3.2.4

Warf
Beiträge: 1908
Registriert: Di 23. Sep 2014, 17:46
OS, Lazarus, FPC: Win10 | Linux
CPU-Target: x86_64

Re: Aus eigener Prozedure Button ausführen?

Beitrag von Warf »

Erwin hat geschrieben:Warf, ich habe es sogar abgeschrieben, damit mehr davon hängen bleibt. Und dennoch hatte ich das all bekannte if then nicht richtig registriert. :roll:
Wird vermutlich am Assigned gelegen haben. Kenne den Befehl nicht wirklich, und somit schwer nachvollziehbar, ob und wie man so was vergleichen kann?

Assigned(Ptr) ist eine alternative zu Ptr <> nil.
Erwin hat geschrieben:Einige (oder gar alle) Alternativen sind also Anfällig für den Knall? Natürlich erst recht, wenn man sie nicht exakt richtig wiedergibt?

Nein, Button1Click funktioniert praktisch immer, hat aber den nachteil, wenn du den Eventhandler im Objektinspektor änderst Musst du jedes Vorkommen von Button1Click auch ändern. Das kann man schnell vergessen.
Button1.Click sollte an sich auch nicht knallen, aber es kann unerwünschte seiteneffekte haben, da Controls auch oft noch weitere Interne funktionalität haben. Das sollte man nur verwenden wenn man das auch wirklich wünscht, wenn man einfach nur seinen eignen Code aus dem Eventhandler ausführen will sollte man aber darauf verzichten.
Die Varianten mit den verschiedenen Sendern kann nur knallen wenn die Funktionen Annahmen über den Sender stellen die nicht stimmen. Im zweifel übergib als Sender immer ein Control das dieses Event wirklich hat, denn damit muss es ja immer gehen.
Button1.OnClick kann knallen wenn das Event bei dem Button nicht gesetzt ist (z.B. im Objektinspektor entfernt wurde), daher das if Assigned, das überprüft ob das Event gesetzt ist (sonst wäre es nil)
Daher ist mMn.

Code: Alles auswählen

if Assigned(Button1.OnClick) then Button1.OnClick(Button1);

die sicherste Lösung, auch wenn keine der anderen wirklich falsch ist
Zuletzt geändert von Warf am Di 26. Jun 2018, 22:22, insgesamt 1-mal geändert.

Mathias
Beiträge: 6164
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Re: Aus eigener Prozedure Button ausführen?

Beitrag von Mathias »

Epcop hat das Zitat offenbar absichtlich verfälscht, um zu stören.
Oder er hat verseht lich irgendwo falsch geklickt.
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

Antworten