TfloatSpinEdit richtig verwenden.

Für Fragen rund um die Ide und zum Debugger
Antworten
Marc
Lazarusforum e. V.
Beiträge: 208
Registriert: Fr 11. Nov 2016, 14:09
OS, Lazarus, FPC: Linux Mint 20 (WinXP VBox)
CPU-Target: 64Bit
Wohnort: Schweiz

TfloatSpinEdit richtig verwenden.

Beitrag von Marc »

Habe ein TfloatspinEdit auf einer Form.

Wenn ich mit dem Mausrad darüber rolle ändert sich der Wert. Alles gut.

Ich muss den Wert aber auch direkt eingeben können.
Dazu habe ich die Funktion von Onchange nach OnEditingDone verlegt
Hoffe das stimmt so?

Unschön, der Der Wert wird genauso dargestellt wie ich in eingegeben habe.
Müsste aber zwei Stellen haben nach dem Komma.
Das kriege ich dann erst, wenn ich wieder mit der Maus rolle.

Weiteres Problem, wenn ich im Formeditor auf den TfloatspinEdit doppelklicke dann steht der Funktionsaufruf auch wieder auf OnChange (zusätzlich zu OnEditingDone).

Mache da wohl was sehr verkehrt?
Good code comes from experience, experience comes from bad code.

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

Re: TfloatSpinEdit richtig verwenden.

Beitrag von wp_xyz »

Wenn du den Code, der ursprünglich bei OnChange und jetzt bei OnEditingDone ausgeführt wird, nicht zeigst, kann dir niemand sagen, ob das richtig ist oder falsch.

Ich tippe auf falsch (oder ich habe die Frage nicht verstanden...). Denn der eingetippte Wert wird automatisch mit der unter DecimalPlaces angegebenen Nachkommazahl dargestellt, wenn das Control den Fokus verliert. Also, du musst nicht mit dem Mausrad rollen, sondern nur ein anderes Control anklicken, was man ja automatisch macht, wenn man ein Formular ausfüllt.

Falls du während der Eingabe schon eine vorgegebene Stellenzahl anzeigen willst, wäre OnChange schon richtig. Aber ich würde von sowas dringend abraten: Wenn der User etwas eintippt, dann erwartet er, dass das Control das anzeigt, was eingegeben worden ist, und nicht, dass das Control selbständig etwas ändert, also z.B. Dezimalstellen anhängt. Die Prüfung der Eingabe wird unnötig erschwert, wenn nicht das dortsteht, was man getippt hat. Und wenn vielleicht auch noch der Cursor nach jedem eingetippten Zeichen an den Textanfang oder ans Textende springt, wird's kriminell. Solche Software ist mir äußerst suspekt und hat auf meiner Platte nur eine sehr kurze Verweildauer.

Marc
Lazarusforum e. V.
Beiträge: 208
Registriert: Fr 11. Nov 2016, 14:09
OS, Lazarus, FPC: Linux Mint 20 (WinXP VBox)
CPU-Target: 64Bit
Wohnort: Schweiz

Re: TfloatSpinEdit richtig verwenden.

Beitrag von Marc »

Danke für die Antwort.

Ich hab das mal hochgeladen.

Es ist irgendwie kein gutes Programm. Habe je eine Abfrage für 'On Change' und für 'Editing done'.
Die kommen sich aber gehörig in die Quere.

Spin edit ist ja Nett soweit,zumal ich schön angeben kann was der gültige Bereich sein soll und es mir die Buchstaben vom Leib hält. ;-)
Aber wichtig, ich muss auch eine vernünftige Eingabe machen können.
Dateianhänge
Calculator.tar.gz
(3.05 KiB) 70-mal heruntergeladen
Good code comes from experience, experience comes from bad code.

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

Re: TfloatSpinEdit richtig verwenden.

Beitrag von wp_xyz »

Was ist denn eine "vernünftige Eingabe"? Bitte definiere genau deine Anforderungen.

Ich rate mal: Das mit den Dezimalstellen im 1.Beitrag ist gar nicht das Problem. das Problem ist, dass du einen Wert eintippen willst, wobei während des Eintippens noch keine Berechnung erfolgen soll, erst nach dem Drücken von ENTER, oder dem Klick auf den Display-Button. Gleichzeitig soll es aber auch möglich sein, mit dem Mausrad, den Auf/Ab-Pfeiltasten oder den SpinButtons am SpinEdit den Wert schrittweise zu ändern - nun soll aber die Berechnung und Aktualisierung der Anzeige sofort erfolgen.

