Ein zweites Form wird aufgerufen und verändert Variablen in Form1

Antworten
Aloxen
Beiträge: 58
Registriert: Di 31. Mai 2022, 17:40

Ein zweites Form wird aufgerufen und verändert Variablen in Form1

Beitrag von Aloxen »

Hallo zusammen,
Ich bin mitten in OpenGl und ich möchte durch den Klick auf eine MenuItem ein zweites Fenster öffnen. In diesem Fenster soll man Werte eintragen können und durch den Buttonklick sollen diese dann die Variable in Form1 überschreiben.

Das heißt es soll durch den Klick auf den Button in Form1 folgender Befehl ausgeführt werden:

Code: Alles auswählen

T_obj.Translate(strtofloat(Edit1.Text), strtofloat(Edit1.Text), strtofloat(Edit1.Text));
T_Obj ist eine Matrix aus Form1 und der Befehl Translate(kommt aus einer extra unit) verschiebt die Matrix. Edit1 bis 3 sind die Eingaben die der Nutzer in Form2 Machen kann. Form2 sieht so aus:
Bild_2022-10-05_175900998.png
Bild_2022-10-05_175900998.png (3.92 KiB) 1730 mal betrachtet
Geöffnet wird es in Form1 bisher wie folgt:

Code: Alles auswählen

procedure TForm1.PosCubeClick(Sender: TObject);
begin
  Form2.ShowModal;
end;  
Ich müsste theoretisch T_obj als globale Variable definieren, also noch vor implements. Wenn ich das machen und in Form2 Form1.T_obj.translate... schreibe sagt es das Form1 unbekannt ist. Wenn ich dann unit1 noch in den uses von Form2 aufnehme kommt die fehlermeldung circular unit references, da ich ja schon für das aufrufen von Form2 unit2 in unit1 verwendet habe.

Wie kann ich die circular unit reference umgehen?

Benutzeravatar
fliegermichl
Lazarusforum e. V.
Beiträge: 1430
Registriert: Do 9. Jun 2011, 09:42
OS, Lazarus, FPC: Lazarus Fixes FPC Stable
CPU-Target: 32/64Bit
Wohnort: Echzell

Re: Ein zweites Form wird aufgerufen und verändert Variablen in Form1

Beitrag von fliegermichl »

Das sind eigentlich zwei Probleme.

Zum ersten sollte man niemals fix den Variablennamen einer Instanz von TForm1 oder jeder anderen Formklasse verwenden, weil:

Code: Alles auswählen

procedure TForm1.ShowForm2;
var f : TForm2;
begin
 f := TForm2.Create(Application);
 f.ShowModal;
 f.Free;
end;
Wenn du jetzt in der Implementierung von TForm2 irgendwo auf die Variable Form2 zugreifst, kracht es.
Weil die Variable heisst f und ist in der Unit in der TForm2 definiert ist, unbekannt.

Die zirkuläre Referenz kann man auflösen, indem unit2 im implementation Teil von unit1 eingebunden wird.
unit1 kann dann wahlweise im interface oder implementation Teil von unit2 eingebunden werden.

Ich mache solche Beziehungen über mehrere Formulare meistens mit Schnittstellenmethoden.

So kann man z.B. in TForm2 eine function Execute(var x, y, z : Single) : boolean; implementieren, die dann etwa so ausschaut.

Code: Alles auswählen

function TForm2.Execute(var x, y, z : Single) : boolean;
begin
 editx.Text := FloatToStr(x);
 edity.Text := FloatToStr(y);
 editz.Text := FloatToStr(z);
 Result := ShowModal = mrOk; // Wenn der Anwender in Form2 auf abbrechen geklickt hat, soll nichts geändert werden
 if Result then
 begin
  x := TryStrToFloat(editx.Text);
  y := ... usw.
 end;
end;
Jetzt kannst du bei TForm1 Buttonclick die Instanz von TForm2 erzeugen, per Execute die Werte übergeben und abhängig davon, ob der Anwender auch den OK Button geklickt hat verwenden.

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