falls das so ist, hast du richtig erkannt, dass OnChange das Ereignis ist, in das du dich mit Berechnung und Anzeige reinhängen kannst. Das Problem ist aber, dass dieses Ereignis auch beim Eintippen des Wertes kommt, wodurch die Berechnung nach jedem Tastendruck und nicht erst nach ENTER bzw. dem Display-Button erfolgt.

Das kannst du verhindern, indem du eine Statusvariable einführst, die protokolliert, ob die Änderung durch Eintippen oder eine andere Ursache erfolgt ist. Im OnChange-Ereignis wird dann geprüft, ob diese Variable gesetzt ist; eine Verabeitung des OnChange-Codes erfolgt nur, wenn die Variable den gewünschten Wert hat.

Angenommen, diese Variable heißt "BerechnungErlaubt" und ist boolean. Dann schreibst du an den Beginn von OnChange, dass die Routine gleich wieder verlassen wird, wenn BerechnungErlaubt false ist:

Code: Alles auswählen

procedure TForm1.FltEdtWiderstandChange(Sender: TObject);
begin
  if not BerechnungErlaubt then
    exit;
...

 Nun musst du einen Handler für OnKeyDown schreiben, in dem du im Prinzip BerechnungErlaubt auf false setzen musst. Allerdings sollen auch die Auf/Ab-Pfeiltasten trotzdem die Berechnung starten, also muss in diesem Fall BerechnungErlaubt true werden:

Code: Alles auswählen

procedure TForm1.FltEdtWiderstandKeyDown(Sender: TObject; var Key: Word;
  Shift: TShiftState);
begin
  BerechnungErlaubt := (Key = VK_UP) or (Key = VK_DOWN)// VK_UP und VK_DOWN benötigen LCLType in "uses"
end;

Außerdem musst du dafür sorgen, dass BerechnungErlaubt im Normalfall true ist. Dazu kannst du im OnCreate-Ereignis des Formulars die Variable mit true initialisieren sowie am Ende jedes OnChange-Ereignisses wieder auf true zurücksetzen:

Code: Alles auswählen

procedure TForm1.Form1Create(Sender: TObject);
begin
  BerechnungErlaubt := true;   // alternativ: gleich bei der Deklaration auf true setzen
end;
 
procedure TForm1.FltEdtWiderstandChange(Sender: TObject);
begin
  if not BerechnungErlaubt then
    exit;
  // dein Code...
  BerechnungErlaubt := true;
end;

Zu einer Funktion "Berechnung":
Statt mit globalen Variablen zu arbeiten, übergib doch Widerstand und Spannung als Parameter:
 

Code: Alles auswählen

procedure Berechnung(Widerstand, Spannung: Real; out Strom: Real);
begin
  Strom := Spannung / Widerstand;
end;

Die globalen Variablen StromI, SpannungU und WiderstandR können dann entfallen.

Aufruf aus dem Formular, z.B. im OnChange

Code: Alles auswählen

procedure TForm1.FltEdtWiderstandChange(Sender: TObject);
var
  StromI: Real;
begin
  if not BerechnungErlaubt then
    exit;
 
  Berechnung(FltEdtWiderstand.value, StrToFloat(edtSpannung.Text), StromI);
  edtStrom.Text := Format('0.00', 1000*StromI)// faktor 1000 für mA.
 
  BerechnungErlaubt := true;
end;

Die Anzeige im edtStrom machst du am besten in Milliampere, sonst reichen die 2 Dezimalstellen nicht aus, um etwas zu erkennen.

Marc
Lazarusforum e. V.
Beiträge: 208
Registriert: Fr 11. Nov 2016, 14:09
OS, Lazarus, FPC: Linux Mint 20 (WinXP VBox)
CPU-Target: 64Bit
Wohnort: Schweiz

Re: TfloatSpinEdit richtig verwenden. (gelöst)

Beitrag von Marc »

Vielen Dank für die ausführliche und verständliche Antwort.

Jetzt müsste ich erstmal vernünftig vernünftig definieren. War wohl etwas schwammig. Muss in die Politik. :oops:

Habe es soweit abgeändert. Jetzt funktioniert es so wie es soll. Die Stromberechnung ist nur ein Beispiel zur Demo.
Das Spin Edit braucht man ja öfters.
Ich habe es nochmal hochgeladen (verbessert).

Nicht das ich jetzt zum Spass gefragt hätte. Nur das eigene Projekt ist schon etwas umfangreich.
Deshalb habe ich das Problem auf ein möglichst kleines Programm gelegt.
Geht besser zum probieren und andere können ev. auch lernen.
Dateianhänge
verbessert.tar.gz
(2.79 KiB) 80-mal heruntergeladen
Good code comes from experience, experience comes from bad code.

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

Re: TfloatSpinEdit richtig verwenden.

Beitrag von wp_xyz »

Da fehlt die lfm-Datei, die Session-Datei (.lps) kannst du weglassen. Als Minimum braucht man .pas, .lfm, .lpi und .lpr. Daher konnte ich nichts testen.

Eine Bemerkung noch: Du bist wahrscheinlich Anfänger, und wenn das keiner sagt, schleppst du diesen Fehler dein restliches Programmierleben mit dir herum: Ich sehe in deinem Code immer wieder Stellen wie:

Code: Alles auswählen

procedure TForm1.FltEdtWiderstandChange(Sender: TObject);
begin
   if not RechnenErlaubtStatus then begin
     form1.Memo1.Lines.Add('FloatSpinEdit : Berechnung nicht erlaubt');
     chkBerErlaubt.Checked := RechnenErlaubtStatus;
     exit;     //hier Prozedur verlassen
   End;
 
   form1.Memo1.Lines.Add('FloatSpinEdit : Berechnung ist erlaubt');
   form1.Memo1.Lines.Add('FloatSpinEdit OnChange : '+FormatFloat('###,##0.00',FltEdtWiderstand.Value));
 ...

Warum schreibst du vor "Memo1" immer "form1"? Die gezeigte Prozedur ist eine Methode der Klasse TForm1, das heißt, sie gilt für alle Variablen ("Instanzen"), deren Typ TForm1 ist. form1 ist aber eine Instanz. Damit schränkst du die Anwendbarkeit der Methode extrem ein, denn sie funktioniert nur noch, wenn eine Instanz form1 existiert. Schon wenn du der Instanz einen anderen Namen gibst, vielleicht "BerechnungsFormular", wirst du eine Überraschung (in der Regel Crash) erleben, denn form1 hat jetzt keine Bedeutung mehr, es müsste ja jetzt Berechnungsformlar anstatt form1 dastehen! Lässt du dagegen form1 weg - also nur: Memo1.Lines.Add(...) -, dann weiß das Formular automatisch, dass das das eigene Memo1 gemeint ist.

Marc
Lazarusforum e. V.
Beiträge: 208
Registriert: Fr 11. Nov 2016, 14:09
OS, Lazarus, FPC: Linux Mint 20 (WinXP VBox)
CPU-Target: 64Bit
Wohnort: Schweiz

Re: TfloatSpinEdit richtig verwenden.

Beitrag von Marc »

Danke nochmal.
Natürlich bin ich Anfänger, und wahrscheinlich nicht mal übermässig talentiert.
Der der Objektorientiert Groschen ist auch noch lange nicht gefallen.
Mir raucht der Kopf wenn ich ' Die gezeigte Prozedur ist eine Methode der Klasse TForm1, das heißt, sie gilt für alle Variablen ("Instanzen"), deren Typ TForm1 ist. form1 ist aber eine Instanz.' lese. Aua.
Aber ich versuche es zu lernen.
Ich habe gerade versucht form1. wegzulassen. Dann kriege ich eine Fehlermeldung 'Identifier not found Memo1'
Dateianhänge
verbessert2.tar.gz
(2.8 KiB) 75-mal heruntergeladen
Good code comes from experience, experience comes from bad code.

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

Re: TfloatSpinEdit richtig verwenden.

Beitrag von wp_xyz »

Marc hat geschrieben:Ich habe gerade versucht form1. wegzulassen. Dann kriege ich eine Fehlermeldung 'Identifier not found Memo1'

Wieso? Geht doch? Oder ist der Fehler nicht in dem geschickten Code?

Ich könnte mir vorstellen, dass du das form1 aus der Prozedur "Berechnung" gelöscht hast. Gerade hier darfst du es nicht löschen, denn das ist keine Methode von TForm1, weil sie nicht zwischen class und end deklariert und in der Implementierung nicht den Klassennamen vorangestellt hat. Also

Code: Alles auswählen

type
  TForm1 = class(TForm)
     Memo1: TMemo;
     procedure BerechnungAlsMethode;
  end;
 
procedure BerechnungAlsProzedur;

Und ohne das vorangestellte form1 kennt die Prozedur das Memo1 nicht. Ist Berechnung dagegen als Methode (so wie oben die BerechnungAlsMethode) deklariert, dann gehört die Prozedur zu TForm1 und kann auf alles zugreifen, was zu TForm1 gehört. Das hat man so gemacht, um zu verhindern, dass man von außen in das teilweise komplizierte und genau aufeinander abgestimmte Zusammenspiel der Objekte eingreifen kann.