Re: Ein zweites Form wird aufgerufen und verändert Variablen in Form1

Beitrag von wp_xyz »

Ich denke, es ist hier ein Design-Fehler, dass Form2 (das mit den Input-Controls) irgendwas von Form1 (das mit dem OpenGL-Control) aufruft. Denn Form1 muss zwangsläufig Form2 erzeugen. Also Form1 braucht Form2 und Form2 braucht Form1... - eine schöne zirkuläre Referenz. Man kann sich zwar daran vorbeimogeln, indem man Form1 in die uses-Liste des Implementation-Abschnitts von Form2 schiebt. Aber irgendwann, wenn noch ein paar Units in Spiel kommen, wird das schnell unübersichtlich, und man weiß nicht mehr, wie man das beheben kann.

Ich würde die Aufgabenverteilung etwas ändern: Form1 erzeugt nach dem Menü-Klick das Form2. Der User gibt in Form2 nur die Daten ein. Wenn die Eingabe beendet ist, holt sich Form1 von Form2 die eingegebenen Daten und speichert sie in eigenen Variablen, schließt Form2 wieder und verwendet die eigenen Variablen, um das OpenGL-Control zu aktualisieren. Der Punkt ist: Das OpenGL-Control gehört zum Form1, und daher kümmert sich Form1 um dessen Aktualisierung, nicht Form2. Form2 ist ein reiner Datenlieferant und weiß im Idealfall gar nicht, dass Form1 ein OpenGLControl hat.

[EDIT] Ich sehe gerade, fliegermichl hat das so ähnlich schon geschrieben...

Benutzeravatar
Winni
Beiträge: 1577
Registriert: Mo 2. Mär 2009, 16:45
OS, Lazarus, FPC: Laz2.2.2, fpc 3.2.2
CPU-Target: 64Bit
Wohnort: Fast Dänemark

Re: Ein zweites Form wird aufgerufen und verändert Variablen in Form1

Beitrag von Winni »

Hi!

Wie schon gesagt:

Nicht umsonst sind circuläre uses zwischen zwei Formen nicht erlaubt und nur mit Tricks zu umgehen.
Wenn man nicht genau weiss, was man da treibt, kann man sehr viel Unfug treiben.

Form1 für die Action, Form2 als reiner Datenlieferant und die von Form1 benötigten Daten in den public Teil von Form2, so dass Du von Form1 darauf zugreifen kannst.


Winni

Aloxen
Beiträge: 58
Registriert: Di 31. Mai 2022, 17:40

Re: Ein zweites Form wird aufgerufen und verändert Variablen in Form1

Beitrag von Aloxen »

@fliegermichl Ich verstehe nicht ganz was diese Procedure machen soll. Das einzige wie ich von Form1 irgendwie die öffnung von Form2 steuern kann ist über MenuItems. Beim klick auf MenuItem1 soll dann dieser Code ausgeführt werden und Form1 geöffnet werden:

Code: Alles auswählen

procedure TForm1.MenuItemClick;
var f : TForm2;
begin
 f := TForm2.Create(Application);
 f.ShowModal;
 f.Free;
end;
Daraufhin sollte sich Form2 öffnen. In Form zwei soll dann der execute Code stehen nehme ich an:

Code: Alles auswählen

function TForm2.Execute(var x, y, z : Single) : boolean;
begin
 editx.Text := FloatToStr(x);
 edity.Text := FloatToStr(y);
 editz.Text := FloatToStr(z);
 Result := ShowModal = mrOk; // Wenn der Anwender in Form2 auf abbrechen geklickt hat, soll nichts geändert werden
 if Result then
 begin
  x := TryStrToFloat(editx.Text);
  y := ... usw.
 end;
end;
Jetzt verstehe ich nicht ganz was der macht. editx.text Ist ja das was der nutzer eingeben soll. Wieso weise ich dem einen string wert zu? Wo genau steht die procedure und was gibt sie aus, woher kommen die Werte und wie fange ich sie in Form1 ab?

Aloxen
Beiträge: 58
Registriert: Di 31. Mai 2022, 17:40