Wie schon im vorigen Beitrag bemerkt, würde ich die Berechnungs-Prozedur so gestalten, dass sie überhaupt nichts aus dem Formular verwendet. Die Berechnung von Strom durch einen Widerstand hängt nur vom Widerstand und der Spannung ab, aber nicht von irgendwelchen Memos auf Formularen. Dann kannst du der Prozedur einen Satz Parameter geben und du sparst dir die unnötigen globalen Variablen StromI, SpannungU und WiderstandR. Globale Variablen machen dir irgendwann Probleme, weil du sie immer und überall ändern kannst. Aber wahrscheinlich willst du in der Berechnungsprozedur auch einen Protokolleintrag für das Memo haben. Das ist etwas anderes. Hier ist kein physikalisches Gesetz nötig, sondern das ist eine Methode des Formulars.

Insgesamt würde ich das in eine Berechnungsprozedur (das physikalische Gesetz) und eine Protokollierungsmethode aufspalten:

Code: Alles auswählen

type
  TForm1 = class(TForm)
    procedure BerechnungMitProtokoll;
    ...
  end;
 
procedure Berechnung(var I: Real; U, R: Real)// Keine Methode. Die Werte aus dem Formular kommen über die Parameter.
begin
  I := U / R;
end;
 
procedure TForm1.BerechnungMitProtokoll;   // Methode, weil sie nur im Zusammenhang mit dem Formular funktioniert.
var
  U, I, R: real;
begin
  R := FltEdtWiderstand.value;
  U := StrToFloat(edtSpannung.Text);
  Memo1.Lines.Add('Berechnung mit Wert : '+FormatFloat('###,##0.00',R));
  Berechnung(I, U, R);
  ...
end;
 
procedure TForm1.FltEdtWiderstandChange(Sender: TObject);
begin
  ...
  BerechnungMitProtokoll;
end;

Und zum Schluss noch ein Vorschlag. Dir muss immer klar sein, dass du heute dein Programm wunderbar verstehst, in ein paar Monaten sind aber alle Details weg. Und da stößt du beim Durchsehen auf eine Prozedur namens "Berechung" - du wirst dich fragen: Was habe ich da berechnet? Nenne das ganze doch "StromBerechnung" und mache es zu einer Funktion, dann weiß man gleich bescheid:

Code: Alles auswählen

function StromBerechnung(R, U: real): Real;
begin
  Result := U/R;
end;

Marc
Lazarusforum e. V.
Beiträge: 208
Registriert: Fr 11. Nov 2016, 14:09
OS, Lazarus, FPC: Linux Mint 20 (WinXP VBox)
CPU-Target: 64Bit
Wohnort: Schweiz

Re: TfloatSpinEdit richtig verwenden.

Beitrag von Marc »

Danke für die Geduld. :roll:
Hast schon viel Zeit investiert.

Genauso wars.
Habe nochmal alles geändert.
Einzig, ich lasse die Variablen mal global. Ist für Dummies übersichtlicher.
Das 'U := StrToFloat(edtSpannung.text); ' wollte auch nicht. Gab 'invalid float ' Fehlermeldung.
Habe das mit 'U := SpannungU;' umschifft.
Dateianhänge
verbessert3.tar.gz
(3.06 KiB) 81-mal heruntergeladen
Good code comes from experience, experience comes from bad code.

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

Re: TfloatSpinEdit richtig verwenden.

Beitrag von wp_xyz »

Ein letztes, bevor ich mich ausklinke: Die formalen Parameter einer Funktion können beim Aufruf natürlich anders heißen. Wichtig ist nur die Reihenfolge. Also: Wenn die Funktion StromBerechung so deklariert ist

Code: Alles auswählen

function StromBerechnung(U, R: Real): Real;

dann muss beim Aufruf an erster Stelle nicht eine Variable namens "U" stehen, sondern du kannst natürlich auch gleich deine globale Spannung, SpannungU, einsetzen. Und genauso für R. Und das Ergebnis muss nicht in einer Variablen I zwischengespeichert werden, bevor du das in die eigentliche Stromvariable StromI kopierst.

Die Methode BerechnungMitProtokoll ist dann einfacher so:

Code: Alles auswählen

procedure TForm1.BerechnungMitProtokoll;   // Methode, weil sie nur im Zusammenhang mit dem Formular funktioniert.
begin
  Memo1.Lines.Add('Berechnung mit Wert : '+FormatFloat('###,##0.00', WiderstandR));
  StromI := StromBerechnung(SpannungU, WiderstandR);
  btnDisplayClick(nil);     // Macht weniger als btnDisplay.Click
end;

Antworten