Re: Ein zweites Form wird aufgerufen und verändert Variablen in Form1

Beitrag von Aloxen »

Ich hab gerade über ne zweite lösung nachgedacht. Wenn ich die Unit2 eh in Unit1 benutze, müsste ich doch auch den Sender von Button1 auf Tform2 abfangen können oder? Wie genau geht das?

Aloxen
Beiträge: 58
Registriert: Di 31. Mai 2022, 17:40

Re: Ein zweites Form wird aufgerufen und verändert Variablen in Form1

Beitrag von Aloxen »

Viel einfacher aber nicht schön. Nach f.Free kann ich ja davon ausgehen dass der Nutzer seine Werte in Form2 Eingetragen hat. Danach Lasse ich Form2.Edit1.Text abfragen. Das funktioniert. Ich würde den Text von den Edits auf "-" legen, damit wenn form1 versucht ein nicht geändertes Textfeld abzufragen nicht irgendwie ein Wert fälschlicher weise eingetragen wird. Das ist zwar nicht schön aber es funktioniert. Falls hier nochmal jemand antwortet und ne bessere Idee hat wäre ich sehr dankbar weil dass nicht der richtige Weg sein kann hihi

wennerer
Beiträge: 507
Registriert: Di 19. Mai 2015, 20:05
OS, Lazarus, FPC: Linux Mint 20 Cinnamon,Lazarus 2.2.6 (rev lazarus_2_2_6) FPC 3.2.2 x86_64-linux-
CPU-Target: x86_64-linux-gtk2

Re: Ein zweites Form wird aufgerufen und verändert Variablen in Form1

Beitrag von wennerer »

Hi Aloxen,
ich hänge dir mal ein Beispiel an. Ich hoffe das es dir was nützt.

Viele Grüße
Bernd
Dateianhänge
project1.zip
(105.66 KiB) 62-mal heruntergeladen

Aloxen
Beiträge: 58
Registriert: Di 31. Mai 2022, 17:40

Re: Ein zweites Form wird aufgerufen und verändert Variablen in Form1

Beitrag von Aloxen »

Nachtrag Funktioniert nicht. Nach dem Das Form2 beendet wurde wird der Wert und jede Variable gelöscht. bzw resetet :(

Aloxen
Beiträge: 58
Registriert: Di 31. Mai 2022, 17:40

Re: Ein zweites Form wird aufgerufen und verändert Variablen in Form1

Beitrag von Aloxen »

wennerer hat geschrieben:
Mi 5. Okt 2022, 22:05
Hi Aloxen,
ich hänge dir mal ein Beispiel an. Ich hoffe das es dir was nützt.

Viele Grüße
Bernd
Hallo Bernd. Die möglichkeit eines Custom Forms kannte ich noch nicht. Dadurch bleibt man auf einer unit und wenn die Forms nicht zu kompliziert werden gibt es ja kein Problem. Ich habe nur wenn ich mein Programm schließe eine Fehlermeldung:
Bild_2022-10-05_222023086.png
Bild_2022-10-05_222023086.png (8.69 KiB) 1674 mal betrachtet
Weißt du was das ist und wie ich das wegbekomme?

wennerer
Beiträge: 507
Registriert: Di 19. Mai 2015, 20:05
OS, Lazarus, FPC: Linux Mint 20 Cinnamon,Lazarus 2.2.6 (rev lazarus_2_2_6) FPC 3.2.2 x86_64-linux-
CPU-Target: x86_64-linux-gtk2

Re: Ein zweites Form wird aufgerufen und verändert Variablen in Form1

Beitrag von wennerer »

In den Projekteinstellung unter Debugger musst du den Haken bei Heaptrace entfernen.

Benutzeravatar
af0815
Lazarusforum e. V.
Beiträge: 6198
Registriert: So 7. Jan 2007, 10:20
OS, Lazarus, FPC: FPC fixes Lazarus fixes per fpcupdeluxe (win,linux,raspi)
CPU-Target: 32Bit (64Bit)
Wohnort: Burgenland
Kontaktdaten:

Re: Ein zweites Form wird aufgerufen und verändert Variablen in Form1

Beitrag von af0815 »

wennerer hat geschrieben:
Mi 5. Okt 2022, 22:29
In den Projekteinstellung unter Debugger musst du den Haken bei Heaptrace entfernen.
Das würde ich NICHT MACHEN, solange das Projekt nicht wirklich fertig ist. Damit verliert man viel an wertvoller Info.
Das habe ich in meiner Projektdatei ganz am ANfang stehen

Code: Alles auswählen

{$if declared(UseHeapTrace)}
  GlobalSkipIfNoLeaks := true; // supported as of debugger version 3.1.1
 {$endif}
Damit kommt das Fenster am Ende nur dann, wenn es notwendigt ist.

Mein volles Konstrukt ist hier, da wird auch noch auf eine Datei umgeleitet. Der Dateiname ist in der Konstanten co_heaptrace hinterlegt.

Code: Alles auswählen

{$if declared(UseHeapTrace)}
const
  co_heaptrc = 'heaptrace.trc';  // or use a filename of your choice, it resides in the same dir like the executable
{$endif}
begin
{$if declared(UseHeapTrace)}
// If you want to show heaptrc report dialog only if there were leaks
//   in your application, then put this command somewhere
//   in your main project source file:
  GlobalSkipIfNoLeaks := true; // supported as of debugger version 3.1.1
  if FileExists(co_heaptrc) then
      DeleteFile(co_heaptrc);
  SetHeapTraceOutput(co_heaptrc); // supported as of debugger version 3.1.1
  //   HaltOnError := false;             // dont halt a the end of the programm
{$endif}
....
....
end;
Blöd kann man ruhig sein, nur zu Helfen muss man sich wissen (oder nachsehen in LazInfos/LazSnippets).

charlytango
Beiträge: 843
Registriert: Sa 12. Sep 2015, 12:10
OS, Lazarus, FPC: Laz stable (2.2.6, 3.x)
CPU-Target: Win 32/64, Linux64
Wohnort: Wien

Re: Ein zweites Form wird aufgerufen und verändert Variablen in Form1

Beitrag von charlytango »

Aloxen hat geschrieben:
Mi 5. Okt 2022, 21:00
Jetzt verstehe ich nicht ganz was der macht. editx.text Ist ja das was der nutzer eingeben soll. Wieso weise ich dem einen string wert zu? Wo genau steht die procedure und was gibt sie aus, woher kommen die Werte und wie fange ich sie in Form1 ab?
ich versuche mich mal in einer Erklärung und hoffe meine Interpretation von @fliegermichl stimmt.:

Fliegermichl hat eine elegante Variante vorgeschlagen mit der in Form2 die aktuellen Werte von Form1.OpenGl1 (oder anderen Werten) in Form 2 angezeigt werden können. Der Benutzer kann die Werte in Form 2 ändern und bei Bestätigung der der Änderung durch drücken/clicken des Buttons (eben jeder der für das modale Formular als mrOK gekennzeichnet wurde) werden die relevanten Einstellungen über die als var definierten Variablen zurückgegeben und gleich an ihren Zielort geschrieben.

Dazu muss in TForm2 eine Prozedur Execute (als public) existieren die das leistet. Nämlich einerseits das hin und herschieben der Daten und andererseits die modale Anzeige des Form2

Aufgerufen wir das ganze aus Form1 (cave: unit der TForm2 muss in die uses-klausel von Form1) mit

Code: Alles auswählen

procedure TForm1.ShowForm2;
var f : TForm2;
begin
 f := TForm2.Create(Application);
 
 {übernimmt auch das Showmodal}
 f.Execute(OpenGl1.x, OpenGl1.y, OpenGl1.z);
  
 f.Free;
end;
Du kannst wahlweise auch auf die Art des Button-Clicks reagieren indem du es so formulierst:

Code: Alles auswählen

if not f.Execute(OpenGl1.x, OpenGl1.y, OpenGl1.z) then
  showmessage('Es wurde nichts geändert');

Antworten